summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rcap
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/rcap')
-rw-r--r--usr/src/cmd/rcap/common/rcapd.h35
-rw-r--r--usr/src/cmd/rcap/common/rcapd_stat.h12
-rw-r--r--usr/src/cmd/rcap/common/utils.c81
-rw-r--r--usr/src/cmd/rcap/common/utils.h14
-rw-r--r--usr/src/cmd/rcap/rcapadm/Makefile9
-rw-r--r--usr/src/cmd/rcap/rcapadm/rcapadm.c61
-rw-r--r--usr/src/cmd/rcap/rcapd/Makefile.rcapd4
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_collection.c72
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_collection_project.c53
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c99
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_main.c862
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_scanner.c31
-rw-r--r--usr/src/cmd/rcap/rcapstat/Makefile9
-rw-r--r--usr/src/cmd/rcap/rcapstat/rcapstat.c64
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));