diff options
Diffstat (limited to 'usr/src/cmd/rcap')
| -rw-r--r-- | usr/src/cmd/rcap/common/rcapd.h | 35 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/common/rcapd_stat.h | 12 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/common/utils.c | 81 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/common/utils.h | 14 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapadm/Makefile | 9 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapadm/rcapadm.c | 61 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapd/Makefile.rcapd | 4 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapd/rcapd_collection.c | 72 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapd/rcapd_collection_project.c | 53 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c | 99 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapd/rcapd_main.c | 862 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapd/rcapd_scanner.c | 31 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapstat/Makefile | 9 | ||||
| -rw-r--r-- | usr/src/cmd/rcap/rcapstat/rcapstat.c | 64 |
14 files changed, 955 insertions, 451 deletions
diff --git a/usr/src/cmd/rcap/common/rcapd.h b/usr/src/cmd/rcap/common/rcapd.h index 89cf5f3d81..7a554c213b 100644 --- a/usr/src/cmd/rcap/common/rcapd.h +++ b/usr/src/cmd/rcap/common/rcapd.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -58,7 +57,21 @@ extern "C" { #define LCST_CAP_REMOVED (1<<1) #define LCST_CAP_ZERO (1<<2) -typedef int64_t rcid_t; +typedef enum { + RCIDT_PROJECT, + RCIDT_ZONE +} rcid_type_t; + +typedef struct { + /* + * The following field could just be a rcid_type_t but it gets + * written out to a file as binary data for communication between + * 64-bit rcapd & 32-bit rcapstat, so we need to force a standard size + * and alignment here. + */ + uint64_t rcid_type; + int64_t rcid_val; +} rcid_t; typedef enum { LCU_COMPLETE, /* an enumeration of all possible collections */ @@ -138,7 +151,6 @@ typedef struct lcollection { uint64_t lcol_rss; /* RSS of all processes (kB) */ uint64_t lcol_image_size; /* image size of all processes (kB) */ uint64_t lcol_rss_cap; /* RSS cap (kB) */ - int lcol_stat_invalidate; /* flag to reset interval statistics */ lcollection_stat_t lcol_stat; /* statistics */ lcollection_stat_t lcol_stat_old; /* previous interval's statistics */ lprocess_t *lcol_lprocess; /* member processes */ @@ -162,12 +174,11 @@ typedef struct lcollection_report { extern int get_psinfo(pid_t, struct psinfo *, int, int(*)(void *, int), void *, lprocess_t *); -extern lcollection_t *lcollection_find(id_t); +extern lcollection_t *lcollection_find(rcid_t *); extern void lcollection_freq_move(lprocess_t *); -extern lcollection_t *lcollection_insert_update(rcid_t, uint64_t, char *, +extern lcollection_t *lcollection_insert_update(rcid_t *, uint64_t, char *, int *changes); extern int lcollection_member(lcollection_t *, lprocess_t *); -extern void lcollection_set_type(rctype_t); extern void lcollection_free(lcollection_t *); extern void lcollection_update(lcollection_update_type_t); extern void list_walk_collection(int (*)(lcollection_t *, void *), void *); @@ -178,12 +189,6 @@ extern void scan_abort(void); extern void check_update_statistics(void); /* - * The collection-specific function determining the collection ID from a - * process' psinfo. - */ -extern rcid_t(*rc_getidbypsinfo)(struct psinfo *); - -/* * Global (in rcapd only) variables. */ extern rcfg_t rcfg; diff --git a/usr/src/cmd/rcap/common/rcapd_stat.h b/usr/src/cmd/rcap/common/rcapd_stat.h index c34ceb36e2..fa769ba643 100644 --- a/usr/src/cmd/rcap/common/rcapd_stat.h +++ b/usr/src/cmd/rcap/common/rcapd_stat.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,7 +43,10 @@ extern "C" { */ #define RC_MODE_LEN 16 typedef struct rcapd_stat_hdr { - pid_t rs_pid; /* pid of producer */ + /* + * sizeof pid_t can vary, so we use a fixed 64-bit quantity. + */ + uint64_t rs_pid; /* pid of producer */ hrtime_t rs_time; /* time recorded */ /* diff --git a/usr/src/cmd/rcap/common/utils.c b/usr/src/cmd/rcap/common/utils.c index f9757a12f6..c01f568915 100644 --- a/usr/src/cmd/rcap/common/utils.c +++ b/usr/src/cmd/rcap/common/utils.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -260,3 +259,77 @@ xatoi(char *p) return (i); } } + +/* + * get_running_zones() calls zone_list(2) to find out how many zones are + * running. It then calls zone_list(2) again to fetch the list of running + * zones (stored in *zents). + */ +int +get_running_zones(uint_t *nzents, zone_entry_t **zents) +{ + zoneid_t *zids; + uint_t nzents_saved; + int i; + zone_entry_t *zentp; + zone_state_t zstate; + + *zents = NULL; + if (zone_list(NULL, nzents) != 0) { + warn(gettext("could not get zoneid list\n")); + return (E_ERROR); + } + +again: + if (*nzents == 0) + return (E_SUCCESS); + + if ((zids = (zoneid_t *)calloc(*nzents, sizeof (zoneid_t))) == NULL) { + warn(gettext("out of memory: zones will not be capped\n")); + return (E_ERROR); + } + + nzents_saved = *nzents; + + if (zone_list(zids, nzents) != 0) { + warn(gettext("could not get zone list\n")); + free(zids); + return (E_ERROR); + } + if (*nzents != nzents_saved) { + /* list changed, try again */ + free(zids); + goto again; + } + + *zents = calloc(*nzents, sizeof (zone_entry_t)); + if (*zents == NULL) { + warn(gettext("out of memory: zones will not be capped\n")); + free(zids); + return (E_ERROR); + } + + zentp = *zents; + for (i = 0; i < *nzents; i++) { + char name[ZONENAME_MAX]; + + if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { + warn(gettext("could not get name for " + "zoneid %d\n"), zids[i]); + continue; + } + + (void) strlcpy(zentp->zname, name, sizeof (zentp->zname)); + zentp->zid = zids[i]; + if (zone_get_state(name, &zstate) != Z_OK || + zstate != ZONE_STATE_RUNNING) + continue; + + + zentp++; + } + *nzents = zentp - *zents; + + free(zids); + return (E_SUCCESS); +} diff --git a/usr/src/cmd/rcap/common/utils.h b/usr/src/cmd/rcap/common/utils.h index 678dee51ab..f952d59bbb 100644 --- a/usr/src/cmd/rcap/common/utils.h +++ b/usr/src/cmd/rcap/common/utils.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,6 +32,7 @@ #include <libintl.h> #include <stdarg.h> #include <time.h> +#include <libzonecfg.h> #ifdef __cplusplus extern "C" { @@ -63,6 +63,11 @@ typedef enum rcm_dst { RCD_SYSLOG /* syslog() daemon facility */ } rcm_dst_t; +typedef struct zone_entry { + zoneid_t zid; + char zname[ZONENAME_MAX]; +} zone_entry_t; + #define LINELEN 256 /* max. message length */ #ifdef DEBUG @@ -95,6 +100,7 @@ extern void vdprintfe(int, char *, va_list); extern void dprintfe(int, char *, ...); extern void hrt2ts(hrtime_t, timestruc_t *); extern int xatoi(char *); +extern int get_running_zones(uint_t *, zone_entry_t **); #ifdef __cplusplus } diff --git a/usr/src/cmd/rcap/rcapadm/Makefile b/usr/src/cmd/rcap/rcapadm/Makefile index 59c1530185..3b4de32953 100644 --- a/usr/src/cmd/rcap/rcapadm/Makefile +++ b/usr/src/cmd/rcap/rcapadm/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -41,7 +40,7 @@ LINTSRCS = $(COMMON_DIR)/utils.c \ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -I$(COMMON_DIR) -LDLIBS += -lumem -ll -lscf +LDLIBS += -lumem -ll -lscf -lzonecfg LINTFLAGS += $(LDLIBS) -mnu diff --git a/usr/src/cmd/rcap/rcapadm/rcapadm.c b/usr/src/cmd/rcap/rcapadm/rcapadm.c index cc9fd290a1..1951682283 100644 --- a/usr/src/cmd/rcap/rcapadm/rcapadm.c +++ b/usr/src/cmd/rcap/rcapadm/rcapadm.c @@ -39,6 +39,8 @@ #include <libscf_priv.h> #include <libintl.h> #include <locale.h> +#include <zone.h> +#include <libzonecfg.h> #include "utils.h" #include "rcapd.h" @@ -61,7 +63,9 @@ usage() " [-c <percent>] " "# set memory cap\n" " " - "# enforcement threshold\n")); + "# enforcement threshold\n" + " [-z <zonename> -m <max-rss>] " + "# update zone memory cap\n")); exit(E_USAGE); } @@ -135,18 +139,54 @@ out: scf_handle_destroy(h); } +/* + * Update the in-kernel memory cap for the specified zone. + */ +static int +update_zone_mcap(char *zonename, char *maxrss) +{ + zoneid_t zone_id; + uint64_t num; + + if (getzoneid() != GLOBAL_ZONEID || zonecfg_in_alt_root()) + return (E_SUCCESS); + + /* get the running zone from the kernel */ + if ((zone_id = getzoneidbyname(zonename)) == -1) { + (void) fprintf(stderr, gettext("zone '%s' must be running\n"), + zonename); + return (E_ERROR); + } + + if (zonecfg_str_to_bytes(maxrss, &num) == -1) { + (void) fprintf(stderr, gettext("invalid max-rss value\n")); + return (E_ERROR); + } + + if (zone_setattr(zone_id, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { + (void) fprintf(stderr, gettext("could not set memory " + "cap for zone '%s'\n"), zonename); + return (E_ERROR); + } + + return (E_SUCCESS); +} + int main(int argc, char *argv[]) { char *subopts, *optval; int modified = 0; + boolean_t refresh = B_FALSE; int opt; + char *zonename; + char *maxrss = NULL; (void) setprogname("rcapadm"); (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); - while ((opt = getopt(argc, argv, "DEc:i:n")) != EOF) { + while ((opt = getopt(argc, argv, "DEc:i:m:nz:")) != EOF) { switch (opt) { case 'n': no_starting_stopping = 1; @@ -203,12 +243,24 @@ main(int argc, char *argv[]) } modified++; break; + case 'm': + maxrss = optarg; + break; + case 'z': + refresh = B_TRUE; + zonename = optarg; + break; default: usage(); } } - if (argc > optind) + /* the -z & -m options must be used together */ + if (argc > optind || (refresh && maxrss == NULL) || + (!refresh && maxrss != NULL)) + usage(); + + if (refresh && (no_starting_stopping > 0 || modified)) usage(); if (rcfg_read(fname, -1, &conf, NULL) < 0) { @@ -232,6 +284,9 @@ main(int argc, char *argv[]) } } + if (refresh) + return (update_zone_mcap(zonename, maxrss)); + if (modified) { if (pressure >= 0) conf.rcfg_memory_cap_enforcement_pressure = pressure; diff --git a/usr/src/cmd/rcap/rcapd/Makefile.rcapd b/usr/src/cmd/rcap/rcapd/Makefile.rcapd index 5fd0d01416..716ea41e38 100644 --- a/usr/src/cmd/rcap/rcapd/Makefile.rcapd +++ b/usr/src/cmd/rcap/rcapd/Makefile.rcapd @@ -35,6 +35,7 @@ SRCS = rcapd_main.c \ rcapd_collection.c \ rcapd_collection_project.c \ + rcapd_collection_zone.c \ rcapd_mapping.c \ rcapd_rfd.c \ rcapd_scanner.c \ @@ -44,6 +45,7 @@ SRCS = rcapd_main.c \ LINTSRCS = ../rcapd_main.c \ ../rcapd_collection.c \ ../rcapd_collection_project.c \ + ../rcapd_collection_zone.c \ ../rcapd_mapping.c \ ../rcapd_rfd.c \ ../rcapd_scanner.c \ @@ -53,7 +55,7 @@ LINTSRCS = ../rcapd_main.c \ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -DDEBUG_MSG CPPFLAGS += -I$(COMMON_DIR) -LDLIBS += -lkstat -ll -lproc -lproject -lumem +LDLIBS += -lkstat -ll -lproc -lproject -lzonecfg -lumem LDLIBS += $(EXTRA_LDLIBS) LINTFLAGS += -u diff --git a/usr/src/cmd/rcap/rcapd/rcapd_collection.c b/usr/src/cmd/rcap/rcapd/rcapd_collection.c index 7dac0e8155..fdaf8dbfe0 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_collection.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_collection.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -41,14 +40,16 @@ #define MAX(x, y) (((x) > (y)) ? (x) : (y)) typedef struct { - rcid_t lfa_colid; + rcid_t *lfa_colidp; lcollection_t *lfa_found; } lcollection_find_arg_t; extern void lcollection_update_project(lcollection_update_type_t, - void(*)(char *, int, uint64_t, int)); -extern void lcollection_set_type_project(); -static void lcollection_update_notification_cb(char *, int, uint64_t, int); + void(*)(char *, char *, int, uint64_t, int)); +extern void lcollection_update_zone(lcollection_update_type_t, + void(*)(char *, char *, int, uint64_t, int)); +static void lcollection_update_notification_cb(char *, char *, int, uint64_t, + int); rcid_t(*rc_getidbypsinfo)(psinfo_t *); uint64_t phys_total = 0; @@ -57,28 +58,8 @@ static lcollection_t *lcollection_head = NULL; void lcollection_update(lcollection_update_type_t ut) { - if (rcfg.rcfg_mode == rctype_project) - lcollection_update_project(ut, - lcollection_update_notification_cb); - else - die(gettext("unknown mode %s\n"), rcfg.rcfg_mode_name); -} - -/* - * Configure which collection type will be used. - */ -void -lcollection_set_type(rctype_t type) -{ - switch (type) { - case rctype_project: - lcollection_set_type_project(); - break; - default: - /* can't happen */ - die(gettext("unknown mode %d\n"), type); - /*NOTREACHED*/ - } + lcollection_update_zone(ut, lcollection_update_notification_cb); + lcollection_update_project(ut, lcollection_update_notification_cb); } /* @@ -93,7 +74,7 @@ lcollection_set_type(rctype_t type) * LCSS_CAP_ZERO */ lcollection_t * -lcollection_insert_update(rcid_t colid, uint64_t rss_cap, char *name, +lcollection_insert_update(rcid_t *colidp, uint64_t rss_cap, char *name, int *changes) { lcollection_t *lcol; @@ -103,7 +84,7 @@ lcollection_insert_update(rcid_t colid, uint64_t rss_cap, char *name, if (rss_cap == 0) *changes |= LCST_CAP_ZERO; - lcol = lcollection_find(colid); + lcol = lcollection_find(colidp); /* * If the specified collection is capped, add it to lcollection. @@ -120,12 +101,13 @@ lcollection_insert_update(rcid_t colid, uint64_t rss_cap, char *name, lcol = malloc(sizeof (*lcol)); if (lcol == NULL) { debug("not enough memory to monitor %s %s", - rcfg.rcfg_mode_name, name); + (colidp->rcid_type == RCIDT_PROJECT ? + "project" : "zone"), name); return (NULL); } (void) bzero(lcol, sizeof (*lcol)); - lcol->lcol_id = colid; + lcol->lcol_id = *colidp; debug("added collection %s\n", name); lcol->lcol_prev = NULL; lcol->lcol_next = lcollection_head; @@ -157,8 +139,8 @@ lcollection_insert_update(rcid_t colid, uint64_t rss_cap, char *name, } static void -lcollection_update_notification_cb(char *name, int changes, uint64_t rss_cap, - int mark) +lcollection_update_notification_cb(char *col_type, char *name, int changes, + uint64_t rss_cap, int mark) { /* * Assume the collection has been updated redundantly if its mark count @@ -168,10 +150,10 @@ lcollection_update_notification_cb(char *name, int changes, uint64_t rss_cap, return; if (changes & LCST_CAP_ZERO) - debug("%s %s: %s\n", rcfg.rcfg_mode_name, name, + debug("%s %s: %s\n", col_type, name, (changes & LCST_CAP_REMOVED) ? "cap removed" : "uncapped"); else - debug("%s %s: cap: %llukB\n", rcfg.rcfg_mode_name, name, + debug("%s %s: cap: %llukB\n", col_type, name, (unsigned long long)rss_cap); } @@ -215,19 +197,23 @@ lcollection_member(lcollection_t *lcol, lprocess_t *lpc) static int lcollection_find_cb(lcollection_t *lcol, void *arg) { - if (lcol->lcol_id == ((lcollection_find_arg_t *)arg)->lfa_colid) { + rcid_t *colidp = ((lcollection_find_arg_t *)arg)->lfa_colidp; + + if (lcol->lcol_id.rcid_type == colidp->rcid_type && + lcol->lcol_id.rcid_val == colidp->rcid_val) { ((lcollection_find_arg_t *)arg)->lfa_found = lcol; return (1); - } else - return (0); + } + + return (0); } lcollection_t * -lcollection_find(id_t colid) +lcollection_find(rcid_t *colidp) { lcollection_find_arg_t lfa; - lfa.lfa_colid = colid; + lfa.lfa_colidp = colidp; lfa.lfa_found = NULL; list_walk_collection(lcollection_find_cb, &lfa); diff --git a/usr/src/cmd/rcap/rcapd/rcapd_collection_project.c b/usr/src/cmd/rcap/rcapd/rcapd_collection_project.c index ba34100f05..eab6d2a94a 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_collection_project.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_collection_project.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -38,24 +37,17 @@ /* round up to next y = 2^n */ #define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) -static rcid_t rc_proj_getidbypsinfo(psinfo_t *); - -void -lcollection_set_type_project(void) -{ - rc_getidbypsinfo = rc_proj_getidbypsinfo; -} - static int lcollection_update_project_cb(const struct project *proj, void *walk_data) { - void(*update_notification_cb)(char *, int, uint64_t, int) = - (void(*)(char *, int, uint64_t, int))walk_data; + void(*update_notification_cb)(char *, char *, int, uint64_t, int) = + (void(*)(char *, char *, int, uint64_t, int))walk_data; char *capattr_abs; char *end; int changes; int64_t max_rss; lcollection_t *lcol; + rcid_t colid; capattr_abs = strstr(proj->pj_attr, PJ_ABS_ATTR_NAME "="); if (capattr_abs != NULL) { @@ -70,17 +62,19 @@ lcollection_update_project_cb(const struct project *proj, void *walk_data) capattr_abs += strlen(PJ_ABS_ATTR_NAME "="); max_rss = ROUNDUP(strtoll(capattr_abs, &end, 10), 1024) / 1024; if (end == capattr_abs || *end != ';' && *end != 0) - warn(gettext("%s %s: malformed %s value " - "'%s'\n"), rcfg.rcfg_mode_name, proj->pj_name, - PJ_ABS_ATTR_NAME, capattr_abs); + warn(gettext("project %s: malformed %s value '%s'\n"), + proj->pj_name, PJ_ABS_ATTR_NAME, capattr_abs); } else max_rss = 0; - lcol = lcollection_insert_update(proj->pj_projid, max_rss, - proj->pj_name, &changes); + colid.rcid_type = RCIDT_PROJECT; + colid.rcid_val = proj->pj_projid; + + lcol = lcollection_insert_update(&colid, max_rss, proj->pj_name, + &changes); if (update_notification_cb != NULL) - update_notification_cb(proj->pj_name, changes, max_rss, (lcol != - NULL) ? lcol->lcol_mark : 0); + update_notification_cb("project", proj->pj_name, changes, + max_rss, (lcol != NULL) ? lcol->lcol_mark : 0); return (0); } @@ -101,10 +95,13 @@ lcollection_update_project_byid_cb(const projid_t id, void *walk_data) static int lcollection_update_onceactive_cb(lcollection_t *lcol, void *walk_data) { - void(*update_notification_cb)(char *, int, uint64_t, int) = - (void(*)(char *, int, uint64_t, int))walk_data; + void(*update_notification_cb)(char *, char *, int, uint64_t, int) = + (void(*)(char *, char *, int, uint64_t, int))walk_data; + + if (lcol->lcol_id.rcid_type != RCIDT_PROJECT) + return (0); - return (lcollection_update_project_byid_cb(lcol->lcol_id, + return (lcollection_update_project_byid_cb(lcol->lcol_id.rcid_val, (void *)update_notification_cb)); } @@ -125,7 +122,7 @@ project_walk_all(int(*cb)(const struct project *, void *), void *walk_data) void lcollection_update_project(lcollection_update_type_t ut, - void(*update_notification_cb)(char *, int, uint64_t, int)) + void(*update_notification_cb)(char *, char *, int, uint64_t, int)) { switch (ut) { case LCU_ACTIVE_ONLY: @@ -154,9 +151,3 @@ lcollection_update_project(lcollection_update_type_t ut, (void *)update_notification_cb); } } - -static rcid_t -rc_proj_getidbypsinfo(psinfo_t *psinfo) -{ - return (psinfo->pr_projid); -} diff --git a/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c b/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c new file mode 100644 index 0000000000..db86aa6276 --- /dev/null +++ b/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c @@ -0,0 +1,99 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <procfs.h> +#include <project.h> +#include <stdlib.h> +#include <strings.h> +#include <zone.h> +#include <libzonecfg.h> +#include "rcapd.h" +#include "utils.h" + +extern boolean_t gz_capped; + + /* round up to next y = 2^n */ +#define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) + +static void +update_zone(zone_entry_t *zent, void *walk_data) +{ + void(*update_notification_cb)(char *, char *, int, uint64_t, int) = + (void(*)(char *, char *, int, uint64_t, int))walk_data; + int changes; + int64_t max_rss; + uint64_t mcap; + lcollection_t *lcol; + rcid_t colid; + + if (zone_getattr(zent->zid, ZONE_ATTR_PHYS_MCAP, &mcap, + sizeof (mcap)) != -1 && mcap != 0) + max_rss = ROUNDUP(mcap, 1024) / 1024; + else + max_rss = 0; + + if (zent->zid == GLOBAL_ZONEID) { + if (max_rss > 0) + gz_capped = B_TRUE; + else + gz_capped = B_FALSE; + } + + + colid.rcid_type = RCIDT_ZONE; + colid.rcid_val = zent->zid; + + lcol = lcollection_insert_update(&colid, max_rss, zent->zname, + &changes); + if (update_notification_cb != NULL) + update_notification_cb("zone", zent->zname, changes, max_rss, + (lcol != NULL) ? lcol->lcol_mark : 0); +} + + +/* ARGSUSED */ +void +lcollection_update_zone(lcollection_update_type_t ut, + void(*update_notification_cb)(char *, char *, int, uint64_t, int)) +{ + int i; + uint_t nzents; + zone_entry_t *zents; + + /* + * Enumerate running zones. + */ + if (get_running_zones(&nzents, &zents) != 0) + return; + + for (i = 0; i < nzents; i++) { + update_zone(&zents[i], (void *)update_notification_cb); + + } + + free(zents); +} diff --git a/usr/src/cmd/rcap/rcapd/rcapd_main.c b/usr/src/cmd/rcap/rcapd/rcapd_main.c index 9c2e8b3c48..960065826e 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_main.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_main.c @@ -61,6 +61,7 @@ #include <unistd.h> #include <zone.h> #include <assert.h> +#include <sys/vm_usage.h> #include "rcapd.h" #include "rcapd_mapping.h" #include "rcapd_rfd.h" @@ -80,30 +81,42 @@ #define STAT_TEMPLATE_SUFFIX ".XXXXXX" /* suffix of mkstemp() arg */ #define DAEMON_UID 1 /* uid to use */ +#define CAPPED_PROJECT 0x01 +#define CAPPED_ZONE 0x02 + typedef struct soft_scan_arg { uint64_t ssa_sum_excess; int64_t ssa_scan_goal; + boolean_t ssa_project_over_cap; } soft_scan_arg_t; +typedef struct sample_col_arg { + boolean_t sca_any_over_cap; + boolean_t sca_project_over_cap; +} sample_col_arg_t; + + static int debug_mode = 0; /* debug mode flag */ static pid_t rcapd_pid; /* rcapd's pid to ensure it's not */ /* scanned */ static kstat_ctl_t *kctl; /* kstat chain */ -static uint64_t new_sp = 0, old_sp = 0; /* measure delta in page scan count */ -static int enforce_caps = 0; /* cap enforcement flag, dependent on */ - /* enforce_soft_caps and */ - /* global_scanner_running */ -static int enforce_soft_caps = 0; /* soft cap enforcement flag, */ - /* depending on memory pressure */ static int memory_pressure = 0; /* physical memory utilization (%) */ static int memory_pressure_sample = 0; /* count of samples */ -static int global_scanner_running = 0; /* global scanning flag, to avoid */ - /* interference with kernel's page */ - /* scanner */ +static long page_size_kb = 0; /* system page size in KB */ +static size_t nvmu_vals = 0; /* # of kernel RSS/swap vals in array */ +static size_t vmu_vals_len = 0; /* size of RSS/swap vals array */ +static vmusage_t *vmu_vals = NULL; /* snapshot of kernel RSS/swap values */ static hrtime_t next_report; /* time of next report */ static int termination_signal = 0; /* terminating signal */ +static zoneid_t my_zoneid = (zoneid_t)-1; +static lcollection_t *gz_col; /* global zone collection */ rcfg_t rcfg; +/* + * Updated when we re-read the collection configurations if this rcapd instance + * is running in the global zone and the global zone is capped. + */ +boolean_t gz_capped = B_FALSE; /* * Flags. @@ -116,9 +129,9 @@ static int verify_statistics(void); static int update_statistics(void); /* - * Checks if a process is marked 'system'. Returns zero only when it is not. + * Checks if a process is marked 'system'. Returns FALSE only when it is not. */ -static int +static boolean_t proc_issystem(pid_t pid) { char pc_clname[PC_CLNMSZ]; @@ -128,22 +141,43 @@ proc_issystem(pid_t pid) return (strcmp(pc_clname, "SYS") == 0); } else { debug("cannot get class-specific scheduling parameters; " - "assuming system process"); - return (-1); + "assuming system process\n"); + return (B_TRUE); } } -/* - * fname is the process name, for debugging messages, and unscannable is a flag - * indicating whether the process should be scanned. - */ static void -lprocess_insert_mark(pid_t pid, id_t colid, char *fname, int unscannable) +lprocess_insert_mark(psinfo_t *psinfop) { + pid_t pid = psinfop->pr_pid; + /* flag indicating whether the process should be scanned. */ + int unscannable = psinfop->pr_nlwp == 0; + rcid_t colid; lcollection_t *lcol; lprocess_t *lproc; - if ((lcol = lcollection_find(colid)) == NULL) + /* + * Determine which collection to put this process into. We only have + * to worry about tracking both zone and project capped processes if + * this rcapd instance is running in the global zone, since we'll only + * see processes in our own projects in a non-global zone. In the + * global zone, if the process belongs to a non-global zone, we only + * need to track it for the capped non-global zone collection. For + * global zone processes, we first attempt to put the process into a + * capped project collection. On the second pass into this function + * the projid will be cleared so we will just track the process for the + * global zone collection as a whole. + */ + if (psinfop->pr_zoneid == my_zoneid && psinfop->pr_projid != -1) { + colid.rcid_type = RCIDT_PROJECT; + colid.rcid_val = psinfop->pr_projid; + } else { + /* try to add to zone collection */ + colid.rcid_type = RCIDT_ZONE; + colid.rcid_val = psinfop->pr_zoneid; + } + + if ((lcol = lcollection_find(&colid)) == NULL) return; /* @@ -193,7 +227,8 @@ lprocess_insert_mark(pid_t pid, id_t colid, char *fname, int unscannable) if (lcollection_member(lcol, lproc)) { lprocess_t *cur = lcol->lcol_lprocess; debug("The collection %lld already has these members, " - "including me, %d!\n", (long long)lcol->lcol_id, + "including me, %d!\n", + (long long)lcol->lcol_id.rcid_val, (int)lproc->lpc_pid); while (cur != NULL) { debug("\t%d\n", (int)cur->lpc_pid); @@ -209,7 +244,10 @@ lprocess_insert_mark(pid_t pid, id_t colid, char *fname, int unscannable) lproc->lpc_prev = NULL; lcol->lcol_lprocess = lproc; - debug("tracking %d %d %s%s\n", (int)colid, (int)pid, fname, + debug("tracking %s %ld %d %s%s\n", + (colid.rcid_type == RCIDT_PROJECT ? "project" : "zone"), + (long)colid.rcid_val, + (int)pid, psinfop->pr_psargs, (lproc->lpc_unscannable != 0) ? " (not scannable)" : ""); lcol->lcol_stat.lcols_proc_in++; } @@ -328,22 +366,28 @@ get_psinfo(pid_t pid, psinfo_t *psinfo, int cached_fd, } /* - * Retrieve the collection membership of all processes in our zone, and update - * the psinfo of those non-system, non-zombie ones in collections. + * Retrieve the collection membership of all processes and update the psinfo of + * those non-system, non-zombie ones in collections. For global zone processes, + * we first attempt to put the process into a capped project collection. We + * also want to track the process for the global zone collection as a whole. */ static void proc_cb(const pid_t pid) { - static zoneid_t ours = (zoneid_t)-1; psinfo_t psinfo; - if (ours == (zoneid_t)-1) - ours = getzoneid(); - - if (get_psinfo(pid, &psinfo, -1, NULL, NULL, NULL) == 0 && - psinfo.pr_zoneid == ours) - lprocess_insert_mark(psinfo.pr_pid, rc_getidbypsinfo(&psinfo), - psinfo.pr_psargs, psinfo.pr_nlwp == 0); + if (get_psinfo(pid, &psinfo, -1, NULL, NULL, NULL) == 0) { + lprocess_insert_mark(&psinfo); + if (gz_capped && psinfo.pr_zoneid == GLOBAL_ZONEID) { + /* + * We also want to track this process for the global + * zone as a whole so add it to the global zone + * collection as well. + */ + psinfo.pr_projid = -1; + lprocess_insert_mark(&psinfo); + } + } } /* @@ -359,57 +403,149 @@ lprocess_update_psinfo_fd_cb(void *arg, int fd) } /* - * Update the RSS of processes in monitored collections. + * Get the system pagesize. */ -/*ARGSUSED*/ -static int -mem_sample_cb(lcollection_t *lcol, lprocess_t *lpc) +static void +get_page_size(void) { - psinfo_t psinfo; + page_size_kb = sysconf(_SC_PAGESIZE) / 1024; + debug("physical page size: %luKB\n", page_size_kb); +} + +static void +tm_fmt(char *msg, hrtime_t t1, hrtime_t t2) +{ + hrtime_t diff = t2 - t1; + + if (diff < MILLISEC) + debug("%s: %lld nanoseconds\n", msg, diff); + else if (diff < MICROSEC) + debug("%s: %.2f microseconds\n", msg, (float)diff / MILLISEC); + else if (diff < NANOSEC) + debug("%s: %.2f milliseconds\n", msg, (float)diff / MICROSEC); + else + debug("%s: %.2f seconds\n", msg, (float)diff / NANOSEC); +} + +/* + * Get the zone's & project's RSS from the kernel. + */ +static void +rss_sample(boolean_t my_zone_only, uint_t col_types) +{ + size_t nres; + size_t i; + uint_t flags; + hrtime_t t1, t2; - if (get_psinfo(lpc->lpc_pid, &psinfo, lpc->lpc_psinfo_fd, - lprocess_update_psinfo_fd_cb, lpc, lpc) == 0) { - lpc->lpc_rss = psinfo.pr_rssize; - lpc->lpc_size = psinfo.pr_size; + if (my_zone_only) { + flags = VMUSAGE_ZONE; } else { - if (errno == ENOENT) - debug("process %d finished\n", (int)lpc->lpc_pid); - else - debug("process %d: cannot read psinfo", - (int)lpc->lpc_pid); - lprocess_free(lpc); + flags = 0; + if (col_types & CAPPED_PROJECT) + flags |= VMUSAGE_PROJECTS; + if (col_types & CAPPED_ZONE && my_zoneid == GLOBAL_ZONEID) + flags |= VMUSAGE_ALL_ZONES; } - return (0); + debug("vmusage sample flags 0x%x\n", flags); + if (flags == 0) + return; + +again: + /* try the current buffer to see if the list will fit */ + nres = vmu_vals_len; + t1 = gethrtime(); + if (getvmusage(flags, my_zone_only ? 0 : rcfg.rcfg_rss_sample_interval, + vmu_vals, &nres) != 0) { + if (errno != EOVERFLOW) { + warn(gettext("can't read RSS from kernel\n")); + return; + } + } + t2 = gethrtime(); + tm_fmt("getvmusage time", t1, t2); + + debug("kernel nres %lu\n", (ulong_t)nres); + + if (nres > vmu_vals_len) { + /* array size is now too small, increase it and try again */ + free(vmu_vals); + + if ((vmu_vals = (vmusage_t *)calloc(nres, + sizeof (vmusage_t))) == NULL) { + warn(gettext("out of memory: could not read RSS from " + "kernel\n")); + vmu_vals_len = nvmu_vals = 0; + return; + } + vmu_vals_len = nres; + goto again; + } + + nvmu_vals = nres; + + debug("vmusage_sample\n"); + for (i = 0; i < nvmu_vals; i++) { + debug("%d: id: %d, type: 0x%x, rss_all: %llu (%lluKB), " + "swap: %llu\n", (int)i, (int)vmu_vals[i].vmu_id, + vmu_vals[i].vmu_type, + (unsigned long long)vmu_vals[i].vmu_rss_all, + (unsigned long long)vmu_vals[i].vmu_rss_all / 1024, + (unsigned long long)vmu_vals[i].vmu_swap_all); + } +} + +static void +update_col_rss(lcollection_t *lcol) +{ + int i; + + lcol->lcol_rss = 0; + lcol->lcol_image_size = 0; + + for (i = 0; i < nvmu_vals; i++) { + if (vmu_vals[i].vmu_id != lcol->lcol_id.rcid_val) + continue; + + if (vmu_vals[i].vmu_type == VMUSAGE_ZONE && + lcol->lcol_id.rcid_type != RCIDT_ZONE) + continue; + + if (vmu_vals[i].vmu_type == VMUSAGE_PROJECTS && + lcol->lcol_id.rcid_type != RCIDT_PROJECT) + continue; + + /* we found the right RSS entry, update the collection vals */ + lcol->lcol_rss = vmu_vals[i].vmu_rss_all / 1024; + lcol->lcol_image_size = vmu_vals[i].vmu_swap_all / 1024; + break; + } } /* * Sample the collection RSS, updating the collection's statistics with the - * results. + * results. Also, sum the rss of all capped projects & return true if + * the collection is over cap. */ -/*ARGSUSED*/ static int rss_sample_col_cb(lcollection_t *lcol, void *arg) { int64_t excess; uint64_t rss; + sample_col_arg_t *col_argp = (sample_col_arg_t *)arg; - /* - * If updating statistics for a new interval, reset the affected - * counters. - */ - if (lcol->lcol_stat_invalidate != 0) { - lcol->lcol_stat_old = lcol->lcol_stat; - lcol->lcol_stat.lcols_min_rss = (int64_t)-1; - lcol->lcol_stat.lcols_max_rss = 0; - lcol->lcol_stat_invalidate = 0; - } + update_col_rss(lcol); lcol->lcol_stat.lcols_rss_sample++; - excess = lcol->lcol_rss - lcol->lcol_rss_cap; rss = lcol->lcol_rss; - if (excess > 0) + excess = rss - lcol->lcol_rss_cap; + if (excess > 0) { lcol->lcol_stat.lcols_rss_act_sum += rss; + col_argp->sca_any_over_cap = B_TRUE; + if (lcol->lcol_id.rcid_type == RCIDT_PROJECT) + col_argp->sca_project_over_cap = B_TRUE; + } lcol->lcol_stat.lcols_rss_sum += rss; if (lcol->lcol_stat.lcols_min_rss > rss) @@ -421,6 +557,30 @@ rss_sample_col_cb(lcollection_t *lcol, void *arg) } /* + * Determine if we have capped projects, capped zones or both. + */ +static int +col_type_cb(lcollection_t *lcol, void *arg) +{ + uint_t *col_type = (uint_t *)arg; + + /* skip uncapped collections */ + if (lcol->lcol_rss_cap == 0) + return (1); + + if (lcol->lcol_id.rcid_type == RCIDT_PROJECT) + *col_type |= CAPPED_PROJECT; + else + *col_type |= CAPPED_ZONE; + + /* once we know everything is capped, we can stop looking */ + if ((*col_type & CAPPED_ZONE) && (*col_type & CAPPED_PROJECT)) + return (1); + + return (0); +} + +/* * Open /proc and walk entries. */ static void @@ -449,23 +609,6 @@ proc_walk_all(void (*cb)(const pid_t)) } /* - * Memory update callback. - */ -static int -memory_all_cb(lcollection_t *lcol, lprocess_t *lpc) -{ - debug_high("%s %s, pid %d: rss += %llu/%llu\n", rcfg.rcfg_mode_name, - lcol->lcol_name, (int)lpc->lpc_pid, - (unsigned long long)lpc->lpc_rss, - (unsigned long long)lpc->lpc_size); - ASSERT(lpc->lpc_rss <= lpc->lpc_size); - lcol->lcol_rss += lpc->lpc_rss; - lcol->lcol_image_size += lpc->lpc_size; - - return (0); -} - -/* * Clear unmarked callback. */ /*ARGSUSED*/ @@ -483,19 +626,6 @@ sweep_process_cb(lcollection_t *lcol, lprocess_t *lpc) } /* - * Memory clear callback. - */ -/*ARGSUSED*/ -static int -collection_zero_mem_cb(lcollection_t *lcol, void *arg) -{ - lcol->lcol_rss = 0; - lcol->lcol_image_size = 0; - - return (0); -} - -/* * Print, for debugging purposes, a collection's recently-sampled RSS and * excess. */ @@ -506,7 +636,8 @@ excess_print_cb(lcollection_t *lcol, void *arg) int64_t excess = lcol->lcol_rss - lcol->lcol_rss_cap; debug("%s %s rss/cap: %llu/%llu, excess = %lld kB\n", - rcfg.rcfg_mode_name, lcol->lcol_name, + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? "project" : "zone"), + lcol->lcol_name, (unsigned long long)lcol->lcol_rss, (unsigned long long)lcol->lcol_rss_cap, (long long)excess); @@ -516,6 +647,10 @@ excess_print_cb(lcollection_t *lcol, void *arg) /* * Scan those collections which have exceeded their caps. + * + * If we're running in the global zone it might have a cap. We don't want to + * do any capping for the global zone yet since we might get under the cap by + * just capping the projects in the global zone. */ /*ARGSUSED*/ static int @@ -523,6 +658,13 @@ scan_cb(lcollection_t *lcol, void *arg) { int64_t excess; + /* skip over global zone collection for now but keep track for later */ + if (lcol->lcol_id.rcid_type == RCIDT_ZONE && + lcol->lcol_id.rcid_val == GLOBAL_ZONEID) { + gz_col = lcol; + return (0); + } + if ((excess = lcol->lcol_rss - lcol->lcol_rss_cap) > 0) { scan(lcol, excess); lcol->lcol_stat.lcols_scan++; @@ -532,6 +674,37 @@ scan_cb(lcollection_t *lcol, void *arg) } /* + * Scan the global zone collection and see if it still exceeds its cap. + * We take into account the effects of capping any global zone projects here. + */ +static void +scan_gz(lcollection_t *lcol, boolean_t project_over_cap) +{ + int64_t excess; + + /* + * If we had projects over their cap and the global zone was also over + * its cap then we need to get the up-to-date global zone rss to + * determine if we are still over the global zone cap. We might have + * gone under while we scanned the capped projects. If there were no + * projects over cap then we can use the rss value we already have for + * the global zone. + */ + excess = lcol->lcol_rss - lcol->lcol_rss_cap; + if (project_over_cap && excess > 0) { + rss_sample(B_TRUE, CAPPED_ZONE); + update_col_rss(lcol); + excess = lcol->lcol_rss - lcol->lcol_rss_cap; + } + + if (excess > 0) { + debug("global zone excess %lldKB\n", (long long)excess); + scan(lcol, excess); + lcol->lcol_stat.lcols_scan++; + } +} + +/* * Do a soft scan of those collections which have excesses. A soft scan is one * in which the cap enforcement pressure is taken into account. The difference * between the utilized physical memory and the cap enforcement pressure will @@ -544,22 +717,72 @@ soft_scan_cb(lcollection_t *lcol, void *a) int64_t excess; soft_scan_arg_t *arg = a; + /* skip over global zone collection for now but keep track for later */ + if (lcol->lcol_id.rcid_type == RCIDT_ZONE && + lcol->lcol_id.rcid_val == GLOBAL_ZONEID) { + gz_col = lcol; + return (0); + } + if ((excess = lcol->lcol_rss - lcol->lcol_rss_cap) > 0) { - debug("col %lld excess %lld scan_goal %lld sum_excess %llu, " - "scanning %lld\n", (long long)lcol->lcol_id, + int64_t adjusted_excess = + excess * arg->ssa_scan_goal / arg->ssa_sum_excess; + + debug("%s %ld excess %lld scan_goal %lld sum_excess %llu, " + "scanning %lld\n", + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? + "project" : "zone"), + (long)lcol->lcol_id.rcid_val, (long long)excess, (long long)arg->ssa_scan_goal, (unsigned long long)arg->ssa_sum_excess, - (long long)(excess * arg->ssa_scan_goal / - arg->ssa_sum_excess)); + (long long)adjusted_excess); - scan(lcol, (int64_t)(excess * arg->ssa_scan_goal / - arg->ssa_sum_excess)); + scan(lcol, adjusted_excess); lcol->lcol_stat.lcols_scan++; } return (0); } +static void +soft_scan_gz(lcollection_t *lcol, void *a) +{ + int64_t excess; + soft_scan_arg_t *arg = a; + + /* + * If we had projects over their cap and the global zone was also over + * its cap then we need to get the up-to-date global zone rss to + * determine if we are still over the global zone cap. We might have + * gone under while we scanned the capped projects. If there were no + * projects over cap then we can use the rss value we already have for + * the global zone. + */ + excess = lcol->lcol_rss - lcol->lcol_rss_cap; + if (arg->ssa_project_over_cap && excess > 0) { + rss_sample(B_TRUE, CAPPED_ZONE); + update_col_rss(lcol); + excess = lcol->lcol_rss - lcol->lcol_rss_cap; + } + + if (excess > 0) { + int64_t adjusted_excess = + excess * arg->ssa_scan_goal / arg->ssa_sum_excess; + + debug("%s %ld excess %lld scan_goal %lld sum_excess %llu, " + "scanning %lld\n", + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? + "project" : "zone"), + (long)lcol->lcol_id.rcid_val, + (long long)excess, (long long)arg->ssa_scan_goal, + (unsigned long long)arg->ssa_sum_excess, + (long long)adjusted_excess); + + scan(lcol, adjusted_excess); + lcol->lcol_stat.lcols_scan++; + } +} + /* * When a scan could happen, but caps aren't enforced tick the * lcols_unenforced_cap counter. @@ -582,8 +805,7 @@ update_phys_total(void) uint64_t old_phys_total; old_phys_total = phys_total; - phys_total = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) - / 1024; + phys_total = (uint64_t)sysconf(_SC_PHYS_PAGES) * page_size_kb; if (phys_total != old_phys_total) debug("physical memory%s: %lluM\n", (old_phys_total == 0 ? "" : " adjusted"), (unsigned long long)(phys_total / 1024)); @@ -687,7 +909,9 @@ static int collection_sweep_cb(lcollection_t *lcol, void *arg) { if (lcol->lcol_mark == 0) { - debug("freeing %s %s\n", rcfg.rcfg_mode_name, lcol->lcol_name); + debug("freeing %s %s\n", + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? + "project" : "zone"), lcol->lcol_name); lcollection_free(lcol); } @@ -710,8 +934,6 @@ finish_configuration(void) rcfg.rcfg_mode_name = "project"; rcfg.rcfg_mode = rctype_project; } - - lcollection_set_type(rcfg.rcfg_mode); } /* @@ -754,7 +976,8 @@ reread_configuration_file(void) * deletions to cap definitions. */ static void -reconfigure(void) +reconfigure(hrtime_t now, hrtime_t *next_configuration, + hrtime_t *next_proc_walk, hrtime_t *next_rss_sample) { debug("reconfigure...\n"); @@ -770,6 +993,31 @@ reconfigure(void) list_walk_collection(collection_clear_cb, NULL); lcollection_update(LCU_ACTIVE_ONLY); /* mark */ list_walk_collection(collection_sweep_cb, NULL); + + *next_configuration = NEXT_EVENT_TIME(now, + rcfg.rcfg_reconfiguration_interval); + + /* + * Reset each event time to the shorter of the previous and new + * intervals. + */ + if (next_report == 0 && rcfg.rcfg_report_interval > 0) + next_report = now; + else + next_report = POSITIVE_MIN(next_report, + NEXT_REPORT_EVENT_TIME(now, rcfg.rcfg_report_interval)); + + if (*next_proc_walk == 0 && rcfg.rcfg_proc_walk_interval > 0) + *next_proc_walk = now; + else + *next_proc_walk = POSITIVE_MIN(*next_proc_walk, + NEXT_EVENT_TIME(now, rcfg.rcfg_proc_walk_interval)); + + if (*next_rss_sample == 0 && rcfg.rcfg_rss_sample_interval > 0) + *next_rss_sample = now; + else + *next_rss_sample = POSITIVE_MIN(*next_rss_sample, + NEXT_EVENT_TIME(now, rcfg.rcfg_rss_sample_interval)); } /* @@ -791,20 +1039,20 @@ static int simple_report_collection_cb(lcollection_t *lcol, void *arg) { #define DELTA(field) \ - (unsigned long long)(lcol->lcol_stat_invalidate ? 0 : \ + (unsigned long long)( \ (lcol->lcol_stat.field - lcol->lcol_stat_old.field)) -#define VALID(field) \ - (unsigned long long)(lcol->lcol_stat_invalidate ? 0 : \ - lcol->lcol_stat.field) debug("%s %s status: succeeded/attempted (k): %llu/%llu, " "ineffective/scans/unenforced/samplings: %llu/%llu/%llu/%llu, RSS " "min/max (k): %llu/%llu, cap %llu kB, processes/thpt: %llu/%llu, " - "%llu scans over %llu ms\n", rcfg.rcfg_mode_name, lcol->lcol_name, + "%llu scans over %llu ms\n", + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? "project" : "zone"), + lcol->lcol_name, DELTA(lcols_pg_eff), DELTA(lcols_pg_att), DELTA(lcols_scan_ineffective), DELTA(lcols_scan), DELTA(lcols_unenforced_cap), DELTA(lcols_rss_sample), - VALID(lcols_min_rss), VALID(lcols_max_rss), + (unsigned long long)lcol->lcol_stat.lcols_min_rss, + (unsigned long long)lcol->lcol_stat.lcols_max_rss, (unsigned long long)lcol->lcol_rss_cap, (unsigned long long)(lcol->lcol_stat.lcols_proc_in - lcol->lcol_stat.lcols_proc_out), DELTA(lcols_proc_out), @@ -812,7 +1060,6 @@ simple_report_collection_cb(lcollection_t *lcol, void *arg) / MILLISEC)); #undef DELTA -#undef VALID return (0); } @@ -838,13 +1085,11 @@ report_collection_cb(lcollection_t *lcol, void *arg) dc.lcol_stat = lcol->lcol_stat; if (write(fd, &dc, sizeof (dc)) == sizeof (dc)) { - /* - * Set a flag to indicate that the exported interval snapshot - * values should be reset at the next sample. - */ - lcol->lcol_stat_invalidate = 1; + lcol->lcol_stat_old = lcol->lcol_stat; } else { - debug("can't write %s %s statistics", rcfg.rcfg_mode_name, + debug("can't write %s %s statistics", + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? + "project" : "zone"), lcol->lcol_name); } @@ -871,8 +1116,9 @@ get_globally_scanned_pages(uint64_t *scannedp) if (kstat_read(kctl, ksp, NULL) != -1) { scanned += ((cpu_stat_t *) ksp->ks_data)->cpu_vminfo.scan; - } else + } else { return (-1); + } } } @@ -881,6 +1127,59 @@ get_globally_scanned_pages(uint64_t *scannedp) } /* + * Determine if the global page scanner is running, during which no memory + * caps should be enforced, to prevent interference with the global page + * scanner. + */ +static boolean_t +is_global_scanner_running() +{ + /* measure delta in page scan count */ + static uint64_t new_sp = 0; + static uint64_t old_sp = 0; + boolean_t res = B_FALSE; + + if (get_globally_scanned_pages(&new_sp) == 0) { + if (old_sp != 0 && (new_sp - old_sp) > 0) { + debug("global memory pressure detected (%llu " + "pages scanned since last interval)\n", + (unsigned long long)(new_sp - old_sp)); + res = B_TRUE; + } + old_sp = new_sp; + } else { + warn(gettext("unable to read cpu statistics")); + new_sp = old_sp; + } + + return (res); +} + +/* + * If soft caps are in use, determine if global memory pressure exceeds the + * configured maximum above which soft caps are enforced. + */ +static boolean_t +must_enforce_soft_caps() +{ + /* + * Check for changes to the amount of installed physical memory, to + * compute the current memory pressure. + */ + update_phys_total(); + + memory_pressure = 100 - (int)((sysconf(_SC_AVPHYS_PAGES) * page_size_kb) + * 100.0 / phys_total); + memory_pressure_sample++; + if (rcfg.rcfg_memory_cap_enforcement_pressure > 0 && + memory_pressure > rcfg.rcfg_memory_cap_enforcement_pressure) { + return (B_TRUE); + } + + return (B_FALSE); +} + +/* * Update the shared statistics file with each collection's current statistics. * Return zero on success. */ @@ -973,6 +1272,26 @@ sum_excess_cb(lcollection_t *lcol, void *arg) return (0); } +/* + * Compute the quantity of memory (in kilobytes) above the cap enforcement + * pressure. Set the scan goal to that quantity (or at most the excess). + */ +static void +compute_soft_scan_goal(soft_scan_arg_t *argp) +{ + /* + * Compute the sum of the collections' excesses, which will be the + * denominator. + */ + argp->ssa_sum_excess = 0; + list_walk_collection(sum_excess_cb, &(argp->ssa_sum_excess)); + + argp->ssa_scan_goal = MIN((sysconf(_SC_PHYS_PAGES) * + (100 - rcfg.rcfg_memory_cap_enforcement_pressure) / 100 - + sysconf(_SC_AVPHYS_PAGES)) * page_size_kb, + argp->ssa_sum_excess); +} + static void rcapd_usage(void) { @@ -1017,6 +1336,112 @@ verify_and_set_privileges(void) priv_freeset(required); } +/* + * This function does the top-level work to determine if we should do any + * memory capping, and if so, it invokes the right call-backs to do the work. + */ +static void +do_capping(hrtime_t now, hrtime_t *next_proc_walk) +{ + boolean_t enforce_caps; + /* soft cap enforcement flag, depending on memory pressure */ + boolean_t enforce_soft_caps; + /* avoid interference with kernel's page scanner */ + boolean_t global_scanner_running; + sample_col_arg_t col_arg; + soft_scan_arg_t arg; + uint_t col_types = 0; + + /* check what kind of collections (project/zone) are capped */ + list_walk_collection(col_type_cb, &col_types); + debug("collection types: 0x%x\n", col_types); + + /* no capped collections, skip checking rss */ + if (col_types == 0) + return; + + /* Determine if soft caps are enforced. */ + enforce_soft_caps = must_enforce_soft_caps(); + + /* Determine if the global page scanner is running. */ + global_scanner_running = is_global_scanner_running(); + + /* + * Sample collections' member processes RSSes and recompute + * collections' excess. + */ + rss_sample(B_FALSE, col_types); + + col_arg.sca_any_over_cap = B_FALSE; + col_arg.sca_project_over_cap = B_FALSE; + list_walk_collection(rss_sample_col_cb, &col_arg); + list_walk_collection(excess_print_cb, NULL); + debug("any collection/project over cap = %d, %d\n", + col_arg.sca_any_over_cap, col_arg.sca_project_over_cap); + + if (enforce_soft_caps) + debug("memory pressure %d%%\n", memory_pressure); + + /* + * Cap enforcement is determined by the previous conditions. + */ + enforce_caps = !global_scanner_running && col_arg.sca_any_over_cap && + (rcfg.rcfg_memory_cap_enforcement_pressure == 0 || + enforce_soft_caps); + + debug("%senforcing caps\n", enforce_caps ? "" : "not "); + + /* + * If soft caps are in use, determine the size of the portion from each + * collection to scan for. + */ + if (enforce_caps && enforce_soft_caps) + compute_soft_scan_goal(&arg); + + /* + * Victimize offending collections. + */ + if (enforce_caps && (!enforce_soft_caps || + (arg.ssa_scan_goal > 0 && arg.ssa_sum_excess > 0))) { + + /* + * Since at least one collection is over its cap & needs + * enforcing, check if it is at least time for a process walk + * (we could be well past time since we only walk /proc when + * we need to) and if so, update each collections process list + * in a single pass through /proc. + */ + if (EVENT_TIME(now, *next_proc_walk)) { + debug("scanning process list...\n"); + proc_walk_all(proc_cb); /* insert & mark */ + list_walk_all(sweep_process_cb); /* free dead procs */ + *next_proc_walk = NEXT_EVENT_TIME(now, + rcfg.rcfg_proc_walk_interval); + } + + gz_col = NULL; + if (enforce_soft_caps) { + debug("scan goal is %lldKB\n", + (long long)arg.ssa_scan_goal); + list_walk_collection(soft_scan_cb, &arg); + if (gz_capped && gz_col != NULL) { + /* process global zone */ + arg.ssa_project_over_cap = + col_arg.sca_project_over_cap; + soft_scan_gz(gz_col, &arg); + } + } else { + list_walk_collection(scan_cb, NULL); + if (gz_capped && gz_col != NULL) { + /* process global zone */ + scan_gz(gz_col, col_arg.sca_project_over_cap); + } + } + } else if (col_arg.sca_any_over_cap) { + list_walk_collection(unenforced_cap_cb, NULL); + } +} + int main(int argc, char *argv[]) { @@ -1029,9 +1454,6 @@ main(int argc, char *argv[]) hrtime_t next_proc_walk; /* time of next /proc scan */ hrtime_t next_configuration; /* time of next configuration */ hrtime_t next_rss_sample; /* (latest) time of next RSS sample */ - int old_enforce_caps; /* track changes in enforcement */ - /* conditions */ - soft_scan_arg_t arg; (void) set_message_priority(RCM_INFO); (void) setprogname("rcapd"); @@ -1125,13 +1547,6 @@ main(int argc, char *argv[]) next_configuration = NEXT_EVENT_TIME(gethrtime(), rcfg.rcfg_reconfiguration_interval); - if (rcfg.rcfg_memory_cap_enforcement_pressure == 0) { - /* - * Always enforce caps when strict caps are used. - */ - enforce_caps = 1; - } - /* * Open the kstat chain. */ @@ -1158,6 +1573,9 @@ main(int argc, char *argv[]) else debug("fd limit: unknown\n"); + get_page_size(); + my_zoneid = getzoneid(); + /* * Handle those signals whose (default) exit disposition * prevents rcapd from finishing scanning before terminating. @@ -1194,9 +1612,9 @@ main(int argc, char *argv[]) /* * Loop forever, monitoring collections' resident set sizes and - * enforcing their caps. Look for changes in caps and process - * membership, as well as responding to requests to reread the - * configuration. Update per-collection statistics periodically. + * enforcing their caps. Look for changes in caps as well as + * responding to requests to reread the configuration. Update + * per-collection statistics periodically. */ while (should_run != 0) { struct timespec ts; @@ -1210,9 +1628,10 @@ main(int argc, char *argv[]) } /* - * Update the process list once every proc_walk_interval. The - * condition of global memory pressure is also checked at the - * same frequency, if strict caps are in use. + * Check the configuration at every next_configuration interval. + * Update the rss data once every next_rss_sample interval. + * The condition of global memory pressure is also checked at + * the same frequency, if strict caps are in use. */ now = gethrtime(); @@ -1222,178 +1641,16 @@ main(int argc, char *argv[]) */ if (EVENT_TIME(now, next_configuration) || should_reconfigure == 1) { - reconfigure(); - next_configuration = NEXT_EVENT_TIME(now, - rcfg.rcfg_reconfiguration_interval); - - /* - * Reset each event time to the shorter of the - * previous and new intervals. - */ - if (next_report == 0 && - rcfg.rcfg_report_interval > 0) - next_report = now; - else - next_report = POSITIVE_MIN(next_report, - NEXT_REPORT_EVENT_TIME(now, - rcfg.rcfg_report_interval)); - if (next_proc_walk == 0 && - rcfg.rcfg_proc_walk_interval > 0) - next_proc_walk = now; - else - next_proc_walk = POSITIVE_MIN(next_proc_walk, - NEXT_EVENT_TIME(now, - rcfg.rcfg_proc_walk_interval)); - if (next_rss_sample == 0 && - rcfg.rcfg_rss_sample_interval > 0) - next_rss_sample = now; - else - next_rss_sample = POSITIVE_MIN(next_rss_sample, - NEXT_EVENT_TIME(now, - rcfg.rcfg_rss_sample_interval)); - + reconfigure(now, &next_configuration, &next_proc_walk, + &next_rss_sample); should_reconfigure = 0; - continue; - } - - if (EVENT_TIME(now, next_proc_walk)) { - debug("scanning process list...\n"); - proc_walk_all(proc_cb); /* mark */ - list_walk_all(sweep_process_cb); - next_proc_walk = NEXT_EVENT_TIME(now, - rcfg.rcfg_proc_walk_interval); } + /* + * Do the main work for enforcing caps. + */ if (EVENT_TIME(now, next_rss_sample)) { - /* - * Check for changes to the amount of installed - * physical memory, to compute the current memory - * pressure. - */ - update_phys_total(); - - /* - * If soft caps are in use, determine if global memory - * pressure exceeds the configured maximum above which - * soft caps are enforced. - */ - memory_pressure = 100 - - (int)((sysconf(_SC_AVPHYS_PAGES) * - (sysconf(_SC_PAGESIZE) / 1024)) * 100.0 / - phys_total); - memory_pressure_sample++; - if (rcfg.rcfg_memory_cap_enforcement_pressure > 0) { - if (memory_pressure > - rcfg.rcfg_memory_cap_enforcement_pressure) { - if (enforce_soft_caps == 0) { - debug("memory pressure %d%%\n", - memory_pressure); - enforce_soft_caps = 1; - } - } else { - if (enforce_soft_caps == 1) - enforce_soft_caps = 0; - } - } - - /* - * Determine if the global page scanner is running, - * while which no memory caps should be enforced, to - * prevent interference with the global page scanner. - */ - if (get_globally_scanned_pages(&new_sp) == 0) { - if (old_sp == 0) - /*EMPTY*/ - ; - else if ((new_sp - old_sp) > 0) { - if (global_scanner_running == 0) { - debug("global memory pressure " - "detected (%llu pages " - "scanned since last " - "interval)\n", - (unsigned long long) - (new_sp - old_sp)); - global_scanner_running = 1; - } - } else if (global_scanner_running == 1) { - debug("global memory pressure " - "relieved\n"); - global_scanner_running = 0; - } - old_sp = new_sp; - } else { - warn(gettext("kstat_read() failed")); - new_sp = old_sp; - } - - /* - * Cap enforcement is determined by the previous two - * conditions. - */ - old_enforce_caps = enforce_caps; - enforce_caps = - (rcfg.rcfg_memory_cap_enforcement_pressure == - 0 || enforce_soft_caps == 1) && - !global_scanner_running; - if (old_enforce_caps != enforce_caps) - debug("%senforcing caps\n", enforce_caps == 0 ? - "not " : ""); - - /* - * Sample collections' member processes' RSSes and - * recompute collections' excess. - */ - list_walk_all(mem_sample_cb); - list_walk_collection(collection_zero_mem_cb, NULL); - list_walk_all(memory_all_cb); - list_walk_collection(rss_sample_col_cb, NULL); - if (rcfg.rcfg_memory_cap_enforcement_pressure > 0) - debug("memory pressure %d%%\n", - memory_pressure); - list_walk_collection(excess_print_cb, NULL); - - /* - * If soft caps are in use, determine the size of the - * portion from each collection to scan for. - */ - if (enforce_soft_caps == 1) { - /* - * Compute the sum of the collections' - * excesses, which will be the denominator. - */ - arg.ssa_sum_excess = 0; - list_walk_collection(sum_excess_cb, - &arg.ssa_sum_excess); - - /* - * Compute the quantity of memory (in - * kilobytes) above the cap enforcement - * pressure. Set the scan goal to that - * quantity (or at most the excess). - */ - arg.ssa_scan_goal = MIN(( - sysconf(_SC_PHYS_PAGES) * (100 - - rcfg.rcfg_memory_cap_enforcement_pressure) - / 100 - sysconf(_SC_AVPHYS_PAGES)) * - (sysconf(_SC_PAGESIZE) / 1024), - arg.ssa_sum_excess); - } - - /* - * Victimize offending collections. - */ - if (enforce_caps == 1 && ((enforce_soft_caps == 1 && - arg.ssa_scan_goal > 0 && arg.ssa_sum_excess > 0) || - (enforce_soft_caps == 0))) - if (enforce_soft_caps == 1) { - debug("scan goal is %lldKB\n", - (long long)arg.ssa_scan_goal); - list_walk_collection(soft_scan_cb, - &arg); - } else - list_walk_collection(scan_cb, NULL); - else - list_walk_collection(unenforced_cap_cb, NULL); + do_capping(now, &next_proc_walk); next_rss_sample = NEXT_EVENT_TIME(now, rcfg.rcfg_rss_sample_interval); @@ -1409,7 +1666,6 @@ main(int argc, char *argv[]) */ now = gethrtime(); next = next_configuration; - next = POSITIVE_MIN(next, next_proc_walk); next = POSITIVE_MIN(next, next_report); next = POSITIVE_MIN(next, next_rss_sample); if (next > now && should_run != 0) { diff --git a/usr/src/cmd/rcap/rcapd/rcapd_scanner.c b/usr/src/cmd/rcap/rcapd/rcapd_scanner.c index 15c503d1b4..b39811b552 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_scanner.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_scanner.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -104,7 +103,8 @@ st_debug(st_debug_level_t level, lcollection_t *lcol, char *msg, ...) buf = malloc(len); if (buf == NULL) return; - (void) snprintf(buf, len, "%s %s scanner %s", rcfg.rcfg_mode_name, + (void) snprintf(buf, len, "%s %s scanner %s", + (lcol->lcol_id.rcid_type == RCIDT_PROJECT ? "project" : "zone"), lcol->lcol_name, msg); va_start(alist, msg); @@ -471,6 +471,7 @@ merge_current_pagedata(lprocess_t *lpc, { prpageheader_t *pghp; int mappings_changed = 0; + uint64_t cnt; if (lpc->lpc_pgdata_fd < 0 || get_pagedata(&pghp, lpc->lpc_pgdata_fd) != 0) { @@ -485,9 +486,12 @@ merge_current_pagedata(lprocess_t *lpc, debug("starting/resuming pagedata collection for %d\n", (int)lpc->lpc_pid); } - debug("process %d: %llu/%llukB r/m'd since last read\n", - (int)lpc->lpc_pid, (unsigned long long)count_pages(pghp, 0, - PG_MODIFIED | PG_REFERENCED, 0), (unsigned long long)lpc->lpc_rss); + + cnt = count_pages(pghp, 0, PG_MODIFIED | PG_REFERENCED, 0); + if (cnt != 0 || lpc->lpc_rss != 0) + debug("process %d: %llu/%llukB rfd/mdfd since last read\n", + (int)lpc->lpc_pid, (unsigned long long)cnt, + (unsigned long long)lpc->lpc_rss); if (lpc->lpc_prpageheader != NULL) { /* * OR the two snapshots. @@ -519,10 +523,12 @@ merge_current_pagedata(lprocess_t *lpc, } else mappings_changed = 1; lpc->lpc_prpageheader = pghp; - debug("process %d: %llu/%llukB r/m'd since hand swept\n", - (int)lpc->lpc_pid, (unsigned long long)count_pages(pghp, 0, - PG_MODIFIED | PG_REFERENCED, 0), - (unsigned long long)lpc->lpc_rss); + + cnt = count_pages(pghp, 0, PG_MODIFIED | PG_REFERENCED, 0); + if (cnt != 0 || lpc->lpc_rss != 0) + debug("process %d: %llu/%llukB rfd/mdfd since hand swept\n", + (int)lpc->lpc_pid, (unsigned long long)cnt, + (unsigned long long)lpc->lpc_rss); if (mappings_changed != 0) { debug("process %d: mappings changed\n", (int)lpc->lpc_pid); if (mappings_changed_cb != NULL) @@ -589,7 +595,6 @@ rss_delta(psinfo_t *new_psinfo, psinfo_t *old_psinfo, lprocess_t *vic) static void unignore_mappings(lprocess_t *lpc) { - debug("clearing ignored set\n"); lmapping_free(&lpc->lpc_ignore); } diff --git a/usr/src/cmd/rcap/rcapstat/Makefile b/usr/src/cmd/rcap/rcapstat/Makefile index 47b9bcfb71..fb436f5684 100644 --- a/usr/src/cmd/rcap/rcapstat/Makefile +++ b/usr/src/cmd/rcap/rcapstat/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -39,7 +38,7 @@ LINTSRCS = $(COMMON_DIR)/utils.c \ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -I$(COMMON_DIR) -LDLIBS += -lumem -ll +LDLIBS += -lumem -ll -lzonecfg LINTFLAGS += $(LDLIBS) -mnu diff --git a/usr/src/cmd/rcap/rcapstat/rcapstat.c b/usr/src/cmd/rcap/rcapstat/rcapstat.c index 722502d05d..47eca3f2fa 100644 --- a/usr/src/cmd/rcap/rcapstat/rcapstat.c +++ b/usr/src/cmd/rcap/rcapstat/rcapstat.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -77,7 +76,8 @@ col_find(rcid_t id) { col_t *col; for (col = col_head; col != NULL; col = col->col_next) - if (col->col_id == id) + if (col->col_id.rcid_type == id.rcid_type && + col->col_id.rcid_val == id.rcid_val) return (col); return (NULL); } @@ -119,7 +119,7 @@ static void usage() { (void) fprintf(stderr, - gettext("usage: rcapstat [-g] [interval [count]]\n")); + gettext("usage: rcapstat [-g] [-p | -z] [interval [count]]\n")); exit(E_USAGE); } @@ -139,12 +139,12 @@ format_size(char *str, uint64_t size, int length) } static int -read_stats() +read_stats(rcid_type_t stat_type) { int fd; int proc_fd; char procfile[20]; - pid_t pid; + uint64_t pid; col_t *col, *col_next; lcollection_report_t report; struct stat st; @@ -169,7 +169,7 @@ read_stats() * Check if rcapd is running */ pid = hdr.rs_pid; - (void) snprintf(procfile, 20, "/proc/%ld/psinfo", pid); + (void) snprintf(procfile, 20, "/proc/%lld/psinfo", pid); if ((proc_fd = open(procfile, O_RDONLY)) < 0) { warn(gettext("rcapd is not active\n")); (void) close(fd); @@ -185,6 +185,9 @@ read_stats() } while (read(fd, &report, sizeof (report)) == sizeof (report)) { + if (report.lcol_id.rcid_type != stat_type) + continue; + col = col_find(report.lcol_id); if (col == NULL) { col = col_insert(report.lcol_id); @@ -291,12 +294,13 @@ print_unformatted_stats(void) } static void -print_stats() +print_stats(rcid_type_t stat_type) { col_t *col; char size[6]; char limit[6]; char rss[6]; + char nproc[6]; char paged_att[6]; char paged_eff[6]; char paged_att_avg[6]; @@ -310,12 +314,21 @@ print_stats() */ if (count == 0 || ncol != 1) (void) printf("%6s %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n", - "id", mode, "nproc", "vm", "rss", "cap", + "id", (stat_type == RCIDT_PROJECT ? "project" : "zone"), + "nproc", "vm", "rss", "cap", "at", "avgat", "pg", "avgpg"); if (++count >= 20 || (count >= 10 && global != 0) || ncol != 1) count = 0; for (col = col_head; col != NULL; col = col->col_next) { + if (col->col_id.rcid_type != stat_type) + continue; + + if (col->col_paged_att == 0) + strlcpy(nproc, "-", sizeof (nproc)); + else + (void) snprintf(nproc, sizeof (nproc), "%lld", + col->col_nproc); format_size(size, col->col_vmsize, 6); format_size(rss, col->col_rsssize, 6); format_size(limit, col->col_rsslimit, 6); @@ -323,8 +336,9 @@ print_stats() format_size(paged_eff, col->col_paged_eff, 6); format_size(paged_att_avg, col->col_paged_att_avg, 6); format_size(paged_eff_avg, col->col_paged_eff_avg, 6); - (void) printf("%6lld %-15s %5lld %5s %5s %5s %5s %5s %5s %5s\n", - (long long)col->col_id, col->col_name, col->col_nproc, + (void) printf("%6lld %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n", + col->col_id.rcid_val, col->col_name, + nproc, size, rss, limit, paged_att, paged_att_avg, paged_eff, paged_eff_avg); @@ -342,20 +356,32 @@ main(int argc, char *argv[]) int count; int always = 1; int opt; + int projects = 0; + int zones = 0; + /* project reporting is the default if no option is specified */ + rcid_type_t stat_type = RCIDT_PROJECT; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); (void) setprogname("rcapstat"); global = unformatted = 0; - while ((opt = getopt(argc, argv, "gu")) != (int)EOF) { + while ((opt = getopt(argc, argv, "gpuz")) != (int)EOF) { switch (opt) { case 'g': global = 1; break; + case 'p': + projects = 1; + stat_type = RCIDT_PROJECT; + break; case 'u': unformatted = 1; break; + case 'z': + stat_type = RCIDT_ZONE; + zones = 1; + break; default: usage(); } @@ -369,22 +395,22 @@ main(int argc, char *argv[]) die(gettext("invalid count specified\n")); always = 0; } - if (argc > optind) + if (argc > optind || (projects > 0 && zones > 0)) usage(); while (always || count-- > 0) { - if (read_stats() != E_SUCCESS) + if (read_stats(stat_type) != E_SUCCESS) return (E_ERROR); if (!unformatted) { - print_stats(); - fflush(stdout); + print_stats(stat_type); + (void) fflush(stdout); if (count || always) (void) sleep(interval); } else { struct stat st; print_unformatted_stats(); - fflush(stdout); + (void) fflush(stdout); while (stat(STAT_FILE_DEFAULT, &st) == 0 && st.st_mtime == stat_mod) usleep((useconds_t)(0.2 * MICROSEC)); |
