summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exception_lists/keywords27
-rw-r--r--usr/src/cmd/Makefile4
-rw-r--r--usr/src/cmd/Makefile.check3
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c14
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.c38
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.h5
-rw-r--r--usr/src/cmd/zonestat/Makefile30
-rw-r--r--usr/src/cmd/zonestat/Makefile.subdirs41
-rw-r--r--usr/src/cmd/zonestat/zonestat/Makefile47
-rw-r--r--usr/src/cmd/zonestat/zonestat/zonestat.c2595
-rw-r--r--usr/src/cmd/zonestat/zonestatd/Makefile59
-rw-r--r--usr/src/cmd/zonestat/zonestatd/svc-zonestat41
-rw-r--r--usr/src/cmd/zonestat/zonestatd/zonestat.xml130
-rw-r--r--usr/src/cmd/zonestat/zonestatd/zonestatd.c4876
-rw-r--r--usr/src/lib/Makefile2
-rw-r--r--usr/src/lib/libzonestat/Makefile53
-rw-r--r--usr/src/lib/libzonestat/Makefile.com44
-rw-r--r--usr/src/lib/libzonestat/amd64/Makefile27
-rw-r--r--usr/src/lib/libzonestat/common/libzonestat.c4194
-rw-r--r--usr/src/lib/libzonestat/common/llib-lzonestat30
-rw-r--r--usr/src/lib/libzonestat/common/mapfile-vers101
-rw-r--r--usr/src/lib/libzonestat/common/zonestat.h277
-rw-r--r--usr/src/lib/libzonestat/common/zonestat_impl.h280
-rw-r--r--usr/src/lib/libzonestat/i386/Makefile26
-rw-r--r--usr/src/lib/libzonestat/sparc/Makefile26
-rw-r--r--usr/src/lib/libzonestat/sparcv9/Makefile27
-rw-r--r--usr/src/pkg/manifests/system-header.mf1
-rw-r--r--usr/src/pkg/manifests/system-zones-internal.mf6
-rw-r--r--usr/src/pkg/manifests/system-zones.mf8
29 files changed, 12996 insertions, 16 deletions
diff --git a/exception_lists/keywords b/exception_lists/keywords
new file mode 100644
index 0000000000..bdc3b602ad
--- /dev/null
+++ b/exception_lists/keywords
@@ -0,0 +1,27 @@
+#
+# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+syntax: glob
+#exception_lists/cstyle
+#exception_lists/hdrchk
+usr/src/cmd/zonestat/zonestat/zonestat.c
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index f12453c223..0524593d6f 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -456,6 +456,7 @@ COMMON_SUBDIRS= \
zonename \
zpool \
zlook \
+ zonestat \
zstreamdump \
ztest
@@ -744,7 +745,8 @@ MSGSUBDIRS= \
zoneadmd \
zonecfg \
zonename \
- zpool
+ zpool \
+ zonestat
$(CLOSED_BUILD)MSGSUBDIRS += \
$(CLOSED)/cmd/iconv \
diff --git a/usr/src/cmd/Makefile.check b/usr/src/cmd/Makefile.check
index 50acd0dc34..45ceb20cad 100644
--- a/usr/src/cmd/Makefile.check
+++ b/usr/src/cmd/Makefile.check
@@ -144,7 +144,8 @@ MANIFEST_SUBDIRS= \
vscan/vscand \
xvm/ipagent \
ypcmd/yppasswd \
- ypcmd/ypupdated
+ ypcmd/ypupdated \
+ zonestat/zonestatd
$(CLOSED_BUILD)MANIFEST_SUBDIRS += \
$(CLOSED)/cmd/cmd-inet/usr.lib/in.iked
diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c
index 209c76d9fc..9e4dca604d 100644
--- a/usr/src/cmd/zoneadmd/vplat.c
+++ b/usr/src/cmd/zoneadmd/vplat.c
@@ -2471,6 +2471,7 @@ add_datalink(zlog_t *zlogp, char *zone_name, datalink_id_t linkid, char *dlname)
{
dladm_status_t err;
boolean_t cpuset, poolset;
+ char *poolp;
/* First check if it's in use by global zone. */
if (zonecfg_ifname_exists(AF_INET, dlname) ||
@@ -2508,8 +2509,9 @@ add_datalink(zlog_t *zlogp, char *zone_name, datalink_id_t linkid, char *dlname)
}
if ((strlen(pool_name) != 0) && !cpuset && !poolset) {
+ poolp = pool_name;
err = dladm_set_linkprop(dld_handle, linkid, "pool",
- &pool_name, 1, DLADM_OPT_ACTIVE);
+ &poolp, 1, DLADM_OPT_ACTIVE);
if (err != DLADM_STATUS_OK) {
zerror(zlogp, B_FALSE, "WARNING: unable to set "
"pool %s to datalink %s", pool_name, dlname);
@@ -4516,6 +4518,8 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid)
zerror(zlogp, B_FALSE, "WARNING: %s",
zonecfg_strerror(res));
}
+
+ /* Update saved pool name in case it has changed */
(void) zonecfg_get_poolname(handle, zone_name, pool_name, MAXPATHLEN);
zonecfg_fini_handle(handle);
@@ -4807,12 +4811,6 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd)
goto error;
}
- if ((pool_name = malloc(MAXPATHLEN)) == NULL) {
- zerror(zlogp, B_TRUE, "memory allocation failed");
- return (Z_NOMEM);
- }
- bzero(pool_name, MAXPATHLEN);
-
/*
* The following actions are not performed when merely mounting a zone
* for administrative use.
@@ -5278,8 +5276,6 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting)
}
}
- free(pool_name);
-
remove_mlps(zlogp, zoneid);
if (zone_destroy(zoneid) != 0) {
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c
index fe7719bbe1..94ab464193 100644
--- a/usr/src/cmd/zoneadmd/zoneadmd.c
+++ b/usr/src/cmd/zoneadmd/zoneadmd.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
@@ -101,11 +101,12 @@
#include <sys/dls_mgmt.h>
#include <libzonecfg.h>
+#include <zonestat_impl.h>
#include "zoneadmd.h"
static char *progname;
char *zone_name; /* zone which we are managing */
-char *pool_name;
+char pool_name[MAXNAMELEN];
char default_brand[MAXNAMELEN];
char brand_name[MAXNAMELEN];
boolean_t zone_isnative;
@@ -496,6 +497,33 @@ brand_poststatechg(zlog_t *zlogp, int state, int cmd)
}
/*
+ * Notify zonestatd of the new zone. If zonestatd is not running, this
+ * will do nothing.
+ */
+static void
+notify_zonestatd(zoneid_t zoneid)
+{
+ int cmd[2];
+ int fd;
+ door_arg_t params;
+
+ fd = open(ZS_DOOR_PATH, O_RDONLY);
+ if (fd < 0)
+ return;
+
+ cmd[0] = ZSD_CMD_NEW_ZONE;
+ cmd[1] = zoneid;
+ params.data_ptr = (char *)&cmd;
+ params.data_size = sizeof (cmd);
+ params.desc_ptr = NULL;
+ params.desc_num = 0;
+ params.rbuf = NULL;
+ params.rsize = NULL;
+ (void) door_call(fd, &params);
+ (void) close(fd);
+}
+
+/*
* Bring a zone up to the pre-boot "ready" stage. The mount_cmd argument is
* 'true' if this is being invoked as part of the processing for the "mount"
* subcommand.
@@ -907,6 +935,12 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
goto bad;
}
+ /*
+ * Inform zonestatd of a new zone so that it can install a door for
+ * the zone to contact it.
+ */
+ notify_zonestatd(zone_id);
+
if (zone_boot(zoneid) == -1) {
zerror(zlogp, B_TRUE, "unable to boot zone");
goto bad;
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h
index da6aa369ed..63b23481d2 100644
--- a/usr/src/cmd/zoneadmd/zoneadmd.h
+++ b/usr/src/cmd/zoneadmd/zoneadmd.h
@@ -20,8 +20,7 @@
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _ZONEADMD_H
@@ -86,7 +85,7 @@ extern mutex_t msglock;
extern boolean_t in_death_throes;
extern boolean_t bringup_failure_recovery;
extern char *zone_name;
-extern char *pool_name;
+extern char pool_name[MAXNAMELEN];
extern char brand_name[MAXNAMELEN];
extern char default_brand[MAXNAMELEN];
extern char boot_args[BOOTARGS_MAX];
diff --git a/usr/src/cmd/zonestat/Makefile b/usr/src/cmd/zonestat/Makefile
new file mode 100644
index 0000000000..00917e6b17
--- /dev/null
+++ b/usr/src/cmd/zonestat/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+.KEEP_STATE:
+
+SUBDIRS = zonestat zonestatd
+MSGSUBDIRS = $(SUBDIRS)
+
+include Makefile.subdirs
diff --git a/usr/src/cmd/zonestat/Makefile.subdirs b/usr/src/cmd/zonestat/Makefile.subdirs
new file mode 100644
index 0000000000..83d7eafe8d
--- /dev/null
+++ b/usr/src/cmd/zonestat/Makefile.subdirs
@@ -0,0 +1,41 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+.KEEP_STATE:
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+_msg := TARGET = _msg
+
+all clean clobber install lint: $(SUBDIRS)
+
+_msg: $(MSGSUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/zonestat/zonestat/Makefile b/usr/src/cmd/zonestat/zonestat/Makefile
new file mode 100644
index 0000000000..2f4681b1ed
--- /dev/null
+++ b/usr/src/cmd/zonestat/zonestat/Makefile
@@ -0,0 +1,47 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+PROG = zonestat
+SRCS = zonestat.c
+OBJS = $(SRCS:%.c=%.o)
+
+include ../../Makefile.cmd
+
+LDLIBS += -lscf -lzonestat -lumem
+LINTFLAGS += -u
+
+.KEEP_STATE:
+
+.PARALLEL:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_PROG
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/zonestat/zonestat/zonestat.c b/usr/src/cmd/zonestat/zonestat/zonestat.c
new file mode 100644
index 0000000000..b66612ee10
--- /dev/null
+++ b/usr/src/cmd/zonestat/zonestat/zonestat.c
@@ -0,0 +1,2595 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/fxpriocntl.h>
+#include <sys/priocntl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <zonestat.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+#define ZSTAT_OK 0
+#define ZSTAT_ERROR 1
+#define ZSTAT_USAGE 2
+
+#define ZSTAT_UNIX_TIMESTAMP 1
+#define ZSTAT_ISO_TIMESTAMP 2
+#define ZSTAT_DATE_TIMESTAMP 3
+
+#define ZSTAT_RES_PHYSICAL_MEMORY 0x1
+#define ZSTAT_RES_VIRTUAL_MEMORY 0x2
+#define ZSTAT_RES_LOCKED_MEMORY 0x4
+#define ZSTAT_RES_MEMORY 0x7
+
+#define ZSTAT_RES_DEFAULT_PSET 0x10
+#define ZSTAT_RES_PSETS 0x20
+#define ZSTAT_RES_SUMMARY 0x40
+
+#define ZSTAT_RES_PROCESSES 0x100
+#define ZSTAT_RES_LWPS 0x200
+#define ZSTAT_RES_LOFI 0x400
+#define ZSTAT_RES_LIMITS 0x700
+
+#define ZSTAT_RES_SHM_MEMORY 0x1000
+#define ZSTAT_RES_SHM_IDS 0x2000
+#define ZSTAT_RES_SEM_IDS 0x4000
+#define ZSTAT_RES_MSG_IDS 0x8000
+#define ZSTAT_RES_SYSV 0xF000
+
+#define ZSTAT_RES_ALL 0xF777
+
+#define ZONESTAT_PHYSICAL_MEMORY "physical-memory"
+#define ZONESTAT_VIRTUAL_MEMORY "virtual-memory"
+#define ZONESTAT_LOCKED_MEMORY "locked-memory"
+#define ZONESTAT_MEMORY "memory"
+
+#define ZONESTAT_DEFAULT_PSET "default-pset"
+#define ZONESTAT_POOL_PSET "pool-pset"
+#define ZONESTAT_PSRSET_PSET "psrset-pset"
+#define ZONESTAT_DEDICATED_CPU "dedicated-cpu"
+#define ZONESTAT_PROCESSOR_SET "processor-set"
+#define ZONESTAT_PSETS "psets"
+#define ZONESTAT_SUMMARY "summary"
+
+#define ZONESTAT_PROCESSES "processes"
+#define ZONESTAT_LWPS "lwps"
+#define ZONESTAT_LOFI "lofi"
+#define ZONESTAT_LIMITS "limits"
+
+#define ZONESTAT_SHM_MEMORY "shm-memory"
+#define ZONESTAT_SHM_IDS "shm-ids"
+#define ZONESTAT_SEM_IDS "sem-ids"
+#define ZONESTAT_MSG_IDS "msg-ids"
+#define ZONESTAT_SYSV "sysv"
+
+#define ZONESTAT_ALL "all"
+
+#define ZONESTAT_NAME_MEM_DEFAULT "mem_default"
+#define ZONESTAT_NAME_VM_DEFAULT "vm_default"
+
+#define ZONESTAT_NAME_AVERAGE "average"
+#define ZONESTAT_NAME_HIGH "high"
+
+#define ZONESTAT_NAME_RESOURCE "resource"
+#define ZONESTAT_NAME_TOTAL "total"
+#define ZONESTAT_NAME_SYSTEM "system"
+#define ZONESTAT_NAME_ZONES "zones"
+#define ZONESTAT_NAME_HEADER "header"
+#define ZONESTAT_NAME_FOOTER "footer"
+
+#define ZONESTAT_NAME_NAME "name"
+#define ZONESTAT_NAME_USED "used"
+#define ZONESTAT_NAME_CAP "cap"
+#define ZONESTAT_NAME_PCAP "pcap"
+#define ZONESTAT_NAME_SHR "shr"
+#define ZONESTAT_NAME_PSHRU "pshru"
+#define ZONESTAT_NAME_CPU "cpu"
+#define ZONESTAT_NAME_PHYSICAL_MEMORY ZONESTAT_PHYSICAL_MEMORY
+#define ZONESTAT_NAME_VIRTUAL_MEMORY ZONESTAT_VIRTUAL_MEMORY
+
+#define ZONESTAT_NAME_SYSTEM_LIMIT "system-limit"
+
+#define ZSTAT_REPORT_FMT_INTERVAL 0
+#define ZSTAT_REPORT_FMT_TOTAL 1
+#define ZSTAT_REPORT_FMT_AVERAGE 2
+#define ZSTAT_REPORT_FMT_HIGH 3
+#define ZSTAT_REPORT_FMT_END 4
+
+#define ZSTAT_REPORT_TEXT_INTERVAL "interval"
+#define ZSTAT_REPORT_TEXT_TOTAL "report-total"
+#define ZSTAT_REPORT_TEXT_AVERAGE "report-average"
+#define ZSTAT_REPORT_TEXT_HIGH "report-high"
+#define ZSTAT_REPORT_TEXT_END "footer"
+
+#define ZSTAT_DURATION_INF ((int)INT_MAX)
+#define ZSTAT_INTERVAL_DEFAULT ((int)INT_MAX)
+#define ZSTAT_REPORT_END ((int)INT_MAX)
+
+#define ZSTAT_SORT_CPU 1
+#define ZSTAT_SORT_PHYSICAL 2
+#define ZSTAT_SORT_VIRTUAL 3
+#define ZSTAT_SORT_USED 4
+#define ZSTAT_SORT_CAP 5
+#define ZSTAT_SORT_PCAP 6
+#define ZSTAT_SORT_SHR 7
+#define ZSTAT_SORT_PSHRU 8
+#define ZSTAT_SORT_NAME 9
+#define ZSTAT_SORT_MAX 10
+
+#define ZSTAT_SUM_MIN_ZONENAME 19
+#define ZSTAT_SUM_HDR_FORMAT "%23s %17s %17s\n"
+#define ZSTAT_SUM_ZONE_FORMAT "%5s %5s %5s %5s %5s %5s %5s %5s %5s %5s\n"
+
+#define ZSTAT_CPU_MIN_PSETNAME 22
+#define ZSTAT_CPU_MIN_ZONENAME 36
+#define ZSTAT_CPU_RES_FORMAT "%13s %11s %11s\n"
+#define ZSTAT_CPU_ZONE_FORMAT "%5s %5s %5s %5s %6s %5s %5s\n"
+
+#define ZSTAT_RESOURCE_MIN_RESNAME 28
+#define ZSTAT_RESOURCE_MIN_ZONENAME 36
+#define ZSTAT_RESOURCE_FORMAT "%13s\n"
+#define ZSTAT_RESOURCE_ZONE_FORMAT "%5s %5s %5s %5s\n"
+
+#define ZS_UINT64_STRLEN 20
+#define ZS_PCT_STRLEN 10
+#define ZS_TIME_STRLEN 20
+#define ZS_NAME_STRLEN 10
+
+time_t g_now_time;
+time_t g_boot_time;
+time_t g_start_time;
+time_t g_end_time;
+int g_interval;
+int g_count;
+int g_report_count;
+time_t g_seconds;
+
+int g_resources;
+zs_ctl_t *g_zsctl;
+boolean_t g_quit = B_FALSE;
+zs_zone_t **g_zone_list;
+int g_zone_num;
+zs_pset_zone_t **g_pz_list;
+int g_pz_num;
+zs_pset_t **g_pset_list;
+int g_pset_num;
+int g_sort_by;
+int g_sorts[ZSTAT_SORT_MAX];
+int g_sort_summary;
+size_t g_max_zonename;
+
+/* Storage for command line arguments. */
+char **arg_zonenames;
+int arg_zonename_count;
+char **arg_resnames;
+int arg_resname_count;
+char **arg_restypes;
+int arg_restype_count;
+char ** arg_reports;
+int arg_report_count;
+char ** arg_sort_list;
+int arg_sort_count;
+char ** arg_line_list;
+int arg_line_count;
+
+time_t arg_starttime;
+time_t arg_endtime;
+uint_t arg_timestamp = ZSTAT_DATE_TIMESTAMP;
+int arg_interval = 5;
+int arg_duration = -1;
+int arg_report = -1;
+
+/* Options with or as arguments */
+boolean_t opt_zonenames = B_FALSE;
+boolean_t opt_resnames = B_FALSE;
+boolean_t opt_restypes = B_FALSE;
+boolean_t opt_start = B_FALSE;
+boolean_t opt_end = B_FALSE;
+boolean_t opt_in = B_FALSE;
+boolean_t opt_out = B_FALSE;
+boolean_t opt_timestamp = B_FALSE;
+boolean_t opt_report = B_FALSE;
+boolean_t opt_sort = B_FALSE;
+
+boolean_t opt_report_high = B_FALSE;
+boolean_t opt_report_total = B_FALSE;
+boolean_t opt_report_average = B_FALSE;
+
+boolean_t opt_line_resource = B_FALSE;
+boolean_t opt_line_total = B_FALSE;
+boolean_t opt_line_system = B_FALSE;
+boolean_t opt_line_zones = B_FALSE;
+boolean_t opt_line_header = B_FALSE;
+boolean_t opt_line_any = B_FALSE;
+
+/* Options without arguments */
+boolean_t opt_quiet_intervals = B_FALSE;
+boolean_t opt_parseable = B_FALSE;
+boolean_t opt_debug = B_FALSE;
+
+static int
+zonestat_usage(boolean_t explicit)
+{
+ FILE *fd = explicit ? stdout : stderr;
+
+ (void) fprintf(fd, gettext("Usage:\n"));
+ (void) fprintf(fd,
+" zonestat [-z zonelist] [-r reslist] [-n namelist]\n"
+" [-T u | d | i] [-R reports] [-q] [-p [-P lines]] [-S cols]\n"
+" interval [duration [report]]\n"
+"\n");
+ (void) fprintf(fd, gettext(
+" Options:\n"
+" %s Report resources of specified types.\n"
+" Valid resource types are:\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\", \"%s\", \"%s\"\n"
+" \"%s\", \"%s\", \"%s\", \"%s\"\n"),
+ "-r",
+ ZONESTAT_VIRTUAL_MEMORY, ZONESTAT_PHYSICAL_MEMORY,
+ ZONESTAT_LOCKED_MEMORY, ZONESTAT_PROCESSOR_SET,
+ ZONESTAT_PROCESSES, ZONESTAT_LWPS, ZONESTAT_LOFI,
+ ZONESTAT_SHM_MEMORY, ZONESTAT_SHM_IDS, ZONESTAT_SEM_IDS,
+ ZONESTAT_MSG_IDS);
+
+ (void) fprintf(fd, gettext(
+" The following resource nicknames can also be specified:\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" \"%s\"\n"),
+ ZONESTAT_SUMMARY, ZONESTAT_MEMORY, ZONESTAT_PSETS,
+ ZONESTAT_DEFAULT_PSET, ZONESTAT_LIMITS, ZONESTAT_SYSV);
+ (void) fprintf(fd, gettext(
+" %s Report resources used by zones\n"
+" %s Report resources with specific names.\n"
+" Valid resource names are:\n"
+" \"%s\"\n"
+" \"%s\"\n"
+" Name of a pool processor set\n"
+" Id of a processor set created with psrset(1m)\n"
+" Name of a zone using dedicated-cpu\n"),
+ "-z", "-n",
+ ZONESTAT_NAME_MEM_DEFAULT, ZONESTAT_NAME_VM_DEFAULT);
+ (void) printf(gettext(
+" %s Print timestamp. Valid timestamps are:\n"
+" \"%s\"\tDate as specifed by date(1) command\n"
+" \"%s\"\tUnix time as returned by time(2)\n"
+" \"%s\"\tISO 8601 timestamp \"%s\"\n"
+" %s Print reports at end or after each report interval.\n"
+" Valid reports are:\n"
+" \"%s\"\tUsage of each zone\n"
+" \"%s\"\tUsage of each zone while running\n"
+" \"%s\"\tMaximum usage of each zone\n"
+" %s Quiet. Do not print intervals. Only print reports.\n"
+" %s Machine parseable output.\n"),
+ "-T", "d", "u", "i", "YYYYMMDDThhmmssZ",
+ "-R", ZONESTAT_NAME_TOTAL, ZONESTAT_NAME_AVERAGE,
+ ZONESTAT_NAME_HIGH,
+ "-q", "-p");
+
+ (void) printf(gettext(
+" %s Select desired lines in parseable output.\n"
+" \"%s\"\tLines describing each resource\n"
+" \"%s\"\tTotal usage of each resource\n"
+" \"%s\"\tSystem usage of each resource\n"
+" \"%s\"\tPer-zone usage of each resource\n"
+" \"%s\"\tHeader lines between intervals and reports\n"),
+ "-P", ZONESTAT_NAME_RESOURCE, ZONESTAT_NAME_TOTAL,
+ ZONESTAT_NAME_SYSTEM, ZONESTAT_NAME_ZONES, ZONESTAT_NAME_HEADER);
+
+ (void) printf(gettext(
+" %s Sort output by the specified columns:\n"
+" \"%s\"\tby name alphanumerically\n"
+" \"%s\"\tby percent of resource used\n"
+" \"%s\"\tby configured cap\n"
+" \"%s\"\tby percent of cap used\n"
+" \"%s\"\tby shares configured\n"
+" \"%s\"\tby percent of share used\n"
+" \"%s\"\tSort summary by cpu\n"
+" \"%s\"\tSort summary by physical memory\n"
+" \"%s\"\tSort summary by virtual memory\n"),
+ "-S", ZONESTAT_NAME_NAME, ZONESTAT_NAME_USED, ZONESTAT_NAME_CAP,
+ ZONESTAT_NAME_PCAP, ZONESTAT_NAME_SHR, ZONESTAT_NAME_PSHRU,
+ ZONESTAT_NAME_CPU, ZONESTAT_NAME_PHYSICAL_MEMORY,
+ ZONESTAT_NAME_VIRTUAL_MEMORY);
+
+ if (!explicit)
+ (void) fputs("\n", fd);
+ return (ZSTAT_USAGE);
+}
+
+/* PRINTFLIKE1 */
+static int
+zonestat_error(const char *fmt, ...)
+{
+ va_list alist;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, "zonestat: Error: ");
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+ return (ZSTAT_ERROR);
+}
+
+static void
+zonestat_determine_lines()
+{
+ int i;
+ boolean_t fail = B_FALSE;
+
+ if (arg_line_count == 0) {
+ opt_line_resource = B_TRUE;
+ opt_line_total = B_TRUE;
+ opt_line_system = B_TRUE;
+ opt_line_zones = B_TRUE;
+ opt_line_header = B_TRUE;
+ }
+ for (i = 0; i < arg_line_count; i++) {
+ if (strcmp(arg_line_list[i], ZONESTAT_NAME_RESOURCE) == 0)
+ opt_line_resource = B_TRUE;
+ else if (strcmp(arg_line_list[i], ZONESTAT_NAME_TOTAL) == 0)
+ opt_line_total = B_TRUE;
+ else if (strcmp(arg_line_list[i], ZONESTAT_NAME_SYSTEM) == 0)
+ opt_line_system = B_TRUE;
+ else if (strcmp(arg_line_list[i], ZONESTAT_NAME_ZONES) == 0)
+ opt_line_zones = B_TRUE;
+ else if (strcmp(arg_line_list[i], ZONESTAT_NAME_HEADER) == 0)
+ opt_line_header = B_TRUE;
+ else {
+ (void) zonestat_error(gettext("Unknown -O arg: %s"),
+ arg_line_list[i]);
+ fail = B_TRUE;
+ }
+ }
+ if (fail == B_TRUE)
+ exit(zonestat_usage(B_FALSE));
+}
+
+static void
+zonestat_determine_reports()
+{
+ int i;
+ boolean_t fail = B_FALSE;
+
+ for (i = 0; i < arg_report_count; i++) {
+ if (strcmp(arg_reports[i], ZONESTAT_NAME_TOTAL) == 0)
+ opt_report_total = B_TRUE;
+ else if (strcmp(arg_reports[i], ZONESTAT_NAME_AVERAGE) == 0)
+ opt_report_average = B_TRUE;
+ else if (strcmp(arg_reports[i], ZONESTAT_NAME_HIGH) == 0)
+ opt_report_high = B_TRUE;
+ else {
+ (void) zonestat_error(gettext("Unknown -R arg: %s"),
+ arg_reports[i]);
+ fail = B_TRUE;
+ }
+ }
+ if (fail == B_TRUE)
+ exit(zonestat_usage(B_FALSE));
+}
+
+/*
+ * Compares list of -S sort arguments to the list of known sorts. Only
+ * one of cpu, physical memory, and virtual memory can be specified.
+ */
+static void
+zonestat_determine_sort()
+{
+ int i, count = 0;
+ boolean_t fail = B_FALSE;
+
+ if (arg_sort_count == 0) {
+ g_sort_summary = ZS_RESOURCE_CPU;
+ g_sorts[0] = ZSTAT_SORT_USED;
+ g_sorts[1] = ZSTAT_SORT_NAME;
+ arg_sort_count = 2;
+ return;
+ }
+
+ if (arg_sort_count > ZSTAT_SORT_MAX)
+ exit(zonestat_error(gettext(
+ "Too many -S sort columns specified")));
+
+ for (i = 0; i < arg_sort_count; i++) {
+ if (strcmp(arg_sort_list[i], ZONESTAT_NAME_NAME) == 0)
+ g_sorts[count++] = ZSTAT_SORT_NAME;
+ else if (strcmp(arg_sort_list[i], ZONESTAT_NAME_USED) == 0)
+ g_sorts[count++] = ZSTAT_SORT_USED;
+ else if (strcmp(arg_sort_list[i], ZONESTAT_NAME_CAP) == 0)
+ g_sorts[count++] = ZSTAT_SORT_CAP;
+ else if (strcmp(arg_sort_list[i], ZONESTAT_NAME_PCAP) == 0)
+ g_sorts[count++] = ZSTAT_SORT_PCAP;
+ else if (strcmp(arg_sort_list[i], ZONESTAT_NAME_SHR) == 0)
+ g_sorts[count++] = ZSTAT_SORT_SHR;
+ else if (strcmp(arg_sort_list[i], ZONESTAT_NAME_PSHRU) == 0)
+ g_sorts[count++] = ZSTAT_SORT_PSHRU;
+ else if (strcmp(arg_sort_list[i], ZONESTAT_NAME_CPU) == 0) {
+ if (g_sort_summary != 0)
+ fail = B_TRUE;
+ g_sort_summary = ZS_RESOURCE_CPU;
+ } else if (strcmp(arg_sort_list[i],
+ ZONESTAT_NAME_PHYSICAL_MEMORY) == 0) {
+ if (g_sort_summary != 0)
+ fail = B_TRUE;
+ g_sort_summary = ZS_RESOURCE_RAM_RSS;
+ } else if (strcmp(arg_sort_list[i],
+ ZONESTAT_NAME_VIRTUAL_MEMORY) == 0) {
+ if (g_sort_summary != 0)
+ fail = B_TRUE;
+ g_sort_summary = ZS_RESOURCE_VM;
+ } else {
+ (void) zonestat_error(gettext("Unknown -S arg: %s"),
+ arg_sort_list[i]);
+ fail = B_TRUE;
+ }
+ }
+ if (g_sort_summary == 0)
+ g_sort_summary = ZS_RESOURCE_CPU;
+
+ if (fail == B_TRUE) {
+ (void) zonestat_error(gettext(
+ "-S: only one of \"%s\", \"%s\", or "
+ "\"%s\" permitted"), "-S", ZONESTAT_NAME_CPU,
+ ZONESTAT_NAME_PHYSICAL_MEMORY,
+ ZONESTAT_NAME_VIRTUAL_MEMORY);
+ exit(zonestat_usage(B_FALSE));
+ }
+}
+
+typedef struct zonestat_resource_struct {
+ char *zr_name;
+ uint_t zr_flag;
+} zonestat_resource_t;
+
+
+/* Used to map resource name strings to resource flags */
+zonestat_resource_t g_resource_list[] = {
+ ZONESTAT_PHYSICAL_MEMORY, ZSTAT_RES_PHYSICAL_MEMORY,
+ ZONESTAT_VIRTUAL_MEMORY, ZSTAT_RES_VIRTUAL_MEMORY,
+ ZONESTAT_LOCKED_MEMORY, ZSTAT_RES_LOCKED_MEMORY,
+ ZONESTAT_MEMORY, ZSTAT_RES_MEMORY,
+ ZONESTAT_PROCESSOR_SET, ZSTAT_RES_PSETS,
+ ZONESTAT_PSETS, ZSTAT_RES_PSETS,
+ ZONESTAT_DEFAULT_PSET, ZSTAT_RES_DEFAULT_PSET,
+ ZONESTAT_PROCESSES, ZSTAT_RES_PROCESSES,
+ ZONESTAT_LWPS, ZSTAT_RES_LWPS,
+ ZONESTAT_LOFI, ZSTAT_RES_LOFI,
+ ZONESTAT_LIMITS, ZSTAT_RES_LIMITS,
+ ZONESTAT_SHM_MEMORY, ZSTAT_RES_SHM_MEMORY,
+ ZONESTAT_SHM_IDS, ZSTAT_RES_SHM_IDS,
+ ZONESTAT_SEM_IDS, ZSTAT_RES_SEM_IDS,
+ ZONESTAT_MSG_IDS, ZSTAT_RES_MSG_IDS,
+ ZONESTAT_SYSV, ZSTAT_RES_SYSV,
+ ZONESTAT_SUMMARY, ZSTAT_RES_SUMMARY,
+ ZONESTAT_ALL, ZSTAT_RES_ALL
+};
+
+/*
+ * Compares list of resources passed to -r to the known list of
+ * resources.
+ */
+static void
+zonestat_determine_resources()
+{
+ int i, j, count;
+ boolean_t found, fail = B_FALSE;
+
+ if (arg_restype_count == 0) {
+ g_resources = ZSTAT_RES_SUMMARY;
+ return;
+ }
+
+ count = sizeof (g_resource_list) / sizeof (zonestat_resource_t);
+
+ for (i = 0; i < arg_restype_count; i++) {
+ found = B_FALSE;
+ for (j = 0; j < count; j++) {
+ if (strcmp(arg_restypes[i], g_resource_list[j].zr_name)
+ == 0) {
+ g_resources |= g_resource_list[j].zr_flag;
+ found = B_TRUE;
+ break;
+ }
+ }
+ if (found == B_FALSE) {
+ (void) zonestat_error(gettext("Unknown resource: %s"),
+ arg_restypes[i]);
+ fail = B_TRUE;
+ }
+ }
+ if (fail == B_TRUE)
+ exit(zonestat_usage(B_FALSE));
+}
+
+/*
+ * Returns 1 if the name matches one of the specified zone names. 0
+ * otherwise. Always matches if no zone names were specified.
+ */
+static int
+zonestat_match_zonename(char *name)
+{
+ int i;
+
+ if (arg_zonename_count == 0)
+ return (1);
+ for (i = 0; i < arg_zonename_count; i++) {
+ if (strcmp(name, arg_zonenames[i]) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * compare name to base, ignoring prefix on name.
+ */
+static int
+zonestat_match_with_prefix(char *prefix, char *name, char *base)
+{
+ size_t prefix_len;
+
+ prefix_len = strlen(prefix);
+ if (strncmp(name, prefix, prefix_len) == 0) {
+ name += prefix_len;
+ if (strcmp(name, base) == 0)
+ return (1);
+ }
+ return (0);
+}
+/*
+ * Returns 1 if the resource matches one of the specified resource names. 0
+ * otherwise. Always matches if no resource names were specified.
+ */
+static int
+zonestat_match_resname(char *name)
+{
+ int i;
+
+ if (arg_resname_count == 0)
+ return (1);
+ for (i = 0; i < arg_resname_count; i++) {
+
+ if (strcmp(name, arg_resnames[i]) == 0)
+ return (1);
+
+ if (zonestat_match_with_prefix("SUNWtmp_", name,
+ arg_resnames[i]))
+ return (1);
+
+ if (zonestat_match_with_prefix("SUNWlegacy_pset_", name,
+ arg_resnames[i]))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Format unsigned uint64_t
+ *
+ * 9999 9999
+ * 99.9K 99999
+ * 9999K 9999999
+ * 99.9M 99999999
+ * 9999M 9999999999
+ * 99.9G 99999999999
+ * 9999G 9999999999999
+ * 99.9T 99999999999999
+ * 9999T 9999999999999999
+ * 99.9P 99999999999999999
+ * 9999P 9999999999999999999
+ * 99.9E UINT64_MAX
+ */
+static void
+format_uint64(uint64_t val, char *str, size_t len)
+{
+ uint64_t high;
+ uint64_t low;
+
+ if (val == UINT64_MAX) {
+ (void) snprintf(str, len, "-");
+ return;
+ }
+ if (val <= 9999) {
+ (void) snprintf(str, len, "%llu", val);
+ return;
+ }
+ if (val <= 99999) {
+ high = val / 1024;
+ low = val * 10 / 1024 - (high * 10);
+ (void) snprintf(str, len, "%llu%1.1lluK", high, low);
+ return;
+ }
+ val = val / 1024;
+ if (val <= 9999 || opt_parseable) {
+ high = val;
+ (void) snprintf(str, len, "%lluK", high);
+ return;
+ }
+ if (val <= 99999) {
+ high = val / 1024;
+ low = val * 10 / 1024 - (high * 10);
+ (void) snprintf(str, len, "%llu.%1.1lluM", high, low);
+ return;
+ }
+ val = val / 1024;
+ if (val <= 9999) {
+ high = val;
+ (void) snprintf(str, len, "%lluM", high);
+ return;
+ }
+ if (val <= 99999) {
+ high = val / 1024;
+ low = val * 10 / 1024 - (high * 10);
+ (void) snprintf(str, len, "%llu.%1.1lluG", high, low);
+ return;
+ }
+ val = val / 1024;
+ if (val <= 9999) {
+ high = val;
+ (void) snprintf(str, len, "%lluG", high);
+ return;
+ }
+ if (val <= 99999) {
+ high = val / 1024;
+ low = val * 10 / 1024 - (high * 10);
+ (void) snprintf(str, len, "%llu.%1.1lluT", high, low);
+ return;
+ }
+ val = val / 1024;
+ if (val <= 9999) {
+ high = val;
+ (void) snprintf(str, len, "%lluT", high);
+ return;
+ }
+ if (val <= 99999) {
+ high = val / 1024;
+ low = val * 10 / 1024 - (high * 10);
+ (void) snprintf(str, len, "%llu.%1.1lluP", high, low);
+ return;
+ }
+ val = val / 1024;
+ if (val <= 9999) {
+ high = val;
+ (void) snprintf(str, len, "%lluP", high);
+ return;
+ }
+ high = val / 1024;
+ low = val * 10 / 1024 - (high * 10);
+ (void) snprintf(str, len, "%llu.%1.1lluE", high, low);
+}
+
+
+static void
+format_pct(uint_t pct, char *str, size_t len)
+{
+ uint_t high;
+ uint_t low;
+
+ if (pct == ZS_PCT_NONE) {
+ (void) snprintf(str, len, "-");
+ return;
+ }
+ /*
+ * pct's are printed as one of:
+ * #.##%
+ * ##.#%
+ * ###%
+ * ####%
+ *
+ * The value is fixed decimal. 10000 equals 100.00 percent.
+ * Percents can exceed 100.00 percent. Percents greater than
+ * 9999% will exceed the 5 column width.
+ */
+ if (pct <= 999 || opt_parseable) {
+ high = pct / 100;
+ low = pct - (high * 100);
+ (void) snprintf(str, len, "%u.%2.2u%%", high, low);
+ } else if (pct <= 9999) {
+ pct = pct / 10;
+ high = pct / 10;
+ low = pct - (high * 10);
+ (void) snprintf(str, len, "%u.%1.1u%%", high, low);
+ } else {
+ pct = pct / 100;
+ (void) snprintf(str, len, "%u%%", pct);
+ }
+}
+/*
+ * Cpu cap is 100 times the number of cpus allocated. It is formatted as a
+ * decimal. Example, a cpu-cap of 50 is 0.50 cpus.
+ *
+ * The cpu cap value can go up to UINT_MAX, so handle all cases even though
+ * the higher ones are nonsense.
+ *
+ * Format Max cpu-cap value for format.
+ * 42.9M 4294967296
+ * 9999K 999999999
+ * 99.9K 9999999
+ * 9999 999999
+ * 999.9 99999
+ * 9.99 999
+ */
+void
+format_cpu(uint64_t cpu, char *str, size_t len)
+{
+
+ uint64_t high;
+ uint64_t low;
+
+ /* #.## cpus */
+ if (cpu <= 999 || opt_parseable) {
+ high = cpu / 100;
+ low = cpu - (high * 100);
+ (void) snprintf(str, len, "%llu.%2.2llu", high, low);
+ return;
+ }
+ /* ##.# cpus */
+ if (cpu <= 99999) {
+ high = cpu / 100;
+ low = cpu - (high * 100);
+ (void) snprintf(str, len, "%llu.%1.1llu", high, low);
+ return;
+ }
+ /* #### cpus */
+ if (cpu <= 999999) {
+ cpu = cpu / 100;
+ (void) snprintf(str, len, "%llu", cpu);
+ return;
+ }
+ /* ##.#K cpus */
+ cpu = cpu / 1000;
+ if (cpu <= 99999) {
+ high = cpu / 100;
+ low = cpu - (high * 100);
+ (void) snprintf(str, len, "%llu.%1.1lluK", high, low);
+ return;
+ }
+ /* ####K cpus */
+ if (cpu <= 999999) {
+ cpu = cpu / 100;
+ (void) snprintf(str, len, "%lluK", cpu);
+ return;
+ }
+ /* ##.#M cpus */
+ cpu = cpu / 1000;
+ if (cpu <= UINT_MAX) {
+ high = cpu / 100;
+ low = cpu - (high * 100);
+ (void) snprintf(str, len, "%llu.%1.1lluM", high, low);
+ return;
+ }
+ (void) snprintf(str, len, "error", high, low);
+}
+
+/*
+ * Format a timetruct as:
+ * HH:MM:SS.SS
+ *
+ * Human readable format omits the fractional seconds.
+ */
+static void
+format_ts(timestruc_t *ts, char *str, size_t len, boolean_t human_readable)
+{
+ uint64_t secs, mins, hours, pct;
+
+ hours = 0;
+ mins = 0;
+
+ secs = ts->tv_sec;
+ pct = ts->tv_nsec / 1000 / 1000 / 10;
+ while (pct >= 100) {
+ pct -= 100;
+ secs++;
+ }
+ if (secs >= 60) {
+ mins = secs / 60;
+ secs = secs % 60;
+ }
+ if (mins >= 60) {
+ hours = mins / 60;
+ mins = mins % 60;
+ }
+ if (human_readable)
+ (void) snprintf(str, len, "%llu:%2.2llu:%2.2llu", hours,
+ mins, secs);
+ else
+ (void) snprintf(str, len, "%llu-%2.2llu-%2.2llu.%2.2llu", hours,
+ mins, secs, pct);
+}
+
+char *g_report_formats[] = {
+ ZSTAT_REPORT_TEXT_INTERVAL,
+ ZSTAT_REPORT_TEXT_TOTAL,
+ ZSTAT_REPORT_TEXT_AVERAGE,
+ ZSTAT_REPORT_TEXT_HIGH,
+ ZSTAT_REPORT_TEXT_END
+};
+
+/* Get the label for the current report type */
+static char *
+zonestat_get_plabel(int format)
+{
+ if (format >= sizeof (g_report_formats) / sizeof (char *))
+ exit(zonestat_error(gettext(
+ "Internal error, invalid report format")));
+
+ return (g_report_formats[format]);
+}
+
+#define ZSTAT_CPULINE "----------CPU----------"
+#define ZSTAT_MEMLINE "----PHYSICAL-----"
+#define ZSTAT_VMLINE "-----VIRTUAL-----"
+
+static void
+zonestat_print_summary_header(size_t namewidth, int report_fmt, uint64_t cpu,
+ uint64_t online, uint64_t mem, uint64_t vm)
+{
+ char str_cpu[ZS_UINT64_STRLEN];
+ char str_online[ZS_UINT64_STRLEN];
+ char str_mem[ZS_UINT64_STRLEN];
+ char str_vm[ZS_UINT64_STRLEN];
+ char name_format[ZS_NAME_STRLEN];
+ char tot_cpu[sizeof (ZSTAT_CPULINE)];
+ char tot_mem[sizeof (ZSTAT_MEMLINE)];
+ char tot_vm[sizeof (ZSTAT_VMLINE)];
+
+ char *label;
+
+ format_uint64(cpu, str_cpu, sizeof (str_cpu));
+ format_uint64(online, str_online, sizeof (str_online));
+ format_uint64(mem, str_mem, sizeof (str_mem));
+ format_uint64(vm, str_vm, sizeof (str_vm));
+
+ if (opt_parseable) {
+ label = zonestat_get_plabel(report_fmt);
+ (void) printf("%s:%s:[%s]:%s:%s:%s:%s\n", label,
+ ZONESTAT_SUMMARY, ZONESTAT_NAME_RESOURCE, str_cpu,
+ str_online, str_mem, str_vm);
+ return;
+ }
+
+ (void) snprintf(tot_cpu, sizeof (tot_cpu), "Cpus/Online: %s/%s",
+ str_cpu, str_online);
+
+ (void) snprintf(tot_mem, sizeof (tot_mem), "Physical: %s", str_mem);
+
+ (void) snprintf(tot_vm, sizeof (tot_vm), "Virtual: %s", str_vm);
+
+ /* Make first column as wide as longest zonename */
+ (void) snprintf(name_format, sizeof (name_format), "%%-%ds ",
+ namewidth);
+ /* LINTED */
+ (void) printf(name_format, "SUMMARY");
+ (void) printf(ZSTAT_SUM_HDR_FORMAT, tot_cpu, tot_mem,
+ tot_vm);
+
+ /* LINTED */
+ (void) printf(name_format, "");
+ (void) printf(ZSTAT_SUM_HDR_FORMAT, ZSTAT_CPULINE,
+ ZSTAT_MEMLINE, ZSTAT_VMLINE);
+
+ (void) snprintf(name_format, sizeof (name_format), "%%%ds ",
+ namewidth);
+ /* LINTED */
+ (void) printf(name_format, "ZONE");
+
+ (void) printf(ZSTAT_SUM_ZONE_FORMAT, "USED", "%PART", "%CAP",
+ "%SHRU", "USED", "PCT", "%CAP", "USED", "PCT", "%CAP");
+}
+
+static void
+zonestat_print_resource__header(size_t namelen, char *restype, char *size)
+{
+ char name_format[ZS_NAME_STRLEN];
+
+ if (opt_parseable)
+ return;
+
+ (void) snprintf(name_format, sizeof (name_format), "%%-%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, restype);
+ (void) printf(ZSTAT_RESOURCE_FORMAT, size);
+}
+
+static void
+zonestat_print_resource_zone_header(size_t namelen)
+{
+ char name_format[ZS_NAME_STRLEN];
+
+ if (opt_parseable)
+ return;
+
+ (void) snprintf(name_format, sizeof (name_format), "%%%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, "ZONE");
+
+ (void) printf(ZSTAT_RESOURCE_ZONE_FORMAT, "USED", "PCT", "CAP", "%CAP");
+}
+
+static void
+zonestat_print_timestamp(time_t t)
+{
+ static char *fmt = NULL;
+ int len;
+ char dstr[64];
+
+ /* We only need to retrieve this once per invocation */
+
+ if (arg_timestamp == ZSTAT_UNIX_TIMESTAMP) {
+ (void) printf("%ld", t);
+ } else if (arg_timestamp == ZSTAT_ISO_TIMESTAMP) {
+
+ len = strftime(dstr, sizeof (dstr), "%Y%m%dT%H%M%SZ",
+ gmtime(&t));
+ if (len > 0)
+ (void) printf("%s", dstr);
+
+ } else {
+
+ if (fmt == NULL)
+ fmt = nl_langinfo(_DATE_FMT);
+
+ len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
+ if (len > 0)
+ (void) printf("%s", dstr);
+ }
+}
+
+static void
+zonestat_print_summary_zone(size_t namewidth, int report_fmt, char *name,
+ uint64_t cused, uint_t ppart, uint_t pccap, uint_t pshru, uint64_t mused,
+ uint_t mpct, uint_t pmcap, uint64_t vused, uint_t vpct, uint_t pvcap)
+{
+ char *label;
+
+ char str_cused[ZS_UINT64_STRLEN];
+ char str_ppart[ZS_PCT_STRLEN];
+ char str_pccap[ZS_PCT_STRLEN];
+ char str_pshru[ZS_PCT_STRLEN];
+ char str_mused[ZS_UINT64_STRLEN];
+ char str_mpct[ZS_PCT_STRLEN];
+ char str_pmcap[ZS_PCT_STRLEN];
+ char str_vused[ZS_UINT64_STRLEN];
+ char str_vpct[ZS_PCT_STRLEN];
+ char str_pvcap[ZS_PCT_STRLEN];
+ char name_format[ZS_NAME_STRLEN];
+
+ format_cpu(cused, str_cused, sizeof (str_cused));
+ format_pct(ppart, str_ppart, sizeof (str_ppart));
+ format_pct(pccap, str_pccap, sizeof (str_pccap));
+ format_pct(pshru, str_pshru, sizeof (str_pshru));
+ format_uint64(mused, str_mused, sizeof (str_mused));
+ format_pct(mpct, str_mpct, sizeof (str_mpct));
+ format_pct(pmcap, str_pmcap, sizeof (str_pmcap));
+ format_uint64(vused, str_vused, sizeof (str_vused));
+ format_pct(vpct, str_vpct, sizeof (str_vpct));
+ format_pct(pvcap, str_pvcap, sizeof (str_pvcap));
+
+ if (opt_parseable) {
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+ (void) printf("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ label, ZONESTAT_SUMMARY, name, str_cused, str_ppart,
+ str_pccap, str_pshru, str_mused, str_mpct, str_pmcap,
+ str_vused, str_vpct, str_pvcap);
+ return;
+ }
+ (void) snprintf(name_format, sizeof (name_format), "%%%ds ",
+ namewidth);
+ /* LINTED */
+ (void) printf(name_format, name);
+ (void) printf(ZSTAT_SUM_ZONE_FORMAT, str_cused, str_ppart,
+ str_pccap, str_pshru, str_mused, str_mpct, str_pmcap, str_vused,
+ str_vpct, str_pvcap);
+}
+
+static void
+zonestat_print_resource_(size_t namelen, int report_fmt, char *res,
+ char *name, uint64_t size)
+{
+ char strsize[ZS_UINT64_STRLEN];
+ char *label;
+ char name_format[ZS_NAME_STRLEN];
+
+ format_uint64(size, strsize, sizeof (strsize));
+ if (opt_parseable) {
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+ (void) printf("%s:%s:%s:[%s]:%s\n", label, res, name,
+ ZONESTAT_NAME_RESOURCE, strsize);
+ return;
+ }
+
+ (void) snprintf(name_format, sizeof (name_format), "%%-%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, name);
+ (void) printf(ZSTAT_RESOURCE_FORMAT, strsize);
+}
+
+static void
+zonestat_print_resource_zone(size_t namelen, int report_fmt, char *restype,
+ char *resname, char *name, uint64_t used, uint_t pct, uint64_t cap,
+ uint_t pctcap)
+{
+ char strused[ZS_UINT64_STRLEN];
+ char strpct[ZS_PCT_STRLEN];
+ char strcap[ZS_UINT64_STRLEN];
+ char strpctcap[ZS_PCT_STRLEN];
+ char name_format[ZS_NAME_STRLEN];
+
+ char *label;
+
+ format_uint64(used, strused, sizeof (strused));
+ format_pct(pct, strpct, sizeof (strpct));
+ if (cap == ZS_LIMIT_NONE)
+ (void) strlcpy(strcap, "-", sizeof (strcap));
+ else
+ format_uint64(cap, strcap, sizeof (strcap));
+
+ if (pctcap == ZS_PCT_NONE)
+ (void) strlcpy(strpctcap, "-", sizeof (strpctcap));
+ else
+ format_pct(pctcap, strpctcap, sizeof (strpctcap));
+
+ if (opt_parseable) {
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+ (void) printf("%s:%s:%s:%s:%s:%s:%s:%s\n", label, restype,
+ resname, name, strused, strpct, strcap, strpctcap);
+ return;
+ }
+
+ (void) snprintf(name_format, sizeof (name_format), "%%%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, name);
+ (void) printf(ZSTAT_RESOURCE_ZONE_FORMAT, strused, strpct, strcap,
+ strpctcap);
+}
+
+/*
+ * Not thread safe.
+ */
+static void
+zonestat_qsort(void *base, size_t nel, size_t width,
+ int (*compar)(const void *, const void *), int by)
+{
+ g_sort_by = by;
+ g_max_zonename = 0;
+ qsort(base, nel, width, compar);
+}
+
+static int
+zonestat_zone_compare_resource(const void *a, const void *b)
+{
+ zs_zone_t *zonea = *(zs_zone_t **)a;
+ zs_zone_t *zoneb = *(zs_zone_t **)b;
+ zs_property_t *prop, *propb;
+ uint64_t resa, resb;
+ uint_t uinta, uintb;
+ int i, res;
+
+ prop = alloca(zs_property_size());
+ propb = alloca(zs_property_size());
+
+ for (i = 0; i < arg_sort_count; i++) {
+
+ /* Sort by order of selection */
+ switch (g_sorts[i]) {
+ case ZSTAT_SORT_USED:
+ resa = zs_resource_used_zone_uint64(zonea, g_sort_by);
+ resb = zs_resource_used_zone_uint64(zoneb, g_sort_by);
+ break;
+ case ZSTAT_SORT_CAP:
+ resa = zs_zone_limit_uint64(zonea, g_sort_by);
+ if (resa == ZS_LIMIT_NONE)
+ resa = 0;
+ resb = zs_zone_limit_uint64(zoneb, g_sort_by);
+ if (resb == ZS_LIMIT_NONE)
+ resb = 0;
+ break;
+ case ZSTAT_SORT_PCAP:
+ uinta = zs_zone_limit_used_pct(zonea, g_sort_by);
+ uintb = zs_zone_limit_used_pct(zoneb, g_sort_by);
+ if (uinta == ZS_PCT_NONE)
+ resa = 0;
+ else
+ resa = uinta;
+ if (uintb == ZS_PCT_NONE)
+ resb = 0;
+ else
+ resb = uintb;
+ break;
+ case ZSTAT_SORT_SHR:
+ zs_zone_property(zonea, ZS_PZ_PROP_CPU_SHARES, prop);
+ resa = zs_property_uint64(prop);
+ if (resa == ZS_LIMIT_NONE)
+ resa = 0;
+ zs_zone_property(zoneb, ZS_PZ_PROP_CPU_SHARES, prop);
+ resb = zs_property_uint64(prop);
+ if (resb == ZS_LIMIT_NONE)
+ resb = 0;
+ break;
+ case ZSTAT_SORT_PSHRU:
+ uinta = zs_zone_limit_used_pct(zonea,
+ ZS_LIMIT_CPU_SHARES);
+ uintb = zs_zone_limit_used_pct(zoneb,
+ ZS_LIMIT_CPU_SHARES);
+ if (uinta == ZS_PCT_NONE)
+ resa = 0;
+ else
+ resa = uinta;
+ if (uintb == ZS_PCT_NONE)
+ resb = 0;
+ else
+ resb = uintb;
+ break;
+ case ZSTAT_SORT_NAME:
+ zs_zone_property(zonea, ZS_ZONE_PROP_NAME, prop);
+ zs_zone_property(zoneb, ZS_ZONE_PROP_NAME, propb);
+
+ res = strcmp(zs_property_string(prop),
+ zs_property_string(propb));
+ if (res != 0)
+ return (res);
+ break;
+ default:
+ exit(zonestat_error(gettext("Internal sort error")));
+ }
+ if (resa < resb)
+ return (1);
+ if (resb < resa)
+ return (-1);
+ }
+ /* No difference, return 0 */
+ return (0);
+}
+/*
+ * Sort psets. Default pset first, then shared psets, then dedicated
+ * psets.
+ */
+static int
+zonestat_pset_compare(const void *a, const void *b)
+{
+ zs_pset_t *pseta = *(zs_pset_t **)a;
+ zs_pset_t *psetb = *(zs_pset_t **)b;
+ zs_property_t *p;
+ uint_t typea, typeb;
+
+
+ p = (zs_property_t *)alloca(zs_property_size());
+ zs_pset_property(pseta, ZS_PSET_PROP_CPUTYPE, p);
+ typea = zs_property_uint(p);
+ zs_pset_property(psetb, ZS_PSET_PROP_CPUTYPE, p);
+ typeb = zs_property_uint(p);
+
+ if (typea == ZS_CPUTYPE_DEFAULT_PSET)
+ return (-1);
+ if (typeb == ZS_CPUTYPE_DEFAULT_PSET)
+ return (1);
+ if (typea == ZS_CPUTYPE_POOL_PSET)
+ return (-1);
+ if (typeb == ZS_CPUTYPE_POOL_PSET)
+ return (1);
+ if (typea == ZS_CPUTYPE_PSRSET_PSET)
+ return (-1);
+ if (typeb == ZS_CPUTYPE_PSRSET_PSET)
+ return (1);
+
+ return (0);
+}
+
+static int
+zonestat_pz_compare_usage(const void *a, const void *b)
+{
+ zs_pset_zone_t *zonea = *(zs_pset_zone_t **)a;
+ zs_pset_zone_t *zoneb = *(zs_pset_zone_t **)b;
+ zs_property_t *prop, *propb;
+ uint64_t resa, resb;
+ uint_t uinta, uintb;
+ int i, res;
+
+ prop = alloca(zs_property_size());
+ propb = alloca(zs_property_size());
+
+ for (i = 0; i < arg_sort_count; i++) {
+
+ /* Sort by order of selection */
+ switch (g_sorts[i]) {
+ case ZSTAT_SORT_USED:
+ resa = zs_pset_zone_used_cpus(zonea);
+ resb = zs_pset_zone_used_cpus(zoneb);
+ break;
+ case ZSTAT_SORT_CAP:
+ zs_pset_zone_property(zonea, ZS_PZ_PROP_CPU_CAP,
+ prop);
+ resa = zs_property_uint64(prop);
+ if (resa == ZS_LIMIT_NONE)
+ resa = 0;
+ zs_pset_zone_property(zoneb, ZS_PZ_PROP_CPU_CAP,
+ prop);
+ resb = zs_property_uint64(prop);
+ if (resb == ZS_LIMIT_NONE)
+ resb = 0;
+ break;
+ case ZSTAT_SORT_PCAP:
+ uinta = zs_pset_zone_used_pct(zonea, ZS_PZ_PCT_CPU_CAP);
+ uintb = zs_pset_zone_used_pct(zoneb, ZS_PZ_PCT_CPU_CAP);
+ if (uinta == ZS_PCT_NONE)
+ resa = 0;
+ else
+ resa = uinta;
+ if (uintb == ZS_PCT_NONE)
+ resb = 0;
+ else
+ resb = uintb;
+ break;
+ case ZSTAT_SORT_SHR:
+ zs_pset_zone_property(zonea, ZS_PZ_PROP_CPU_SHARES,
+ prop);
+ resa = zs_property_uint64(prop);
+ if (resa == ZS_LIMIT_NONE)
+ resa = 0;
+ zs_pset_zone_property(zoneb, ZS_PZ_PROP_CPU_SHARES,
+ prop);
+ resb = zs_property_uint64(prop);
+ if (resb == ZS_LIMIT_NONE)
+ resb = 0;
+ break;
+ case ZSTAT_SORT_PSHRU:
+ uinta = zs_pset_zone_used_pct(zonea,
+ ZS_PZ_PCT_CPU_SHARES);
+ uintb = zs_pset_zone_used_pct(zoneb,
+ ZS_PZ_PCT_CPU_SHARES);
+ if (uinta == ZS_PCT_NONE)
+ resa = 0;
+ else
+ resa = uinta;
+ if (uintb == ZS_PCT_NONE)
+ resb = 0;
+ else
+ resb = uintb;
+ break;
+ case ZSTAT_SORT_NAME:
+ zs_zone_property(zs_pset_zone_get_zone(zonea),
+ ZS_ZONE_PROP_NAME, prop);
+ zs_zone_property(zs_pset_zone_get_zone(zoneb),
+ ZS_ZONE_PROP_NAME, propb);
+
+ res = strcmp(zs_property_string(prop),
+ zs_property_string(propb));
+ if (res != 0)
+ return (res);
+ break;
+ default:
+ exit(zonestat_error(gettext("Internal sort error")));
+ }
+ if (resa < resb)
+ return (1);
+ if (resb < resa)
+ return (-1);
+ }
+ /* No difference, return 0 */
+ return (0);
+}
+
+
+static void
+zonestat_print_summary(int report_fmt, zs_usage_t *u)
+{
+ int num, i;
+ zs_zone_t *z;
+ uint64_t cpus, online, tot_mem, tot_vm;
+ uint64_t cused, mused, vused;
+ uint_t ppart, pshru, pccap, mpct, pmcap, vpct, pvcap;
+ char zonename[ZS_ZONENAME_MAX];
+ zs_property_t *prop;
+ size_t namewidth = 0, len;
+
+ prop = (zs_property_t *)alloca(zs_property_size());
+
+ zs_resource_property(u, ZS_RESOURCE_CPU, ZS_RESOURCE_PROP_CPU_TOTAL,
+ prop);
+ cpus = zs_property_uint64(prop);
+
+ zs_resource_property(u, ZS_RESOURCE_CPU,
+ ZS_RESOURCE_PROP_CPU_ONLINE, prop);
+ online = zs_property_uint64(prop);
+
+ tot_mem = zs_resource_total_uint64(u, ZS_RESOURCE_RAM_RSS);
+ tot_vm = zs_resource_total_uint64(u, ZS_RESOURCE_VM);
+
+again:
+ num = zs_zone_list(u, g_zone_list, g_zone_num);
+ if (num > g_zone_num) {
+ if (g_zone_list != NULL)
+ free(g_zone_list);
+ g_zone_list = (zs_zone_t **) malloc(sizeof (zs_zone_t *) * num);
+ g_zone_num = num;
+ goto again;
+ }
+
+ /* Find the longest zone name to set output width. */
+ namewidth = ZSTAT_SUM_MIN_ZONENAME;
+ for (i = 0; i < num; i++) {
+ z = g_zone_list[i];
+ (void) zs_zone_property(z, ZS_ZONE_PROP_NAME, prop);
+ len = strlen(zs_property_string(prop));
+ if (len > namewidth)
+ namewidth = len;
+ }
+ zonestat_print_summary_header(namewidth, report_fmt, cpus, online,
+ tot_mem, tot_vm);
+
+ zonestat_qsort(g_zone_list, num, sizeof (zs_zone_t *),
+ zonestat_zone_compare_resource, g_sort_summary);
+
+ cused = zs_resource_used_uint64(u, ZS_RESOURCE_CPU, ZS_USER_ALL);
+ mused = zs_resource_used_uint64(u, ZS_RESOURCE_RAM_RSS, ZS_USER_ALL);
+ vused = zs_resource_used_uint64(u, ZS_RESOURCE_VM, ZS_USER_ALL);
+
+ ppart = zs_resource_used_pct(u, ZS_RESOURCE_CPU, ZS_USER_ALL);
+ mpct = zs_resource_used_pct(u, ZS_RESOURCE_RAM_RSS, ZS_USER_ALL);
+ vpct = zs_resource_used_pct(u, ZS_RESOURCE_VM, ZS_USER_ALL);
+
+ if (opt_line_total) {
+ (void) snprintf(zonename, sizeof (zonename), "[%s]",
+ ZONESTAT_NAME_TOTAL);
+ zonestat_print_summary_zone(namewidth, report_fmt, zonename,
+ cused, ppart, ZS_PCT_NONE, ZS_PCT_NONE, mused, mpct,
+ ZS_PCT_NONE, vused, vpct, ZS_PCT_NONE);
+ }
+ cused = zs_resource_used_uint64(u, ZS_RESOURCE_CPU, ZS_USER_KERNEL);
+ mused = zs_resource_used_uint64(u, ZS_RESOURCE_RAM_RSS, ZS_USER_KERNEL);
+ vused = zs_resource_used_uint64(u, ZS_RESOURCE_VM, ZS_USER_KERNEL);
+
+ ppart = zs_resource_used_pct(u, ZS_RESOURCE_CPU, ZS_USER_KERNEL);
+ mpct = zs_resource_used_pct(u, ZS_RESOURCE_RAM_RSS, ZS_USER_KERNEL);
+ vpct = zs_resource_used_pct(u, ZS_RESOURCE_VM, ZS_USER_KERNEL);
+
+ if (opt_line_system) {
+ (void) snprintf(zonename, sizeof (zonename), "[%s]",
+ ZONESTAT_NAME_SYSTEM);
+ zonestat_print_summary_zone(namewidth, report_fmt, zonename,
+ cused, ppart, ZS_PCT_NONE, ZS_PCT_NONE, mused, mpct,
+ ZS_PCT_NONE, vused, vpct, ZS_PCT_NONE);
+ }
+ for (i = 0; i < num; i++) {
+
+ z = g_zone_list[i];
+
+ zs_zone_property(z, ZS_ZONE_PROP_NAME, prop);
+ (void) strlcpy(zonename, zs_property_string(prop),
+ sizeof (zonename));
+
+ cused = zs_resource_used_zone_uint64(z, ZS_RESOURCE_CPU);
+ mused = zs_resource_used_zone_uint64(z, ZS_RESOURCE_RAM_RSS);
+ vused = zs_resource_used_zone_uint64(z, ZS_RESOURCE_VM);
+
+ ppart = zs_resource_used_zone_pct(z, ZS_RESOURCE_CPU);
+ mpct = zs_resource_used_zone_pct(z, ZS_RESOURCE_RAM_RSS);
+ vpct = zs_resource_used_zone_pct(z, ZS_RESOURCE_VM);
+
+ pshru = zs_zone_limit_used_pct(z, ZS_LIMIT_CPU_SHARES);
+ pccap = zs_zone_limit_used_pct(z, ZS_LIMIT_CPU);
+ pmcap = zs_zone_limit_used_pct(z, ZS_LIMIT_RAM_RSS);
+ pvcap = zs_zone_limit_used_pct(z, ZS_LIMIT_VM);
+
+ zonestat_print_summary_zone(namewidth, report_fmt, zonename,
+ cused, ppart, pccap, pshru, mused, mpct, pmcap, vused, vpct,
+ pvcap);
+ }
+
+ if (!opt_parseable)
+ (void) printf("\n");
+ (void) fflush(stdout);
+}
+
+static void
+zonestat_print_res(int report_fmt, char *header, char *sizename, char *resname,
+ char *name, zs_usage_t *u, int res, int limit)
+{
+ zs_zone_t *zone;
+ char zonename[ZS_ZONENAME_MAX];
+ uint64_t size;
+ uint64_t used;
+ uint64_t cap;
+ uint_t pct;
+ uint_t pctcap;
+ zs_property_t *prop;
+ int num, i;
+ size_t namelen, len;
+
+ prop = (zs_property_t *)alloca(zs_property_size());
+
+ /* See if resource matches specified resource names */
+ if (zonestat_match_resname(name) == 0)
+ return;
+
+ namelen = strlen(resname);
+ if (ZSTAT_RESOURCE_MIN_RESNAME > namelen)
+ namelen = ZSTAT_RESOURCE_MIN_RESNAME;
+
+ zonestat_print_resource__header(namelen, header, sizename);
+
+ size = zs_resource_total_uint64(u, res);
+
+ if (opt_line_resource)
+ zonestat_print_resource_(namelen, report_fmt, resname, name,
+ size);
+
+again:
+ num = zs_zone_list(u, g_zone_list, g_zone_num);
+ if (num > g_zone_num) {
+ if (g_zone_list != NULL)
+ free(g_zone_list);
+ g_zone_list = (zs_zone_t **) malloc(sizeof (zs_zone_t *) * num);
+ g_zone_num = num;
+ goto again;
+ }
+ namelen = ZSTAT_RESOURCE_MIN_ZONENAME;
+ for (i = 0; i < num; i++) {
+ zone = g_zone_list[i];
+ (void) zs_zone_property(zone, ZS_ZONE_PROP_NAME, prop);
+ len = strlen(zs_property_string(prop));
+ if (len > namelen)
+ namelen = len;
+ }
+
+ zonestat_print_resource_zone_header(namelen);
+
+ used = zs_resource_used_uint64(u, res, ZS_USER_ALL);
+ pct = zs_resource_used_pct(u, res, ZS_USER_ALL);
+
+ if (opt_line_total) {
+ (void) snprintf(zonename, sizeof (zonename), "[%s]",
+ ZONESTAT_NAME_TOTAL);
+ zonestat_print_resource_zone(namelen, report_fmt, resname,
+ name, zonename, used, pct, ZS_LIMIT_NONE, ZS_PCT_NONE);
+ }
+ used = zs_resource_used_uint64(u, res, ZS_USER_KERNEL);
+ pct = zs_resource_used_pct(u, res, ZS_USER_KERNEL);
+
+ if (opt_line_system) {
+ (void) snprintf(zonename, sizeof (zonename), "[%s]",
+ ZONESTAT_NAME_SYSTEM);
+ zonestat_print_resource_zone(namelen, report_fmt, resname, name,
+ zonename, used, pct, ZS_LIMIT_NONE, ZS_PCT_NONE);
+ }
+ zonestat_qsort(g_zone_list, num, sizeof (zs_zone_t *),
+ zonestat_zone_compare_resource, res);
+
+ for (i = 0; i < num; i++) {
+
+ zone = g_zone_list[i];
+ zs_zone_property(zone, ZS_ZONE_PROP_NAME, prop);
+ (void) strlcpy(zonename, zs_property_string(prop),
+ sizeof (zonename));
+
+ if (zonestat_match_zonename(zonename) == 0)
+ continue;
+
+ used = zs_resource_used_zone_uint64(zone, res);
+ pct = zs_resource_used_zone_pct(zone, res);
+
+ cap = zs_zone_limit_uint64(zone, limit);
+ pctcap = zs_zone_limit_used_pct(zone, limit);
+
+ if (opt_line_zones)
+ zonestat_print_resource_zone(namelen, report_fmt,
+ resname, name, zonename, used, pct, cap, pctcap);
+ }
+ if (!opt_parseable)
+ (void) printf("\n");
+}
+
+static void
+zonestat_print_cpu_res_header(size_t namelen)
+{
+ char name_format[ZS_NAME_STRLEN];
+
+ if (opt_parseable)
+ return;
+
+ (void) snprintf(name_format, sizeof (name_format), "%%-%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, "PROCESSOR_SET");
+ (void) printf(ZSTAT_CPU_RES_FORMAT, "TYPE", "ONLINE/CPUS", "MIN/MAX");
+}
+static void
+zonestat_print_cpu_zone_header(size_t namelen)
+{
+ char name_format[ZS_NAME_STRLEN];
+
+ if (opt_parseable)
+ return;
+
+ (void) snprintf(name_format, sizeof (name_format), "%%%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, "ZONE");
+
+ (void) printf(ZSTAT_CPU_ZONE_FORMAT, "USED", "PCT", "CAP",
+ "%CAP", "SHRS", "%SHR", "%SHRU");
+}
+
+static void
+zonestat_print_cpu_res(size_t namelen, int report_fmt, char *cputype,
+ char *name, uint64_t online, uint64_t size, uint64_t min, uint64_t max,
+ timestruc_t *ts)
+{
+ char online_str[ZS_UINT64_STRLEN];
+ char size_str[ZS_UINT64_STRLEN];
+ char min_str[ZS_UINT64_STRLEN];
+ char max_str[ZS_UINT64_STRLEN];
+ char cpus_str[ZS_UINT64_STRLEN + ZS_UINT64_STRLEN + 1];
+ char minmax_str[ZS_UINT64_STRLEN + ZS_UINT64_STRLEN + 1];
+ char ts_str[ZS_TIME_STRLEN];
+ char name_format[ZS_NAME_STRLEN];
+
+ char *label;
+
+ format_uint64(online, online_str, sizeof (online_str));
+ format_uint64(size, size_str, sizeof (size_str));
+ format_uint64(min, min_str, sizeof (min_str));
+ format_uint64(max, max_str, sizeof (max_str));
+ format_ts(ts, ts_str, sizeof (ts_str), B_FALSE);
+
+ if (opt_parseable) {
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+
+ (void) printf("%s:%s:%s:%s:[%s]:%s:%s:%s:%s:%s\n", label,
+ ZONESTAT_PROCESSOR_SET, cputype, name,
+ ZONESTAT_NAME_RESOURCE, online_str, size_str, min_str,
+ max_str, ts_str);
+ return;
+ }
+
+ (void) snprintf(cpus_str, sizeof (cpus_str), "%s/%s", online_str,
+ size_str);
+ (void) snprintf(minmax_str, sizeof (minmax_str), "%s/%s", min_str,
+ max_str);
+
+ (void) snprintf(name_format, sizeof (name_format), "%%-%ds ", namelen);
+ /* LINTED */
+ (void) printf(name_format, name);
+ (void) printf(ZSTAT_CPU_RES_FORMAT, cputype, cpus_str, minmax_str);
+}
+
+static void
+zonestat_print_cpu_zone(size_t namelen, int report_fmt, char *cputype,
+ char *name, char *zonename, uint64_t used, uint_t pct, uint64_t cap,
+ uint_t pct_cap, uint64_t shares, uint_t scheds, uint_t pct_shares,
+ uint_t pct_shares_used, timestruc_t *ts, boolean_t report_conflict)
+{
+ char used_str[ZS_UINT64_STRLEN];
+ char pct_str[ZS_PCT_STRLEN];
+ char cap_str[ZS_UINT64_STRLEN];
+ char pct_cap_str[ZS_PCT_STRLEN];
+ char shares_str[ZS_UINT64_STRLEN];
+ char pct_shares_str[ZS_PCT_STRLEN];
+ char pct_shares_used_str[ZS_PCT_STRLEN];
+ char ts_str[ZS_TIME_STRLEN];
+ char name_format[ZS_NAME_STRLEN];
+ char *label;
+
+ format_cpu(used, used_str, sizeof (used_str));
+ format_pct(pct, pct_str, sizeof (pct_str));
+ format_ts(ts, ts_str, sizeof (ts_str), B_FALSE);
+
+ if (cap == ZS_LIMIT_NONE)
+ (void) strlcpy(cap_str, "-", sizeof (cap_str));
+ else
+ format_cpu(cap, cap_str, sizeof (cap_str));
+
+ if (pct_cap == ZS_PCT_NONE)
+ (void) strlcpy(pct_cap_str, "-", sizeof (pct_cap_str));
+ else
+ format_pct(pct_cap, pct_cap_str, sizeof (pct_cap_str));
+
+ if ((scheds & ZS_SCHED_CONFLICT) &&
+ (!(scheds & ZS_SCHED_FSS)))
+ (void) strlcpy(shares_str, "no-fss", sizeof (shares_str));
+ else if (shares == ZS_LIMIT_NONE)
+ (void) strlcpy(shares_str, "-", sizeof (shares_str));
+ else if (shares == ZS_SHARES_UNLIMITED)
+ (void) strlcpy(shares_str, "inf", sizeof (shares_str));
+ else
+ format_uint64(shares, shares_str, sizeof (shares_str));
+
+ if (pct_shares == ZS_PCT_NONE)
+ (void) strlcpy(pct_shares_str, "-", sizeof (pct_shares_str));
+ else
+ format_pct(pct_shares, pct_shares_str,
+ sizeof (pct_shares_str));
+
+ if (pct_shares_used == ZS_PCT_NONE) {
+ (void) strlcpy(pct_shares_used_str, "-",
+ sizeof (pct_shares_used_str));
+ } else {
+ format_pct(pct_shares_used, pct_shares_used_str,
+ sizeof (pct_shares_used_str));
+ }
+ if (opt_parseable) {
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+
+ (void) printf("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n", label,
+ ZONESTAT_PROCESSOR_SET, cputype, name, zonename, used_str,
+ pct_str, cap_str, pct_cap_str, shares_str, pct_shares_str,
+ pct_shares_used_str, ts_str);
+ return;
+ } else {
+ (void) snprintf(name_format, sizeof (name_format), "%%%ds ",
+ namelen);
+ /* LINTED */
+ (void) printf(name_format, zonename);
+
+ (void) printf(ZSTAT_CPU_ZONE_FORMAT, used_str,
+ pct_str, cap_str, pct_cap_str, shares_str, pct_shares_str,
+ pct_shares_used_str);
+ }
+ /* Report if zone has mix of schedulers conflicting with FSS */
+ if (report_conflict && (scheds & ZS_SCHED_CONFLICT) &&
+ (scheds & ZS_SCHED_FSS)) {
+ /* LINTED */
+ (void) printf(name_format, "");
+ (void) printf(" mixed schedulers found:");
+ (void) printf(" FSS");
+ if (scheds & ZS_SCHED_TS)
+ (void) printf(", TS");
+ if (scheds & ZS_SCHED_IA)
+ (void) printf(", IA");
+ if (scheds & ZS_SCHED_FX)
+ (void) printf(", FX");
+ (void) printf("\n");
+ }
+}
+
+static void
+zonestat_print_pset(int report_fmt, zs_pset_t *pset, char *cputype)
+{
+ zs_pset_zone_t *pz;
+ zs_zone_t *zone;
+ uint64_t cpus;
+ uint64_t size;
+ uint64_t min;
+ uint64_t max;
+ uint_t scheds;
+ uint64_t used;
+ uint_t pct;
+ uint64_t cap;
+ uint_t pct_cap;
+ uint64_t shares;
+ uint_t pct_shares;
+ uint_t pct_shares_used;
+ char psetname[ZS_PSETNAME_MAX];
+ char zonename[ZS_PSETNAME_MAX];
+ char *name;
+ zs_property_t *prop;
+ boolean_t zone_match;
+ int num, i;
+ timestruc_t ts;
+ size_t namelen, len;
+
+ prop = (zs_property_t *)alloca(zs_property_size());
+
+ zs_pset_property(pset, ZS_PSET_PROP_NAME, prop);
+ (void) strlcpy(psetname, zs_property_string(prop), sizeof (psetname));
+
+ /* Check if pset contains specified zone */
+ if (arg_zonename_count > 0) {
+ zone_match = B_FALSE;
+ for (pz = zs_pset_zone_first(pset); pz != NULL;
+ pz = zs_pset_zone_next(pset, pz)) {
+ zone = zs_pset_zone_get_zone(pz);
+ (void) zs_zone_property(zone, ZS_ZONE_PROP_NAME, prop);
+ (void) strlcpy(zonename, zs_property_string(prop),
+ sizeof (zonename));
+
+ if (zonestat_match_zonename(zonename) == 1) {
+ zone_match = B_TRUE;
+ break;
+ }
+ }
+ if (zone_match == B_FALSE)
+ return;
+ }
+
+ if (zonestat_match_resname(psetname) == 0)
+ return;
+
+ zs_pset_property(pset, ZS_PSET_PROP_ONLINE, prop);
+ cpus = zs_property_uint64(prop);
+ zs_pset_property(pset, ZS_PSET_PROP_SIZE, prop);
+ size = zs_property_uint64(prop);
+ zs_pset_property(pset, ZS_PSET_PROP_MIN, prop);
+ min = zs_property_uint64(prop);
+ zs_pset_property(pset, ZS_PSET_PROP_MAX, prop);
+ max = zs_property_uint64(prop);
+ zs_pset_total_time(pset, &ts);
+
+ /* Strip off SUNWtmp_ from pset name */
+ name = psetname;
+ if (strncmp(psetname, "SUNWtmp_", strlen("SUNWtmp_")) == 0) {
+ name = strchr(psetname, '_');
+ name++;
+ }
+
+ /* Strip off SUNWlegacy_pst for psrset psets */
+ if (strncmp(psetname, "SUNWlegacy_pset_",
+ strlen("SUNWlegacy_pset_")) == 0) {
+ name = strrchr(psetname, '_');
+ name++;
+ }
+
+ namelen = strlen(name);
+ if (ZSTAT_CPU_MIN_PSETNAME > namelen)
+ namelen = ZSTAT_CPU_MIN_PSETNAME;
+
+ zonestat_print_cpu_res_header(namelen);
+
+ if (opt_line_resource)
+ zonestat_print_cpu_res(namelen, report_fmt, cputype, name, cpus,
+ size, min, max, &ts);
+
+again:
+ num = zs_pset_zone_list(pset, g_pz_list, g_pz_num);
+ if (num > g_pz_num) {
+ if (g_pz_list != NULL)
+ free(g_pz_list);
+ g_pz_list = (zs_pset_zone_t **)malloc(
+ sizeof (zs_pset_zone_t *) * num);
+ g_pz_num = num;
+ goto again;
+ }
+
+ /* Find longest zone name in pset */
+ namelen = ZSTAT_CPU_MIN_ZONENAME;
+ for (i = 0; i < num; i++) {
+ pz = g_pz_list[i];
+ zone = zs_pset_zone_get_zone(pz);
+ zs_zone_property(zone, ZS_ZONE_PROP_NAME, prop);
+ len = strlen(zs_property_string(prop));
+ if (len > namelen)
+ namelen = len;
+ }
+
+ qsort(g_pz_list, num, sizeof (zs_pset_zone_t *),
+ zonestat_pz_compare_usage);
+
+ zonestat_print_cpu_zone_header(namelen);
+
+ zs_pset_property(pset, ZS_PSET_PROP_CPU_SHARES, prop);
+ shares = zs_property_uint64(prop);
+ zs_pset_property(pset, ZS_PSET_PROP_SCHEDULERS, prop);
+ scheds = zs_property_uint(prop);
+
+ zs_pset_used_time(pset, ZS_USER_ALL, &ts);
+ used = zs_pset_used_cpus(pset, ZS_USER_ALL);
+ pct = zs_pset_used_pct(pset, ZS_USER_ALL);
+
+ if (opt_line_total) {
+ (void) snprintf(zonename, sizeof (zonename), "[%s]",
+ ZONESTAT_NAME_TOTAL);
+ zonestat_print_cpu_zone(namelen, report_fmt, cputype, name,
+ zonename, used, pct, ZS_LIMIT_NONE, ZS_PCT_NONE, shares,
+ scheds, ZS_PCT_NONE, ZS_PCT_NONE, &ts, B_FALSE);
+ }
+ zs_pset_used_time(pset, ZS_USER_KERNEL, &ts);
+ used = zs_pset_used_cpus(pset, ZS_USER_KERNEL);
+ pct = zs_pset_used_pct(pset, ZS_USER_KERNEL);
+
+ if (opt_line_system) {
+ (void) snprintf(zonename, sizeof (zonename), "[%s]",
+ ZONESTAT_NAME_SYSTEM);
+ zonestat_print_cpu_zone(namelen, report_fmt, cputype, name,
+ zonename, used, pct, ZS_LIMIT_NONE, ZS_PCT_NONE,
+ ZS_LIMIT_NONE, 0, ZS_PCT_NONE, ZS_PCT_NONE, &ts, B_FALSE);
+ }
+ for (i = 0; i < num; i++) {
+
+ pz = g_pz_list[i];
+ zone = zs_pset_zone_get_zone(pz);
+ zs_zone_property(zone, ZS_ZONE_PROP_NAME, prop);
+ (void) strlcpy(zonename, zs_property_string(prop),
+ sizeof (zonename));
+
+ if (zonestat_match_zonename(zonename) == 0)
+ continue;
+
+ zs_pset_zone_property(pz, ZS_PZ_PROP_CPU_CAP, prop);
+ cap = zs_property_uint64(prop);
+
+ zs_pset_zone_property(pz, ZS_PZ_PROP_CPU_SHARES, prop);
+ shares = zs_property_uint64(prop);
+ zs_pset_zone_property(pz, ZS_PZ_PROP_SCHEDULERS, prop);
+ scheds = zs_property_uint(prop);
+
+ used = zs_pset_zone_used_cpus(pz);
+ zs_pset_zone_used_time(pz, &ts);
+ pct = zs_pset_zone_used_pct(pz, ZS_PZ_PCT_PSET);
+ pct_cap = zs_pset_zone_used_pct(pz, ZS_PZ_PCT_CPU_CAP);
+ pct_shares = zs_pset_zone_used_pct(pz, ZS_PZ_PCT_PSET_SHARES);
+ pct_shares_used = zs_pset_zone_used_pct(pz,
+ ZS_PZ_PCT_CPU_SHARES);
+
+ if (opt_line_zones)
+ zonestat_print_cpu_zone(namelen, report_fmt, cputype,
+ name, zonename, used, pct, cap, pct_cap, shares,
+ scheds, pct_shares, pct_shares_used, &ts, B_TRUE);
+ }
+ if (!opt_parseable)
+ (void) printf("\n");
+}
+
+/* ARGSUSED */
+static void
+zonestat_quithandler(int sig)
+{
+ g_quit = B_TRUE;
+}
+
+static void
+zonestat_print_footer(int report_fmt)
+{
+ char *label;
+
+ if (!opt_parseable)
+ return;
+
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+ (void) printf("%s:%s:", label, ZONESTAT_NAME_FOOTER);
+ zonestat_print_timestamp(g_now_time);
+ (void) printf("%d:%ld\n", g_interval, g_seconds);
+ (void) fflush(stdout);
+}
+
+static void
+zonestat_print_header(int report_fmt)
+{
+ char *label;
+ timestruc_t ts;
+ char string[ZS_TIME_STRLEN];
+
+ if (!opt_parseable) {
+
+ /* Human readable header */
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(", ");
+ }
+ if (report_fmt == ZSTAT_REPORT_FMT_INTERVAL) {
+ ts.tv_sec = g_seconds;
+ ts.tv_nsec = 0;
+ format_ts(&ts, string, sizeof (string), B_TRUE);
+ (void) printf("Interval: %d, Duration: %s\n", g_count,
+ string);
+ (void) fflush(stdout);
+ return;
+ } else {
+ switch (report_fmt) {
+ case ZSTAT_REPORT_FMT_TOTAL:
+ label = "Report: Total Usage";
+ break;
+ case ZSTAT_REPORT_FMT_AVERAGE:
+ label = "Report: Average Usage";
+ break;
+ case ZSTAT_REPORT_FMT_HIGH:
+ label = "Report: High Usage";
+ break;
+ default:
+ exit(zonestat_error(gettext(
+ "Internal error, invalid header")));
+ }
+ /* Left are the report header formats */
+ (void) printf("%s\n", label);
+ (void) printf(" Start: ");
+ zonestat_print_timestamp(g_start_time);
+ (void) printf("\n End: ");
+ zonestat_print_timestamp(g_end_time);
+ (void) printf("\n");
+ ts.tv_sec = g_seconds;
+ ts.tv_nsec = 0;
+ format_ts(&ts, string, sizeof (string), B_TRUE);
+ (void) printf(" Intervals: %d, Duration: %s\n",
+ g_count, string);
+
+ (void) fflush(stdout);
+ return;
+ }
+ }
+
+ if (!opt_line_header)
+ return;
+
+ /* Parseable header */
+ if (opt_timestamp) {
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":");
+ }
+ label = zonestat_get_plabel(report_fmt);
+
+ (void) printf("%s:%s:", label, ZONESTAT_NAME_HEADER);
+ if (report_fmt == ZSTAT_REPORT_FMT_INTERVAL) {
+ (void) printf("since-last-interval:");
+ zonestat_print_timestamp(g_now_time);
+ (void) printf(":%d:%ld\n", g_count, g_seconds);
+ (void) fflush(stdout);
+ return;
+ }
+
+ /* Left are the report header formats */
+ zonestat_print_timestamp(g_start_time);
+ (void) printf(":");
+ zonestat_print_timestamp(g_end_time);
+ (void) printf(":");
+ (void) printf("%d:%ld\n", g_interval, g_seconds);
+ (void) fflush(stdout);
+}
+
+static void
+zonestat_print_psets(int report_fmt, zs_usage_t *u)
+{
+ zs_pset_t *pset;
+ char *psettype;
+ uint_t cputype, num, i;
+ zs_property_t *p;
+
+again:
+ num = zs_pset_list(u, g_pset_list, g_pset_num);
+ if (num > g_pset_num) {
+ if (g_pset_list != NULL)
+ free(g_pset_list);
+ g_pset_list = (zs_pset_t **)malloc(
+ sizeof (zs_pset_t *) * num);
+ g_pset_num = num;
+ goto again;
+ }
+
+ /* Sort, default pset first, then pool, psrset, and dedicated psets */
+ qsort(g_pset_list, num, sizeof (zs_pset_t *), zonestat_pset_compare);
+
+ p = (zs_property_t *)alloca(zs_property_size());
+ for (i = 0; i < num; i++) {
+ pset = g_pset_list[i];
+ (void) zs_pset_property(pset, ZS_PSET_PROP_CPUTYPE, p);
+ cputype = zs_property_uint(p);
+ if (cputype == ZS_CPUTYPE_DEFAULT_PSET &&
+ (g_resources & (ZSTAT_RES_PSETS |
+ ZSTAT_RES_DEFAULT_PSET))) {
+ psettype = ZONESTAT_DEFAULT_PSET;
+ } else if (cputype == ZS_CPUTYPE_POOL_PSET &&
+ (g_resources & ZSTAT_RES_PSETS)) {
+ psettype = ZONESTAT_POOL_PSET;
+ } else if (cputype == ZS_CPUTYPE_PSRSET_PSET &&
+ (g_resources & ZSTAT_RES_PSETS)) {
+ psettype = ZONESTAT_PSRSET_PSET;
+ } else if (cputype == ZS_CPUTYPE_DEDICATED &&
+ (g_resources & ZSTAT_RES_PSETS)) {
+ psettype = ZONESTAT_DEDICATED_CPU;
+ } else {
+ continue;
+ }
+ zonestat_print_pset(report_fmt, pset, psettype);
+ }
+}
+
+static void
+zonestat_print_resources(int report_fmt, zs_usage_t *usage)
+{
+ if (g_resources & ZSTAT_RES_SUMMARY)
+ zonestat_print_summary(report_fmt, usage);
+
+ if (g_resources & ZSTAT_RES_PHYSICAL_MEMORY)
+ zonestat_print_res(report_fmt, "PHYSICAL-MEMORY",
+ "SYSTEM MEMORY", ZONESTAT_PHYSICAL_MEMORY,
+ ZONESTAT_NAME_MEM_DEFAULT, usage,
+ ZS_RESOURCE_RAM_RSS, ZS_LIMIT_RAM_RSS);
+ if (g_resources & ZSTAT_RES_VIRTUAL_MEMORY)
+ zonestat_print_res(report_fmt, "VIRTUAL-MEMORY",
+ "SYSTEM MEMORY", ZONESTAT_VIRTUAL_MEMORY,
+ ZONESTAT_NAME_VM_DEFAULT, usage,
+ ZS_RESOURCE_VM, ZS_LIMIT_VM);
+ if (g_resources & ZSTAT_RES_LOCKED_MEMORY)
+ zonestat_print_res(report_fmt, "LOCKED-MEMORY", "SYSTEM MEMORY",
+ ZONESTAT_LOCKED_MEMORY, ZONESTAT_NAME_MEM_DEFAULT, usage,
+ ZS_RESOURCE_RAM_LOCKED, ZS_LIMIT_RAM_LOCKED);
+
+ if (g_resources & (ZSTAT_RES_PSETS | ZSTAT_RES_DEFAULT_PSET))
+ zonestat_print_psets(report_fmt, usage);
+
+ if (g_resources & ZSTAT_RES_PROCESSES)
+ zonestat_print_res(report_fmt, "PROCESSES", "SYSTEM LIMIT",
+ ZONESTAT_PROCESSES, ZONESTAT_NAME_SYSTEM_LIMIT,
+ usage, ZS_RESOURCE_PROCESSES, ZS_LIMIT_PROCESSES);
+
+ if (g_resources & ZSTAT_RES_LWPS)
+ zonestat_print_res(report_fmt, "LWPS", "SYSTEM LIMIT",
+ ZONESTAT_LWPS, ZONESTAT_NAME_SYSTEM_LIMIT, usage,
+ ZS_RESOURCE_LWPS, ZS_LIMIT_LWPS);
+ if (g_resources & ZSTAT_RES_LOFI)
+ zonestat_print_res(report_fmt, "LOFI", "SYSTEM LIMIT",
+ ZONESTAT_LOFI, ZONESTAT_NAME_SYSTEM_LIMIT,
+ usage, ZS_RESOURCE_LOFI, ZS_LIMIT_LOFI);
+
+ if (g_resources & ZSTAT_RES_SHM_MEMORY)
+ zonestat_print_res(report_fmt, "SHM_MEMORY", "SYSTEM LIMIT",
+ ZONESTAT_SHM_MEMORY, ZONESTAT_NAME_SYSTEM_LIMIT,
+ usage, ZS_RESOURCE_SHM_MEMORY, ZS_LIMIT_SHM_MEMORY);
+
+ if (g_resources & ZSTAT_RES_SHM_IDS)
+ zonestat_print_res(report_fmt, "SHM_IDS", "SYSTEM LIMIT",
+ ZONESTAT_SHM_IDS, ZONESTAT_NAME_SYSTEM_LIMIT,
+ usage, ZS_RESOURCE_SHM_IDS, ZS_LIMIT_SHM_IDS);
+
+ if (g_resources & ZSTAT_RES_SEM_IDS)
+ zonestat_print_res(report_fmt, "SEM_IDS", "SYSTEM LIMIT",
+ ZONESTAT_SEM_IDS, ZONESTAT_NAME_SYSTEM_LIMIT,
+ usage, ZS_RESOURCE_SEM_IDS, ZS_LIMIT_SEM_IDS);
+
+ if (g_resources & ZSTAT_RES_MSG_IDS)
+ zonestat_print_res(report_fmt, "MSG_IDS", "SYSTEM LIMIT",
+ ZONESTAT_MSG_IDS, ZONESTAT_NAME_SYSTEM_LIMIT,
+ usage, ZS_RESOURCE_MSG_IDS, ZS_LIMIT_MSG_IDS);
+}
+
+/*
+ * Adds comma seperated list of names to array of names
+ * Returns new total number of names.
+ */
+static size_t
+zonestat_parse_names(char *names, char ***namelist, size_t count)
+{
+ size_t num, i;
+ char *next, *string;
+
+ string = strdup(names);
+ if (string == NULL)
+ exit(zonestat_error(gettext("Out of Memory")));
+
+ /* count names, delimiting with '\0'. */
+ next = string;
+ num = 1;
+ while ((next = strchr(next, ',')) != NULL) {
+ *next++ = '\0';
+ num++;
+ }
+
+ /* Resise names array */
+ *namelist = realloc(*namelist, sizeof (char *) * (num + count));
+ if (*namelist == NULL)
+ exit(zonestat_error(gettext("Out of Memory")));
+
+ /* add names to names array */
+ next = string;
+ for (i = 0; i < num; i++) {
+ (*namelist)[count + i] = next;
+ next += strlen(next) + 1;
+ }
+ return (count + num);
+}
+
+static int
+zonestat_extract_int(char *start, char *end, char **tail)
+{
+ int val;
+ int save;
+
+ save = *end;
+ *end = '\0';
+ errno = 0;
+ val = strtol(start, tail, 0);
+ *end = save;
+ if (errno != 0 || *tail == start)
+ return (-1);
+
+ return (val);
+}
+
+/*
+ * parses and [nh][nm][hs] notation into seconds
+ */
+static int
+zonestat_parse_time(char *string, boolean_t *formatted)
+{
+ int seconds = 0;
+ int minutes = 0;
+ int hours = 0;
+ char *this, *next, *end;
+
+ *formatted = B_FALSE;
+
+ /* Look for special tokens */
+ if (strcmp("default", string) == 0)
+ return (ZSTAT_INTERVAL_DEFAULT);
+
+ if (strcmp("inf", string) == 0)
+ return (ZSTAT_DURATION_INF);
+
+ /* Look for hours */
+ this = string;
+ next = strchr(this, 'h');
+ if (next != NULL) {
+ if ((hours = zonestat_extract_int(this, next, &end)) == -1)
+ return (-1);
+
+ *formatted = B_TRUE;
+ this = next + 1;
+ end++;
+ }
+
+ /* Look for minutes delimiter */
+ next = strrchr(this, 'm');
+ if (next != NULL) {
+ if ((minutes = zonestat_extract_int(this, next, &end)) == -1)
+ return (-1);
+
+ *formatted = B_TRUE;
+ this = next + 1;
+ end++;
+ }
+
+ /* Look for seconds delimiter */
+ next = strrchr(this, 's');
+ if (next != NULL) {
+ if ((seconds = zonestat_extract_int(this, next, &end)) == -1)
+ return (-1);
+
+ *formatted = B_TRUE;
+ this = next + 1;
+ end++;
+ }
+
+ /* No delimiter found. Treat as seconds */
+ if (*formatted == B_FALSE) {
+ errno = 0;
+ seconds = strtol(this, &end, 0);
+ if (errno != 0 || end == this)
+ return (-1);
+ }
+
+ if (*end != '\0')
+ return (-1);
+
+ seconds += (minutes * 60);
+ seconds += (hours * 60 * 60);
+
+ return (seconds);
+}
+
+static void
+zonestat_print_reports(zs_usage_set_t *set)
+{
+ zs_usage_t *usage_print;
+
+ if (opt_report_total == B_TRUE) {
+ usage_print = zs_usage_set_compute(set,
+ ZS_COMPUTE_SET_TOTAL);
+ zonestat_print_header(ZSTAT_REPORT_FMT_TOTAL);
+ zonestat_print_resources(ZSTAT_REPORT_FMT_TOTAL, usage_print);
+ zonestat_print_footer(ZSTAT_REPORT_FMT_TOTAL);
+ (void) fflush(stdout);
+ }
+ if (opt_report_average == B_TRUE) {
+ usage_print = zs_usage_set_compute(set,
+ ZS_COMPUTE_SET_AVERAGE);
+ zonestat_print_header(ZSTAT_REPORT_FMT_AVERAGE);
+ zonestat_print_resources(ZSTAT_REPORT_FMT_AVERAGE, usage_print);
+ zonestat_print_footer(ZSTAT_REPORT_FMT_AVERAGE);
+ (void) fflush(stdout);
+ }
+ if (opt_report_high == B_TRUE) {
+ usage_print = zs_usage_set_compute(set,
+ ZS_COMPUTE_SET_HIGH);
+ zonestat_print_header(ZSTAT_REPORT_FMT_HIGH);
+ zonestat_print_resources(ZSTAT_REPORT_FMT_HIGH, usage_print);
+ zonestat_print_footer(ZSTAT_REPORT_FMT_HIGH);
+ (void) fflush(stdout);
+ }
+}
+
+static void
+zonestat_set_fx()
+{
+ pcinfo_t pcinfo;
+ pcparms_t pcparms;
+
+ (void) strlcpy(pcinfo.pc_clname, "FX", sizeof (pcinfo.pc_clname));
+ if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) {
+ return;
+ }
+ pcparms.pc_cid = pcinfo.pc_cid;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_upri = 60;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_uprilim = 60;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_tqsecs = 0;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_tqnsecs = FX_NOCHANGE;
+ (void) priocntl(P_PID, getpid(), PC_SETPARMS, (caddr_t)&pcparms);
+}
+
+static time_t
+zonestat_time()
+{
+ time_t t;
+
+ t = time(NULL);
+ if (t < 0 && g_quit == B_FALSE)
+ exit(zonestat_error(gettext(
+ "Unable to fetch current time")));
+
+ return (t);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int arg;
+ time_t now, next, start, next_report;
+ zs_usage_t *usage, *usage_last = NULL, *usage_print;
+ zs_usage_set_t *set;
+ boolean_t formatted;
+ scf_simple_prop_t *prop;
+ uint64_t *intervalp;
+ char *not_responding;
+
+ /* Process command line options and args */
+ while ((arg = getopt(argc, argv, "z:r:n:T:R:qpP:S:D?"))
+ != EOF) {
+ switch (arg) {
+ case 'z':
+ opt_zonenames = B_TRUE;
+ arg_zonename_count = zonestat_parse_names(optarg,
+ &arg_zonenames, arg_zonename_count);
+ break;
+ case 'r':
+ arg_restype_count = zonestat_parse_names(optarg,
+ &arg_restypes, arg_restype_count);
+ opt_restypes = B_TRUE;
+ break;
+ case 'n':
+ opt_resnames = B_TRUE;
+ arg_resname_count = zonestat_parse_names(optarg,
+ &arg_resnames, arg_resname_count);
+ break;
+ case 'R':
+ opt_report = B_TRUE;
+ arg_report_count = zonestat_parse_names(optarg,
+ &arg_reports, arg_report_count);
+ break;
+ case 'S':
+ opt_sort = B_TRUE;
+ arg_sort_count = zonestat_parse_names(optarg,
+ &arg_sort_list, arg_sort_count);
+ break;
+ case 'T':
+ opt_timestamp = B_TRUE;
+ if (strcmp(optarg, "u") == 0) {
+ arg_timestamp = ZSTAT_UNIX_TIMESTAMP;
+ } else if (strcmp(optarg, "d") == 0) {
+ arg_timestamp = ZSTAT_DATE_TIMESTAMP;
+ } else if (strcmp(optarg, "i") == 0) {
+ arg_timestamp = ZSTAT_ISO_TIMESTAMP;
+ } else {
+ (void) zonestat_error(gettext(
+ "Invalid -T arg \"%s\". "
+ "Must be 'u', 'i', or 'd'."), optarg);
+ return (zonestat_usage(B_FALSE));
+ }
+ break;
+ case 'q':
+ opt_quiet_intervals = B_TRUE;
+ break;
+ case 'p':
+ opt_parseable = B_TRUE;
+ break;
+ case 'P':
+ opt_line_any = B_TRUE;
+ arg_line_count = zonestat_parse_names(optarg,
+ &arg_line_list, arg_line_count);
+ break;
+ case 'D':
+ opt_debug = B_TRUE;
+ break;
+ case '?':
+ return (zonestat_usage(B_TRUE));
+ default:
+ return (zonestat_usage(B_FALSE));
+ }
+ }
+
+ if (opt_line_any & (!opt_parseable)) {
+ (void) zonestat_error(gettext("-P requires -p"));
+ return (zonestat_usage(B_FALSE));
+ }
+
+ if (opt_timestamp && arg_timestamp == ZSTAT_DATE_TIMESTAMP &&
+ opt_parseable) {
+ (void) zonestat_error(gettext(
+ "-T d invalid with -p. Use -T [u | i]"));
+ return (zonestat_usage(B_FALSE));
+
+ }
+ /* Default to ISO timetamp in parseable output */
+ if (!opt_timestamp && opt_parseable)
+ arg_timestamp = ZSTAT_ISO_TIMESTAMP;
+
+ /* Get the interval and count */
+ optind++;
+ if (argc >= optind) {
+ if ((arg_interval = zonestat_parse_time(argv[optind - 1],
+ &formatted)) < 0 || arg_interval == 0) {
+ (void) zonestat_error(gettext(
+ "Invalid interval: \"%s\""), argv[optind - 1]);
+ return (zonestat_usage(B_FALSE));
+ }
+ } else {
+ (void) zonestat_error(gettext("Interval required."));
+ return (zonestat_usage(B_FALSE));
+ }
+
+ if (arg_interval == ZSTAT_INTERVAL_DEFAULT) {
+ /* Get the configured sample interval */
+ prop = scf_simple_prop_get(NULL,
+ "svc:/system/zones-monitoring:default", "config",
+ "sample_interval");
+
+ if (prop == NULL) {
+ return (zonestat_error(gettext(
+ "Unable to fetch SMF property "
+ "\"config/sample_interval\"")));
+ }
+ if (scf_simple_prop_type(prop) != SCF_TYPE_COUNT) {
+ return (zonestat_error(gettext("Malformed SMF property "
+ "\"config/sample_interval\". Must be of type "
+ "\"count\"")));
+ }
+ intervalp = scf_simple_prop_next_count(prop);
+ arg_interval = *intervalp;
+ if (arg_interval == 0)
+ return (zonestat_error(gettext("Malformed SMF property "
+ "\"config/sample_interval\". Must be greater than"
+ "zero")));
+
+ scf_simple_prop_free(prop);
+ }
+ optind++;
+ if (argc >= optind) {
+ if ((arg_duration = zonestat_parse_time(argv[optind - 1],
+ &formatted)) < 0 || arg_duration == 0) {
+ (void) zonestat_error(gettext(
+ "Invalid duration: \"%s\""), argv[optind - 1]);
+ return (zonestat_usage(B_FALSE));
+ }
+ /* If not formatted [nh][nm][ns], treat as count */
+ if (arg_duration != ZSTAT_DURATION_INF &&
+ formatted == B_FALSE)
+ arg_duration *= arg_interval;
+ } else {
+ arg_duration = ZSTAT_DURATION_INF;
+ }
+ optind++;
+ if (argc >= optind) {
+ if ((arg_report = zonestat_parse_time(argv[optind - 1],
+ &formatted)) < 0 || arg_report == 0) {
+ (void) zonestat_error(gettext(
+ "Invalid report period: \"%s\""), argv[optind - 1]);
+ return (zonestat_usage(B_FALSE));
+ }
+ /* If not formatted as [nh][nm][ns] treat as count */
+ if (formatted == B_FALSE)
+ arg_report *= arg_interval;
+ } else {
+ arg_report = ZSTAT_REPORT_END;
+ }
+
+ if (opt_quiet_intervals && (!opt_report)) {
+ (void) zonestat_error(gettext("-q requires -R"));
+ return (zonestat_usage(B_FALSE));
+ }
+
+ /* Figure out what resources to report on */
+ zonestat_determine_resources();
+ zonestat_determine_reports();
+ zonestat_determine_lines();
+ zonestat_determine_sort();
+
+ /* Done parsing args beyond this point */
+
+ (void) signal(SIGINT, zonestat_quithandler);
+ (void) signal(SIGTERM, zonestat_quithandler);
+ (void) signal(SIGHUP, zonestat_quithandler);
+
+ /* Run at high priority to keep up with busy system */
+ zonestat_set_fx();
+
+ not_responding = gettext(
+ "Zones monitoring service \"svc:/system/zones-monitoring:default\" "
+ "not enabled or responding.");
+
+ /* Open zone statistics */
+ g_zsctl = zs_open();
+ if (g_zsctl == NULL) {
+ if (errno == EPERM)
+ return (zonestat_error(gettext("Permission denied")));
+ if (errno == EINTR || errno == ESRCH) {
+ (void) zonestat_error(not_responding);
+ return (3);
+ }
+ if (errno == ENOTSUP)
+ return (zonestat_error(gettext(
+ "Mismatched zonestat version. "
+ "Re-install system/zones package.")));
+
+ return (zonestat_error(gettext(
+ "Unexpected error. Unable to open zone statistics.")));
+ }
+ usage_last = zs_usage_read(g_zsctl);
+ if (usage_last == NULL) {
+ if (errno == EINTR && g_quit == B_TRUE)
+ return (0);
+ (void) zonestat_error(not_responding);
+ return (3);
+ }
+ set = zs_usage_set_alloc(g_zsctl);
+
+ g_start_time = g_now_time = start = now = zonestat_time();
+ g_interval = arg_interval;
+ g_report_count = g_count = g_seconds = 0;
+
+ if (opt_quiet_intervals == B_FALSE && opt_parseable == B_FALSE)
+ (void) printf(gettext(
+ "Collecting data for first interval...\n"));
+
+ for (;;) {
+ time_t tosleep;
+
+ g_now_time = now = zonestat_time();
+
+ if (arg_report != ZSTAT_REPORT_END)
+ next_report = start + ((g_report_count + 1) *
+ arg_report);
+
+ /*
+ * Sleep till next interval.
+ */
+ g_count++;
+ next = g_start_time + (g_count) * g_interval;
+ /*
+ * Skip to next interval if due to busy system, zonestat did
+ * not complete in time.
+ */
+ while (now >= g_start_time + ((g_count + 1) * g_interval))
+ g_count++;
+
+ while (now < next) {
+ /* Sleep until at next interval */
+ tosleep = next - now;
+ (void) sleep(tosleep);
+ now = zonestat_time();
+ if (g_quit == B_TRUE)
+ goto interval_loop_done;
+ }
+
+ g_seconds = now - start;
+ g_now_time = now;
+ if ((usage = zs_usage_read(g_zsctl)) == NULL) {
+ if (errno == EINTR && g_quit == B_TRUE)
+ break;
+ (void) zonestat_error(not_responding);
+ return (3);
+ }
+
+ /* Compute cpu used since last interval */
+ usage_print = zs_usage_compute(NULL, usage_last,
+ usage, ZS_COMPUTE_USAGE_INTERVAL);
+ if (usage_print == NULL)
+ (void) zonestat_error(gettext("Out of Memory"));
+
+
+ if (opt_quiet_intervals == B_TRUE)
+ goto interval_print_end;
+
+ zonestat_print_header(ZSTAT_REPORT_FMT_INTERVAL);
+ zonestat_print_resources(ZSTAT_REPORT_FMT_INTERVAL,
+ usage_print);
+ zonestat_print_footer(ZSTAT_REPORT_FMT_INTERVAL);
+ (void) fflush(stdout);
+
+interval_print_end:
+ (void) zs_usage_set_add(set, usage_print);
+
+
+ /* Print reports if they are due */
+ if (opt_report && arg_report != ZSTAT_REPORT_END &&
+ now >= next_report) {
+ g_end_time = now;
+ zonestat_print_reports(set);
+ zs_usage_set_free(set);
+ set = zs_usage_set_alloc();
+ g_start_time = now;
+ g_report_count++;
+ }
+ zs_usage_free(usage_last);
+ usage_last = usage;
+ if (arg_duration != ZSTAT_DURATION_INF &&
+ g_seconds >= arg_duration)
+ break;
+ }
+interval_loop_done:
+
+ /* Print last reports if due */
+ g_end_time = g_now_time;
+ if (opt_report && zs_usage_set_count(set) > 0 &&
+ (arg_report == ZSTAT_REPORT_END || now < next_report))
+ zonestat_print_reports(set);
+
+ zs_usage_set_free(set);
+ if (usage_last != NULL)
+ zs_usage_free(usage_last);
+
+ if (g_zsctl != NULL)
+ zs_close(g_zsctl);
+
+ return (0);
+}
diff --git a/usr/src/cmd/zonestat/zonestatd/Makefile b/usr/src/cmd/zonestat/zonestatd/Makefile
new file mode 100644
index 0000000000..c5b574f0a4
--- /dev/null
+++ b/usr/src/cmd/zonestat/zonestatd/Makefile
@@ -0,0 +1,59 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+MANIFEST = zonestat.xml
+SVCMETHOD = svc-zonestat
+PROG = zonestatd
+
+include ../../Makefile.cmd
+
+ROOTCMDDIR = $(ROOTLIB)/zones
+ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)
+
+SRCS = zonestatd.c
+
+CPPFLAGS += -I/usr/include/libxml2
+LDLIBS += -lkstat -lpool -lexacct -lscf \
+ -lcontract -lcmdutils -lumem
+
+LINTFLAGS += -u
+
+OBJS = $(SRCS:%.c=%.o)
+
+.KEEP_STATE:
+
+.PARALLEL:
+
+all: $(PROG)
+
+install: all $(ROOTCMD) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_PROG
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/zonestat/zonestatd/svc-zonestat b/usr/src/cmd/zonestat/zonestatd/svc-zonestat
new file mode 100644
index 0000000000..7776d75406
--- /dev/null
+++ b/usr/src/cmd/zonestat/zonestatd/svc-zonestat
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# 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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+. /lib/svc/share/smf_include.sh
+mode=$1
+
+case "$mode" in
+'stop')
+ smf_kill_contract $2 TERM
+ /usr/lib/zones/zonestatd -c
+ exit $SMF_EXIT_OK
+ ;;
+*)
+ exit $SMF_EXIT_ERR_FATAL
+ ;;
+esac
+
+exit $SMF_EXIT_ERR_FATAL
+
diff --git a/usr/src/cmd/zonestat/zonestatd/zonestat.xml b/usr/src/cmd/zonestat/zonestatd/zonestat.xml
new file mode 100644
index 0000000000..e756cc273a
--- /dev/null
+++ b/usr/src/cmd/zonestat/zonestatd/zonestat.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+
+ Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+-->
+
+<service_bundle type='manifest' name='system/zones-monitoring'>
+
+<service
+ name='system/zones-monitoring'
+ type='service'
+ version='1'>
+ <create_default_instance enabled="true"/>
+ <single_instance/>
+
+ <dependency
+ name='fs'
+ grouping='require_any'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/minimal' />
+ </dependency>
+
+ <dependency
+ name='scheduler'
+ type='service'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/scheduler' />
+ </dependency>
+
+ <dependency
+ name='process_accounting'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/extended-accounting:process' />
+ </dependency>
+
+ <dependency
+ name='pools'
+ type='service'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/pools' />
+ </dependency>
+
+ <dependent
+ name='zones'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/zones' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/lib/zones/zonestatd'
+ timeout_seconds='300'>
+ <method_context>
+ <method_credential user='root'/>
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/lib/svc/method/svc-zonestat %m %{restarter/contract}'
+ timeout_seconds='300'>
+ <method_context>
+ <method_credential user='root'/>
+ </method_context>
+ </exec_method>
+
+ <property_group name='config' type='application'>
+ <propval name='sample_interval' type='count' value='5' />
+ <propval name='value_authorization' type='astring'
+ value='solaris.zones.manage' />
+ </property_group>
+
+ <property_group name='general' type='framework'>
+ <!-- to start/stop zones monitoring service -->
+ <propval name='action_authorization' type='astring'
+ value='solaris.zones.manage' />
+ <propval name='value_authorization' type='astring'
+ value='solaris.zones.manage' />
+ </property_group>
+
+ <stability value='Stable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Zones Monitoring Daemon
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='zonestatd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/zonestat/zonestatd/zonestatd.c b/usr/src/cmd/zonestat/zonestatd/zonestatd.c
new file mode 100644
index 0000000000..b764551131
--- /dev/null
+++ b/usr/src/cmd/zonestat/zonestatd/zonestatd.c
@@ -0,0 +1,4876 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <alloca.h>
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <door.h>
+#include <errno.h>
+#include <exacct.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <kstat.h>
+#include <libcontract.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <zonestat.h>
+#include <zonestat_impl.h>
+#include <limits.h>
+#include <pool.h>
+#include <procfs.h>
+#include <rctl.h>
+#include <thread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <synch.h>
+#include <sys/acctctl.h>
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#include <sys/fork.h>
+#include <sys/param.h>
+#include <sys/priocntl.h>
+#include <sys/fxpriocntl.h>
+#include <sys/processor.h>
+#include <sys/pset.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/swap.h>
+#include <sys/systeminfo.h>
+#include <thread.h>
+#include <sys/list.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vm_usage.h>
+#include <sys/wait.h>
+#include <sys/zone.h>
+#include <time.h>
+#include <ucred.h>
+#include <unistd.h>
+#include <vm/anon.h>
+#include <zone.h>
+#include <zonestat.h>
+
+#define MAX_PSET_NAME 1024 /* Taken from PV_NAME_MAX_LEN */
+#define ZSD_PSET_UNLIMITED UINT16_MAX
+#define ZONESTAT_EXACCT_FILE "/var/adm/exacct/zonestat-process"
+
+/*
+ * zonestatd implements gathering cpu and memory utilization data for
+ * running zones. It has these components:
+ *
+ * zsd_server:
+ * Door server to respond to client connections. Each client
+ * will connect using libzonestat.so, which will open and
+ * call /var/tmp/.zonestat_door. Each connecting client is given
+ * a file descriptor to the stat server.
+ *
+ * The zsd_server also responds to zoneadmd, which reports when a
+ * new zone is booted. This is used to fattach the zsd_server door
+ * into the new zone.
+ *
+ * zsd_stat_server:
+ * Receives client requests for the current utilization data. Each
+ * client request will cause zonestatd to update the current utilization
+ * data by kicking the stat_thread.
+ *
+ * If the client is in a non-global zone, the utilization data will
+ * be filtered to only show the given zone. The usage by all other zones
+ * will be added to the system utilization.
+ *
+ * stat_thread:
+ * The stat thread implements querying the system to determine the
+ * current utilization data for each running zone. This includes
+ * inspecting the system's processor set configuration, as well as details
+ * of each zone, such as their configured limits, and which processor
+ * sets they are running in.
+ *
+ * The stat_thread will only update memory utilization data as often as
+ * the configured config/sample_interval on the zones-monitoring service.
+ */
+
+/*
+ * The private vmusage structure unfortunately uses size_t types, and assumes
+ * the caller's bitness matches the kernel's bitness. Since the getvmusage()
+ * system call is contracted, and zonestatd is 32 bit, the following structures
+ * are used to interact with a 32bit or 64 bit kernel.
+ */
+typedef struct zsd_vmusage32 {
+ id_t vmu_zoneid;
+ uint_t vmu_type;
+ id_t vmu_id;
+
+ uint32_t vmu_rss_all;
+ uint32_t vmu_rss_private;
+ uint32_t vmu_rss_shared;
+ uint32_t vmu_swap_all;
+ uint32_t vmu_swap_private;
+ uint32_t vmu_swap_shared;
+} zsd_vmusage32_t;
+
+typedef struct zsd_vmusage64 {
+ id_t vmu_zoneid;
+ uint_t vmu_type;
+ id_t vmu_id;
+ /*
+ * An amd64 kernel will align the following uint64_t members, but a
+ * 32bit i386 process will not without help.
+ */
+ int vmu_align_next_members_on_8_bytes;
+ uint64_t vmu_rss_all;
+ uint64_t vmu_rss_private;
+ uint64_t vmu_rss_shared;
+ uint64_t vmu_swap_all;
+ uint64_t vmu_swap_private;
+ uint64_t vmu_swap_shared;
+} zsd_vmusage64_t;
+
+struct zsd_zone;
+
+/* Used to store a zone's usage of a pset */
+typedef struct zsd_pset_usage {
+ struct zsd_zone *zsu_zone;
+ struct zsd_pset *zsu_pset;
+
+ list_node_t zsu_next;
+
+ zoneid_t zsu_zoneid;
+ boolean_t zsu_found; /* zone bound at end of interval */
+ boolean_t zsu_active; /* zone was bound during interval */
+ boolean_t zsu_new; /* zone newly bound in this interval */
+ boolean_t zsu_deleted; /* zone was unbound in this interval */
+ boolean_t zsu_empty; /* no procs in pset in this interval */
+ time_t zsu_start; /* time when zone was found in pset */
+ hrtime_t zsu_hrstart; /* time when zone was found in pset */
+ uint64_t zsu_cpu_shares;
+ uint_t zsu_scheds; /* schedulers found in this pass */
+ timestruc_t zsu_cpu_usage; /* cpu time used */
+} zsd_pset_usage_t;
+
+/* Used to store a pset's utilization */
+typedef struct zsd_pset {
+ psetid_t zsp_id;
+ list_node_t zsp_next;
+ char zsp_name[ZS_PSETNAME_MAX];
+
+ uint_t zsp_cputype; /* default, dedicated or shared */
+ boolean_t zsp_found; /* pset found at end of interval */
+ boolean_t zsp_new; /* pset new in this interval */
+ boolean_t zsp_deleted; /* pset deleted in this interval */
+ boolean_t zsp_active; /* pset existed during interval */
+ boolean_t zsp_empty; /* no processes in pset */
+ time_t zsp_start;
+ hrtime_t zsp_hrstart;
+
+ uint64_t zsp_online; /* online cpus in interval */
+ uint64_t zsp_size; /* size in this interval */
+ uint64_t zsp_min; /* configured min in this interval */
+ uint64_t zsp_max; /* configured max in this interval */
+ int64_t zsp_importance; /* configured max in this interval */
+
+ uint_t zsp_scheds; /* scheds of processes found in pset */
+ uint64_t zsp_cpu_shares; /* total shares in this interval */
+
+ timestruc_t zsp_total_time;
+ timestruc_t zsp_usage_kern;
+ timestruc_t zsp_usage_zones;
+
+ /* Individual zone usages of pset */
+ list_t zsp_usage_list;
+ int zsp_nusage;
+
+ /* Summed kstat values from individual cpus in pset */
+ timestruc_t zsp_idle;
+ timestruc_t zsp_intr;
+ timestruc_t zsp_kern;
+ timestruc_t zsp_user;
+
+} zsd_pset_t;
+
+/* Used to track an individual cpu's utilization as reported by kstats */
+typedef struct zsd_cpu {
+ processorid_t zsc_id;
+ list_node_t zsc_next;
+ psetid_t zsc_psetid;
+ psetid_t zsc_psetid_prev;
+ zsd_pset_t *zsc_pset;
+
+ boolean_t zsc_found; /* cpu online in this interval */
+ boolean_t zsc_onlined; /* cpu onlined during this interval */
+ boolean_t zsc_offlined; /* cpu offlined during this interval */
+ boolean_t zsc_active; /* cpu online during this interval */
+ boolean_t zsc_allocated; /* True if cpu has ever been found */
+
+ /* kstats this interval */
+ uint64_t zsc_nsec_idle;
+ uint64_t zsc_nsec_intr;
+ uint64_t zsc_nsec_kern;
+ uint64_t zsc_nsec_user;
+
+ /* kstats in most recent interval */
+ uint64_t zsc_nsec_idle_prev;
+ uint64_t zsc_nsec_intr_prev;
+ uint64_t zsc_nsec_kern_prev;
+ uint64_t zsc_nsec_user_prev;
+
+ /* Total kstat increases since zonestatd started reading kstats */
+ timestruc_t zsc_idle;
+ timestruc_t zsc_intr;
+ timestruc_t zsc_kern;
+ timestruc_t zsc_user;
+
+} zsd_cpu_t;
+
+/* Used to describe an individual zone and its utilization */
+typedef struct zsd_zone {
+ zoneid_t zsz_id;
+ list_node_t zsz_next;
+ char zsz_name[ZS_ZONENAME_MAX];
+ uint_t zsz_cputype;
+ uint_t zsz_iptype;
+ time_t zsz_start;
+ hrtime_t zsz_hrstart;
+
+ char zsz_pool[ZS_POOLNAME_MAX];
+ char zsz_pset[ZS_PSETNAME_MAX];
+ int zsz_default_sched;
+ /* These are deduced by inspecting processes */
+ psetid_t zsz_psetid;
+ uint_t zsz_scheds;
+
+ boolean_t zsz_new; /* zone booted during this interval */
+ boolean_t zsz_deleted; /* halted during this interval */
+ boolean_t zsz_active; /* running in this interval */
+ boolean_t zsz_empty; /* no processes in this interval */
+ boolean_t zsz_gone; /* not installed in this interval */
+ boolean_t zsz_found; /* Running at end of this interval */
+
+ uint64_t zsz_cpu_shares;
+ uint64_t zsz_cpu_cap;
+ uint64_t zsz_ram_cap;
+ uint64_t zsz_locked_cap;
+ uint64_t zsz_vm_cap;
+
+ uint64_t zsz_cpus_online;
+ timestruc_t zsz_cpu_usage; /* cpu time of cpu cap */
+ timestruc_t zsz_cap_time; /* cpu time of cpu cap */
+ timestruc_t zsz_share_time; /* cpu time of share of cpu */
+ timestruc_t zsz_pset_time; /* time of all psets zone is bound to */
+
+ uint64_t zsz_usage_ram;
+ uint64_t zsz_usage_locked;
+ uint64_t zsz_usage_vm;
+
+ uint64_t zsz_processes_cap;
+ uint64_t zsz_lwps_cap;
+ uint64_t zsz_shm_cap;
+ uint64_t zsz_shmids_cap;
+ uint64_t zsz_semids_cap;
+ uint64_t zsz_msgids_cap;
+ uint64_t zsz_lofi_cap;
+
+ uint64_t zsz_processes;
+ uint64_t zsz_lwps;
+ uint64_t zsz_shm;
+ uint64_t zsz_shmids;
+ uint64_t zsz_semids;
+ uint64_t zsz_msgids;
+ uint64_t zsz_lofi;
+
+} zsd_zone_t;
+
+/*
+ * Used to track the cpu usage of an individual processes.
+ *
+ * zonestatd sweeps /proc each interval and charges the cpu usage of processes.
+ * to their zone. As processes exit, their extended accounting records are
+ * read and the difference of their total and known usage is charged to their
+ * zone.
+ *
+ * If a process is never seen in /proc, the total usage on its extended
+ * accounting record will be charged to its zone.
+ */
+typedef struct zsd_proc {
+ list_node_t zspr_next;
+ pid_t zspr_ppid;
+ psetid_t zspr_psetid;
+ zoneid_t zspr_zoneid;
+ int zspr_sched;
+ timestruc_t zspr_usage;
+} zsd_proc_t;
+
+/* Used to track the overall resource usage of the system */
+typedef struct zsd_system {
+
+ uint64_t zss_ram_total;
+ uint64_t zss_ram_kern;
+ uint64_t zss_ram_zones;
+
+ uint64_t zss_locked_kern;
+ uint64_t zss_locked_zones;
+
+ uint64_t zss_vm_total;
+ uint64_t zss_vm_kern;
+ uint64_t zss_vm_zones;
+
+ uint64_t zss_swap_total;
+ uint64_t zss_swap_used;
+
+ timestruc_t zss_idle;
+ timestruc_t zss_intr;
+ timestruc_t zss_kern;
+ timestruc_t zss_user;
+
+ timestruc_t zss_cpu_total_time;
+ timestruc_t zss_cpu_usage_kern;
+ timestruc_t zss_cpu_usage_zones;
+
+ uint64_t zss_maxpid;
+ uint64_t zss_processes_max;
+ uint64_t zss_lwps_max;
+ uint64_t zss_shm_max;
+ uint64_t zss_shmids_max;
+ uint64_t zss_semids_max;
+ uint64_t zss_msgids_max;
+ uint64_t zss_lofi_max;
+
+ uint64_t zss_processes;
+ uint64_t zss_lwps;
+ uint64_t zss_shm;
+ uint64_t zss_shmids;
+ uint64_t zss_semids;
+ uint64_t zss_msgids;
+ uint64_t zss_lofi;
+
+ uint64_t zss_ncpus;
+ uint64_t zss_ncpus_online;
+
+} zsd_system_t;
+
+/*
+ * A dumping ground for various information and structures used to compute
+ * utilization.
+ *
+ * This structure is used to track the system while clients are connected.
+ * When The first client connects, a zsd_ctl is allocated and configured by
+ * zsd_open(). When all clients disconnect, the zsd_ctl is closed.
+ */
+typedef struct zsd_ctl {
+ kstat_ctl_t *zsctl_kstat_ctl;
+
+ /* To track extended accounting */
+ int zsctl_proc_fd; /* Log currently being used */
+ ea_file_t zsctl_proc_eaf;
+ struct stat64 zsctl_proc_stat;
+ int zsctl_proc_open;
+ int zsctl_proc_fd_next; /* Log file to use next */
+ ea_file_t zsctl_proc_eaf_next;
+ struct stat64 zsctl_proc_stat_next;
+ int zsctl_proc_open_next;
+
+ /* pool configuration handle */
+ pool_conf_t *zsctl_pool_conf;
+ int zsctl_pool_status;
+ int zsctl_pool_changed;
+
+ /* The above usage tacking structures */
+ zsd_system_t *zsctl_system;
+ list_t zsctl_zones;
+ list_t zsctl_psets;
+ list_t zsctl_cpus;
+ zsd_cpu_t *zsctl_cpu_array;
+ zsd_proc_t *zsctl_proc_array;
+
+ /* Various system info */
+ uint64_t zsctl_maxcpuid;
+ uint64_t zsctl_maxproc;
+ uint64_t zsctl_kern_bits;
+ uint64_t zsctl_pagesize;
+
+ /* Used to track time available under a cpu cap. */
+ uint64_t zsctl_hrtime;
+ uint64_t zsctl_hrtime_prev;
+ timestruc_t zsctl_hrtime_total;
+
+ struct timeval zsctl_timeofday;
+
+ /* Caches for arrays allocated for use by various system calls */
+ psetid_t *zsctl_pset_cache;
+ uint_t zsctl_pset_ncache;
+ processorid_t *zsctl_cpu_cache;
+ uint_t zsctl_cpu_ncache;
+ zoneid_t *zsctl_zone_cache;
+ uint_t zsctl_zone_ncache;
+ struct swaptable *zsctl_swap_cache;
+ uint64_t zsctl_swap_cache_size;
+ uint64_t zsctl_swap_cache_num;
+ zsd_vmusage64_t *zsctl_vmusage_cache;
+ uint64_t zsctl_vmusage_cache_num;
+
+ /* Info about procfs for scanning /proc */
+ struct dirent *zsctl_procfs_dent;
+ long zsctl_procfs_dent_size;
+ pool_value_t *zsctl_pool_vals[3];
+
+ /* Counts on tracked entities */
+ uint_t zsctl_nzones;
+ uint_t zsctl_npsets;
+ uint_t zsctl_npset_usages;
+} zsd_ctl_t;
+
+zsd_ctl_t *g_ctl;
+boolean_t g_open; /* True if g_ctl is open */
+int g_hasclient; /* True if any clients are connected */
+
+/*
+ * The usage cache is updated by the stat_thread, and copied to clients by
+ * the zsd_stat_server. Mutex and cond are to synchronize between the
+ * stat_thread and the stat_server.
+ */
+zs_usage_cache_t *g_usage_cache;
+mutex_t g_usage_cache_lock;
+cond_t g_usage_cache_kick;
+uint_t g_usage_cache_kickers;
+cond_t g_usage_cache_wait;
+char *g_usage_cache_buf;
+uint_t g_usage_cache_bufsz;
+uint64_t g_gen_next;
+
+/* fds of door servers */
+int g_server_door;
+int g_stat_door;
+
+/*
+ * Starting and current time. Used to throttle memory calculation, and to
+ * mark new zones and psets with their boot and creation time.
+ */
+time_t g_now;
+time_t g_start;
+hrtime_t g_hrnow;
+hrtime_t g_hrstart;
+uint64_t g_interval;
+
+/*
+ * main() thread.
+ */
+thread_t g_main;
+
+/* PRINTFLIKE1 */
+static void
+zsd_warn(const char *fmt, ...)
+{
+ va_list alist;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, gettext("zonestat: Warning: "));
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+}
+
+/* PRINTFLIKE1 */
+static void
+zsd_error(const char *fmt, ...)
+{
+ va_list alist;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, gettext("zonestat: Error: "));
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+ exit(1);
+}
+
+/* Turns on extended accounting if not configured externally */
+int
+zsd_enable_cpu_stats()
+{
+ char *path = ZONESTAT_EXACCT_FILE;
+ char oldfile[MAXPATHLEN];
+ int ret, state = AC_ON;
+ ac_res_t res[6];
+
+ /*
+ * Start a new accounting file if accounting not configured
+ * externally.
+ */
+
+ res[0].ar_id = AC_PROC_PID;
+ res[0].ar_state = AC_ON;
+ res[1].ar_id = AC_PROC_ANCPID;
+ res[1].ar_state = AC_ON;
+ res[2].ar_id = AC_PROC_CPU;
+ res[2].ar_state = AC_ON;
+ res[3].ar_id = AC_PROC_TIME;
+ res[3].ar_state = AC_ON;
+ res[4].ar_id = AC_PROC_ZONENAME;
+ res[4].ar_state = AC_ON;
+ res[5].ar_id = AC_NONE;
+ res[5].ar_state = AC_ON;
+ if (acctctl(AC_PROC | AC_RES_SET, res, sizeof (res)) != 0) {
+ zsd_warn(gettext("Unable to set accounting resources"));
+ return (-1);
+ }
+ /* Only set accounting file if none is configured */
+ ret = acctctl(AC_PROC | AC_FILE_GET, oldfile, sizeof (oldfile));
+ if (ret < 0) {
+
+ (void) unlink(path);
+ if (acctctl(AC_PROC | AC_FILE_SET, path, strlen(path) + 1)
+ == -1) {
+ zsd_warn(gettext("Unable to set accounting file"));
+ return (-1);
+ }
+ }
+ if (acctctl(AC_PROC | AC_STATE_SET, &state, sizeof (state)) == -1) {
+ zsd_warn(gettext("Unable to enable accounting"));
+ return (-1);
+ }
+ return (0);
+}
+
+/* Turns off extended accounting if not configured externally */
+int
+zsd_disable_cpu_stats()
+{
+ char *path = ZONESTAT_EXACCT_FILE;
+ int ret, state = AC_OFF;
+ ac_res_t res[6];
+ char oldfile[MAXPATHLEN];
+
+ /* If accounting file is externally configured, leave it alone */
+ ret = acctctl(AC_PROC | AC_FILE_GET, oldfile, sizeof (oldfile));
+ if (ret == 0 && strcmp(oldfile, path) != 0)
+ return (0);
+
+ res[0].ar_id = AC_PROC_PID;
+ res[0].ar_state = AC_OFF;
+ res[1].ar_id = AC_PROC_ANCPID;
+ res[1].ar_state = AC_OFF;
+ res[2].ar_id = AC_PROC_CPU;
+ res[2].ar_state = AC_OFF;
+ res[3].ar_id = AC_PROC_TIME;
+ res[3].ar_state = AC_OFF;
+ res[4].ar_id = AC_PROC_ZONENAME;
+ res[4].ar_state = AC_OFF;
+ res[5].ar_id = AC_NONE;
+ res[5].ar_state = AC_OFF;
+ if (acctctl(AC_PROC | AC_RES_SET, res, sizeof (res)) != 0) {
+ zsd_warn(gettext("Unable to clear accounting resources"));
+ return (-1);
+ }
+ if (acctctl(AC_PROC | AC_FILE_SET, NULL, 0) == -1) {
+ zsd_warn(gettext("Unable to clear accounting file"));
+ return (-1);
+ }
+ if (acctctl(AC_PROC | AC_STATE_SET, &state, sizeof (state)) == -1) {
+ zsd_warn(gettext("Unable to diable accounting"));
+ return (-1);
+ }
+
+ (void) unlink(path);
+ return (0);
+}
+
+/*
+ * If not configured externally, deletes the current extended accounting file
+ * and starts a new one.
+ *
+ * Since the stat_thread holds an open handle to the accounting file, it will
+ * read all remaining entries from the old file before switching to
+ * read the new one.
+ */
+int
+zsd_roll_exacct(void)
+{
+ int ret;
+ char *path = ZONESTAT_EXACCT_FILE;
+ char oldfile[MAXPATHLEN];
+
+ /* If accounting file is externally configured, leave it alone */
+ ret = acctctl(AC_PROC | AC_FILE_GET, oldfile, sizeof (oldfile));
+ if (ret == 0 && strcmp(oldfile, path) != 0)
+ return (0);
+
+ if (unlink(path) != 0)
+ /* Roll it next time */
+ return (0);
+
+ if (acctctl(AC_PROC | AC_FILE_SET, path, strlen(path) + 1) == -1) {
+ zsd_warn(gettext("Unable to set accounting file"));
+ return (-1);
+ }
+ return (0);
+}
+
+/* Contract stuff for zone_enter() */
+int
+init_template(void)
+{
+ int fd;
+ int err = 0;
+
+ fd = open64(CTFS_ROOT "/process/template", O_RDWR);
+ if (fd == -1)
+ return (-1);
+
+ /*
+ * For now, zoneadmd doesn't do anything with the contract.
+ * Deliver no events, don't inherit, and allow it to be orphaned.
+ */
+ err |= ct_tmpl_set_critical(fd, 0);
+ err |= ct_tmpl_set_informative(fd, 0);
+ err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
+ err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
+ if (err || ct_tmpl_activate(fd)) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+/*
+ * Contract stuff for zone_enter()
+ */
+int
+contract_latest(ctid_t *id)
+{
+ int cfd, r;
+ ct_stathdl_t st;
+ ctid_t result;
+
+ if ((cfd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1)
+ return (errno);
+
+ if ((r = ct_status_read(cfd, CTD_COMMON, &st)) != 0) {
+ (void) close(cfd);
+ return (r);
+ }
+
+ result = ct_status_get_id(st);
+ ct_status_free(st);
+ (void) close(cfd);
+
+ *id = result;
+ return (0);
+}
+
+static int
+close_on_exec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1))
+ return (0);
+ return (-1);
+}
+
+int
+contract_open(ctid_t ctid, const char *type, const char *file, int oflag)
+{
+ char path[PATH_MAX];
+ int n, fd;
+
+ if (type == NULL)
+ type = "all";
+
+ n = snprintf(path, PATH_MAX, CTFS_ROOT "/%s/%ld/%s", type, ctid, file);
+ if (n >= sizeof (path)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ fd = open64(path, oflag);
+ if (fd != -1) {
+ if (close_on_exec(fd) == -1) {
+ int err = errno;
+ (void) close(fd);
+ errno = err;
+ return (-1);
+ }
+ }
+ return (fd);
+}
+
+int
+contract_abandon_id(ctid_t ctid)
+{
+ int fd, err;
+
+ fd = contract_open(ctid, "all", "ctl", O_WRONLY);
+ if (fd == -1)
+ return (errno);
+
+ err = ct_ctl_abandon(fd);
+ (void) close(fd);
+
+ return (err);
+}
+/*
+ * Attach the zsd_server to a zone. Called for each zone when zonestatd
+ * starts, and for each newly booted zone when zoneadmd contacts the zsd_server
+ *
+ * Zone_enter is used to avoid reaching into zone to fattach door.
+ */
+static void
+zsd_fattach_zone(zoneid_t zid, int door, boolean_t detach_only)
+{
+ char *path = ZS_DOOR_PATH;
+ int fd, pid, stat, tmpl_fd;
+ ctid_t ct;
+
+ if ((tmpl_fd = init_template()) == -1) {
+ zsd_warn("Unable to init template");
+ return;
+ }
+
+ pid = forkx(0);
+ if (pid < 0) {
+ (void) ct_tmpl_clear(tmpl_fd);
+ zsd_warn(gettext(
+ "Unable to fork to add zonestat to zoneid %d\n"), zid);
+ return;
+ }
+
+ if (pid == 0) {
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ if (zid != 0 && zone_enter(zid) != 0) {
+ if (errno == EINVAL) {
+ _exit(0);
+ }
+ _exit(1);
+ }
+ (void) fdetach(path);
+ (void) unlink(path);
+ if (detach_only)
+ _exit(0);
+ fd = open(path, O_CREAT|O_RDWR, 0644);
+ if (fd < 0)
+ _exit(2);
+ if (fattach(door, path) != 0)
+ _exit(3);
+ _exit(0);
+ }
+ if (contract_latest(&ct) == -1)
+ ct = -1;
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ (void) contract_abandon_id(ct);
+ while (waitpid(pid, &stat, 0) != pid)
+ ;
+ if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0)
+ return;
+
+ zsd_warn(gettext("Unable to attach door to zoneid: %d"), zid);
+
+ if (WEXITSTATUS(stat) == 1)
+ zsd_warn(gettext("Cannot entering zone"));
+ else if (WEXITSTATUS(stat) == 2)
+ zsd_warn(gettext("Unable to create door file: %s"), path);
+ else if (WEXITSTATUS(stat) == 3)
+ zsd_warn(gettext("Unable to fattach file: %s"), path);
+
+ zsd_warn(gettext("Internal error entering zone: %d"), zid);
+}
+
+/*
+ * Zone lookup and allocation functions to manage list of currently running
+ * zones.
+ */
+static zsd_zone_t *
+zsd_lookup_zone(zsd_ctl_t *ctl, char *zonename, zoneid_t zoneid)
+{
+ zsd_zone_t *zone;
+
+ for (zone = list_head(&ctl->zsctl_zones); zone != NULL;
+ zone = list_next(&ctl->zsctl_zones, zone)) {
+ if (strcmp(zone->zsz_name, zonename) == 0) {
+ if (zoneid != -1)
+ zone->zsz_id = zoneid;
+ return (zone);
+ }
+ }
+ return (NULL);
+}
+
+static zsd_zone_t *
+zsd_lookup_zone_byid(zsd_ctl_t *ctl, zoneid_t zoneid)
+{
+ zsd_zone_t *zone;
+
+ for (zone = list_head(&ctl->zsctl_zones); zone != NULL;
+ zone = list_next(&ctl->zsctl_zones, zone)) {
+ if (zone->zsz_id == zoneid)
+ return (zone);
+ }
+ return (NULL);
+}
+
+static zsd_zone_t *
+zsd_allocate_zone(zsd_ctl_t *ctl, char *zonename, zoneid_t zoneid)
+{
+ zsd_zone_t *zone;
+
+ if ((zone = (zsd_zone_t *)calloc(1, sizeof (zsd_zone_t))) == NULL)
+ return (NULL);
+
+ (void) strlcpy(zone->zsz_name, zonename, sizeof (zone->zsz_name));
+ zone->zsz_id = zoneid;
+ zone->zsz_found = B_FALSE;
+
+ /*
+ * Allocate as deleted so if not found in first pass, zone is deleted
+ * from list. This can happen if zone is returned by zone_list, but
+ * exits before first attempt to fetch zone details.
+ */
+ zone->zsz_start = g_now;
+ zone->zsz_hrstart = g_hrnow;
+ zone->zsz_deleted = B_TRUE;
+
+ zone->zsz_cpu_shares = ZS_LIMIT_NONE;
+ zone->zsz_cpu_cap = ZS_LIMIT_NONE;
+ zone->zsz_ram_cap = ZS_LIMIT_NONE;
+ zone->zsz_locked_cap = ZS_LIMIT_NONE;
+ zone->zsz_vm_cap = ZS_LIMIT_NONE;
+
+ zone->zsz_processes_cap = ZS_LIMIT_NONE;
+ zone->zsz_lwps_cap = ZS_LIMIT_NONE;
+ zone->zsz_shm_cap = ZS_LIMIT_NONE;
+ zone->zsz_shmids_cap = ZS_LIMIT_NONE;
+ zone->zsz_semids_cap = ZS_LIMIT_NONE;
+ zone->zsz_msgids_cap = ZS_LIMIT_NONE;
+ zone->zsz_lofi_cap = ZS_LIMIT_NONE;
+
+ ctl->zsctl_nzones++;
+
+ return (zone);
+}
+
+static zsd_zone_t *
+zsd_lookup_insert_zone(zsd_ctl_t *ctl, char *zonename, zoneid_t zoneid)
+{
+ zsd_zone_t *zone, *tmp;
+
+ if ((zone = zsd_lookup_zone(ctl, zonename, zoneid)) != NULL)
+ return (zone);
+
+ if ((zone = zsd_allocate_zone(ctl, zonename, zoneid)) == NULL)
+ return (NULL);
+
+ /* Insert sorted by zonename */
+ tmp = list_head(&ctl->zsctl_zones);
+ while (tmp != NULL && strcmp(zonename, tmp->zsz_name) > 0)
+ tmp = list_next(&ctl->zsctl_zones, tmp);
+
+ list_insert_before(&ctl->zsctl_zones, tmp, zone);
+ return (zone);
+}
+
+/*
+ * Mark all zones as not existing. As zones are found, they will
+ * be marked as existing. If a zone is not found, then it must have
+ * halted.
+ */
+static void
+zsd_mark_zones_start(zsd_ctl_t *ctl)
+{
+
+ zsd_zone_t *zone;
+
+ for (zone = list_head(&ctl->zsctl_zones); zone != NULL;
+ zone = list_next(&ctl->zsctl_zones, zone)) {
+ zone->zsz_found = B_FALSE;
+ }
+}
+
+/*
+ * Mark each zone as not using pset. If processes are found using the
+ * pset, the zone will remain bound to the pset. If none of a zones
+ * processes are bound to the pset, the zone's usage of the pset will
+ * be deleted.
+ *
+ */
+static void
+zsd_mark_pset_usage_start(zsd_pset_t *pset)
+{
+ zsd_pset_usage_t *usage;
+
+ for (usage = list_head(&pset->zsp_usage_list);
+ usage != NULL;
+ usage = list_next(&pset->zsp_usage_list, usage)) {
+ usage->zsu_found = B_FALSE;
+ usage->zsu_empty = B_TRUE;
+ }
+}
+
+/*
+ * Mark each pset as not existing. If a pset is found, it will be marked
+ * as existing. If a pset is not found, it wil be deleted.
+ */
+static void
+zsd_mark_psets_start(zsd_ctl_t *ctl)
+{
+ zsd_pset_t *pset;
+
+ for (pset = list_head(&ctl->zsctl_psets); pset != NULL;
+ pset = list_next(&ctl->zsctl_psets, pset)) {
+ pset->zsp_found = B_FALSE;
+ zsd_mark_pset_usage_start(pset);
+ }
+}
+
+/*
+ * A pset was found. Update its information
+ */
+static void
+zsd_mark_pset_found(zsd_pset_t *pset, uint_t type, uint64_t online,
+ uint64_t size, uint64_t min, uint64_t max, int64_t importance)
+{
+ pset->zsp_empty = B_TRUE;
+ pset->zsp_deleted = B_FALSE;
+
+ assert(pset->zsp_found == B_FALSE);
+
+ /* update pset flags */
+ if (pset->zsp_active == B_FALSE)
+ /* pset not seen on previous interval. It is new. */
+ pset->zsp_new = B_TRUE;
+ else
+ pset->zsp_new = B_FALSE;
+
+ pset->zsp_found = B_TRUE;
+ pset->zsp_cputype = type;
+ pset->zsp_online = online;
+ pset->zsp_size = size;
+ pset->zsp_min = min;
+ pset->zsp_max = max;
+ pset->zsp_importance = importance;
+ pset->zsp_cpu_shares = 0;
+ pset->zsp_scheds = 0;
+ pset->zsp_active = B_TRUE;
+}
+
+/*
+ * A zone's process was found using a pset. Charge the process to the pset and
+ * the per-zone data for the pset.
+ */
+static void
+zsd_mark_pset_usage_found(zsd_pset_usage_t *usage, uint_t sched)
+{
+ zsd_zone_t *zone = usage->zsu_zone;
+ zsd_pset_t *pset = usage->zsu_pset;
+
+ /* Nothing to do if already found */
+ if (usage->zsu_found == B_TRUE)
+ goto add_stats;
+
+ usage->zsu_found = B_TRUE;
+ usage->zsu_empty = B_FALSE;
+
+ usage->zsu_deleted = B_FALSE;
+ /* update usage flags */
+ if (usage->zsu_active == B_FALSE)
+ usage->zsu_new = B_TRUE;
+ else
+ usage->zsu_new = B_FALSE;
+
+ usage->zsu_scheds = 0;
+ usage->zsu_cpu_shares = ZS_LIMIT_NONE;
+ usage->zsu_active = B_TRUE;
+ pset->zsp_empty = B_FALSE;
+ zone->zsz_empty = B_FALSE;
+
+add_stats:
+ /* Detect zone's pset id, and if it is bound to multiple psets */
+ if (zone->zsz_psetid == ZS_PSET_ERROR)
+ zone->zsz_psetid = pset->zsp_id;
+ else if (zone->zsz_psetid != pset->zsp_id)
+ zone->zsz_psetid = ZS_PSET_MULTI;
+
+ usage->zsu_scheds |= sched;
+ pset->zsp_scheds |= sched;
+ zone->zsz_scheds |= sched;
+
+ /* Record if FSS is co-habitating with conflicting scheduler */
+ if ((pset->zsp_scheds & ZS_SCHED_FSS) &&
+ usage->zsu_scheds & (
+ ZS_SCHED_TS | ZS_SCHED_IA | ZS_SCHED_FX)) {
+ usage->zsu_scheds |= ZS_SCHED_CONFLICT;
+
+ pset->zsp_scheds |= ZS_SCHED_CONFLICT;
+ }
+
+}
+
+/* Add cpu time for a process to a pset, zone, and system totals */
+static void
+zsd_add_usage(zsd_ctl_t *ctl, zsd_pset_usage_t *usage, timestruc_t *delta)
+{
+ zsd_system_t *system = ctl->zsctl_system;
+ zsd_zone_t *zone = usage->zsu_zone;
+ zsd_pset_t *pset = usage->zsu_pset;
+
+ TIMESTRUC_ADD_TIMESTRUC(usage->zsu_cpu_usage, *delta);
+ TIMESTRUC_ADD_TIMESTRUC(pset->zsp_usage_zones, *delta);
+ TIMESTRUC_ADD_TIMESTRUC(zone->zsz_cpu_usage, *delta);
+ TIMESTRUC_ADD_TIMESTRUC(system->zss_cpu_usage_zones, *delta);
+}
+
+/* Determine which processor sets have been deleted */
+static void
+zsd_mark_psets_end(zsd_ctl_t *ctl)
+{
+ zsd_pset_t *pset, *tmp;
+
+ /*
+ * Mark pset as not exists, and deleted if it existed
+ * previous interval.
+ */
+ pset = list_head(&ctl->zsctl_psets);
+ while (pset != NULL) {
+ if (pset->zsp_found == B_FALSE) {
+ pset->zsp_empty = B_TRUE;
+ if (pset->zsp_deleted == B_TRUE) {
+ tmp = pset;
+ pset = list_next(&ctl->zsctl_psets, pset);
+ list_remove(&ctl->zsctl_psets, tmp);
+ free(tmp);
+ ctl->zsctl_npsets--;
+ continue;
+ } else {
+ /* Pset vanished during this interval */
+ pset->zsp_new = B_FALSE;
+ pset->zsp_deleted = B_TRUE;
+ pset->zsp_active = B_TRUE;
+ }
+ }
+ pset = list_next(&ctl->zsctl_psets, pset);
+ }
+}
+
+/* Determine which zones are no longer bound to processor sets */
+static void
+zsd_mark_pset_usages_end(zsd_ctl_t *ctl)
+{
+ zsd_pset_t *pset;
+ zsd_zone_t *zone;
+ zsd_pset_usage_t *usage, *tmp;
+
+ /*
+ * Mark pset as not exists, and deleted if it existed previous
+ * interval.
+ */
+ for (pset = list_head(&ctl->zsctl_psets); pset != NULL;
+ pset = list_next(&ctl->zsctl_psets, pset)) {
+ usage = list_head(&pset->zsp_usage_list);
+ while (usage != NULL) {
+ /*
+ * Mark pset as not exists, and deleted if it existed
+ * previous interval.
+ */
+ if (usage->zsu_found == B_FALSE ||
+ usage->zsu_zone->zsz_deleted == B_TRUE ||
+ usage->zsu_pset->zsp_deleted == B_TRUE) {
+ tmp = usage;
+ usage = list_next(&pset->zsp_usage_list,
+ usage);
+ list_remove(&pset->zsp_usage_list, tmp);
+ free(tmp);
+ pset->zsp_nusage--;
+ ctl->zsctl_npset_usages--;
+ continue;
+ } else {
+ usage->zsu_new = B_FALSE;
+ usage->zsu_deleted = B_TRUE;
+ usage->zsu_active = B_TRUE;
+ }
+ /* Add cpu shares for usages that are in FSS */
+ zone = usage->zsu_zone;
+ if (usage->zsu_scheds & ZS_SCHED_FSS &&
+ zone->zsz_cpu_shares != ZS_SHARES_UNLIMITED &&
+ zone->zsz_cpu_shares != 0) {
+ zone = usage->zsu_zone;
+ usage->zsu_cpu_shares = zone->zsz_cpu_shares;
+ pset->zsp_cpu_shares += zone->zsz_cpu_shares;
+ }
+ usage = list_next(&pset->zsp_usage_list,
+ usage);
+ }
+ }
+}
+
+/* A zone has been found. Update its information */
+static void
+zsd_mark_zone_found(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t cpu_shares,
+ uint64_t cpu_cap, uint64_t ram_cap, uint64_t locked_cap,
+ uint64_t vm_cap, uint64_t processes_cap, uint64_t processes,
+ uint64_t lwps_cap, uint64_t lwps, uint64_t shm_cap, uint64_t shm,
+ uint64_t shmids_cap, uint64_t shmids, uint64_t semids_cap,
+ uint64_t semids, uint64_t msgids_cap, uint64_t msgids, uint64_t lofi_cap,
+ uint64_t lofi, char *poolname, char *psetname, uint_t sched, uint_t cputype,
+ uint_t iptype)
+{
+ zsd_system_t *sys = ctl->zsctl_system;
+
+ assert(zone->zsz_found == B_FALSE);
+
+ /*
+ * Mark zone as exists, and new if it did not exist in previous
+ * interval.
+ */
+ zone->zsz_found = B_TRUE;
+ zone->zsz_empty = B_TRUE;
+ zone->zsz_deleted = B_FALSE;
+
+ /*
+ * Zone is new. Assume zone's properties are the same over entire
+ * interval.
+ */
+ if (zone->zsz_active == B_FALSE)
+ zone->zsz_new = B_TRUE;
+ else
+ zone->zsz_new = B_FALSE;
+
+ (void) strlcpy(zone->zsz_pool, poolname, sizeof (zone->zsz_pool));
+ (void) strlcpy(zone->zsz_pset, psetname, sizeof (zone->zsz_pset));
+ zone->zsz_default_sched = sched;
+
+ /* Schedulers updated later as processes are found */
+ zone->zsz_scheds = 0;
+
+ /* Cpus updated later as psets bound are identified */
+ zone->zsz_cpus_online = 0;
+
+ zone->zsz_cputype = cputype;
+ zone->zsz_iptype = iptype;
+ zone->zsz_psetid = ZS_PSET_ERROR;
+ zone->zsz_cpu_cap = cpu_cap;
+ zone->zsz_cpu_shares = cpu_shares;
+ zone->zsz_ram_cap = ram_cap;
+ zone->zsz_locked_cap = locked_cap;
+ zone->zsz_vm_cap = vm_cap;
+ zone->zsz_processes_cap = processes_cap;
+ zone->zsz_processes = processes;
+ zone->zsz_lwps_cap = lwps_cap;
+ zone->zsz_lwps = lwps;
+ zone->zsz_shm_cap = shm_cap;
+ zone->zsz_shm = shm;
+ zone->zsz_shmids_cap = shmids_cap;
+ zone->zsz_shmids = shmids;
+ zone->zsz_semids_cap = semids_cap;
+ zone->zsz_semids = semids;
+ zone->zsz_msgids_cap = msgids_cap;
+ zone->zsz_msgids = msgids;
+ zone->zsz_lofi_cap = lofi_cap;
+ zone->zsz_lofi = lofi;
+
+ sys->zss_processes += processes;
+ sys->zss_lwps += lwps;
+ sys->zss_shm += shm;
+ sys->zss_shmids += shmids;
+ sys->zss_semids += semids;
+ sys->zss_msgids += msgids;
+ sys->zss_lofi += lofi;
+ zone->zsz_active = B_TRUE;
+}
+
+
+/* Determine which zones have halted */
+static void
+zsd_mark_zones_end(zsd_ctl_t *ctl)
+{
+ zsd_zone_t *zone, *tmp;
+
+ /*
+ * Mark zone as not existing, or delete if it did not exist in
+ * previous interval.
+ */
+ zone = list_head(&ctl->zsctl_zones);
+ while (zone != NULL) {
+ if (zone->zsz_found == B_FALSE) {
+ zone->zsz_empty = B_TRUE;
+ if (zone->zsz_deleted == B_TRUE) {
+ /*
+ * Zone deleted in prior interval,
+ * so it no longer exists.
+ */
+ tmp = zone;
+ zone = list_next(&ctl->zsctl_zones, zone);
+ list_remove(&ctl->zsctl_zones, tmp);
+ free(tmp);
+ ctl->zsctl_nzones--;
+ continue;
+ } else {
+ zone->zsz_new = B_FALSE;
+ zone->zsz_deleted = B_TRUE;
+ zone->zsz_active = B_TRUE;
+ }
+ }
+ zone = list_next(&ctl->zsctl_zones, zone);
+ }
+}
+
+/*
+ * Mark cpus as not existing. If a cpu is found, it will be updated. If
+ * a cpu is not found, then it must have gone offline, so it will be
+ * deleted.
+ *
+ * The kstat tracking data is rolled so that the usage since the previous
+ * interval can be determined.
+ */
+static void
+zsd_mark_cpus_start(zsd_ctl_t *ctl, boolean_t roll)
+{
+ zsd_cpu_t *cpu;
+
+ /*
+ * Mark all cpus as not existing. As cpus are found, they will
+ * be marked as existing.
+ */
+ for (cpu = list_head(&ctl->zsctl_cpus); cpu != NULL;
+ cpu = list_next(&ctl->zsctl_cpus, cpu)) {
+ cpu->zsc_found = B_FALSE;
+ if (cpu->zsc_active == B_TRUE && roll) {
+ cpu->zsc_psetid_prev = cpu->zsc_psetid;
+ cpu->zsc_nsec_idle_prev = cpu->zsc_nsec_idle;
+ cpu->zsc_nsec_intr_prev = cpu->zsc_nsec_intr;
+ cpu->zsc_nsec_kern_prev = cpu->zsc_nsec_kern;
+ cpu->zsc_nsec_user_prev = cpu->zsc_nsec_user;
+ }
+ }
+}
+
+/*
+ * An array the size of the maximum number of cpus is kept. Within this array
+ * a list of the online cpus is maintained.
+ */
+zsd_cpu_t *
+zsd_lookup_insert_cpu(zsd_ctl_t *ctl, processorid_t cpuid)
+{
+ zsd_cpu_t *cpu;
+
+ assert(cpuid < ctl->zsctl_maxcpuid);
+ cpu = &(ctl->zsctl_cpu_array[cpuid]);
+ assert(cpuid == cpu->zsc_id);
+
+ if (cpu->zsc_allocated == B_FALSE) {
+ cpu->zsc_allocated = B_TRUE;
+ list_insert_tail(&ctl->zsctl_cpus, cpu);
+ }
+ return (cpu);
+}
+
+/* A cpu has been found. Update its information */
+static void
+zsd_mark_cpu_found(zsd_cpu_t *cpu, zsd_pset_t *pset, psetid_t psetid)
+{
+ /*
+ * legacy processor sets, the cpu may move while zonestatd is
+ * inspecting, causing it to be found twice. In this case, just
+ * leave cpu in the first processor set in which it was found.
+ */
+ if (cpu->zsc_found == B_TRUE)
+ return;
+
+ /* Mark cpu as online */
+ cpu->zsc_found = B_TRUE;
+ cpu->zsc_offlined = B_FALSE;
+ cpu->zsc_pset = pset;
+ /*
+ * cpu is newly online.
+ */
+ if (cpu->zsc_active == B_FALSE) {
+ /*
+ * Cpu is newly online.
+ */
+ cpu->zsc_onlined = B_TRUE;
+ cpu->zsc_psetid = psetid;
+ cpu->zsc_psetid_prev = psetid;
+ } else {
+ /*
+ * cpu online during previous interval. Save properties at
+ * start of interval
+ */
+ cpu->zsc_onlined = B_FALSE;
+ cpu->zsc_psetid = psetid;
+
+ }
+ cpu->zsc_active = B_TRUE;
+}
+
+/* Remove all offlined cpus from the list of tracked cpus */
+static void
+zsd_mark_cpus_end(zsd_ctl_t *ctl)
+{
+ zsd_cpu_t *cpu, *tmp;
+ int id;
+
+ /* Mark cpu as online or offline */
+ cpu = list_head(&ctl->zsctl_cpus);
+ while (cpu != NULL) {
+ if (cpu->zsc_found == B_FALSE) {
+ if (cpu->zsc_offlined == B_TRUE) {
+ /*
+ * cpu offlined in prior interval. It is gone.
+ */
+ tmp = cpu;
+ cpu = list_next(&ctl->zsctl_cpus, cpu);
+ list_remove(&ctl->zsctl_cpus, tmp);
+ /* Clear structure for future use */
+ id = tmp->zsc_id;
+ bzero(tmp, sizeof (zsd_cpu_t));
+ tmp->zsc_id = id;
+ tmp->zsc_allocated = B_FALSE;
+ tmp->zsc_psetid = ZS_PSET_ERROR;
+ tmp->zsc_psetid_prev = ZS_PSET_ERROR;
+
+ } else {
+ /*
+ * cpu online at start of interval. Treat
+ * as still online, since it was online for
+ * some portion of the interval.
+ */
+ cpu->zsc_offlined = B_TRUE;
+ cpu->zsc_onlined = B_FALSE;
+ cpu->zsc_active = B_TRUE;
+ cpu->zsc_psetid = cpu->zsc_psetid_prev;
+ cpu->zsc_pset = NULL;
+ }
+ }
+ cpu = list_next(&ctl->zsctl_cpus, cpu);
+ }
+}
+
+/* Some utility functions for managing the list of processor sets */
+static zsd_pset_t *
+zsd_lookup_pset_byid(zsd_ctl_t *ctl, psetid_t psetid)
+{
+ zsd_pset_t *pset;
+
+ for (pset = list_head(&ctl->zsctl_psets); pset != NULL;
+ pset = list_next(&ctl->zsctl_psets, pset)) {
+ if (pset->zsp_id == psetid)
+ return (pset);
+ }
+ return (NULL);
+}
+
+static zsd_pset_t *
+zsd_lookup_pset(zsd_ctl_t *ctl, char *psetname, psetid_t psetid)
+{
+ zsd_pset_t *pset;
+
+ for (pset = list_head(&ctl->zsctl_psets); pset != NULL;
+ pset = list_next(&ctl->zsctl_psets, pset)) {
+ if (strcmp(pset->zsp_name, psetname) == 0) {
+ if (psetid != -1)
+ pset->zsp_id = psetid;
+ return (pset);
+ }
+ }
+ return (NULL);
+}
+
+static zsd_pset_t *
+zsd_allocate_pset(zsd_ctl_t *ctl, char *psetname, psetid_t psetid)
+{
+ zsd_pset_t *pset;
+
+ if ((pset = (zsd_pset_t *)calloc(1, sizeof (zsd_pset_t))) == NULL)
+ return (NULL);
+
+ (void) strlcpy(pset->zsp_name, psetname, sizeof (pset->zsp_name));
+ pset->zsp_id = psetid;
+ pset->zsp_found = B_FALSE;
+ /*
+ * Allocate as deleted so if not found in first pass, pset is deleted
+ * from list. This can happen if pset is returned by pset_list, but
+ * is destroyed before first attempt to fetch pset details.
+ */
+ list_create(&pset->zsp_usage_list, sizeof (zsd_pset_usage_t),
+ offsetof(zsd_pset_usage_t, zsu_next));
+
+ pset->zsp_hrstart = g_hrnow;
+ pset->zsp_deleted = B_TRUE;
+ pset->zsp_empty = B_TRUE;
+ ctl->zsctl_npsets++;
+
+ return (pset);
+}
+
+static zsd_pset_t *
+zsd_lookup_insert_pset(zsd_ctl_t *ctl, char *psetname, psetid_t psetid)
+{
+ zsd_pset_t *pset, *tmp;
+
+ if ((pset = zsd_lookup_pset(ctl, psetname, psetid)) != NULL)
+ return (pset);
+
+ if ((pset = zsd_allocate_pset(ctl, psetname, psetid)) == NULL)
+ return (NULL);
+
+ /* Insert sorted by psetname */
+ tmp = list_head(&ctl->zsctl_psets);
+ while (tmp != NULL && strcmp(psetname, tmp->zsp_name) > 0)
+ tmp = list_next(&ctl->zsctl_psets, tmp);
+
+ list_insert_before(&ctl->zsctl_psets, tmp, pset);
+ return (pset);
+}
+
+/* Some utility functions for managing the list of zones using each pset */
+static zsd_pset_usage_t *
+zsd_lookup_usage(zsd_pset_t *pset, zsd_zone_t *zone)
+{
+ zsd_pset_usage_t *usage;
+
+ for (usage = list_head(&pset->zsp_usage_list); usage != NULL;
+ usage = list_next(&pset->zsp_usage_list, usage))
+ if (usage->zsu_zone == zone)
+ return (usage);
+
+ return (NULL);
+}
+
+static zsd_pset_usage_t *
+zsd_allocate_pset_usage(zsd_ctl_t *ctl, zsd_pset_t *pset, zsd_zone_t *zone)
+{
+ zsd_pset_usage_t *usage;
+
+ if ((usage = (zsd_pset_usage_t *)calloc(1, sizeof (zsd_pset_usage_t)))
+ == NULL)
+ return (NULL);
+
+ list_link_init(&usage->zsu_next);
+ usage->zsu_zone = zone;
+ usage->zsu_zoneid = zone->zsz_id;
+ usage->zsu_pset = pset;
+ usage->zsu_found = B_FALSE;
+ usage->zsu_active = B_FALSE;
+ usage->zsu_new = B_FALSE;
+ /*
+ * Allocate as not deleted. If a process is found in a pset for
+ * a zone, the usage will not be deleted until at least the next
+ * interval.
+ */
+ usage->zsu_start = g_now;
+ usage->zsu_hrstart = g_hrnow;
+ usage->zsu_deleted = B_FALSE;
+ usage->zsu_empty = B_TRUE;
+ usage->zsu_scheds = 0;
+ usage->zsu_cpu_shares = ZS_LIMIT_NONE;
+
+ ctl->zsctl_npset_usages++;
+ pset->zsp_nusage++;
+
+ return (usage);
+}
+
+static zsd_pset_usage_t *
+zsd_lookup_insert_usage(zsd_ctl_t *ctl, zsd_pset_t *pset, zsd_zone_t *zone)
+{
+ zsd_pset_usage_t *usage, *tmp;
+
+ if ((usage = zsd_lookup_usage(pset, zone))
+ != NULL)
+ return (usage);
+
+ if ((usage = zsd_allocate_pset_usage(ctl, pset, zone)) == NULL)
+ return (NULL);
+
+ tmp = list_head(&pset->zsp_usage_list);
+ while (tmp != NULL && strcmp(zone->zsz_name, tmp->zsu_zone->zsz_name)
+ > 0)
+ tmp = list_next(&pset->zsp_usage_list, tmp);
+
+ list_insert_before(&pset->zsp_usage_list, tmp, usage);
+ return (usage);
+}
+
+static void
+zsd_refresh_system(zsd_ctl_t *ctl)
+{
+ zsd_system_t *system = ctl->zsctl_system;
+
+ /* Re-count these values each interval */
+ system->zss_processes = 0;
+ system->zss_lwps = 0;
+ system->zss_shm = 0;
+ system->zss_shmids = 0;
+ system->zss_semids = 0;
+ system->zss_msgids = 0;
+ system->zss_lofi = 0;
+}
+
+
+/* Reads each cpu's kstats, and adds the usage to the cpu's pset */
+static void
+zsd_update_cpu_stats(zsd_ctl_t *ctl, zsd_cpu_t *cpu)
+{
+ zsd_system_t *sys;
+ processorid_t cpuid;
+ zsd_pset_t *pset_prev;
+ zsd_pset_t *pset;
+ kstat_t *kstat;
+ kstat_named_t *knp;
+ kid_t kid;
+ uint64_t idle, intr, kern, user;
+
+ sys = ctl->zsctl_system;
+ pset = cpu->zsc_pset;
+ knp = NULL;
+ kid = -1;
+ cpuid = cpu->zsc_id;
+
+ /* Get the cpu time totals for this cpu */
+ kstat = kstat_lookup(ctl->zsctl_kstat_ctl, "cpu", cpuid, "sys");
+ if (kstat == NULL)
+ return;
+
+ kid = kstat_read(ctl->zsctl_kstat_ctl, kstat, NULL);
+ if (kid == -1)
+ return;
+
+ knp = kstat_data_lookup(kstat, "cpu_nsec_idle");
+ if (knp == NULL || knp->data_type != KSTAT_DATA_UINT64)
+ return;
+
+ idle = knp->value.ui64;
+
+ knp = kstat_data_lookup(kstat, "cpu_nsec_kernel");
+ if (knp == NULL || knp->data_type != KSTAT_DATA_UINT64)
+ return;
+
+ kern = knp->value.ui64;
+
+ knp = kstat_data_lookup(kstat, "cpu_nsec_user");
+ if (knp == NULL || knp->data_type != KSTAT_DATA_UINT64)
+ return;
+
+ user = knp->value.ui64;
+
+ /*
+ * Tracking intr time per cpu just exists for future enhancements.
+ * The value is presently always zero.
+ */
+ intr = 0;
+ cpu->zsc_nsec_idle = idle;
+ cpu->zsc_nsec_intr = intr;
+ cpu->zsc_nsec_kern = kern;
+ cpu->zsc_nsec_user = user;
+
+ if (cpu->zsc_onlined == B_TRUE) {
+ /*
+ * cpu is newly online. There is no reference value,
+ * so just record its current stats for comparison
+ * on next stat read.
+ */
+ cpu->zsc_nsec_idle_prev = cpu->zsc_nsec_idle;
+ cpu->zsc_nsec_intr_prev = cpu->zsc_nsec_intr;
+ cpu->zsc_nsec_kern_prev = cpu->zsc_nsec_kern;
+ cpu->zsc_nsec_user_prev = cpu->zsc_nsec_user;
+ return;
+ }
+
+ /*
+ * Calculate relative time since previous refresh.
+ * Paranoia. Don't let time go backwards.
+ */
+ idle = intr = kern = user = 0;
+ if (cpu->zsc_nsec_idle > cpu->zsc_nsec_idle_prev)
+ idle = cpu->zsc_nsec_idle - cpu->zsc_nsec_idle_prev;
+
+ if (cpu->zsc_nsec_intr > cpu->zsc_nsec_intr_prev)
+ intr = cpu->zsc_nsec_intr - cpu->zsc_nsec_intr_prev;
+
+ if (cpu->zsc_nsec_kern > cpu->zsc_nsec_kern_prev)
+ kern = cpu->zsc_nsec_kern - cpu->zsc_nsec_kern_prev;
+
+ if (cpu->zsc_nsec_user > cpu->zsc_nsec_user_prev)
+ user = cpu->zsc_nsec_user - cpu->zsc_nsec_user_prev;
+
+ /* Update totals for cpu usage */
+ TIMESTRUC_ADD_NANOSEC(cpu->zsc_idle, idle);
+ TIMESTRUC_ADD_NANOSEC(cpu->zsc_intr, intr);
+ TIMESTRUC_ADD_NANOSEC(cpu->zsc_kern, kern);
+ TIMESTRUC_ADD_NANOSEC(cpu->zsc_user, user);
+
+ /*
+ * Add cpu's stats to its pset if it is known to be in
+ * the pset since previous read.
+ */
+ if (cpu->zsc_psetid == cpu->zsc_psetid_prev ||
+ cpu->zsc_psetid_prev == ZS_PSET_ERROR ||
+ (pset_prev = zsd_lookup_pset_byid(ctl,
+ cpu->zsc_psetid_prev)) == NULL) {
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_idle, idle);
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_intr, intr);
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_kern, kern);
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_user, user);
+ } else {
+ /*
+ * Last pset was different than current pset.
+ * Best guess is to split usage between the two.
+ */
+ TIMESTRUC_ADD_NANOSEC(pset_prev->zsp_idle, idle / 2);
+ TIMESTRUC_ADD_NANOSEC(pset_prev->zsp_intr, intr / 2);
+ TIMESTRUC_ADD_NANOSEC(pset_prev->zsp_kern, kern / 2);
+ TIMESTRUC_ADD_NANOSEC(pset_prev->zsp_user, user / 2);
+
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_idle,
+ (idle / 2) + (idle % 2));
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_intr,
+ (intr / 2) + (intr % 2));
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_kern,
+ (kern / 2) + (kern % 2));
+ TIMESTRUC_ADD_NANOSEC(pset->zsp_user,
+ (user / 2) + (user % 2));
+ }
+ TIMESTRUC_ADD_NANOSEC(sys->zss_idle, idle);
+ TIMESTRUC_ADD_NANOSEC(sys->zss_intr, intr);
+ TIMESTRUC_ADD_NANOSEC(sys->zss_kern, kern);
+ TIMESTRUC_ADD_NANOSEC(sys->zss_user, user);
+}
+
+/* Determine the details of a processor set by pset_id */
+static int
+zsd_get_pool_pset(zsd_ctl_t *ctl, psetid_t psetid, char *psetname,
+ size_t namelen, uint_t *cputype, uint64_t *online, uint64_t *size,
+ uint64_t *min, uint64_t *max, int64_t *importance)
+{
+ uint_t old, num;
+
+ pool_conf_t *conf = ctl->zsctl_pool_conf;
+ pool_value_t **vals = ctl->zsctl_pool_vals;
+ pool_resource_t **res_list = NULL;
+ pool_resource_t *pset;
+ pool_component_t **cpus = NULL;
+ processorid_t *cache;
+ const char *string;
+ uint64_t uint64;
+ int64_t int64;
+ int i, ret, type;
+
+ if (ctl->zsctl_pool_status == POOL_DISABLED) {
+
+ /*
+ * Inspect legacy psets
+ */
+ for (;;) {
+ old = num = ctl->zsctl_cpu_ncache;
+ ret = pset_info(psetid, &type, &num,
+ ctl->zsctl_cpu_cache);
+ if (ret < 0) {
+ /* pset is gone. Tell caller to retry */
+ errno = EINTR;
+ return (-1);
+ }
+ if (num <= old) {
+ /* Success */
+ break;
+ }
+ if ((cache = (processorid_t *)realloc(
+ ctl->zsctl_cpu_cache, num *
+ sizeof (processorid_t))) != NULL) {
+ ctl->zsctl_cpu_ncache = num;
+ ctl->zsctl_cpu_cache = cache;
+ } else {
+ /*
+ * Could not allocate to get new cpu list.
+ */
+ zsd_warn(gettext(
+ "Could not allocate for cpu list"));
+ errno = ENOMEM;
+ return (-1);
+ }
+ }
+ /*
+ * Old school pset. Just make min and max equal
+ * to its size
+ */
+ if (psetid == ZS_PSET_DEFAULT) {
+ *cputype = ZS_CPUTYPE_DEFAULT_PSET;
+ (void) strlcpy(psetname, "pset_default", namelen);
+ } else {
+ *cputype = ZS_CPUTYPE_PSRSET_PSET;
+ (void) snprintf(psetname, namelen,
+ "SUNWlegacy_pset_%d", psetid);
+ }
+
+ /*
+ * Just treat legacy pset as a simple pool pset
+ */
+ *online = num;
+ *size = num;
+ *min = num;
+ *max = num;
+ *importance = 1;
+
+ return (0);
+ }
+
+ /* Look up the pool pset using the pset id */
+ res_list = NULL;
+ pool_value_set_int64(vals[1], psetid);
+ if (pool_value_set_name(vals[1], "pset.sys_id")
+ != PO_SUCCESS)
+ goto err;
+
+ if (pool_value_set_name(vals[0], "type") != PO_SUCCESS)
+ goto err;
+ if (pool_value_set_string(vals[0], "pset") != PO_SUCCESS)
+ goto err;
+ if ((res_list = pool_query_resources(conf, &num, vals)) == NULL)
+ goto err;
+ if (num != 1)
+ goto err;
+ pset = res_list[0];
+ free(res_list);
+ res_list = NULL;
+ if (pool_get_property(conf, pool_resource_to_elem(conf, pset),
+ "pset.name", vals[0]) != POC_STRING ||
+ pool_value_get_string(vals[0], &string) != PO_SUCCESS)
+ goto err;
+
+ (void) strlcpy(psetname, string, namelen);
+ if (strncmp(psetname, "SUNWtmp", strlen("SUNWtmp")) == 0)
+ *cputype = ZS_CPUTYPE_DEDICATED;
+ else if (psetid == ZS_PSET_DEFAULT)
+ *cputype = ZS_CPUTYPE_DEFAULT_PSET;
+ else
+ *cputype = ZS_CPUTYPE_POOL_PSET;
+
+ /* Get size, min, max, and importance */
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ pset), "pset.size", vals[0]) == POC_UINT &&
+ pool_value_get_uint64(vals[0], &uint64) == PO_SUCCESS)
+ *size = uint64;
+ else
+ *size = 0;
+
+ /* Get size, min, max, and importance */
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ pset), "pset.min", vals[0]) == POC_UINT &&
+ pool_value_get_uint64(vals[0], &uint64) == PO_SUCCESS)
+ *min = uint64;
+ else
+ *min = 0;
+ if (*min >= ZSD_PSET_UNLIMITED)
+ *min = ZS_LIMIT_NONE;
+
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ pset), "pset.max", vals[0]) == POC_UINT &&
+ pool_value_get_uint64(vals[0], &uint64) == PO_SUCCESS)
+ *max = uint64;
+ else
+ *max = ZS_LIMIT_NONE;
+
+ if (*max >= ZSD_PSET_UNLIMITED)
+ *max = ZS_LIMIT_NONE;
+
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ pset), "pset.importance", vals[0]) == POC_INT &&
+ pool_value_get_int64(vals[0], &int64) == PO_SUCCESS)
+ *importance = int64;
+ else
+ *importance = (uint64_t)1;
+
+ *online = 0;
+ if (*size == 0)
+ return (0);
+
+ /* get cpus */
+ cpus = pool_query_resource_components(conf, pset, &num, NULL);
+ if (cpus == NULL)
+ goto err;
+
+ /* Make sure there is space for cpu id list */
+ if (num > ctl->zsctl_cpu_ncache) {
+ if ((cache = (processorid_t *)realloc(
+ ctl->zsctl_cpu_cache, num *
+ sizeof (processorid_t))) != NULL) {
+ ctl->zsctl_cpu_ncache = num;
+ ctl->zsctl_cpu_cache = cache;
+ } else {
+ /*
+ * Could not allocate to get new cpu list.
+ */
+ zsd_warn(gettext(
+ "Could not allocate for cpu list"));
+ goto err;
+ }
+ }
+
+ /* count the online cpus */
+ for (i = 0; i < num; i++) {
+ if (pool_get_property(conf, pool_component_to_elem(
+ conf, cpus[i]), "cpu.status", vals[0]) != POC_STRING ||
+ pool_value_get_string(vals[0], &string) != PO_SUCCESS)
+ goto err;
+
+ if (strcmp(string, "on-line") != 0 &&
+ strcmp(string, "no-intr") != 0)
+ continue;
+
+ if (pool_get_property(conf, pool_component_to_elem(
+ conf, cpus[i]), "cpu.sys_id", vals[0]) != POC_INT ||
+ pool_value_get_int64(vals[0], &int64) != PO_SUCCESS)
+ goto err;
+
+ (*online)++;
+ ctl->zsctl_cpu_cache[i] = (psetid_t)int64;
+ }
+ free(cpus);
+ return (0);
+err:
+ if (res_list != NULL)
+ free(res_list);
+ if (cpus != NULL)
+ free(cpus);
+
+ /*
+ * The pools operations should succeed since the conf is a consistent
+ * snapshot. Tell caller there is no need to retry.
+ */
+ errno = EINVAL;
+ return (-1);
+}
+
+/*
+ * Update the current list of processor sets.
+ * This also updates the list of online cpus, and each cpu's pset membership.
+ */
+static void
+zsd_refresh_psets(zsd_ctl_t *ctl)
+{
+ int i, j, ret, state;
+ uint_t old, num;
+ uint_t cputype;
+ int64_t sys_id, importance;
+ uint64_t online, size, min, max;
+ zsd_system_t *system;
+ zsd_pset_t *pset;
+ zsd_cpu_t *cpu;
+ psetid_t *cache;
+ char psetname[ZS_PSETNAME_MAX];
+ processorid_t cpuid;
+ pool_value_t *pv_save = NULL;
+ pool_resource_t **res_list = NULL;
+ pool_resource_t *res;
+ pool_value_t **vals;
+ pool_conf_t *conf;
+ boolean_t roll_cpus = B_TRUE;
+
+ /* Zero cpu counters to recount them */
+ system = ctl->zsctl_system;
+ system->zss_ncpus = 0;
+ system->zss_ncpus_online = 0;
+retry:
+ ret = pool_get_status(&state);
+ if (ret == 0 && state == POOL_ENABLED) {
+
+ conf = ctl->zsctl_pool_conf;
+ vals = ctl->zsctl_pool_vals;
+ pv_save = vals[1];
+ vals[1] = NULL;
+
+ if (ctl->zsctl_pool_status == POOL_DISABLED) {
+ if (pool_conf_open(ctl->zsctl_pool_conf,
+ pool_dynamic_location(), PO_RDONLY) == 0) {
+ ctl->zsctl_pool_status = POOL_ENABLED;
+ ctl->zsctl_pool_changed = POU_PSET;
+ }
+ } else {
+ ctl->zsctl_pool_changed = 0;
+ ret = pool_conf_update(ctl->zsctl_pool_conf,
+ &(ctl->zsctl_pool_changed));
+ if (ret < 0) {
+ /* Pools must have become disabled */
+ (void) pool_conf_close(ctl->zsctl_pool_conf);
+ ctl->zsctl_pool_status = POOL_DISABLED;
+ if (pool_error() == POE_SYSTEM && errno ==
+ ENOTACTIVE)
+ goto retry;
+
+ zsd_warn(gettext(
+ "Unable to update pool configuration"));
+ /* Not able to get pool info. Don't update. */
+ goto err;
+ }
+ }
+ /* Get the list of psets using libpool */
+ if (pool_value_set_name(vals[0], "type") != PO_SUCCESS)
+ goto err;
+
+ if (pool_value_set_string(vals[0], "pset") != PO_SUCCESS)
+ goto err;
+ if ((res_list = pool_query_resources(conf, &num, vals))
+ == NULL)
+ goto err;
+
+ if (num > ctl->zsctl_pset_ncache) {
+ if ((cache = (psetid_t *)realloc(ctl->zsctl_pset_cache,
+ (num) * sizeof (psetid_t))) == NULL) {
+ goto err;
+ }
+ ctl->zsctl_pset_ncache = num;
+ ctl->zsctl_pset_cache = cache;
+ }
+ /* Save the pset id of each pset */
+ for (i = 0; i < num; i++) {
+ res = res_list[i];
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ res), "pset.sys_id", vals[0]) != POC_INT ||
+ pool_value_get_int64(vals[0], &sys_id)
+ != PO_SUCCESS)
+ goto err;
+ ctl->zsctl_pset_cache[i] = (int)sys_id;
+ }
+ vals[1] = pv_save;
+ pv_save = NULL;
+ } else {
+ if (ctl->zsctl_pool_status == POOL_ENABLED) {
+ (void) pool_conf_close(ctl->zsctl_pool_conf);
+ ctl->zsctl_pool_status = POOL_DISABLED;
+ }
+ /* Get the pset list using legacy psets */
+ for (;;) {
+ old = num = ctl->zsctl_pset_ncache;
+ (void) pset_list(ctl->zsctl_pset_cache, &num);
+ if ((num + 1) <= old) {
+ break;
+ }
+ if ((cache = (psetid_t *)realloc(ctl->zsctl_pset_cache,
+ (num + 1) * sizeof (psetid_t))) != NULL) {
+ ctl->zsctl_pset_ncache = num + 1;
+ ctl->zsctl_pset_cache = cache;
+ } else {
+ /*
+ * Could not allocate to get new pset list.
+ * Give up
+ */
+ return;
+ }
+ }
+ /* Add the default pset to list */
+ ctl->zsctl_pset_cache[num] = ctl->zsctl_pset_cache[0];
+ ctl->zsctl_pset_cache[0] = ZS_PSET_DEFAULT;
+ num++;
+ }
+psets_changed:
+ zsd_mark_cpus_start(ctl, roll_cpus);
+ zsd_mark_psets_start(ctl);
+ roll_cpus = B_FALSE;
+
+ /* Refresh cpu membership of all psets */
+ for (i = 0; i < num; i++) {
+
+ /* Get pool pset information */
+ sys_id = ctl->zsctl_pset_cache[i];
+ if (zsd_get_pool_pset(ctl, sys_id, psetname, sizeof (psetname),
+ &cputype, &online, &size, &min, &max, &importance)
+ != 0) {
+ if (errno == EINTR)
+ goto psets_changed;
+ zsd_warn(gettext("Failed to get info for pset %d"),
+ sys_id);
+ continue;
+ }
+
+ system->zss_ncpus += size;
+ system->zss_ncpus_online += online;
+
+ pset = zsd_lookup_insert_pset(ctl, psetname,
+ ctl->zsctl_pset_cache[i]);
+
+ /* update pset info */
+ zsd_mark_pset_found(pset, cputype, online, size, min,
+ max, importance);
+
+ /* update each cpu in pset */
+ for (j = 0; j < pset->zsp_online; j++) {
+ cpuid = ctl->zsctl_cpu_cache[j];
+ cpu = zsd_lookup_insert_cpu(ctl, cpuid);
+ zsd_mark_cpu_found(cpu, pset, sys_id);
+ }
+ }
+err:
+ if (res_list != NULL)
+ free(res_list);
+ if (pv_save != NULL)
+ vals[1] = pv_save;
+}
+
+
+
+/*
+ * Fetch the current pool and pset name for the given zone.
+ */
+static void
+zsd_get_zone_pool_pset(zsd_ctl_t *ctl, zsd_zone_t *zone,
+ char *pool, int poollen, char *pset, int psetlen, uint_t *cputype)
+{
+ poolid_t poolid;
+ pool_t **pools = NULL;
+ pool_resource_t **res_list = NULL;
+ char poolname[ZS_POOLNAME_MAX];
+ char psetname[ZS_PSETNAME_MAX];
+ pool_conf_t *conf = ctl->zsctl_pool_conf;
+ pool_value_t *pv_save = NULL;
+ pool_value_t **vals = ctl->zsctl_pool_vals;
+ const char *string;
+ int ret;
+ int64_t int64;
+ uint_t num;
+
+ ret = zone_getattr(zone->zsz_id, ZONE_ATTR_POOLID,
+ &poolid, sizeof (poolid));
+ if (ret < 0)
+ goto lookup_done;
+
+ pv_save = vals[1];
+ vals[1] = NULL;
+ pools = NULL;
+ res_list = NULL;
+
+ /* Default values if lookup fails */
+ (void) strlcpy(poolname, "pool_default", sizeof (poolname));
+ (void) strlcpy(psetname, "pset_default", sizeof (poolname));
+ *cputype = ZS_CPUTYPE_DEFAULT_PSET;
+
+ /* no dedicated cpu if pools are disabled */
+ if (ctl->zsctl_pool_status == POOL_DISABLED)
+ goto lookup_done;
+
+ /* Get the pool name using the id */
+ pool_value_set_int64(vals[0], poolid);
+ if (pool_value_set_name(vals[0], "pool.sys_id") != PO_SUCCESS)
+ goto lookup_done;
+
+ if ((pools = pool_query_pools(conf, &num, vals)) == NULL)
+ goto lookup_done;
+
+ if (num != 1)
+ goto lookup_done;
+
+ if (pool_get_property(conf, pool_to_elem(conf, pools[0]),
+ "pool.name", vals[0]) != POC_STRING ||
+ pool_value_get_string(vals[0], &string) != PO_SUCCESS)
+ goto lookup_done;
+ (void) strlcpy(poolname, (char *)string, sizeof (poolname));
+
+ /* Get the name of the pset for the pool */
+ if (pool_value_set_name(vals[0], "type") != PO_SUCCESS)
+ goto lookup_done;
+
+ if (pool_value_set_string(vals[0], "pset") != PO_SUCCESS)
+ goto lookup_done;
+
+ if ((res_list = pool_query_pool_resources(conf, pools[0], &num, vals))
+ == NULL)
+ goto lookup_done;
+
+ if (num != 1)
+ goto lookup_done;
+
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ res_list[0]), "pset.sys_id", vals[0]) != POC_INT ||
+ pool_value_get_int64(vals[0], &int64) != PO_SUCCESS)
+ goto lookup_done;
+
+ if (int64 == ZS_PSET_DEFAULT)
+ *cputype = ZS_CPUTYPE_DEFAULT_PSET;
+
+ if (pool_get_property(conf, pool_resource_to_elem(conf,
+ res_list[0]), "pset.name", vals[0]) != POC_STRING ||
+ pool_value_get_string(vals[0], &string) != PO_SUCCESS)
+ goto lookup_done;
+
+ (void) strlcpy(psetname, (char *)string, sizeof (psetname));
+
+ if (strncmp(psetname, "SUNWtmp_", strlen("SUNWtmp_")) == 0)
+ *cputype = ZS_CPUTYPE_DEDICATED;
+ if (strncmp(psetname, "SUNW_legacy_", strlen("SUNW_legacy_")) == 0)
+ *cputype = ZS_CPUTYPE_PSRSET_PSET;
+ else
+ *cputype = ZS_CPUTYPE_POOL_PSET;
+
+lookup_done:
+
+ if (pv_save != NULL)
+ vals[1] = pv_save;
+
+ if (res_list)
+ free(res_list);
+ if (pools)
+ free(pools);
+
+ (void) strlcpy(pool, poolname, poollen);
+ (void) strlcpy(pset, psetname, psetlen);
+}
+
+/* Convert scheduler names to ZS_* scheduler flags */
+static uint_t
+zsd_schedname2int(char *clname, int pri)
+{
+ uint_t sched = 0;
+
+ if (strcmp(clname, "TS") == 0) {
+ sched = ZS_SCHED_TS;
+ } else if (strcmp(clname, "IA") == 0) {
+ sched = ZS_SCHED_IA;
+ } else if (strcmp(clname, "FX") == 0) {
+ if (pri > 59) {
+ sched = ZS_SCHED_FX_60;
+ } else {
+ sched = ZS_SCHED_FX;
+ }
+ } else if (strcmp(clname, "RT") == 0) {
+ sched = ZS_SCHED_RT;
+
+ } else if (strcmp(clname, "FSS") == 0) {
+ sched = ZS_SCHED_FSS;
+ }
+ return (sched);
+}
+
+static uint64_t
+zsd_get_zone_rctl_limit(char *name)
+{
+ rctlblk_t *rblk;
+
+ rblk = (rctlblk_t *)alloca(rctlblk_size());
+ if (getrctl(name, NULL, rblk, RCTL_FIRST)
+ != 0) {
+ return (ZS_LIMIT_NONE);
+ }
+ return (rctlblk_get_value(rblk));
+}
+
+static uint64_t
+zsd_get_zone_rctl_usage(char *name)
+{
+ rctlblk_t *rblk;
+
+ rblk = (rctlblk_t *)alloca(rctlblk_size());
+ if (getrctl(name, NULL, rblk, RCTL_USAGE)
+ != 0) {
+ return (0);
+ }
+ return (rctlblk_get_value(rblk));
+}
+
+#define ZSD_NUM_RCTL_VALS 19
+
+/*
+ * Fetch the limit information for a zone. This uses zone_enter() as the
+ * getrctl(2) system call only returns rctl information for the zone of
+ * the caller.
+ */
+static int
+zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares,
+ uint64_t *cpu_cap, uint64_t *ram_cap, uint64_t *locked_cap,
+ uint64_t *vm_cap, uint64_t *processes_cap, uint64_t *processes,
+ uint64_t *lwps_cap, uint64_t *lwps, uint64_t *shm_cap, uint64_t *shm,
+ uint64_t *shmids_cap, uint64_t *shmids, uint64_t *semids_cap,
+ uint64_t *semids, uint64_t *msgids_cap, uint64_t *msgids,
+ uint64_t *lofi_cap, uint64_t *lofi, uint_t *sched)
+{
+ int p[2], pid, tmpl_fd, ret;
+ ctid_t ct;
+ char class[PC_CLNMSZ];
+ uint64_t vals[ZSD_NUM_RCTL_VALS];
+ zsd_system_t *sys = ctl->zsctl_system;
+ int i = 0;
+ int res = 0;
+
+ /* Treat all caps as no cap on error */
+ *cpu_shares = ZS_LIMIT_NONE;
+ *cpu_cap = ZS_LIMIT_NONE;
+ *ram_cap = ZS_LIMIT_NONE;
+ *locked_cap = ZS_LIMIT_NONE;
+ *vm_cap = ZS_LIMIT_NONE;
+
+ *processes_cap = ZS_LIMIT_NONE;
+ *lwps_cap = ZS_LIMIT_NONE;
+ *shm_cap = ZS_LIMIT_NONE;
+ *shmids_cap = ZS_LIMIT_NONE;
+ *semids_cap = ZS_LIMIT_NONE;
+ *msgids_cap = ZS_LIMIT_NONE;
+ *lofi_cap = ZS_LIMIT_NONE;
+
+ *processes = 0;
+ *lwps = 0;
+ *shm = 0;
+ *shmids = 0;
+ *semids = 0;
+ *msgids = 0;
+ *lofi = 0;
+
+ /* Get the ram cap first since it is a zone attr */
+ ret = zone_getattr(zone->zsz_id, ZONE_ATTR_PHYS_MCAP,
+ ram_cap, sizeof (*ram_cap));
+ if (ret < 0 || *ram_cap == 0)
+ *ram_cap = ZS_LIMIT_NONE;
+
+ /* Get the zone's default scheduling class */
+ ret = zone_getattr(zone->zsz_id, ZONE_ATTR_SCHED_CLASS,
+ class, sizeof (class));
+ if (ret < 0)
+ return (-1);
+
+ *sched = zsd_schedname2int(class, 0);
+
+ /* rctl caps must be fetched from within the zone */
+ if (pipe(p) != 0)
+ return (-1);
+
+ if ((tmpl_fd = init_template()) == -1) {
+ (void) close(p[0]);
+ (void) close(p[1]);
+ return (-1);
+ }
+ pid = forkx(0);
+ if (pid < 0) {
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(p[0]);
+ (void) close(p[1]);
+ return (-1);
+ }
+ if (pid == 0) {
+
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ (void) close(p[0]);
+ if (zone->zsz_id != getzoneid()) {
+ if (zone_enter(zone->zsz_id) < 0) {
+ (void) close(p[1]);
+ _exit(0);
+ }
+ }
+
+ /* Get caps for zone, and write them to zonestatd parent. */
+ vals[i++] = zsd_get_zone_rctl_limit("zone.cpu-shares");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.cpu-cap");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-locked-memory");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-swap");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-processes");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-processes");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-lwps");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-lwps");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-shm-memory");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-shm-memory");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-shm-ids");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-shm-ids");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-sem-ids");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-sem-ids");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-msg-ids");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-msg-ids");
+ vals[i++] = zsd_get_zone_rctl_limit("zone.max-lofi");
+ vals[i++] = zsd_get_zone_rctl_usage("zone.max-lofi");
+
+ if (write(p[1], vals, ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) !=
+ ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) {
+ (void) close(p[1]);
+ _exit(1);
+ }
+
+ (void) close(p[1]);
+ _exit(0);
+ }
+ if (contract_latest(&ct) == -1)
+ ct = -1;
+
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ (void) close(p[1]);
+ while (waitpid(pid, NULL, 0) != pid)
+ ;
+
+ /* Read cap from child in zone */
+ if (read(p[0], vals, ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) !=
+ ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) {
+ res = -1;
+ goto cleanup;
+ }
+ i = 0;
+ *cpu_shares = vals[i++];
+ *cpu_cap = vals[i++];
+ *locked_cap = vals[i++];
+ *vm_cap = vals[i++];
+ *processes_cap = vals[i++];
+ *processes = vals[i++];
+ *lwps_cap = vals[i++];
+ *lwps = vals[i++];
+ *shm_cap = vals[i++];
+ *shm = vals[i++];
+ *shmids_cap = vals[i++];
+ *shmids = vals[i++];
+ *semids_cap = vals[i++];
+ *semids = vals[i++];
+ *msgids_cap = vals[i++];
+ *msgids = vals[i++];
+ *lofi_cap = vals[i++];
+ *lofi = vals[i++];
+
+ /* Interpret maximum values as no cap */
+ if (*cpu_cap == UINT32_MAX || *cpu_cap == 0)
+ *cpu_cap = ZS_LIMIT_NONE;
+ if (*processes_cap == sys->zss_processes_max)
+ *processes_cap = ZS_LIMIT_NONE;
+ if (*lwps_cap == sys->zss_lwps_max)
+ *lwps_cap = ZS_LIMIT_NONE;
+ if (*shm_cap == sys->zss_shm_max)
+ *shm_cap = ZS_LIMIT_NONE;
+ if (*shmids_cap == sys->zss_shmids_max)
+ *shmids_cap = ZS_LIMIT_NONE;
+ if (*semids_cap == sys->zss_semids_max)
+ *semids_cap = ZS_LIMIT_NONE;
+ if (*msgids_cap == sys->zss_msgids_max)
+ *msgids_cap = ZS_LIMIT_NONE;
+ if (*lofi_cap == sys->zss_lofi_max)
+ *lofi_cap = ZS_LIMIT_NONE;
+
+
+cleanup:
+ (void) close(p[0]);
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ (void) contract_abandon_id(ct);
+
+ return (res);
+}
+
+/* Update the current list of running zones */
+static void
+zsd_refresh_zones(zsd_ctl_t *ctl)
+{
+ zsd_zone_t *zone;
+ uint_t old, num;
+ ushort_t flags;
+ int i, ret;
+ zoneid_t *cache;
+ uint64_t cpu_shares;
+ uint64_t cpu_cap;
+ uint64_t ram_cap;
+ uint64_t locked_cap;
+ uint64_t vm_cap;
+ uint64_t processes_cap;
+ uint64_t processes;
+ uint64_t lwps_cap;
+ uint64_t lwps;
+ uint64_t shm_cap;
+ uint64_t shm;
+ uint64_t shmids_cap;
+ uint64_t shmids;
+ uint64_t semids_cap;
+ uint64_t semids;
+ uint64_t msgids_cap;
+ uint64_t msgids;
+ uint64_t lofi_cap;
+ uint64_t lofi;
+
+ char zonename[ZS_ZONENAME_MAX];
+ char poolname[ZS_POOLNAME_MAX];
+ char psetname[ZS_PSETNAME_MAX];
+ uint_t sched;
+ uint_t cputype;
+ uint_t iptype;
+
+ /* Get the current list of running zones */
+ for (;;) {
+ old = num = ctl->zsctl_zone_ncache;
+ (void) zone_list(ctl->zsctl_zone_cache, &num);
+ if (num <= old)
+ break;
+ if ((cache = (zoneid_t *)realloc(ctl->zsctl_zone_cache,
+ (num) * sizeof (zoneid_t))) != NULL) {
+ ctl->zsctl_zone_ncache = num;
+ ctl->zsctl_zone_cache = cache;
+ } else {
+ /* Could not allocate to get new zone list. Give up */
+ return;
+ }
+ }
+
+ zsd_mark_zones_start(ctl);
+
+ for (i = 0; i < num; i++) {
+
+ ret = getzonenamebyid(ctl->zsctl_zone_cache[i],
+ zonename, sizeof (zonename));
+ if (ret < 0)
+ continue;
+
+ zone = zsd_lookup_insert_zone(ctl, zonename,
+ ctl->zsctl_zone_cache[i]);
+
+ ret = zone_getattr(ctl->zsctl_zone_cache[i], ZONE_ATTR_FLAGS,
+ &flags, sizeof (flags));
+ if (ret < 0)
+ continue;
+
+ if (flags & ZF_NET_EXCL)
+ iptype = ZS_IPTYPE_EXCLUSIVE;
+ else
+ iptype = ZS_IPTYPE_SHARED;
+
+ zsd_get_zone_pool_pset(ctl, zone, poolname, sizeof (poolname),
+ psetname, sizeof (psetname), &cputype);
+
+ if (zsd_get_zone_caps(ctl, zone, &cpu_shares, &cpu_cap,
+ &ram_cap, &locked_cap, &vm_cap, &processes_cap, &processes,
+ &lwps_cap, &lwps, &shm_cap, &shm, &shmids_cap, &shmids,
+ &semids_cap, &semids, &msgids_cap, &msgids, &lofi_cap,
+ &lofi, &sched) != 0)
+ continue;
+
+ zsd_mark_zone_found(ctl, zone, cpu_shares, cpu_cap, ram_cap,
+ locked_cap, vm_cap, processes_cap, processes, lwps_cap,
+ lwps, shm_cap, shm, shmids_cap, shmids, semids_cap,
+ semids, msgids_cap, msgids, lofi_cap, lofi, poolname,
+ psetname, sched, cputype, iptype);
+ }
+}
+
+/* Fetch the details of a process from its psinfo_t */
+static void
+zsd_get_proc_info(zsd_ctl_t *ctl, psinfo_t *psinfo, psetid_t *psetid,
+ psetid_t *prev_psetid, zoneid_t *zoneid, zoneid_t *prev_zoneid,
+ timestruc_t *delta, uint_t *sched)
+{
+ timestruc_t d;
+ zsd_proc_t *proc;
+
+ /* Get cached data for proc */
+ proc = &(ctl->zsctl_proc_array[psinfo->pr_pid]);
+ *psetid = psinfo->pr_lwp.pr_bindpset;
+
+ if (proc->zspr_psetid == ZS_PSET_ERROR)
+ *prev_psetid = *psetid;
+ else
+ *prev_psetid = proc->zspr_psetid;
+
+ *zoneid = psinfo->pr_zoneid;
+ if (proc->zspr_zoneid == -1)
+ *prev_zoneid = *zoneid;
+ else
+ *prev_zoneid = proc->zspr_zoneid;
+
+ TIMESTRUC_DELTA(d, psinfo->pr_time, proc->zspr_usage);
+ *delta = d;
+
+ *sched = zsd_schedname2int(psinfo->pr_lwp.pr_clname,
+ psinfo->pr_lwp.pr_pri);
+
+ /* Update cached data for proc */
+ proc->zspr_psetid = psinfo->pr_lwp.pr_bindpset;
+ proc->zspr_zoneid = psinfo->pr_zoneid;
+ proc->zspr_sched = *sched;
+ proc->zspr_usage.tv_sec = psinfo->pr_time.tv_sec;
+ proc->zspr_usage.tv_nsec = psinfo->pr_time.tv_nsec;
+ proc->zspr_ppid = psinfo->pr_ppid;
+}
+
+/*
+ * Reset the known cpu usage of a process. This is done after a process
+ * exits so that if the pid is recycled, data from its previous life is
+ * not reused
+ */
+static void
+zsd_flush_proc_info(zsd_proc_t *proc)
+{
+ proc->zspr_usage.tv_sec = 0;
+ proc->zspr_usage.tv_nsec = 0;
+}
+
+/*
+ * Open the current extended accounting file. On initialization, open the
+ * file as the current file to be used. Otherwise, open the file as the
+ * next file to use of the current file reaches EOF.
+ */
+static int
+zsd_open_exacct(zsd_ctl_t *ctl, boolean_t init)
+{
+ int ret, oret, state, trys = 0, flags;
+ int *fd, *open;
+ ea_file_t *eaf;
+ struct stat64 *stat;
+ char path[MAXPATHLEN];
+
+ /*
+ * The accounting file is first opened at the tail. Following
+ * opens to new accounting files are opened at the head.
+ */
+ if (init == B_TRUE) {
+ flags = EO_NO_VALID_HDR | EO_TAIL;
+ fd = &ctl->zsctl_proc_fd;
+ eaf = &ctl->zsctl_proc_eaf;
+ stat = &ctl->zsctl_proc_stat;
+ open = &ctl->zsctl_proc_open;
+ } else {
+ flags = EO_NO_VALID_HDR | EO_HEAD;
+ fd = &ctl->zsctl_proc_fd_next;
+ eaf = &ctl->zsctl_proc_eaf_next;
+ stat = &ctl->zsctl_proc_stat_next;
+ open = &ctl->zsctl_proc_open_next;
+ }
+
+ *fd = -1;
+ *open = 0;
+retry:
+ /* open accounting files for cpu consumption */
+ ret = acctctl(AC_STATE_GET | AC_PROC, &state, sizeof (state));
+ if (ret != 0) {
+ zsd_warn(gettext("Unable to get process accounting state"));
+ goto err;
+ }
+ if (state != AC_ON) {
+ if (trys > 0) {
+ zsd_warn(gettext(
+ "Unable to enable process accounting"));
+ goto err;
+ }
+ (void) zsd_enable_cpu_stats();
+ trys++;
+ goto retry;
+ }
+
+ ret = acctctl(AC_FILE_GET | AC_PROC, path, sizeof (path));
+ if (ret != 0) {
+ zsd_warn(gettext("Unable to get process accounting file"));
+ goto err;
+ }
+
+ if ((*fd = open64(path, O_RDONLY, 0)) >= 0 &&
+ (oret = ea_fdopen(eaf, *fd, NULL, flags, O_RDONLY)) == 0)
+ ret = fstat64(*fd, stat);
+
+ if (*fd < 0 || oret < 0 || ret < 0) {
+ struct timespec ts;
+
+ /*
+ * It is possible the accounting file is momentarily unavailable
+ * because it is being rolled. Try for up to half a second.
+ *
+ * If failure to open accounting file persists, give up.
+ */
+ if (oret == 0)
+ (void) ea_close(eaf);
+ else if (*fd >= 0)
+ (void) close(*fd);
+ if (trys > 500) {
+ zsd_warn(gettext(
+ "Unable to open process accounting file"));
+ goto err;
+ }
+ /* wait one millisecond */
+ ts.tv_sec = 0;
+ ts.tv_nsec = NANOSEC / 1000;
+ (void) nanosleep(&ts, NULL);
+ goto retry;
+ }
+ *open = 1;
+ return (0);
+err:
+ if (*fd >= 0)
+ (void) close(*fd);
+ *open = 0;
+ *fd = -1;
+ return (-1);
+}
+
+/*
+ * Walk /proc and charge each process to its zone and processor set.
+ * Then read exacct data for exited processes, and charge them as well.
+ */
+static void
+zsd_refresh_procs(zsd_ctl_t *ctl, boolean_t init)
+{
+ DIR *dir;
+ struct dirent *dent;
+ psinfo_t psinfo;
+ int fd, ret;
+ zsd_proc_t *proc, *pproc, *tmp, *next;
+ list_t pplist, plist;
+ zsd_zone_t *zone, *prev_zone;
+ zsd_pset_t *pset, *prev_pset;
+ psetid_t psetid, prev_psetid;
+ zoneid_t zoneid, prev_zoneid;
+ zsd_pset_usage_t *usage, *prev_usage;
+ char path[MAXPATHLEN];
+
+ ea_object_t object;
+ ea_object_t pobject;
+ boolean_t hrtime_expired = B_FALSE;
+ struct timeval interval_end;
+
+ timestruc_t delta, d1, d2;
+ uint_t sched = 0;
+
+ /*
+ * Get the current accounting file. The current accounting file
+ * may be different than the file in use, as the accounting file
+ * may have been rolled, or manually changed by an admin.
+ */
+ ret = zsd_open_exacct(ctl, init);
+ if (ret != 0) {
+ zsd_warn(gettext("Unable to track process accounting"));
+ return;
+ }
+
+ /*
+ * Mark the current time as the interval end time. Don't track
+ * processes that exit after this time.
+ */
+ (void) gettimeofday(&interval_end, NULL);
+
+ dir = opendir("/proc");
+ if (dir == NULL) {
+ zsd_warn(gettext("Unable to open /proc"));
+ return;
+ }
+
+ dent = ctl->zsctl_procfs_dent;
+
+ (void) memset(dent, 0, ctl->zsctl_procfs_dent_size);
+
+ /* Walk all processes and compute each zone's usage on each pset. */
+ while (readdir_r(dir, dent) != 0) {
+
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ (void) snprintf(path, sizeof (path), "/proc/%s/psinfo",
+ dent->d_name);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
+ (void) close(fd);
+ continue;
+ }
+ (void) close(fd);
+
+ zsd_get_proc_info(ctl, &psinfo, &psetid, &prev_psetid,
+ &zoneid, &prev_zoneid, &delta, &sched);
+
+ d1.tv_sec = delta.tv_sec / 2;
+ d1.tv_nsec = delta.tv_nsec / 2;
+ d2.tv_sec = (delta.tv_sec / 2) + (delta.tv_sec % 2);
+ d2.tv_nsec = (delta.tv_nsec / 2) + (delta.tv_nsec % 2);
+
+ /* Get the zone and pset this process is running in */
+ zone = zsd_lookup_zone_byid(ctl, zoneid);
+ if (zone == NULL)
+ continue;
+ pset = zsd_lookup_pset_byid(ctl, psetid);
+ if (pset == NULL)
+ continue;
+ usage = zsd_lookup_insert_usage(ctl, pset, zone);
+ if (usage == NULL)
+ continue;
+
+ /*
+ * Get the usage of the previous zone and pset if they were
+ * different.
+ */
+ if (zoneid != prev_zoneid)
+ prev_zone = zsd_lookup_zone_byid(ctl, prev_zoneid);
+ else
+ prev_zone = NULL;
+
+ if (psetid != prev_psetid)
+ prev_pset = zsd_lookup_pset_byid(ctl, prev_psetid);
+ else
+ prev_pset = NULL;
+
+ prev_usage = NULL;
+ if (prev_zone != NULL || prev_pset != NULL) {
+ if (prev_zone == NULL)
+ prev_zone = zone;
+ if (prev_pset == NULL)
+ prev_pset = pset;
+
+ prev_usage = zsd_lookup_insert_usage(ctl, prev_pset,
+ prev_zone);
+ }
+
+ /* Update the usage with the processes info */
+ if (prev_usage == NULL) {
+ zsd_mark_pset_usage_found(usage, sched);
+ } else {
+ zsd_mark_pset_usage_found(usage, sched);
+ zsd_mark_pset_usage_found(prev_usage, sched);
+ }
+
+ /*
+ * First time around is just to get a starting point. All
+ * usages will be zero.
+ */
+ if (init == B_TRUE)
+ continue;
+
+ if (prev_usage == NULL) {
+ zsd_add_usage(ctl, usage, &delta);
+ } else {
+ zsd_add_usage(ctl, usage, &d1);
+ zsd_add_usage(ctl, prev_usage, &d2);
+ }
+ }
+ (void) closedir(dir);
+
+ /*
+ * No need to collect exited proc data on initialization. Just
+ * caching the usage of the known processes to get a zero starting
+ * point.
+ */
+ if (init == B_TRUE)
+ return;
+
+ /*
+ * Add accounting records to account for processes which have
+ * exited.
+ */
+ list_create(&plist, sizeof (zsd_proc_t),
+ offsetof(zsd_proc_t, zspr_next));
+ list_create(&pplist, sizeof (zsd_proc_t),
+ offsetof(zsd_proc_t, zspr_next));
+
+ for (;;) {
+ pid_t pid;
+ pid_t ppid;
+ timestruc_t user, sys, proc_usage;
+ timestruc_t finish;
+ int numfound = 0;
+
+ bzero(&object, sizeof (object));
+ proc = NULL;
+ zone = NULL;
+ pset = NULL;
+ usage = NULL;
+ ret = ea_get_object(&ctl->zsctl_proc_eaf, &object);
+ if (ret == EO_ERROR) {
+ if (ea_error() == EXR_EOF) {
+
+ struct stat64 *stat;
+ struct stat64 *stat_next;
+
+ /*
+ * See if the next accounting file is the
+ * same as the current accounting file.
+ */
+ stat = &(ctl->zsctl_proc_stat);
+ stat_next = &(ctl->zsctl_proc_stat_next);
+ if (stat->st_ino == stat_next->st_ino &&
+ stat->st_dev == stat_next->st_dev) {
+ /*
+ * End of current accounting file is
+ * reached, so finished. Clear EOF
+ * bit for next time around.
+ */
+ ea_clear(&ctl->zsctl_proc_eaf);
+ break;
+ } else {
+ /*
+ * Accounting file has changed. Move
+ * to current accounting file.
+ */
+ (void) ea_close(&ctl->zsctl_proc_eaf);
+
+ ctl->zsctl_proc_fd =
+ ctl->zsctl_proc_fd_next;
+ ctl->zsctl_proc_eaf =
+ ctl->zsctl_proc_eaf_next;
+ ctl->zsctl_proc_stat =
+ ctl->zsctl_proc_stat_next;
+
+ ctl->zsctl_proc_fd_next = -1;
+ ctl->zsctl_proc_open_next = 0;
+ continue;
+ }
+ } else {
+ /*
+ * Other accounting error. Give up on
+ * accounting.
+ */
+ goto ea_err;
+ }
+ }
+ /* Skip if not a process group */
+ if ((object.eo_catalog & EXT_TYPE_MASK) != EXT_GROUP ||
+ (object.eo_catalog & EXD_DATA_MASK) != EXD_GROUP_PROC) {
+ (void) ea_free_item(&object, EUP_ALLOC);
+ continue;
+ }
+
+ /* The process group entry should be complete */
+ while (numfound < 9) {
+ bzero(&pobject, sizeof (pobject));
+ ret = ea_get_object(&ctl->zsctl_proc_eaf,
+ &pobject);
+ if (ret < 0) {
+ (void) ea_free_item(&object, EUP_ALLOC);
+ zsd_warn(
+ "unable to get process accounting data");
+ goto ea_err;
+ }
+ /* Next entries should be process data */
+ if ((pobject.eo_catalog & EXT_TYPE_MASK) ==
+ EXT_GROUP) {
+ (void) ea_free_item(&object, EUP_ALLOC);
+ (void) ea_free_item(&pobject, EUP_ALLOC);
+ zsd_warn(
+ "process data of wrong type");
+ goto ea_err;
+ }
+ switch (pobject.eo_catalog & EXD_DATA_MASK) {
+ case EXD_PROC_PID:
+ pid = pobject.eo_item.ei_uint32;
+ proc = &(ctl->zsctl_proc_array[pid]);
+ /*
+ * This process should not be currently in
+ * the list of processes to process.
+ */
+ assert(!list_link_active(&proc->zspr_next));
+ numfound++;
+ break;
+ case EXD_PROC_ANCPID:
+ ppid = pobject.eo_item.ei_uint32;
+ pproc = &(ctl->zsctl_proc_array[ppid]);
+ numfound++;
+ break;
+ case EXD_PROC_ZONENAME:
+ zone = zsd_lookup_zone(ctl,
+ pobject.eo_item.ei_string, -1);
+ numfound++;
+ break;
+ case EXD_PROC_CPU_USER_SEC:
+ user.tv_sec =
+ pobject.eo_item.ei_uint64;
+ numfound++;
+ break;
+ case EXD_PROC_CPU_USER_NSEC:
+ user.tv_nsec =
+ pobject.eo_item.ei_uint64;
+ numfound++;
+ break;
+ case EXD_PROC_CPU_SYS_SEC:
+ sys.tv_sec =
+ pobject.eo_item.ei_uint64;
+ numfound++;
+ break;
+ case EXD_PROC_CPU_SYS_NSEC:
+ sys.tv_nsec =
+ pobject.eo_item.ei_uint64;
+ numfound++;
+ break;
+ case EXD_PROC_FINISH_SEC:
+ finish.tv_sec =
+ pobject.eo_item.ei_uint64;
+ numfound++;
+ break;
+ case EXD_PROC_FINISH_NSEC:
+ finish.tv_nsec =
+ pobject.eo_item.ei_uint64;
+ numfound++;
+ break;
+ }
+ (void) ea_free_item(&pobject, EUP_ALLOC);
+ }
+ (void) ea_free_item(&object, EUP_ALLOC);
+ if (numfound != 9) {
+ zsd_warn(gettext(
+ "Malformed process accounting entry found"));
+ goto proc_done;
+ }
+
+ if (finish.tv_sec > interval_end.tv_sec ||
+ (finish.tv_sec == interval_end.tv_sec &&
+ finish.tv_nsec > (interval_end.tv_usec * 1000)))
+ hrtime_expired = B_TRUE;
+
+ /*
+ * Try to identify the zone and pset to which this
+ * exited process belongs.
+ */
+ if (zone == NULL)
+ goto proc_done;
+
+ /* Save proc info */
+ proc->zspr_ppid = ppid;
+ proc->zspr_zoneid = zone->zsz_id;
+
+ prev_psetid = ZS_PSET_ERROR;
+ sched = 0;
+
+ /*
+ * The following tries to deduce the processes pset.
+ *
+ * First choose pset and sched using cached value from the
+ * most recent time the process has been seen.
+ *
+ * pset and sched can change across zone_enter, so make sure
+ * most recent sighting of this process was in the same
+ * zone before using most recent known value.
+ *
+ * If there is no known value, use value of processes
+ * parent. If parent is unknown, walk parents until a known
+ * parent is found.
+ *
+ * If no parent in the zone is found, use the zone's default
+ * pset and scheduling class.
+ */
+ if (proc->zspr_psetid != ZS_PSET_ERROR) {
+ prev_psetid = proc->zspr_psetid;
+ pset = zsd_lookup_pset_byid(ctl, prev_psetid);
+ sched = proc->zspr_sched;
+ } else if (pproc->zspr_zoneid == zone->zsz_id &&
+ pproc->zspr_psetid != ZS_PSET_ERROR) {
+ prev_psetid = pproc->zspr_psetid;
+ pset = zsd_lookup_pset_byid(ctl, prev_psetid);
+ sched = pproc->zspr_sched;
+ }
+
+ if (pset == NULL) {
+ /*
+ * Process or processes parent has never been seen.
+ * Save to deduce a known parent later.
+ */
+ proc_usage = sys;
+ TIMESTRUC_ADD_TIMESTRUC(proc_usage, user);
+ TIMESTRUC_DELTA(delta, proc_usage,
+ proc->zspr_usage);
+ proc->zspr_usage = delta;
+ list_insert_tail(&plist, proc);
+ continue;
+ }
+
+ /* Add the zone's usage to the pset */
+ usage = zsd_lookup_insert_usage(ctl, pset, zone);
+ if (usage == NULL)
+ goto proc_done;
+
+ zsd_mark_pset_usage_found(usage, sched);
+
+ /* compute the usage to add for the exited proc */
+ proc_usage = sys;
+ TIMESTRUC_ADD_TIMESTRUC(proc_usage, user);
+ TIMESTRUC_DELTA(delta, proc_usage,
+ proc->zspr_usage);
+
+ zsd_add_usage(ctl, usage, &delta);
+proc_done:
+ zsd_flush_proc_info(proc);
+
+ if (hrtime_expired == B_TRUE)
+ break;
+ }
+ /*
+ * close next accounting file.
+ */
+ if (ctl->zsctl_proc_open_next) {
+ (void) ea_close(
+ &ctl->zsctl_proc_eaf_next);
+ ctl->zsctl_proc_open_next = 0;
+ ctl->zsctl_proc_fd_next = -1;
+ }
+
+ /* For the remaining processes, use pset and sched of a known parent */
+ proc = list_head(&plist);
+ while (proc != NULL) {
+ next = proc;
+ for (;;) {
+ if (next->zspr_ppid == 0 || next->zspr_ppid == -1) {
+ /*
+ * Kernel process, or parent is unknown, skip
+ * process, remove from process list.
+ */
+ tmp = proc;
+ proc = list_next(&plist, proc);
+ list_link_init(&tmp->zspr_next);
+ break;
+ }
+ pproc = &(ctl->zsctl_proc_array[next->zspr_ppid]);
+ if (pproc->zspr_zoneid != proc->zspr_zoneid) {
+ /*
+ * Parent in different zone. Save process and
+ * use zone's default pset and sched below
+ */
+ tmp = proc;
+ proc = list_next(&plist, proc);
+ list_remove(&plist, tmp);
+ list_insert_tail(&pplist, tmp);
+ break;
+ }
+ /* Parent has unknown pset, Search parent's parent */
+ if (pproc->zspr_psetid == ZS_PSET_ERROR) {
+ next = pproc;
+ continue;
+ }
+ /* Found parent with known pset. Use its info */
+ proc->zspr_psetid = pproc->zspr_psetid;
+ proc->zspr_sched = pproc->zspr_sched;
+ next->zspr_psetid = pproc->zspr_psetid;
+ next->zspr_sched = pproc->zspr_sched;
+ zone = zsd_lookup_zone_byid(ctl,
+ proc->zspr_zoneid);
+ if (zone == NULL) {
+ tmp = proc;
+ proc = list_next(&plist, proc);
+ list_remove(&plist, tmp);
+ list_link_init(&tmp->zspr_next);
+ break;
+ }
+ pset = zsd_lookup_pset_byid(ctl,
+ proc->zspr_psetid);
+ if (pset == NULL) {
+ tmp = proc;
+ proc = list_next(&plist, proc);
+ list_remove(&plist, tmp);
+ list_link_init(&tmp->zspr_next);
+ break;
+ }
+ /* Add the zone's usage to the pset */
+ usage = zsd_lookup_insert_usage(ctl, pset, zone);
+ if (usage == NULL) {
+ tmp = proc;
+ proc = list_next(&plist, proc);
+ list_remove(&plist, tmp);
+ list_link_init(&tmp->zspr_next);
+ break;
+ }
+ zsd_mark_pset_usage_found(usage, proc->zspr_sched);
+ zsd_add_usage(ctl, usage, &proc->zspr_usage);
+ zsd_flush_proc_info(proc);
+ tmp = proc;
+ proc = list_next(&plist, proc);
+ list_remove(&plist, tmp);
+ list_link_init(&tmp->zspr_next);
+ break;
+ }
+ }
+ /*
+ * Process has never been seen. Using zone info to
+ * determine pset and scheduling class.
+ */
+ proc = list_head(&pplist);
+ while (proc != NULL) {
+
+ zone = zsd_lookup_zone_byid(ctl, proc->zspr_zoneid);
+ if (zone == NULL)
+ goto next;
+ if (zone->zsz_psetid != ZS_PSET_ERROR &&
+ zone->zsz_psetid != ZS_PSET_MULTI) {
+ prev_psetid = zone->zsz_psetid;
+ pset = zsd_lookup_pset_byid(ctl, prev_psetid);
+ } else {
+ pset = zsd_lookup_pset(ctl, zone->zsz_pset, -1);
+ if (pset != NULL)
+ prev_psetid = pset->zsp_id;
+ }
+ if (pset == NULL)
+ goto next;
+
+ sched = zone->zsz_scheds;
+ /*
+ * Ignore FX high scheduling class if it is not the
+ * only scheduling class in the zone.
+ */
+ if (sched != ZS_SCHED_FX_60)
+ sched &= (~ZS_SCHED_FX_60);
+ /*
+ * If more than one scheduling class has been found
+ * in the zone, use zone's default scheduling class for
+ * this process.
+ */
+ if ((sched & (sched - 1)) != 0)
+ sched = zone->zsz_default_sched;
+
+ /* Add the zone's usage to the pset */
+ usage = zsd_lookup_insert_usage(ctl, pset, zone);
+ if (usage == NULL)
+ goto next;
+
+ zsd_mark_pset_usage_found(usage, sched);
+ zsd_add_usage(ctl, usage, &proc->zspr_usage);
+next:
+ tmp = proc;
+ proc = list_next(&pplist, proc);
+ zsd_flush_proc_info(tmp);
+ list_link_init(&tmp->zspr_next);
+ }
+ return;
+ea_err:
+ /*
+ * Close the next accounting file if we have not transitioned to it
+ * yet.
+ */
+ if (ctl->zsctl_proc_open_next) {
+ (void) ea_close(&ctl->zsctl_proc_eaf_next);
+ ctl->zsctl_proc_open_next = 0;
+ ctl->zsctl_proc_fd_next = -1;
+ }
+}
+
+/*
+ * getvmusage(2) uses size_t's in the passwd data structure, which differ
+ * in size for 32bit and 64 bit kernels. Since this is a contracted interface,
+ * and zonestatd does not necessarily match the kernel's bitness, marshal
+ * results appropriately.
+ */
+static int
+zsd_getvmusage(zsd_ctl_t *ctl, uint_t flags, time_t age, zsd_vmusage64_t *buf,
+ uint64_t *nres)
+{
+ zsd_vmusage32_t *vmu32;
+ zsd_vmusage64_t *vmu64;
+ uint32_t nres32;
+ int i;
+ int ret;
+
+ if (ctl->zsctl_kern_bits == 32) {
+ nres32 = *nres;
+ ret = syscall(SYS_rusagesys, _RUSAGESYS_GETVMUSAGE,
+ flags, age, (uintptr_t)buf, (uintptr_t)&nres32);
+ *nres = nres32;
+ if (ret == 0 && buf != NULL) {
+ /*
+ * An array of vmusage32_t's has been returned.
+ * Convert it to an array of vmusage64_t's.
+ */
+ vmu32 = (zsd_vmusage32_t *)buf;
+ vmu64 = (zsd_vmusage64_t *)buf;
+ for (i = nres32 - 1; i >= 0; i--) {
+
+ vmu64[i].vmu_zoneid = vmu32[i].vmu_zoneid;
+ vmu64[i].vmu_type = vmu32[i].vmu_type;
+ vmu64[i].vmu_type = vmu32[i].vmu_type;
+ vmu64[i].vmu_rss_all = vmu32[i].vmu_rss_all;
+ vmu64[i].vmu_rss_private =
+ vmu32[i].vmu_rss_private;
+ vmu64[i].vmu_rss_shared =
+ vmu32[i].vmu_rss_shared;
+ vmu64[i].vmu_swap_all = vmu32[i].vmu_swap_all;
+ vmu64[i].vmu_swap_private =
+ vmu32[i].vmu_swap_private;
+ vmu64[i].vmu_swap_shared =
+ vmu32[i].vmu_swap_shared;
+ }
+ }
+ return (ret);
+ } else {
+ /*
+ * kernel is 64 bit, so use 64 bit structures as zonestat
+ * expects.
+ */
+ return (syscall(SYS_rusagesys, _RUSAGESYS_GETVMUSAGE,
+ flags, age, (uintptr_t)buf, (uintptr_t)nres));
+
+ }
+}
+
+/*
+ * Update the current physical, virtual, and locked memory usage of the
+ * running zones.
+ */
+static void
+zsd_refresh_memory(zsd_ctl_t *ctl, boolean_t init)
+{
+
+ uint64_t phys_total;
+ uint64_t phys_used;
+ uint64_t phys_zones;
+ uint64_t phys_zones_overcount;
+ uint64_t phys_zones_extra;
+ uint64_t phys_zones_credit;
+
+ uint64_t vm_free;
+ uint64_t vm_used;
+
+ uint64_t disk_swap_total;
+ uint64_t disk_swap_used; /* disk swap with contents */
+
+ uint64_t physmem;
+ uint64_t pp_kernel;
+ uint64_t arc_size = 0;
+ struct anoninfo ani;
+
+ int num_swap_devices;
+ struct swaptable *swt;
+ struct swapent *swent;
+ size_t swt_size;
+ char *path;
+
+ zsd_vmusage64_t *vmusage;
+ uint64_t num_vmusage;
+
+ int i, ret;
+
+ zsd_system_t *sys;
+ zsd_zone_t *zone;
+ int vmu_nzones;
+
+ kstat_t *kstat;
+ char kstat_name[KSTAT_STRLEN];
+ kstat_named_t *knp;
+ kid_t kid;
+
+ if (init)
+ return;
+
+ sys = ctl->zsctl_system;
+
+ /* interrogate swap devices to find the amount of disk swap */
+disk_swap_again:
+ num_swap_devices = swapctl(SC_GETNSWP, NULL);
+
+ if (num_swap_devices == 0) {
+ sys->zss_swap_total = disk_swap_total = 0;
+ sys->zss_swap_used = disk_swap_used = 0;
+ /* No disk swap */
+ goto disk_swap_done;
+ }
+ /* see if swap table needs to be larger */
+ if (num_swap_devices > ctl->zsctl_swap_cache_num) {
+ swt_size = sizeof (int) +
+ (num_swap_devices * sizeof (struct swapent)) +
+ (num_swap_devices * MAXPATHLEN);
+ if (ctl->zsctl_swap_cache != NULL)
+ free(ctl->zsctl_swap_cache);
+
+ swt = (struct swaptable *)malloc(swt_size);
+ if (swt == NULL) {
+ /*
+ * Could not allocate to get list of swap devices.
+ * Just use data from the most recent read, which will
+ * be zero if this is the first read.
+ */
+ zsd_warn(gettext("Unable to allocate to determine "
+ "virtual memory"));
+ disk_swap_total = sys->zss_swap_total;
+ disk_swap_used = sys->zss_swap_used;
+ goto disk_swap_done;
+ }
+ swent = swt->swt_ent;
+ path = (char *)swt + (sizeof (int) +
+ num_swap_devices * sizeof (swapent_t));
+ for (i = 0; i < num_swap_devices; i++, swent++) {
+ swent->ste_path = path;
+ path += MAXPATHLEN;
+ }
+ swt->swt_n = num_swap_devices;
+ ctl->zsctl_swap_cache = swt;
+ ctl->zsctl_swap_cache_size = swt_size;
+ ctl->zsctl_swap_cache_num = num_swap_devices;
+ }
+ num_swap_devices = swapctl(SC_LIST, ctl->zsctl_swap_cache);
+ if (num_swap_devices < 0) {
+ /* More swap devices have arrived */
+ if (errno == ENOMEM)
+ goto disk_swap_again;
+
+ zsd_warn(gettext("Unable to determine disk swap devices"));
+ /* Unexpected error. Use existing data */
+ disk_swap_total = sys->zss_swap_total;
+ disk_swap_used = sys->zss_swap_used;
+ goto disk_swap_done;
+ }
+
+ /* add up the disk swap */
+ disk_swap_total = 0;
+ disk_swap_used = 0;
+ swent = ctl->zsctl_swap_cache->swt_ent;
+ for (i = 0; i < num_swap_devices; i++, swent++) {
+ disk_swap_total += swent->ste_pages;
+ disk_swap_used += (swent->ste_pages - swent->ste_free);
+ }
+ disk_swap_total *= ctl->zsctl_pagesize;
+ disk_swap_used *= ctl->zsctl_pagesize;
+
+ sys->zss_swap_total = disk_swap_total;
+ sys->zss_swap_used = disk_swap_used;
+
+disk_swap_done:
+
+ /* get system pages kstat */
+ kid = -1;
+ kstat = kstat_lookup(ctl->zsctl_kstat_ctl, "unix", 0, "system_pages");
+ if (kstat == NULL)
+ zsd_warn(gettext("Unable to lookup system pages kstat"));
+ else
+ kid = kstat_read(ctl->zsctl_kstat_ctl, kstat, NULL);
+
+ if (kid == -1) {
+ zsd_warn(gettext("Unable to read system pages kstat"));
+ return;
+ } else {
+ knp = kstat_data_lookup(kstat, "physmem");
+ if (knp == NULL) {
+ zsd_warn(gettext("Unable to read physmem"));
+ } else {
+ if (knp->data_type == KSTAT_DATA_UINT64)
+ physmem = knp->value.ui64;
+ else if (knp->data_type == KSTAT_DATA_UINT32)
+ physmem = knp->value.ui32;
+ else
+ return;
+ }
+ knp = kstat_data_lookup(kstat, "pp_kernel");
+ if (knp == NULL) {
+ zsd_warn(gettext("Unable to read pp_kernel"));
+ } else {
+ if (knp->data_type == KSTAT_DATA_UINT64)
+ pp_kernel = knp->value.ui64;
+ else if (knp->data_type == KSTAT_DATA_UINT32)
+ pp_kernel = knp->value.ui32;
+ else
+ return;
+ }
+ }
+ physmem *= ctl->zsctl_pagesize;
+ pp_kernel *= ctl->zsctl_pagesize;
+
+ /* get the zfs arc size if available */
+ arc_size = 0;
+ kid = -1;
+ kstat = kstat_lookup(ctl->zsctl_kstat_ctl, "zfs", 0, "arcstats");
+ if (kstat != NULL)
+ kid = kstat_read(ctl->zsctl_kstat_ctl, kstat, NULL);
+ if (kid != -1) {
+ knp = kstat_data_lookup(kstat, "size");
+ if (knp != NULL)
+ if (knp->data_type == KSTAT_DATA_UINT64)
+ arc_size = knp->value.ui64;
+ }
+
+ /* Try to get swap information */
+ if (swapctl(SC_AINFO, &ani) < 0) {
+ zsd_warn(gettext("Unable to get swap info"));
+ return;
+ }
+
+vmusage_again:
+ /* getvmusage to get physical memory usage */
+ vmusage = ctl->zsctl_vmusage_cache;
+ num_vmusage = ctl->zsctl_vmusage_cache_num;
+
+ ret = zsd_getvmusage(ctl, VMUSAGE_SYSTEM | VMUSAGE_ALL_ZONES, 0,
+ vmusage, &num_vmusage);
+
+ if (ret != 0) {
+ /* Unexpected error. Use existing data */
+ if (errno != EOVERFLOW) {
+ zsd_warn(gettext(
+ "Unable to read physical memory usage"));
+ phys_zones = sys->zss_ram_zones;
+ goto vmusage_done;
+ }
+ }
+ /* vmusage results cache too small */
+ if (num_vmusage > ctl->zsctl_vmusage_cache_num) {
+
+ size_t size = sizeof (zsd_vmusage64_t) * num_vmusage;
+
+ if (ctl->zsctl_vmusage_cache != NULL)
+ free(ctl->zsctl_vmusage_cache);
+ vmusage = (zsd_vmusage64_t *)malloc(size);
+ if (vmusage == NULL) {
+ zsd_warn(gettext("Unable to alloc to determine "
+ "physical memory usage"));
+ phys_zones = sys->zss_ram_zones;
+ goto vmusage_done;
+ }
+ ctl->zsctl_vmusage_cache = vmusage;
+ ctl->zsctl_vmusage_cache_num = num_vmusage;
+ goto vmusage_again;
+ }
+
+ phys_zones_overcount = 0;
+ vmu_nzones = 0;
+ for (i = 0; i < num_vmusage; i++) {
+ switch (vmusage[i].vmu_type) {
+ case VMUSAGE_SYSTEM:
+ /* total pages backing user process mappings */
+ phys_zones = sys->zss_ram_zones =
+ vmusage[i].vmu_rss_all;
+ break;
+ case VMUSAGE_ZONE:
+ vmu_nzones++;
+ phys_zones_overcount += vmusage[i].vmu_rss_all;
+ zone = zsd_lookup_zone_byid(ctl, vmusage[i].vmu_id);
+ if (zone != NULL)
+ zone->zsz_usage_ram = vmusage[i].vmu_rss_all;
+ break;
+ default:
+ break;
+ }
+ }
+ /*
+ * Figure how much memory was double counted due to text sharing
+ * between zones. Credit this back so that the sum of the zones
+ * equals the total zone ram usage;
+ */
+ phys_zones_extra = phys_zones_overcount - phys_zones;
+ phys_zones_credit = phys_zones_extra / vmu_nzones;
+
+vmusage_done:
+
+ /* walk the zones to get swap and locked kstats. Fetch ram cap. */
+ sys->zss_locked_zones = 0;
+ sys->zss_vm_zones = 0;
+ for (zone = list_head(&ctl->zsctl_zones); zone != NULL;
+ zone = list_next(&ctl->zsctl_zones, zone)) {
+
+ /* If zone halted during interval, show memory usage as none */
+ if (zone->zsz_active == B_FALSE ||
+ zone->zsz_deleted == B_TRUE) {
+ zone->zsz_usage_ram = 0;
+ zone->zsz_usage_vm = 0;
+ zone->zsz_usage_locked = 0;
+ continue;
+ }
+
+ if (phys_zones_credit > 0) {
+ if (zone->zsz_usage_ram > phys_zones_credit) {
+ zone->zsz_usage_ram -= phys_zones_credit;
+ }
+ }
+ /*
+ * Get zone's swap usage. Since zone could have halted,
+ * treats as zero if cannot read
+ */
+ zone->zsz_usage_vm = 0;
+ (void) snprintf(kstat_name, sizeof (kstat_name),
+ "swapresv_zone_%d", zone->zsz_id);
+ kid = -1;
+ kstat = kstat_lookup(ctl->zsctl_kstat_ctl, "caps",
+ zone->zsz_id, kstat_name);
+ if (kstat != NULL)
+ kid = kstat_read(ctl->zsctl_kstat_ctl, kstat, NULL);
+ if (kid != -1) {
+ knp = kstat_data_lookup(kstat, "usage");
+ if (knp != NULL &&
+ knp->data_type == KSTAT_DATA_UINT64) {
+ zone->zsz_usage_vm = knp->value.ui64;
+ sys->zss_vm_zones += knp->value.ui64;
+ }
+ }
+ /*
+ * Get zone's locked usage. Since zone could have halted,
+ * treats as zero if cannot read
+ */
+ zone->zsz_usage_locked = 0;
+ (void) snprintf(kstat_name, sizeof (kstat_name),
+ "lockedmem_zone_%d", zone->zsz_id);
+ kid = -1;
+ kstat = kstat_lookup(ctl->zsctl_kstat_ctl, "caps",
+ zone->zsz_id, kstat_name);
+ if (kstat != NULL)
+ kid = kstat_read(ctl->zsctl_kstat_ctl, kstat, NULL);
+ if (kid != -1) {
+ knp = kstat_data_lookup(kstat, "usage");
+ if (knp != NULL &&
+ knp->data_type == KSTAT_DATA_UINT64) {
+ zone->zsz_usage_locked = knp->value.ui64;
+ /*
+ * Since locked memory accounting for zones
+ * can double count ddi locked memory, cap each
+ * zone's locked usage at its ram usage.
+ */
+ if (zone->zsz_usage_locked >
+ zone->zsz_usage_ram)
+ zone->zsz_usage_locked =
+ zone->zsz_usage_ram;
+ sys->zss_locked_zones +=
+ zone->zsz_usage_locked;
+ }
+ }
+ }
+
+ phys_total =
+ sysconf(_SC_PHYS_PAGES) * ctl->zsctl_pagesize;
+
+ phys_used = (sysconf(_SC_PHYS_PAGES) - sysconf(_SC_AVPHYS_PAGES))
+ * ctl->zsctl_pagesize;
+
+ /* Compute remaining statistics */
+ sys->zss_ram_total = phys_total;
+ sys->zss_ram_zones = phys_zones;
+ sys->zss_ram_kern = phys_used - phys_zones - arc_size;
+
+ /*
+ * The total for kernel locked memory should include
+ * segkp locked pages, but oh well. The arc size is subtracted,
+ * as that physical memory is reclaimable.
+ */
+ sys->zss_locked_kern = pp_kernel - arc_size;
+ /* Add memory used by kernel startup and obp to kernel locked */
+ if ((phys_total - physmem) > 0)
+ sys->zss_locked_kern += phys_total - physmem;
+
+ /*
+ * Add in the portion of (RAM+DISK) that is not available as swap,
+ * and consider it swap used by the kernel.
+ */
+ sys->zss_vm_total = phys_total + disk_swap_total;
+ vm_free = (ani.ani_max - ani.ani_resv) * ctl->zsctl_pagesize;
+ vm_used = sys->zss_vm_total - vm_free;
+ sys->zss_vm_kern = vm_used - sys->zss_vm_zones - arc_size;
+}
+
+/*
+ * Charge each cpu's usage to its processor sets. Also add the cpu's total
+ * time to each zone using the processor set. This tracks the maximum
+ * amount of cpu time that a zone could have used.
+ */
+static void
+zsd_refresh_cpu_stats(zsd_ctl_t *ctl, boolean_t init)
+{
+ zsd_system_t *sys;
+ zsd_zone_t *zone;
+ zsd_pset_usage_t *usage;
+ zsd_cpu_t *cpu;
+ zsd_cpu_t *cpu_next;
+ zsd_pset_t *pset;
+ timestruc_t ts;
+ uint64_t hrtime;
+ timestruc_t delta;
+
+ /* Update the per-cpu kstat data */
+ cpu_next = list_head(&ctl->zsctl_cpus);
+ while (cpu_next != NULL) {
+ cpu = cpu_next;
+ cpu_next = list_next(&ctl->zsctl_cpus, cpu);
+ zsd_update_cpu_stats(ctl, cpu);
+ }
+ /* Update the elapsed real time */
+ hrtime = gethrtime();
+ if (init) {
+ /* first time around, store hrtime for future comparision */
+ ctl->zsctl_hrtime = hrtime;
+ ctl->zsctl_hrtime_prev = hrtime;
+
+ } else {
+ /* Compute increase in hrtime since the most recent read */
+ ctl->zsctl_hrtime_prev = ctl->zsctl_hrtime;
+ ctl->zsctl_hrtime = hrtime;
+ if ((hrtime = hrtime - ctl->zsctl_hrtime_prev) > 0)
+ TIMESTRUC_ADD_NANOSEC(ctl->zsctl_hrtime_total, hrtime);
+ }
+
+ /* On initialization, all psets have zero time */
+ if (init)
+ return;
+
+ for (pset = list_head(&ctl->zsctl_psets); pset != NULL;
+ pset = list_next(&ctl->zsctl_psets, pset)) {
+
+ if (pset->zsp_active == B_FALSE) {
+ zsd_warn(gettext("Internal error,inactive pset found"));
+ continue;
+ }
+
+ /* sum total used time for pset */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ TIMESTRUC_ADD_TIMESTRUC(ts, pset->zsp_intr);
+ TIMESTRUC_ADD_TIMESTRUC(ts, pset->zsp_kern);
+ TIMESTRUC_ADD_TIMESTRUC(ts, pset->zsp_user);
+ /* kernel time in pset is total time minus zone time */
+ TIMESTRUC_DELTA(pset->zsp_usage_kern, ts,
+ pset->zsp_usage_zones);
+ if (pset->zsp_usage_kern.tv_sec < 0 ||
+ pset->zsp_usage_kern.tv_nsec < 0) {
+ pset->zsp_usage_kern.tv_sec = 0;
+ pset->zsp_usage_kern.tv_nsec = 0;
+ }
+ /* Total pset elapsed time is used time plus idle time */
+ TIMESTRUC_ADD_TIMESTRUC(ts, pset->zsp_idle);
+
+ TIMESTRUC_DELTA(delta, ts, pset->zsp_total_time);
+
+ for (usage = list_head(&pset->zsp_usage_list); usage != NULL;
+ usage = list_next(&pset->zsp_usage_list, usage)) {
+
+ zone = usage->zsu_zone;
+ if (usage->zsu_cpu_shares != ZS_LIMIT_NONE &&
+ usage->zsu_cpu_shares != ZS_SHARES_UNLIMITED &&
+ usage->zsu_cpu_shares != 0) {
+ /*
+ * Figure out how many nanoseconds of share time
+ * to give to the zone
+ */
+ hrtime = delta.tv_sec;
+ hrtime *= NANOSEC;
+ hrtime += delta.tv_nsec;
+ hrtime *= usage->zsu_cpu_shares;
+ hrtime /= pset->zsp_cpu_shares;
+ TIMESTRUC_ADD_NANOSEC(zone->zsz_share_time,
+ hrtime);
+ }
+ /* Add pset time to each zone using pset */
+ TIMESTRUC_ADD_TIMESTRUC(zone->zsz_pset_time, delta);
+
+ zone->zsz_cpus_online += pset->zsp_online;
+ }
+ pset->zsp_total_time = ts;
+ }
+
+ for (zone = list_head(&ctl->zsctl_zones); zone != NULL;
+ zone = list_next(&ctl->zsctl_zones, zone)) {
+
+ /* update cpu cap tracking if the zone has a cpu cap */
+ if (zone->zsz_cpu_cap != ZS_LIMIT_NONE) {
+ uint64_t elapsed;
+
+ elapsed = ctl->zsctl_hrtime - ctl->zsctl_hrtime_prev;
+ elapsed *= zone->zsz_cpu_cap;
+ elapsed = elapsed / 100;
+ TIMESTRUC_ADD_NANOSEC(zone->zsz_cap_time, elapsed);
+ }
+ }
+ sys = ctl->zsctl_system;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ TIMESTRUC_ADD_TIMESTRUC(ts, sys->zss_intr);
+ TIMESTRUC_ADD_TIMESTRUC(ts, sys->zss_kern);
+ TIMESTRUC_ADD_TIMESTRUC(ts, sys->zss_user);
+
+ /* kernel time in pset is total time minus zone time */
+ TIMESTRUC_DELTA(sys->zss_cpu_usage_kern, ts,
+ sys->zss_cpu_usage_zones);
+ if (sys->zss_cpu_usage_kern.tv_sec < 0 ||
+ sys->zss_cpu_usage_kern.tv_nsec < 0) {
+ sys->zss_cpu_usage_kern.tv_sec = 0;
+ sys->zss_cpu_usage_kern.tv_nsec = 0;
+ }
+ /* Total pset elapsed time is used time plus idle time */
+ TIMESTRUC_ADD_TIMESTRUC(ts, sys->zss_idle);
+ sys->zss_cpu_total_time = ts;
+}
+
+/*
+ * Saves current usage data to a cache that is read by libzonestat when
+ * calling zs_usage_read().
+ *
+ * All pointers in the cached data structure are set to NULL. When
+ * libzonestat reads the cached data, it will set the pointers relative to
+ * its address space.
+ */
+static void
+zsd_usage_cache_update(zsd_ctl_t *ctl)
+{
+ zs_usage_cache_t *cache;
+ zs_usage_cache_t *old;
+ zs_usage_t *usage;
+
+ zs_system_t *sys;
+ zsd_system_t *dsys;
+ zs_zone_t *zone = NULL;
+ zsd_zone_t *dzone;
+ zs_pset_t *pset = NULL;
+ zsd_pset_t *dpset;
+ zs_pset_zone_t *pusage;
+ zsd_pset_usage_t *dpusage;
+
+ char *next;
+ uint_t size, i, j;
+
+ size =
+ sizeof (zs_usage_cache_t) +
+ sizeof (zs_usage_t) +
+ sizeof (zs_system_t) +
+ sizeof (zs_zone_t) * ctl->zsctl_nzones +
+ sizeof (zs_pset_t) * ctl->zsctl_npsets +
+ sizeof (zs_pset_zone_t) * ctl->zsctl_npset_usages;
+
+ cache = (zs_usage_cache_t *)malloc(size);
+ if (cache == NULL) {
+ zsd_warn(gettext("Unable to allocate usage cache\n"));
+ return;
+ }
+
+ next = (char *)cache;
+ cache->zsuc_size = size - sizeof (zs_usage_cache_t);
+ next += sizeof (zs_usage_cache_t);
+
+ /* LINTED */
+ usage = cache->zsuc_usage = (zs_usage_t *)next;
+ next += sizeof (zs_usage_t);
+ usage->zsu_start = g_start;
+ usage->zsu_hrstart = g_hrstart;
+ usage->zsu_time = g_now;
+ usage->zsu_hrtime = g_hrnow;
+ usage->zsu_nzones = ctl->zsctl_nzones;
+ usage->zsu_npsets = ctl->zsctl_npsets;
+ usage->zsu_system = NULL;
+
+ /* LINTED */
+ sys = (zs_system_t *)next;
+ next += sizeof (zs_system_t);
+ dsys = ctl->zsctl_system;
+ sys->zss_ram_total = dsys->zss_ram_total;
+ sys->zss_ram_kern = dsys->zss_ram_kern;
+ sys->zss_ram_zones = dsys->zss_ram_zones;
+ sys->zss_locked_kern = dsys->zss_locked_kern;
+ sys->zss_locked_zones = dsys->zss_locked_zones;
+ sys->zss_vm_total = dsys->zss_vm_total;
+ sys->zss_vm_kern = dsys->zss_vm_kern;
+ sys->zss_vm_zones = dsys->zss_vm_zones;
+ sys->zss_swap_total = dsys->zss_swap_total;
+ sys->zss_swap_used = dsys->zss_swap_used;
+ sys->zss_ncpus = dsys->zss_ncpus;
+ sys->zss_ncpus_online = dsys->zss_ncpus_online;
+
+ sys->zss_processes_max = dsys->zss_maxpid;
+ sys->zss_lwps_max = dsys->zss_lwps_max;
+ sys->zss_shm_max = dsys->zss_shm_max;
+ sys->zss_shmids_max = dsys->zss_shmids_max;
+ sys->zss_semids_max = dsys->zss_semids_max;
+ sys->zss_msgids_max = dsys->zss_msgids_max;
+ sys->zss_lofi_max = dsys->zss_lofi_max;
+
+ sys->zss_processes = dsys->zss_processes;
+ sys->zss_lwps = dsys->zss_lwps;
+ sys->zss_shm = dsys->zss_shm;
+ sys->zss_shmids = dsys->zss_shmids;
+ sys->zss_semids = dsys->zss_semids;
+ sys->zss_msgids = dsys->zss_msgids;
+ sys->zss_lofi = dsys->zss_lofi;
+
+ sys->zss_cpu_total_time = dsys->zss_cpu_total_time;
+ sys->zss_cpu_usage_zones = dsys->zss_cpu_usage_zones;
+ sys->zss_cpu_usage_kern = dsys->zss_cpu_usage_kern;
+
+ for (i = 0, dzone = list_head(&ctl->zsctl_zones);
+ i < ctl->zsctl_nzones;
+ i++, dzone = list_next(&ctl->zsctl_zones, dzone)) {
+ /* LINTED */
+ zone = (zs_zone_t *)next;
+ next += sizeof (zs_zone_t);
+ list_link_init(&zone->zsz_next);
+ zone->zsz_system = NULL;
+
+ (void) strlcpy(zone->zsz_name, dzone->zsz_name,
+ sizeof (zone->zsz_name));
+ (void) strlcpy(zone->zsz_pool, dzone->zsz_pool,
+ sizeof (zone->zsz_pool));
+ (void) strlcpy(zone->zsz_pset, dzone->zsz_pset,
+ sizeof (zone->zsz_pset));
+ zone->zsz_id = dzone->zsz_id;
+ zone->zsz_cputype = dzone->zsz_cputype;
+ zone->zsz_iptype = dzone->zsz_iptype;
+ zone->zsz_start = dzone->zsz_start;
+ zone->zsz_hrstart = dzone->zsz_hrstart;
+ zone->zsz_scheds = dzone->zsz_scheds;
+ zone->zsz_cpu_shares = dzone->zsz_cpu_shares;
+ zone->zsz_cpu_cap = dzone->zsz_cpu_cap;
+ zone->zsz_ram_cap = dzone->zsz_ram_cap;
+ zone->zsz_vm_cap = dzone->zsz_vm_cap;
+ zone->zsz_locked_cap = dzone->zsz_locked_cap;
+ zone->zsz_cpu_usage = dzone->zsz_cpu_usage;
+ zone->zsz_cpus_online = dzone->zsz_cpus_online;
+ zone->zsz_pset_time = dzone->zsz_pset_time;
+ zone->zsz_cap_time = dzone->zsz_cap_time;
+ zone->zsz_share_time = dzone->zsz_share_time;
+ zone->zsz_usage_ram = dzone->zsz_usage_ram;
+ zone->zsz_usage_locked = dzone->zsz_usage_locked;
+ zone->zsz_usage_vm = dzone->zsz_usage_vm;
+
+ zone->zsz_processes_cap = dzone->zsz_processes_cap;
+ zone->zsz_lwps_cap = dzone->zsz_lwps_cap;
+ zone->zsz_shm_cap = dzone->zsz_shm_cap;
+ zone->zsz_shmids_cap = dzone->zsz_shmids_cap;
+ zone->zsz_semids_cap = dzone->zsz_semids_cap;
+ zone->zsz_msgids_cap = dzone->zsz_msgids_cap;
+ zone->zsz_lofi_cap = dzone->zsz_lofi_cap;
+
+ zone->zsz_processes = dzone->zsz_processes;
+ zone->zsz_lwps = dzone->zsz_lwps;
+ zone->zsz_shm = dzone->zsz_shm;
+ zone->zsz_shmids = dzone->zsz_shmids;
+ zone->zsz_semids = dzone->zsz_semids;
+ zone->zsz_msgids = dzone->zsz_msgids;
+ zone->zsz_lofi = dzone->zsz_lofi;
+ }
+
+ for (i = 0, dpset = list_head(&ctl->zsctl_psets);
+ i < ctl->zsctl_npsets;
+ i++, dpset = list_next(&ctl->zsctl_psets, dpset)) {
+ /* LINTED */
+ pset = (zs_pset_t *)next;
+ next += sizeof (zs_pset_t);
+ list_link_init(&pset->zsp_next);
+ (void) strlcpy(pset->zsp_name, dpset->zsp_name,
+ sizeof (pset->zsp_name));
+ pset->zsp_id = dpset->zsp_id;
+ pset->zsp_cputype = dpset->zsp_cputype;
+ pset->zsp_start = dpset->zsp_start;
+ pset->zsp_hrstart = dpset->zsp_hrstart;
+ pset->zsp_online = dpset->zsp_online;
+ pset->zsp_size = dpset->zsp_size;
+ pset->zsp_min = dpset->zsp_min;
+ pset->zsp_max = dpset->zsp_max;
+ pset->zsp_importance = dpset->zsp_importance;
+ pset->zsp_scheds = dpset->zsp_scheds;
+ pset->zsp_cpu_shares = dpset->zsp_cpu_shares;
+ pset->zsp_total_time = dpset->zsp_total_time;
+ pset->zsp_usage_kern = dpset->zsp_usage_kern;
+ pset->zsp_usage_zones = dpset->zsp_usage_zones;
+ pset->zsp_nusage = dpset->zsp_nusage;
+ /* Add pset usages for pset */
+ for (j = 0, dpusage = list_head(&dpset->zsp_usage_list);
+ j < dpset->zsp_nusage;
+ j++, dpusage = list_next(&dpset->zsp_usage_list, dpusage)) {
+ /* LINTED */
+ pusage = (zs_pset_zone_t *)next;
+ next += sizeof (zs_pset_zone_t);
+ /* pointers are computed by client */
+ pusage->zspz_pset = NULL;
+ pusage->zspz_zone = NULL;
+ list_link_init(&pusage->zspz_next);
+ pusage->zspz_zoneid = dpusage->zsu_zone->zsz_id;
+ pusage->zspz_start = dpusage->zsu_start;
+ pusage->zspz_hrstart = dpusage->zsu_hrstart;
+ pusage->zspz_hrstart = dpusage->zsu_hrstart;
+ pusage->zspz_cpu_shares = dpusage->zsu_cpu_shares;
+ pusage->zspz_scheds = dpusage->zsu_scheds;
+ pusage->zspz_cpu_usage = dpusage->zsu_cpu_usage;
+ }
+ }
+
+ /* Update the current cache pointer */
+ (void) mutex_lock(&g_usage_cache_lock);
+ old = g_usage_cache;
+ cache->zsuc_ref = 1;
+ cache->zsuc_gen = g_gen_next;
+ usage->zsu_gen = g_gen_next;
+ usage->zsu_size = size;
+ g_usage_cache = cache;
+ if (old != NULL) {
+ old->zsuc_ref--;
+ if (old->zsuc_ref == 0)
+ free(old);
+ }
+ g_gen_next++;
+ /* Wake up any clients that are waiting for this calculation */
+ if (g_usage_cache_kickers > 0) {
+ (void) cond_broadcast(&g_usage_cache_wait);
+ }
+ (void) mutex_unlock(&g_usage_cache_lock);
+}
+
+static zs_usage_cache_t *
+zsd_usage_cache_hold_locked()
+{
+ zs_usage_cache_t *ret;
+
+ ret = g_usage_cache;
+ ret->zsuc_ref++;
+ return (ret);
+}
+
+void
+zsd_usage_cache_rele(zs_usage_cache_t *cache)
+{
+ (void) mutex_lock(&g_usage_cache_lock);
+ cache->zsuc_ref--;
+ if (cache->zsuc_ref == 0)
+ free(cache);
+ (void) mutex_unlock(&g_usage_cache_lock);
+}
+
+/* Close the handles held by zsd_open() */
+void
+zsd_close(zsd_ctl_t *ctl)
+{
+ zsd_zone_t *zone;
+ zsd_pset_t *pset;
+ zsd_pset_usage_t *usage;
+ zsd_cpu_t *cpu;
+ int id;
+
+ if (ctl->zsctl_kstat_ctl) {
+ (void) kstat_close(ctl->zsctl_kstat_ctl);
+ ctl->zsctl_kstat_ctl = NULL;
+ }
+ if (ctl->zsctl_proc_open) {
+ (void) ea_close(&ctl->zsctl_proc_eaf);
+ ctl->zsctl_proc_open = 0;
+ ctl->zsctl_proc_fd = -1;
+ }
+ if (ctl->zsctl_pool_conf) {
+ if (ctl->zsctl_pool_status == POOL_ENABLED)
+ (void) pool_conf_close(ctl->zsctl_pool_conf);
+ ctl->zsctl_pool_status = POOL_DISABLED;
+ }
+
+ while ((zone = list_head(&ctl->zsctl_zones)) != NULL) {
+ list_remove(&ctl->zsctl_zones, zone);
+ free(zone);
+ ctl->zsctl_nzones--;
+ }
+
+ while ((pset = list_head(&ctl->zsctl_psets)) != NULL) {
+ while ((usage = list_head(&pset->zsp_usage_list))
+ != NULL) {
+ list_remove(&pset->zsp_usage_list, usage);
+ ctl->zsctl_npset_usages--;
+ free(usage);
+ }
+ list_remove(&ctl->zsctl_psets, pset);
+ free(pset);
+ ctl->zsctl_npsets--;
+ }
+
+ /* Release all cpus being tracked */
+ while (cpu = list_head(&ctl->zsctl_cpus)) {
+ list_remove(&ctl->zsctl_cpus, cpu);
+ id = cpu->zsc_id;
+ bzero(cpu, sizeof (zsd_cpu_t));
+ cpu->zsc_id = id;
+ cpu->zsc_allocated = B_FALSE;
+ cpu->zsc_psetid = ZS_PSET_ERROR;
+ cpu->zsc_psetid_prev = ZS_PSET_ERROR;
+ }
+
+ assert(ctl->zsctl_npset_usages == 0);
+ assert(ctl->zsctl_npsets == 0);
+ assert(ctl->zsctl_nzones == 0);
+ (void) zsd_disable_cpu_stats();
+}
+
+
+/*
+ * Update the utilization data for all zones and processor sets.
+ */
+static int
+zsd_read(zsd_ctl_t *ctl, boolean_t init, boolean_t do_memory)
+{
+ (void) kstat_chain_update(ctl->zsctl_kstat_ctl);
+ (void) gettimeofday(&(ctl->zsctl_timeofday), NULL);
+
+ zsd_refresh_system(ctl);
+
+ /*
+ * Memory calculation is expensive. Only update it on sample
+ * intervals.
+ */
+ if (do_memory == B_TRUE)
+ zsd_refresh_memory(ctl, init);
+ zsd_refresh_zones(ctl);
+ zsd_refresh_psets(ctl);
+ zsd_refresh_procs(ctl, init);
+ zsd_refresh_cpu_stats(ctl, init);
+
+ /*
+ * Delete objects that no longer exist.
+ * Pset usages must be deleted first as they point to zone and
+ * pset objects.
+ */
+ zsd_mark_pset_usages_end(ctl);
+ zsd_mark_psets_end(ctl);
+ zsd_mark_cpus_end(ctl);
+ zsd_mark_zones_end(ctl);
+
+ /*
+ * Save results for clients.
+ */
+ zsd_usage_cache_update(ctl);
+
+ /*
+ * Roll process accounting file.
+ */
+ (void) zsd_roll_exacct();
+ return (0);
+}
+
+/*
+ * Get the system rctl, which is the upper most limit
+ */
+static uint64_t
+zsd_get_system_rctl(char *name)
+{
+ rctlblk_t *rblk, *rblk_last;
+
+ rblk = (rctlblk_t *)alloca(rctlblk_size());
+ rblk_last = (rctlblk_t *)alloca(rctlblk_size());
+
+ if (getrctl(name, NULL, rblk_last, RCTL_FIRST) != 0)
+ return (ZS_LIMIT_NONE);
+
+ while (getrctl(name, rblk_last, rblk, RCTL_NEXT) == 0)
+ (void) bcopy(rblk, rblk_last, rctlblk_size());
+
+ return (rctlblk_get_value(rblk_last));
+}
+
+/*
+ * Open any necessary subsystems for collecting utilization data,
+ * allocate and initialize data structures, and get initial utilization.
+ *
+ * Errors:
+ * ENOMEM out of memory
+ * EINVAL other error
+ */
+static zsd_ctl_t *
+zsd_open(zsd_ctl_t *ctl)
+{
+ zsd_system_t *system;
+
+ char path[MAXPATHLEN];
+ long pathmax;
+ struct statvfs svfs;
+ int ret;
+ int i;
+ size_t size;
+ int err;
+
+ if (ctl == NULL && (ctl = (zsd_ctl_t *)calloc(1,
+ sizeof (zsd_ctl_t))) == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ ctl->zsctl_proc_fd = -1;
+
+ /* open kstats */
+ if (ctl->zsctl_kstat_ctl == NULL &&
+ (ctl->zsctl_kstat_ctl = kstat_open()) == NULL) {
+ err = errno;
+ zsd_warn(gettext("Unable to open kstats"));
+ errno = err;
+ if (errno != ENOMEM)
+ errno = EAGAIN;
+ goto err;
+ }
+
+ /*
+ * These are set when the accounting file is opened by
+ * zsd_update_procs()
+ */
+ ctl->zsctl_proc_fd = -1;
+ ctl->zsctl_proc_fd_next = -1;
+ ctl->zsctl_proc_open = 0;
+ ctl->zsctl_proc_open_next = 0;
+
+check_exacct:
+ (void) zsd_enable_cpu_stats();
+
+ /* Create structures to track usage */
+ if (ctl->zsctl_system == NULL && (ctl->zsctl_system = (zsd_system_t *)
+ calloc(1, sizeof (zsd_system_t))) == NULL) {
+ ret = -1;
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ system = ctl->zsctl_system;
+ /* get the kernel bitness to know structure layout for getvmusage */
+ ret = sysinfo(SI_ARCHITECTURE_64, path, sizeof (path));
+ if (ret < 0)
+ ctl->zsctl_kern_bits = 32;
+ else
+ ctl->zsctl_kern_bits = 64;
+ ctl->zsctl_pagesize = sysconf(_SC_PAGESIZE);
+
+ size = sysconf(_SC_CPUID_MAX);
+ ctl->zsctl_maxcpuid = size;
+ if (ctl->zsctl_cpu_array == NULL && (ctl->zsctl_cpu_array =
+ (zsd_cpu_t *)calloc(size + 1, sizeof (zsd_cpu_t))) == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ for (i = 0; i <= ctl->zsctl_maxcpuid; i++) {
+ ctl->zsctl_cpu_array[i].zsc_id = i;
+ ctl->zsctl_cpu_array[i].zsc_allocated = B_FALSE;
+ ctl->zsctl_cpu_array[i].zsc_psetid = ZS_PSET_ERROR;
+ ctl->zsctl_cpu_array[i].zsc_psetid_prev = ZS_PSET_ERROR;
+ }
+ if (statvfs("/proc", &svfs) != 0 ||
+ strcmp("/proc", svfs.f_fstr) != 0) {
+ zsd_warn(gettext("/proc not a procfs filesystem"));
+ errno = EINVAL;
+ goto err;
+ }
+
+ size = sysconf(_SC_MAXPID) + 1;
+ ctl->zsctl_maxproc = size;
+ if (ctl->zsctl_proc_array == NULL &&
+ (ctl->zsctl_proc_array = (zsd_proc_t *)calloc(size,
+ sizeof (zsd_proc_t))) == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ for (i = 0; i <= ctl->zsctl_maxproc; i++) {
+ list_link_init(&(ctl->zsctl_proc_array[i].zspr_next));
+ ctl->zsctl_proc_array[i].zspr_psetid = ZS_PSET_ERROR;
+ ctl->zsctl_proc_array[i].zspr_zoneid = -1;
+ ctl->zsctl_proc_array[i].zspr_usage.tv_sec = 0;
+ ctl->zsctl_proc_array[i].zspr_usage.tv_nsec = 0;
+ ctl->zsctl_proc_array[i].zspr_ppid = -1;
+ }
+
+ list_create(&ctl->zsctl_zones, sizeof (zsd_zone_t),
+ offsetof(zsd_zone_t, zsz_next));
+
+ list_create(&ctl->zsctl_psets, sizeof (zsd_pset_t),
+ offsetof(zsd_pset_t, zsp_next));
+
+ list_create(&ctl->zsctl_cpus, sizeof (zsd_cpu_t),
+ offsetof(zsd_cpu_t, zsc_next));
+
+ pathmax = pathconf("/proc", _PC_NAME_MAX);
+ if (pathmax < 0) {
+ zsd_warn(gettext("Unable to determine max path of /proc"));
+ errno = EINVAL;
+ goto err;
+ }
+ size = sizeof (struct dirent) + pathmax + 1;
+
+ ctl->zsctl_procfs_dent_size = size;
+ if (ctl->zsctl_procfs_dent == NULL &&
+ (ctl->zsctl_procfs_dent = (struct dirent *)calloc(1, size))
+ == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+
+ if (ctl->zsctl_pool_conf == NULL &&
+ (ctl->zsctl_pool_conf = pool_conf_alloc()) == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ ctl->zsctl_pool_status = POOL_DISABLED;
+ ctl->zsctl_pool_changed = 0;
+
+ if (ctl->zsctl_pool_vals[0] == NULL &&
+ (ctl->zsctl_pool_vals[0] = pool_value_alloc()) == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ if (ctl->zsctl_pool_vals[1] == NULL &&
+ (ctl->zsctl_pool_vals[1] = pool_value_alloc()) == NULL) {
+ zsd_warn(gettext("Out of Memory"));
+ errno = ENOMEM;
+ goto err;
+ }
+ ctl->zsctl_pool_vals[2] = NULL;
+
+ /*
+ * get system limits
+ */
+ system->zss_maxpid = size = sysconf(_SC_MAXPID);
+ system->zss_processes_max = zsd_get_system_rctl("zone.max-processes");
+ system->zss_lwps_max = zsd_get_system_rctl("zone.max-lwps");
+ system->zss_shm_max = zsd_get_system_rctl("zone.max-shm-memory");
+ system->zss_shmids_max = zsd_get_system_rctl("zone.max-shm-ids");
+ system->zss_semids_max = zsd_get_system_rctl("zone.max-sem-ids");
+ system->zss_msgids_max = zsd_get_system_rctl("zone.max-msg-ids");
+ system->zss_lofi_max = zsd_get_system_rctl("zone.max-lofi");
+
+ g_gen_next = 1;
+
+ if (zsd_read(ctl, B_TRUE, B_FALSE) != 0)
+ zsd_warn(gettext("Reading zone statistics failed"));
+
+ return (ctl);
+err:
+ if (ctl)
+ zsd_close(ctl);
+
+ return (NULL);
+}
+
+/* Copy utilization data to buffer, filtering data if non-global zone. */
+static void
+zsd_usage_filter(zoneid_t zid, zs_usage_cache_t *cache, zs_usage_t *usage,
+ boolean_t is_gz)
+{
+ zs_usage_t *cusage;
+ zs_system_t *sys, *csys;
+ zs_zone_t *zone, *czone;
+ zs_pset_t *pset, *cpset;
+ zs_pset_zone_t *pz, *cpz, *foundpz;
+ size_t size = 0, csize = 0;
+ char *start, *cstart;
+ int i, j;
+ timestruc_t delta;
+
+ /* Privileged users in the global zone get everything */
+ if (is_gz) {
+ cusage = cache->zsuc_usage;
+ (void) bcopy(cusage, usage, cusage->zsu_size);
+ return;
+ }
+
+ /* Zones just get their own usage */
+ cusage = cache->zsuc_usage;
+
+ start = (char *)usage;
+ cstart = (char *)cusage;
+ size += sizeof (zs_usage_t);
+ csize += sizeof (zs_usage_t);
+
+ usage->zsu_start = cusage->zsu_start;
+ usage->zsu_hrstart = cusage->zsu_hrstart;
+ usage->zsu_time = cusage->zsu_time;
+ usage->zsu_hrtime = cusage->zsu_hrtime;
+ usage->zsu_gen = cusage->zsu_gen;
+ usage->zsu_nzones = 1;
+ usage->zsu_npsets = 0;
+
+ /* LINTED */
+ sys = (zs_system_t *)(start + size);
+ /* LINTED */
+ csys = (zs_system_t *)(cstart + csize);
+ size += sizeof (zs_system_t);
+ csize += sizeof (zs_system_t);
+
+ /* Save system limits but not usage */
+ *sys = *csys;
+ sys->zss_ncpus = 0;
+ sys->zss_ncpus_online = 0;
+
+ /* LINTED */
+ zone = (zs_zone_t *)(start + size);
+ /* LINTED */
+ czone = (zs_zone_t *)(cstart + csize);
+ /* Find the matching zone */
+ for (i = 0; i < cusage->zsu_nzones; i++) {
+ if (czone->zsz_id == zid) {
+ *zone = *czone;
+ size += sizeof (zs_zone_t);
+ }
+ csize += sizeof (zs_zone_t);
+ /* LINTED */
+ czone = (zs_zone_t *)(cstart + csize);
+ }
+ sys->zss_ram_kern += (sys->zss_ram_zones - zone->zsz_usage_ram);
+ sys->zss_ram_zones = zone->zsz_usage_ram;
+
+ sys->zss_vm_kern += (sys->zss_vm_zones - zone->zsz_usage_vm);
+ sys->zss_vm_zones = zone->zsz_usage_vm;
+
+ sys->zss_locked_kern += (sys->zss_locked_zones -
+ zone->zsz_usage_locked);
+ sys->zss_locked_zones = zone->zsz_usage_locked;
+
+ TIMESTRUC_DELTA(delta, sys->zss_cpu_usage_zones, zone->zsz_cpu_usage);
+ TIMESTRUC_ADD_TIMESTRUC(sys->zss_cpu_usage_kern, delta);
+ sys->zss_cpu_usage_zones = zone->zsz_cpu_usage;
+
+ /* LINTED */
+ pset = (zs_pset_t *)(start + size);
+ /* LINTED */
+ cpset = (zs_pset_t *)(cstart + csize);
+ for (i = 0; i < cusage->zsu_npsets; i++) {
+ csize += sizeof (zs_pset_t);
+ /* LINTED */
+ cpz = (zs_pset_zone_t *)(csize + cstart);
+ foundpz = NULL;
+ for (j = 0; j < cpset->zsp_nusage; j++) {
+ if (cpz->zspz_zoneid == zid)
+ foundpz = cpz;
+
+ csize += sizeof (zs_pset_zone_t);
+ /* LINTED */
+ cpz = (zs_pset_zone_t *)(csize + cstart);
+ }
+ if (foundpz != NULL) {
+ size += sizeof (zs_pset_t);
+ /* LINTED */
+ pz = (zs_pset_zone_t *)(start + size);
+ size += sizeof (zs_pset_zone_t);
+
+ *pset = *cpset;
+ *pz = *foundpz;
+
+ TIMESTRUC_DELTA(delta, pset->zsp_usage_zones,
+ pz->zspz_cpu_usage);
+ TIMESTRUC_ADD_TIMESTRUC(pset->zsp_usage_kern, delta);
+ pset->zsp_usage_zones = pz->zspz_cpu_usage;
+ pset->zsp_nusage = 1;
+ usage->zsu_npsets++;
+ sys->zss_ncpus += pset->zsp_size;
+ sys->zss_ncpus_online += pset->zsp_online;
+ }
+ /* LINTED */
+ cpset = (zs_pset_t *)(cstart + csize);
+ }
+ usage->zsu_size = size;
+}
+
+/*
+ * Respond to new connections from libzonestat.so. Also respond to zoneadmd,
+ * which reports new zones.
+ */
+/* ARGSUSED */
+static void
+zsd_server(void *cookie, char *argp, size_t arg_size,
+ door_desc_t *dp, uint_t n_desc)
+{
+ int *args, cmd;
+ door_desc_t door;
+ ucred_t *ucred;
+ const priv_set_t *eset;
+
+ if (argp == DOOR_UNREF_DATA) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+
+ if (arg_size != sizeof (cmd) * 2) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+
+ /* LINTED */
+ args = (int *)argp;
+ cmd = args[0];
+
+ /* If connection, return door to stat server */
+ if (cmd == ZSD_CMD_CONNECT) {
+
+ /* Verify client compilation version */
+ if (args[1] != ZS_VERSION) {
+ args[1] = ZSD_STATUS_VERSION_MISMATCH;
+ (void) door_return(argp, sizeof (cmd) * 2, NULL, 0);
+ thr_exit(NULL);
+ }
+ ucred = alloca(ucred_size());
+ /* Verify client permission */
+ if (door_ucred(&ucred) != 0) {
+ args[1] = ZSD_STATUS_INTERNAL_ERROR;
+ (void) door_return(argp, sizeof (cmd) * 2, NULL, 0);
+ thr_exit(NULL);
+ }
+
+ eset = ucred_getprivset(ucred, PRIV_EFFECTIVE);
+ if (eset == NULL) {
+ args[1] = ZSD_STATUS_INTERNAL_ERROR;
+ (void) door_return(argp, sizeof (cmd) * 2, NULL, 0);
+ thr_exit(NULL);
+ }
+ if (!priv_ismember(eset, PRIV_PROC_INFO)) {
+ args[1] = ZSD_STATUS_PERMISSION;
+ (void) door_return(argp, sizeof (cmd) * 2, NULL, 0);
+ thr_exit(NULL);
+ }
+
+ /* Return stat server door */
+ args[1] = ZSD_STATUS_OK;
+ door.d_attributes = DOOR_DESCRIPTOR;
+ door.d_data.d_desc.d_descriptor = g_stat_door;
+ (void) door_return(argp, sizeof (cmd) * 2, &door, 1);
+ thr_exit(NULL);
+ }
+
+ /* Respond to zoneadmd informing zonestatd of a new zone */
+ if (cmd == ZSD_CMD_NEW_ZONE) {
+ zsd_fattach_zone(args[1], g_server_door, B_FALSE);
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+
+ args[1] = ZSD_STATUS_INTERNAL_ERROR;
+ (void) door_return(argp, sizeof (cmd) * 2, NULL, 0);
+ thr_exit(NULL);
+}
+
+/*
+ * Respond to libzonestat.so clients with the current utlilzation data.
+ */
+/* ARGSUSED */
+static void
+zsd_stat_server(void *cookie, char *argp, size_t arg_size,
+ door_desc_t *dp, uint_t n_desc)
+{
+ uint64_t *args, cmd;
+ zs_usage_cache_t *cache;
+ int ret;
+ char *rvalp;
+ size_t rvals;
+ zs_usage_t *usage;
+ ucred_t *ucred;
+ zoneid_t zoneid;
+ const priv_set_t *eset;
+ boolean_t is_gz = B_FALSE;
+
+ /* Tell stat thread there are no more clients */
+ if (argp == DOOR_UNREF_DATA) {
+ (void) mutex_lock(&g_usage_cache_lock);
+ g_hasclient = B_FALSE;
+ (void) cond_signal(&g_usage_cache_kick);
+ (void) mutex_unlock(&g_usage_cache_lock);
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ if (arg_size != sizeof (cmd) * 2) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ /* LINTED */
+ args = (uint64_t *)argp;
+ cmd = args[0];
+ if (cmd != ZSD_CMD_READ) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ ucred = alloca(ucred_size());
+ if (door_ucred(&ucred) != 0) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ zoneid = ucred_getzoneid(ucred);
+
+ if (zoneid == GLOBAL_ZONEID)
+ is_gz = B_TRUE;
+
+ eset = ucred_getprivset(ucred, PRIV_EFFECTIVE);
+ if (eset == NULL) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ if (!priv_ismember(eset, PRIV_PROC_INFO)) {
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ (void) mutex_lock(&g_usage_cache_lock);
+ g_hasclient = B_TRUE;
+
+ /*
+ * Force a new cpu calculation for client. This will force a
+ * new memory calculation if the memory data is older than the
+ * sample period.
+ */
+ g_usage_cache_kickers++;
+ (void) cond_signal(&g_usage_cache_kick);
+ ret = cond_wait(&g_usage_cache_wait, &g_usage_cache_lock);
+ g_usage_cache_kickers--;
+ if (ret != 0 && errno == EINTR) {
+ (void) mutex_unlock(&g_usage_cache_lock);
+ zsd_warn(gettext(
+ "Interrupted before writing usage size to client\n"));
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ cache = zsd_usage_cache_hold_locked();
+ if (cache == NULL) {
+ zsd_warn(gettext("Usage cache empty.\n"));
+ (void) door_return(NULL, 0, NULL, 0);
+ thr_exit(NULL);
+ }
+ (void) mutex_unlock(&g_usage_cache_lock);
+
+ /* Copy current usage data to stack to send to client */
+ usage = (zs_usage_t *)alloca(cache->zsuc_size);
+
+ /* Filter out results if caller is non-global zone */
+ zsd_usage_filter(zoneid, cache, usage, is_gz);
+
+ rvalp = (void *)usage;
+ rvals = usage->zsu_size;
+ zsd_usage_cache_rele(cache);
+
+ (void) door_return(rvalp, rvals, 0, NULL);
+ thr_exit(NULL);
+}
+
+static volatile boolean_t g_quit;
+
+/* ARGSUSED */
+static void
+zonestat_quithandler(int sig)
+{
+ g_quit = B_TRUE;
+}
+
+/*
+ * The stat thread generates new utilization data when clients request
+ * it. It also manages opening and closing the subsystems used to gather
+ * data depending on if clients exist.
+ */
+/* ARGSUSED */
+void *
+stat_thread(void *arg)
+{
+ time_t start;
+ time_t now;
+ time_t next_memory;
+ boolean_t do_memory;
+ boolean_t do_read;
+ boolean_t do_close;
+
+ start = time(NULL);
+ if (start < 0) {
+ if (g_quit == B_TRUE)
+ goto quit;
+ zsd_warn(gettext("Unable to fetch current time"));
+ g_quit = B_TRUE;
+ goto quit;
+ }
+
+ next_memory = start;
+ while (g_quit == B_FALSE) {
+ for (;;) {
+ /*
+ * These are used to decide if the most recent memory
+ * calculation was within a sample interval,
+ * and weather or not the usage collection needs to
+ * be opened or closed.
+ */
+ do_memory = B_FALSE;
+ do_read = B_FALSE;
+ do_close = B_FALSE;
+
+ /*
+ * If all clients have gone, close usage collecting
+ */
+ (void) mutex_lock(&g_usage_cache_lock);
+ if (!g_hasclient && g_open == B_TRUE) {
+ do_close = B_TRUE;
+ (void) mutex_unlock(&g_usage_cache_lock);
+ break;
+ }
+ if (g_quit == B_TRUE) {
+ (void) mutex_unlock(
+ &g_usage_cache_lock);
+ break;
+ }
+ /*
+ * Wait for a usage data request
+ */
+ if (g_usage_cache_kickers == 0) {
+ (void) cond_wait(&g_usage_cache_kick,
+ &g_usage_cache_lock);
+ }
+ now = time(NULL);
+ if (now < 0) {
+ if (g_quit == B_TRUE) {
+ (void) mutex_unlock(
+ &g_usage_cache_lock);
+ goto quit;
+ }
+ g_quit = B_TRUE;
+ (void) mutex_unlock(&g_usage_cache_lock);
+ zsd_warn(gettext(
+ "Unable to fetch current time"));
+ goto quit;
+ }
+ if (g_hasclient) {
+ do_read = B_TRUE;
+ if (now >= next_memory) {
+ do_memory = B_TRUE;
+ next_memory = now + g_interval;
+ }
+ } else {
+ do_close = B_TRUE;
+ }
+ (void) mutex_unlock(&g_usage_cache_lock);
+ if (do_read || do_close)
+ break;
+ }
+ g_now = now;
+ g_hrnow = gethrtime();
+ if (g_hasclient && g_open == B_FALSE) {
+ g_start = g_now;
+ g_hrstart = g_hrnow;
+ g_ctl = zsd_open(g_ctl);
+ if (g_ctl == NULL)
+ zsd_warn(gettext(
+ "Unable to open zone statistics"));
+ else
+ g_open = B_TRUE;
+ }
+ if (do_read && g_ctl) {
+ if (zsd_read(g_ctl, B_FALSE, do_memory) != 0) {
+ zsd_warn(gettext(
+ "Unable to read zone statistics"));
+ g_quit = B_TRUE;
+ return (NULL);
+ }
+ }
+ (void) mutex_lock(&g_usage_cache_lock);
+ if (!g_hasclient && g_open == B_TRUE && g_ctl) {
+ (void) mutex_unlock(&g_usage_cache_lock);
+ zsd_close(g_ctl);
+ g_open = B_FALSE;
+ } else {
+ (void) mutex_unlock(&g_usage_cache_lock);
+ }
+ }
+quit:
+ if (g_open)
+ zsd_close(g_ctl);
+
+ (void) thr_kill(g_main, SIGINT);
+ thr_exit(NULL);
+ return (NULL);
+}
+
+void
+zsd_set_fx()
+{
+ pcinfo_t pcinfo;
+ pcparms_t pcparms;
+
+ (void) strlcpy(pcinfo.pc_clname, "FX", sizeof (pcinfo.pc_clname));
+ if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) {
+ zsd_warn(gettext("cannot get FX class parameters"));
+ return;
+ }
+ pcparms.pc_cid = pcinfo.pc_cid;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_upri = 60;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_uprilim = 60;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_tqsecs = 0;
+ ((fxparms_t *)pcparms.pc_clparms)->fx_tqnsecs = FX_NOCHANGE;
+ if (priocntl(P_PID, getpid(), PC_SETPARMS, (caddr_t)&pcparms) == -1)
+ zsd_warn(gettext("cannot enter the FX class"));
+}
+
+static int pipe_fd;
+
+static void
+daemonize_ready(char status)
+{
+ /*
+ * wake the parent with a clue
+ */
+ (void) write(pipe_fd, &status, 1);
+ (void) close(pipe_fd);
+}
+
+static int
+daemonize_start(void)
+{
+ char data;
+ int status;
+
+ int filedes[2];
+ pid_t pid;
+
+ (void) close(0);
+ (void) dup2(2, 1);
+
+ if (pipe(filedes) < 0)
+ return (-1);
+
+ (void) fflush(NULL);
+
+ if ((pid = fork1()) < 0)
+ return (-1);
+
+ if (pid != 0) {
+ /*
+ * parent
+ */
+ struct sigaction act;
+
+ act.sa_sigaction = SIG_DFL;
+ (void) sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ (void) sigaction(SIGPIPE, &act, NULL); /* ignore SIGPIPE */
+
+ (void) close(filedes[1]);
+ if (read(filedes[0], &data, 1) == 1) {
+ /* forward ready code via exit status */
+ exit(data);
+ }
+ status = -1;
+ (void) wait4(pid, &status, 0, NULL);
+ /* daemon process exited before becoming ready */
+ if (WIFEXITED(status)) {
+ /* assume daemon process printed useful message */
+ exit(WEXITSTATUS(status));
+ } else {
+ zsd_warn(gettext("daemon process killed or died"));
+ exit(1);
+ }
+ }
+
+ /*
+ * child
+ */
+ pipe_fd = filedes[1];
+ (void) close(filedes[0]);
+
+ /*
+ * generic Unix setup
+ */
+ (void) setsid();
+ (void) umask(0000);
+
+ return (0);
+}
+
+static void
+fattach_all_zones(boolean_t detach_only)
+{
+ zoneid_t *zids;
+ uint_t nzids, nzids_last;
+ int i;
+
+again:
+ (void) zone_list(NULL, &nzids);
+ nzids_last = nzids;
+ zids = (zoneid_t *)malloc(sizeof (zoneid_t) * nzids_last);
+ if (zids == NULL)
+ zsd_error(gettext("Out of memory"));
+
+ (void) zone_list(zids, &nzids);
+ if (nzids > nzids_last) {
+ free(zids);
+ goto again;
+ }
+ for (i = 0; i < nzids; i++)
+ zsd_fattach_zone(zids[i], g_server_door, detach_only);
+
+ free(zids);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ int arg;
+ thread_t tid;
+ scf_simple_prop_t *prop;
+ uint64_t *intervalp;
+ boolean_t opt_cleanup = B_FALSE;
+
+ g_main = thr_self();
+ g_quit = B_FALSE;
+ (void) signal(SIGINT, zonestat_quithandler);
+ (void) signal(SIGTERM, zonestat_quithandler);
+ (void) signal(SIGHUP, zonestat_quithandler);
+/* (void) sigignore(SIGCHLD); */
+ (void) sigignore(SIGPIPE);
+
+ if (getzoneid() != GLOBAL_ZONEID)
+ zsd_error(gettext("Must be run from global zone only"));
+
+ while ((arg = getopt(argc, argv, "c"))
+ != EOF) {
+ switch (arg) {
+ case 'c':
+ opt_cleanup = B_TRUE;
+ break;
+ default:
+ zsd_error(gettext("Invalid option"));
+ }
+ }
+
+ if (opt_cleanup) {
+ if (zsd_disable_cpu_stats() != 0)
+ exit(1);
+ else
+ exit(0);
+ }
+
+ /* Get the configured sample interval */
+ prop = scf_simple_prop_get(NULL, "svc:/system/zones-monitoring:default",
+ "config", "sample_interval");
+ if (prop == NULL)
+ zsd_error(gettext("Unable to fetch SMF property "
+ "\"config/sample_interval\""));
+
+ if (scf_simple_prop_type(prop) != SCF_TYPE_COUNT)
+ zsd_error(gettext("Malformed SMF property "
+ "\"config/sample_interval\". Must be of type \"count\""));
+
+ intervalp = scf_simple_prop_next_count(prop);
+ g_interval = *intervalp;
+ if (g_interval == 0)
+ zsd_error(gettext("Malformed SMF property "
+ "\"config/sample_interval\". Must be greater than zero"));
+
+ scf_simple_prop_free(prop);
+
+ if (daemonize_start() < 0)
+ zsd_error(gettext("Unable to start daemon\n"));
+
+ /* Run at high priority */
+ zsd_set_fx();
+
+ (void) mutex_init(&g_usage_cache_lock, USYNC_THREAD, NULL);
+ (void) cond_init(&g_usage_cache_kick, USYNC_THREAD, NULL);
+ (void) cond_init(&g_usage_cache_wait, USYNC_THREAD, NULL);
+
+ g_server_door = door_create(zsd_server, NULL,
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+ if (g_server_door < 0)
+ zsd_error(gettext("Unable to create server door\n"));
+
+
+ g_stat_door = door_create(zsd_stat_server, NULL, DOOR_UNREF_MULTI |
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+ if (g_stat_door < 0)
+ zsd_error(gettext("Unable to create statistics door\n"));
+
+ fattach_all_zones(B_FALSE);
+
+ if (thr_create(NULL, 0, stat_thread, NULL, 0, &tid) != 0)
+ zsd_error(gettext("Unable to create statistics thread\n"));
+
+ daemonize_ready(0);
+
+ /* Wait for signal to quit */
+ while (g_quit == B_FALSE)
+ (void) pause();
+
+ /* detach doors */
+ fattach_all_zones(B_TRUE);
+
+ (void) door_revoke(g_server_door);
+ (void) door_revoke(g_stat_door);
+
+ /* kick stat thread and wait for it to close the statistics */
+ (void) mutex_lock(&g_usage_cache_lock);
+ g_quit = B_TRUE;
+ (void) cond_signal(&g_usage_cache_kick);
+ (void) mutex_unlock(&g_usage_cache_lock);
+end:
+ (void) thr_join(tid, NULL, NULL);
+ return (0);
+}
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index e9e9af7d0f..69443e121b 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -209,6 +209,7 @@ SUBDIRS += \
libbrand .WAIT \
libzonecfg \
libzoneinfo \
+ libzonestat \
libtsnet \
libtsol \
gss_mechs/mech_spnego \
@@ -479,6 +480,7 @@ HDRSUBDIRS= \
libzfs \
libzfs_jni \
libzoneinfo \
+ libzonestat \
hal \
policykit \
lvm \
diff --git a/usr/src/lib/libzonestat/Makefile b/usr/src/lib/libzonestat/Makefile
new file mode 100644
index 0000000000..078a881c3c
--- /dev/null
+++ b/usr/src/lib/libzonestat/Makefile
@@ -0,0 +1,53 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.lib
+
+HDRS = zonestat.h zonestat_impl.h
+HDRDIR = common
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libzonestat/Makefile.com b/usr/src/lib/libzonestat/Makefile.com
new file mode 100644
index 0000000000..65ab999206
--- /dev/null
+++ b/usr/src/lib/libzonestat/Makefile.com
@@ -0,0 +1,44 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+LIBRARY= libzonestat.a
+VERS= .1
+OBJECTS= libzonestat.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lcmdutils -lumem -lc
+
+SRCDIR = ../common
+CPPFLAGS += -I../common -mt -D_POSIX_PTHREAD_SEMANTICS
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libzonestat/amd64/Makefile b/usr/src/lib/libzonestat/amd64/Makefile
new file mode 100644
index 0000000000..9eeb4ef71d
--- /dev/null
+++ b/usr/src/lib/libzonestat/amd64/Makefile
@@ -0,0 +1,27 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libzonestat/common/libzonestat.c b/usr/src/lib/libzonestat/common/libzonestat.c
new file mode 100644
index 0000000000..a66c48d564
--- /dev/null
+++ b/usr/src/lib/libzonestat/common/libzonestat.c
@@ -0,0 +1,4194 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <alloca.h>
+#include <assert.h>
+#include <door.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zonestat.h>
+#include <zonestat_impl.h>
+
+#define ZSD_PCT_INT 10000
+#define ZSD_PCT_DOUBLE 10000.0
+
+#define ZSD_ONE_CPU 100
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#define ZS_MAXTS(a, b) ((b).tv_sec > (a).tv_sec || \
+ ((b).tv_sec == (a).tv_sec && (b).tv_nsec > (a).tv_nsec) ? (b) : (a))
+
+
+/* Compute max, treating ZS_LIMIT_NONE as zero */
+#define ZS_MAXOF(a, b) { \
+ if ((b) != ZS_LIMIT_NONE) { \
+ if ((a) == ZS_LIMIT_NONE) \
+ (a) = (b); \
+ else if ((b) > (a)) \
+ (b) = (a); \
+ } \
+ }
+
+/* Add two caps together, treating ZS_LIMIT_NONE as zero */
+#define ZS_ADD_CAP(a, b) { \
+ if ((b) != ZS_LIMIT_NONE) { \
+ if ((a) == ZS_LIMIT_NONE) \
+ (a) = (b); \
+ else \
+ (a) += (b); \
+ } \
+ }
+
+#define ZS_MAXOFTS(a, b) { \
+ if ((b).tv_sec > (a).tv_sec) (a) = (b); \
+ else if ((b).tv_nsec > (a).tv_nsec) (a) = (b); }
+
+/*
+ * Functions for reading and manipulating resource usage.
+ */
+static int
+zs_connect_zonestatd()
+{
+ int fd;
+
+ fd = open(ZS_DOOR_PATH, O_RDONLY);
+ return (fd);
+}
+
+static zs_zone_t *
+zs_lookup_zone_byid(zs_usage_t *u, zoneid_t zid)
+{
+ zs_zone_t *zone;
+
+ for (zone = list_head(&u->zsu_zone_list); zone != NULL;
+ zone = list_next(&u->zsu_zone_list, zone)) {
+ if (zone->zsz_id == zid)
+ return (zone);
+ }
+ return (NULL);
+}
+
+static zs_zone_t *
+zs_lookup_zone_byname(zs_usage_t *u, char *name)
+{
+ zs_zone_t *zone;
+
+ for (zone = list_head(&u->zsu_zone_list); zone != NULL;
+ zone = list_next(&u->zsu_zone_list, zone)) {
+ if (strcmp(zone->zsz_name, name) == 0)
+ return (zone);
+ }
+ return (NULL);
+}
+
+static zs_usage_t *
+zs_usage_alloc()
+{
+ zs_usage_t *u;
+ zs_system_t *s;
+
+ u = (zs_usage_t *)calloc(sizeof (zs_usage_t), 1);
+ if (u == NULL)
+ return (NULL);
+
+ s = (zs_system_t *)calloc(sizeof (zs_system_t), 1);
+ if (s == NULL) {
+ free(u);
+ return (NULL);
+ }
+
+ u->zsu_mmap = B_FALSE;
+ u->zsu_system = s;
+ list_create(&u->zsu_zone_list, sizeof (zs_zone_t),
+ offsetof(zs_zone_t, zsz_next));
+ list_create(&u->zsu_pset_list, sizeof (zs_pset_t),
+ offsetof(zs_pset_t, zsp_next));
+
+ return (u);
+}
+
+static void
+zs_zone_add_usage(zs_zone_t *old, zs_zone_t *new, int func)
+{
+
+ if (func == ZS_COMPUTE_USAGE_HIGH) {
+
+ /* Compute max of caps */
+ ZS_MAXOF(old->zsz_cpu_cap, new->zsz_cpu_cap);
+ ZS_MAXOF(old->zsz_cpu_shares, new->zsz_cpu_shares);
+ ZS_MAXOF(old->zsz_ram_cap, new->zsz_ram_cap);
+ ZS_MAXOF(old->zsz_locked_cap, new->zsz_locked_cap);
+ ZS_MAXOF(old->zsz_vm_cap, new->zsz_vm_cap);
+ ZS_MAXOF(old->zsz_processes_cap, new->zsz_processes_cap);
+ ZS_MAXOF(old->zsz_lwps_cap, new->zsz_lwps_cap);
+ ZS_MAXOF(old->zsz_shm_cap, new->zsz_shm_cap);
+ ZS_MAXOF(old->zsz_shmids_cap, new->zsz_shmids_cap);
+ ZS_MAXOF(old->zsz_semids_cap, new->zsz_semids_cap);
+ ZS_MAXOF(old->zsz_msgids_cap, new->zsz_msgids_cap);
+ ZS_MAXOF(old->zsz_lofi_cap, new->zsz_lofi_cap);
+
+ /* Compute max memory and limit usages */
+ ZS_MAXOF(old->zsz_usage_ram, new->zsz_usage_ram);
+ ZS_MAXOF(old->zsz_usage_locked, new->zsz_usage_locked);
+ ZS_MAXOF(old->zsz_usage_ram, new->zsz_usage_ram);
+
+ ZS_MAXOF(old->zsz_processes, new->zsz_processes);
+ ZS_MAXOF(old->zsz_lwps, new->zsz_lwps);
+ ZS_MAXOF(old->zsz_shm, new->zsz_shm);
+ ZS_MAXOF(old->zsz_shmids, new->zsz_shmids);
+ ZS_MAXOF(old->zsz_semids, new->zsz_semids);
+ ZS_MAXOF(old->zsz_msgids, new->zsz_msgids);
+ ZS_MAXOF(old->zsz_lofi, new->zsz_lofi);
+
+ ZS_MAXOF(old->zsz_cpus_online, new->zsz_cpus_online);
+
+ ZS_MAXOFTS(old->zsz_cpu_usage, new->zsz_cpu_usage);
+ ZS_MAXOFTS(old->zsz_pset_time, new->zsz_pset_time);
+ ZS_MAXOFTS(old->zsz_cap_time, new->zsz_cap_time);
+ ZS_MAXOFTS(old->zsz_share_time, new->zsz_share_time);
+ return;
+ }
+
+ ZS_ADD_CAP(old->zsz_cpu_cap, new->zsz_cpu_cap);
+ ZS_ADD_CAP(old->zsz_ram_cap, new->zsz_ram_cap);
+ ZS_ADD_CAP(old->zsz_locked_cap, new->zsz_locked_cap);
+ ZS_ADD_CAP(old->zsz_vm_cap, new->zsz_vm_cap);
+ ZS_ADD_CAP(old->zsz_processes_cap, new->zsz_processes_cap);
+ ZS_ADD_CAP(old->zsz_lwps_cap, new->zsz_lwps_cap);
+ ZS_ADD_CAP(old->zsz_shm_cap, new->zsz_shm_cap);
+ ZS_ADD_CAP(old->zsz_shmids_cap, new->zsz_shmids_cap);
+ ZS_ADD_CAP(old->zsz_semids_cap, new->zsz_semids_cap);
+ ZS_ADD_CAP(old->zsz_msgids_cap, new->zsz_msgids_cap);
+ ZS_ADD_CAP(old->zsz_lofi_cap, new->zsz_lofi_cap);
+
+ /* Add in memory and limit usages */
+ old->zsz_usage_ram += new->zsz_usage_ram;
+ old->zsz_usage_locked += new->zsz_usage_locked;
+ old->zsz_usage_vm += new->zsz_usage_vm;
+
+ old->zsz_processes += new->zsz_processes;
+ old->zsz_lwps += new->zsz_lwps;
+ old->zsz_shm += new->zsz_shm;
+ old->zsz_shmids += new->zsz_shmids;
+ old->zsz_semids += new->zsz_semids;
+ old->zsz_msgids += new->zsz_msgids;
+ old->zsz_lofi += new->zsz_lofi;
+
+ old->zsz_cpus_online += new->zsz_cpus_online;
+ old->zsz_cpu_shares += new->zsz_cpu_shares;
+
+ TIMESTRUC_ADD_TIMESTRUC(old->zsz_cpu_usage, new->zsz_cpu_usage);
+ TIMESTRUC_ADD_TIMESTRUC(old->zsz_pset_time, new->zsz_pset_time);
+ TIMESTRUC_ADD_TIMESTRUC(old->zsz_cap_time, new->zsz_cap_time);
+ TIMESTRUC_ADD_TIMESTRUC(old->zsz_share_time, new->zsz_share_time);
+}
+
+static int
+zs_usage_compute_zones(zs_usage_t *ures, zs_usage_t *uold, zs_usage_t *unew,
+ int func)
+{
+ zs_system_t *sres;
+ zs_zone_t *zold, *znew, *zres;
+
+ sres = ures->zsu_system;
+ /*
+ * Walk zones, assume lists are always sorted the same. Include
+ * all zones that exist in the new usage.
+ */
+ zold = list_head(&uold->zsu_zone_list);
+ znew = list_head(&unew->zsu_zone_list);
+
+ while (zold != NULL && znew != NULL) {
+
+ int cmp;
+
+ cmp = strcmp(zold->zsz_name, znew->zsz_name);
+ if (cmp > 0) {
+ /*
+ * Old interval does not contain zone in new
+ * interval. Zone is new. Add zone to result.
+ */
+ if (ures != unew) {
+ zres = (zs_zone_t *)calloc(sizeof (zs_zone_t),
+ 1);
+ if (zres == NULL)
+ return (-1);
+ *zres = *znew;
+
+ zres->zsz_system = sres;
+ list_link_init(&zres->zsz_next);
+ zres->zsz_intervals = 0;
+ if (ures == uold)
+ list_insert_before(&uold->zsu_zone_list,
+ zold, zres);
+ else
+ list_insert_tail(&ures->zsu_zone_list,
+ zres);
+
+ } else {
+ zres = znew;
+ }
+
+ if (func == ZS_COMPUTE_USAGE_AVERAGE)
+ zres->zsz_intervals++;
+
+ znew = list_next(&unew->zsu_zone_list, znew);
+ continue;
+
+ } else if (cmp < 0) {
+ /*
+ * Start interval contains zones that is not in the
+ * end interval. This zone is gone. Leave zone in
+ * old usage, but do not add it to result usage
+ */
+ zold = list_next(&uold->zsu_zone_list, zold);
+ continue;
+ }
+
+ /* Zone is in both start and end interval. Compute interval */
+ if (ures == uold) {
+ zres = zold;
+ } else if (ures == unew) {
+ zres = znew;
+ } else {
+ /* add zone to new usage */
+ zres = (zs_zone_t *)calloc(sizeof (zs_zone_t), 1);
+ if (zres == NULL)
+ return (-1);
+ *zres = *znew;
+ zres->zsz_system = sres;
+ list_insert_tail(&ures->zsu_zone_list, zres);
+ }
+ if (func == ZS_COMPUTE_USAGE_AVERAGE)
+ zres->zsz_intervals++;
+ if (func == ZS_COMPUTE_USAGE_INTERVAL) {
+ /*
+ * If zone is in the old interval, but has been
+ * rebooted, don't subtract its old interval usage
+ */
+ if (zres->zsz_hrstart > uold->zsu_hrtime) {
+ znew = list_next(&unew->zsu_zone_list, znew);
+ zold = list_next(&uold->zsu_zone_list, zold);
+ continue;
+ }
+ TIMESTRUC_DELTA(zres->zsz_cpu_usage,
+ znew->zsz_cpu_usage, zold->zsz_cpu_usage);
+ TIMESTRUC_DELTA(zres->zsz_cap_time, znew->zsz_cap_time,
+ zold->zsz_cap_time);
+ TIMESTRUC_DELTA(zres->zsz_share_time,
+ znew->zsz_share_time, zold->zsz_share_time);
+ TIMESTRUC_DELTA(zres->zsz_pset_time,
+ znew->zsz_pset_time, zold->zsz_pset_time);
+ } else {
+ zs_zone_add_usage(zres, znew, func);
+ }
+ znew = list_next(&unew->zsu_zone_list, znew);
+ zold = list_next(&uold->zsu_zone_list, zold);
+ }
+
+ if (ures == unew)
+ return (0);
+
+ /* Add in any remaining zones in the new interval */
+ while (znew != NULL) {
+ zres = (zs_zone_t *)calloc(sizeof (zs_zone_t), 1);
+ if (zres == NULL)
+ return (-1);
+ *zres = *znew;
+ zres->zsz_system = sres;
+ if (func == ZS_COMPUTE_USAGE_AVERAGE)
+ zres->zsz_intervals++;
+ if (ures == uold)
+ list_insert_tail(&uold->zsu_zone_list, zres);
+ else
+ list_insert_tail(&ures->zsu_zone_list, zres);
+
+ znew = list_next(&unew->zsu_zone_list, znew);
+ }
+ return (0);
+}
+
+static void
+zs_pset_zone_add_usage(zs_pset_zone_t *old, zs_pset_zone_t *new, int func)
+{
+ if (func == ZS_COMPUTE_USAGE_HIGH) {
+ ZS_MAXOF(old->zspz_cpu_shares, new->zspz_cpu_shares);
+ ZS_MAXOFTS(old->zspz_cpu_usage, new->zspz_cpu_usage);
+ return;
+ }
+ old->zspz_cpu_shares += new->zspz_cpu_shares;
+ TIMESTRUC_ADD_TIMESTRUC(old->zspz_cpu_usage, new->zspz_cpu_usage);
+}
+
+static int
+zs_usage_compute_pset_usage(zs_usage_t *uold, zs_usage_t *ures,
+ zs_pset_t *pres, zs_pset_t *pold, zs_pset_t *pnew, int func)
+{
+ zs_pset_zone_t *puold, *punew, *pures;
+
+ /*
+ * Walk psets usages, assume lists are always sorted the same. Include
+ * all pset usages that exist in the new pset.
+ */
+ if (pold == NULL)
+ puold = NULL;
+ else
+ puold = list_head(&pold->zsp_usage_list);
+ punew = list_head(&pnew->zsp_usage_list);
+
+ while (puold != NULL && punew != NULL) {
+
+ int cmp;
+
+ cmp = strcmp(puold->zspz_zone->zsz_name,
+ punew->zspz_zone->zsz_name);
+ if (cmp > 0) {
+ /*
+ * Old interval does not contain usage new
+ * interval. Usage is new.
+ */
+ if (pres != pnew) {
+ pures = (zs_pset_zone_t *)malloc(
+ sizeof (zs_pset_zone_t));
+ if (pures == NULL)
+ return (-1);
+ *pures = *punew;
+
+ pures->zspz_pset = pres;
+ pures->zspz_zone = zs_lookup_zone_byname(ures,
+ punew->zspz_zone->zsz_name);
+ assert(pures->zspz_zone != NULL);
+ pures->zspz_intervals = 0;
+ if (pres == pold)
+ list_insert_before(
+ &pold->zsp_usage_list, puold,
+ pures);
+ else
+ list_insert_tail(&pres->zsp_usage_list,
+ pures);
+ } else {
+ pures = punew;
+ }
+ if (func == ZS_COMPUTE_USAGE_AVERAGE)
+ pures->zspz_intervals++;
+ else if (func == ZS_COMPUTE_USAGE_TOTAL) {
+ /* Add pset's time so far to the zone usage */
+ TIMESTRUC_ADD_TIMESTRUC(
+ pures->zspz_zone->zsz_pset_time,
+ pres->zsp_total_time);
+ pures->zspz_zone->zsz_cpus_online +=
+ pres->zsp_online;
+ }
+
+ punew = list_next(&pnew->zsp_usage_list, punew);
+ continue;
+ } else if (cmp < 0) {
+
+ /*
+ * Old interval contains pset_zone that is not in the
+ * new interval. This zone is no longer using the
+ * pset. Leave pset_zone in old interval, but do not
+ * add it to result usage.
+ *
+ * For total utilization, add pset time to zone that
+ * has run in this pset before.
+ */
+ if (func == ZS_COMPUTE_USAGE_TOTAL) {
+ /* Add new pset time to the zone usage */
+ TIMESTRUC_ADD_TIMESTRUC(
+ puold->zspz_zone->zsz_pset_time,
+ pnew->zsp_total_time);
+ puold->zspz_zone->zsz_cpus_online +=
+ pnew->zsp_online;
+ }
+ puold = list_next(&pold->zsp_usage_list, puold);
+ continue;
+ }
+ /*
+ * Zone is using pset in both start and end interval. Compute
+ * interval
+ */
+ if (pres == pold) {
+ pures = puold;
+ } else if (pres == pnew) {
+ pures = punew;
+ } else {
+ pures = (zs_pset_zone_t *)malloc(
+ sizeof (zs_pset_zone_t));
+ if (pures == NULL)
+ return (-1);
+ *pures = *punew;
+ pures->zspz_pset = pres;
+ pures->zspz_zone = zs_lookup_zone_byname(ures,
+ punew->zspz_zone->zsz_name);
+ assert(pures->zspz_zone != NULL);
+ list_insert_tail(&pres->zsp_usage_list, pures);
+ }
+ if (func == ZS_COMPUTE_USAGE_AVERAGE)
+ pures->zspz_intervals++;
+
+ if (func == ZS_COMPUTE_USAGE_INTERVAL) {
+ /*
+ * If pset usage has been destroyed and re-created
+ * since start interval, don't subtract the start
+ * interval.
+ */
+ if (punew->zspz_hrstart > uold->zsu_hrtime) {
+ punew = list_next(&pnew->zsp_usage_list, punew);
+ puold = list_next(&pold->zsp_usage_list, puold);
+ continue;
+ }
+ TIMESTRUC_DELTA(pures->zspz_cpu_usage,
+ punew->zspz_cpu_usage, puold->zspz_cpu_usage);
+ } else {
+ zs_pset_zone_add_usage(pures, punew, func);
+ }
+ punew = list_next(&pnew->zsp_usage_list, punew);
+ puold = list_next(&pold->zsp_usage_list, puold);
+ }
+ if (func == ZS_COMPUTE_USAGE_TOTAL) {
+ while (puold != NULL) {
+ TIMESTRUC_ADD_TIMESTRUC(
+ puold->zspz_zone->zsz_pset_time,
+ pnew->zsp_total_time);
+ puold->zspz_zone->zsz_cpus_online +=
+ pnew->zsp_online;
+ puold = list_next(&pold->zsp_usage_list, puold);
+ }
+ }
+
+ /* No need to add new pset zone usages if result pset is new pset */
+ if (pres == pnew)
+ return (0);
+
+ /* Add in any remaining new psets in the new interval */
+ while (punew != NULL) {
+ pures = (zs_pset_zone_t *)calloc(sizeof (zs_pset_zone_t), 1);
+ if (pures == NULL)
+ return (-1);
+ *pures = *punew;
+ pures->zspz_pset = pres;
+ pures->zspz_zone = zs_lookup_zone_byname(ures,
+ punew->zspz_zone->zsz_name);
+ assert(pures->zspz_zone != NULL);
+ if (func == ZS_COMPUTE_USAGE_AVERAGE)
+ pures->zspz_intervals++;
+ if (pres == pold)
+ list_insert_tail(&pold->zsp_usage_list, pures);
+ else
+ list_insert_tail(&pres->zsp_usage_list, pures);
+
+ punew = list_next(&pnew->zsp_usage_list, punew);
+ }
+ return (0);
+}
+
+static void
+zs_pset_add_usage(zs_pset_t *old, zs_pset_t *new, int func)
+{
+
+ if (func == ZS_COMPUTE_USAGE_HIGH) {
+ ZS_MAXOF(old->zsp_online, new->zsp_online);
+ ZS_MAXOF(old->zsp_size, new->zsp_size);
+ ZS_MAXOF(old->zsp_min, new->zsp_min);
+ ZS_MAXOF(old->zsp_max, new->zsp_max);
+ ZS_MAXOF(old->zsp_importance, new->zsp_importance);
+ ZS_MAXOF(old->zsp_cpu_shares, new->zsp_cpu_shares);
+ ZS_MAXOFTS(old->zsp_total_time, new->zsp_total_time);
+ ZS_MAXOFTS(old->zsp_usage_kern, new->zsp_usage_kern);
+ ZS_MAXOFTS(old->zsp_usage_zones, new->zsp_usage_zones);
+ return;
+ }
+ old->zsp_online += new->zsp_online;
+ old->zsp_size += new->zsp_size;
+ old->zsp_min += new->zsp_min;
+ old->zsp_max += new->zsp_max;
+ old->zsp_importance += new->zsp_importance;
+ old->zsp_cpu_shares += new->zsp_cpu_shares;
+ TIMESTRUC_ADD_TIMESTRUC(old->zsp_total_time, new->zsp_total_time);
+ TIMESTRUC_ADD_TIMESTRUC(old->zsp_usage_kern, new->zsp_usage_kern);
+ TIMESTRUC_ADD_TIMESTRUC(old->zsp_usage_zones, new->zsp_usage_zones);
+}
+
+static int
+zs_usage_compute_psets(zs_usage_t *ures, zs_usage_t *uold, zs_usage_t *unew,
+ int func)
+{
+ zs_pset_t *pold, *pnew, *pres;
+
+ /*
+ * Walk psets, assume lists are always sorted the same. Include
+ * all psets that exist at the end of the interval.
+ */
+ pold = list_head(&uold->zsu_pset_list);
+ pnew = list_head(&unew->zsu_pset_list);
+
+ while (pold != NULL && pnew != NULL) {
+
+ int cmp;
+
+ cmp = strcmp(pold->zsp_name, pnew->zsp_name);
+ if (cmp > 0) {
+ /*
+ * Old interval does not contain pset in new
+ * interval. Pset is new.
+ */
+ if (ures != unew) {
+ pres = (zs_pset_t *)malloc(sizeof (zs_pset_t));
+ if (pres == NULL)
+ return (-1);
+ *pres = *pnew;
+ pres->zsp_intervals = 0;
+ list_create(&pres->zsp_usage_list,
+ sizeof (zs_pset_zone_t),
+ offsetof(zs_pset_zone_t, zspz_next));
+
+ if (ures == uold)
+ list_insert_before(&uold->zsu_pset_list,
+ pold, pres);
+ else
+ list_insert_tail(&ures->zsu_pset_list,
+ pres);
+
+ } else {
+ pres = pnew;
+ }
+ if (zs_usage_compute_pset_usage(uold, ures, pres,
+ NULL, pnew, func) != 0)
+ return (-1);
+
+ if (func == ZS_COMPUTE_USAGE_AVERAGE ||
+ func == ZS_COMPUTE_USAGE_TOTAL)
+ pres->zsp_intervals++;
+ pnew = list_next(&unew->zsu_pset_list, pnew);
+ continue;
+
+ } else if (cmp < 0) {
+ /*
+ * Start interval contains psets that is not in the
+ * end interval. This pset is gone. Leave pset in
+ * old usage, but do not add it to result usage.
+ */
+ pold = list_next(&uold->zsu_pset_list, pold);
+ continue;
+ }
+
+ /* Pset is in both start and end interval. Compute interval */
+ if (ures == uold) {
+ pres = pold;
+ } else if (ures == unew) {
+ pres = pnew;
+ } else {
+ pres = (zs_pset_t *)calloc(sizeof (zs_pset_t), 1);
+ if (pres == NULL)
+ return (-1);
+
+ *pres = *pnew;
+ list_create(&pres->zsp_usage_list,
+ sizeof (zs_pset_zone_t),
+ offsetof(zs_pset_zone_t, zspz_next));
+ list_insert_tail(&ures->zsu_pset_list, pres);
+ }
+ if (func == ZS_COMPUTE_USAGE_AVERAGE ||
+ func == ZS_COMPUTE_USAGE_TOTAL)
+ pres->zsp_intervals++;
+ if (func == ZS_COMPUTE_USAGE_INTERVAL) {
+ /*
+ * If pset as been destroyed and re-created since start
+ * interval, don't subtract the start interval.
+ */
+ if (pnew->zsp_hrstart > uold->zsu_hrtime) {
+ goto usages;
+ }
+ TIMESTRUC_DELTA(pres->zsp_total_time,
+ pnew->zsp_total_time, pold->zsp_total_time);
+
+ TIMESTRUC_DELTA(pres->zsp_usage_kern,
+ pnew->zsp_usage_kern, pold->zsp_usage_kern);
+ TIMESTRUC_DELTA(pres->zsp_usage_zones,
+ pnew->zsp_usage_zones, pold->zsp_usage_zones);
+ } else {
+ zs_pset_add_usage(pres, pnew, func);
+ }
+usages:
+ if (zs_usage_compute_pset_usage(uold, ures, pres, pold,
+ pnew, func) != 0)
+ return (-1);
+
+ pnew = list_next(&unew->zsu_pset_list, pnew);
+ pold = list_next(&uold->zsu_pset_list, pold);
+ }
+
+ if (ures == unew)
+ return (0);
+
+ /* Add in any remaining psets in the new interval */
+ while (pnew != NULL) {
+ pres = (zs_pset_t *)calloc(sizeof (zs_pset_t), 1);
+ if (pres == NULL)
+ return (-1);
+ *pres = *pnew;
+ list_create(&pres->zsp_usage_list,
+ sizeof (zs_pset_zone_t),
+ offsetof(zs_pset_zone_t, zspz_next));
+ if (func == ZS_COMPUTE_USAGE_AVERAGE ||
+ func == ZS_COMPUTE_USAGE_TOTAL)
+ pres->zsp_intervals++;
+ if (ures == uold)
+ list_insert_tail(&uold->zsu_pset_list, pres);
+ else
+ list_insert_tail(&ures->zsu_pset_list, pres);
+
+ if (zs_usage_compute_pset_usage(uold, ures, pres, NULL,
+ pnew, func) != 0)
+ return (-1);
+
+ pnew = list_next(&unew->zsu_pset_list, pnew);
+ }
+ return (0);
+}
+
+static int
+zs_zone_name(zs_zone_t *zone, char *name, size_t len)
+{
+ return (strlcpy(name, zone->zsz_name, len));
+}
+
+static zoneid_t
+zs_zone_id(zs_zone_t *zone)
+{
+ return (zone->zsz_id);
+}
+
+static uint_t
+zs_zone_iptype(zs_zone_t *zone)
+{
+ return (zone->zsz_iptype);
+}
+
+static uint_t
+zs_zone_cputype(zs_zone_t *zone)
+{
+ return (zone->zsz_cputype);
+}
+
+static int
+zs_zone_poolname(zs_zone_t *zone, char *name, size_t len)
+{
+ return (strlcpy(name, zone->zsz_pool, len));
+}
+
+static int
+zs_zone_psetname(zs_zone_t *zone, char *name, size_t len)
+{
+ return (strlcpy(name, zone->zsz_pset, len));
+}
+
+static uint_t
+zs_zone_schedulers(zs_zone_t *zone)
+{
+ return (zone->zsz_scheds);
+}
+
+static uint64_t
+zs_ts_used_scale(timestruc_t *total, timestruc_t *used, uint64_t scale,
+ boolean_t cap_at_100)
+{
+ double dtotal, dused, pct, dscale;
+
+ /* If no time yet, treat as zero */
+ if (total->tv_sec == 0 && total->tv_nsec == 0)
+ return (0);
+
+ dtotal = (double)total->tv_sec +
+ ((double)total->tv_nsec / (double)NANOSEC);
+ dused = (double)used->tv_sec +
+ ((double)used->tv_nsec / (double)NANOSEC);
+
+ dscale = (double)scale;
+ pct = dused / dtotal * dscale;
+ if (cap_at_100 && pct > dscale)
+ pct = dscale;
+
+ return ((uint_t)pct);
+}
+
+/*
+ * Convert total and used time into percent used.
+ */
+static uint_t
+zs_ts_used_pct(timestruc_t *total, timestruc_t *used, boolean_t cap_at_100)
+{
+ return ((uint_t)zs_ts_used_scale(total, used, ZSD_PCT_INT, cap_at_100));
+}
+
+/*
+ * Convert total and used time, plus number of cpus, into number of cpus
+ * used, where 100 equals 1 cpu used.
+ */
+static uint64_t
+zs_ts_used_cpus(timestruc_t *total, timestruc_t *used, uint_t ncpus,
+ boolean_t cap_at_100)
+{
+ return (zs_ts_used_scale(total, used, ncpus * ZSD_ONE_CPU, cap_at_100));
+}
+
+static uint64_t
+zs_zone_cpu_shares(zs_zone_t *zone)
+{
+ /* No processes found in FSS */
+ if ((zone->zsz_scheds & ZS_SCHED_FSS) == 0)
+ return (ZS_LIMIT_NONE);
+
+ return (zone->zsz_cpu_shares);
+}
+
+static uint64_t
+zs_zone_cpu_cap(zs_zone_t *zone)
+{
+ return (zone->zsz_cpu_cap);
+}
+
+static uint64_t
+zs_zone_cpu_cap_used(zs_zone_t *zone)
+{
+ if (zone->zsz_cpu_cap == ZS_LIMIT_NONE)
+ return (ZS_LIMIT_NONE);
+
+ return (zs_ts_used_cpus(&zone->zsz_cap_time, &zone->zsz_cpu_usage,
+ zone->zsz_cpus_online, B_TRUE));
+}
+
+static uint64_t
+zs_zone_cpu_shares_used(zs_zone_t *zone)
+{
+ if (zone->zsz_cpu_shares == ZS_LIMIT_NONE)
+ return (ZS_LIMIT_NONE);
+
+ if (zone->zsz_cpu_shares == ZS_SHARES_UNLIMITED)
+ return (ZS_LIMIT_NONE);
+
+ if ((zone->zsz_scheds & ZS_SCHED_FSS) == 0)
+ return (ZS_LIMIT_NONE);
+
+ return (zs_ts_used_scale(&zone->zsz_share_time, &zone->zsz_cpu_usage,
+ zone->zsz_cpu_shares, B_FALSE));
+}
+
+static void
+zs_zone_cpu_cap_time(zs_zone_t *zone, timestruc_t *ts)
+{
+ *ts = zone->zsz_cap_time;
+}
+
+static void
+zs_zone_cpu_share_time(zs_zone_t *zone, timestruc_t *ts)
+{
+ *ts = zone->zsz_share_time;
+}
+
+static void
+zs_zone_cpu_cap_time_used(zs_zone_t *zone, timestruc_t *ts)
+{
+ *ts = zone->zsz_cpu_usage;
+}
+
+static void
+zs_zone_cpu_share_time_used(zs_zone_t *zone, timestruc_t *ts)
+{
+ *ts = zone->zsz_cpu_usage;
+}
+
+
+static uint64_t
+zs_uint64_used_scale(uint64_t total, uint64_t used, uint64_t scale,
+ boolean_t cap_at_100)
+{
+ double dtotal, dused, pct, dscale;
+
+ /* If no time yet, treat as zero */
+ if (total == 0)
+ return (0);
+
+ dtotal = (double)total;
+ dused = (double)used;
+
+ dscale = (double)scale;
+ pct = dused / dtotal * dscale;
+ if (cap_at_100 && pct > dscale)
+ pct = dscale;
+
+ return ((uint64_t)pct);
+}
+
+/*
+ * Convert a total and used value into a percent used.
+ */
+static uint_t
+zs_uint64_used_pct(uint64_t total, uint64_t used, boolean_t cap_at_100)
+{
+ return ((uint_t)zs_uint64_used_scale(total, used, ZSD_PCT_INT,
+ cap_at_100));
+}
+
+static uint_t
+zs_zone_cpu_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_cpu_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ return (zs_ts_used_pct(&zone->zsz_cap_time, &zone->zsz_cpu_usage,
+ B_TRUE));
+}
+
+static uint_t
+zs_zone_cpu_shares_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_cpu_shares == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_cpu_shares == ZS_SHARES_UNLIMITED)
+ return (ZS_PCT_NONE);
+
+ if ((zone->zsz_scheds & ZS_SCHED_FSS) == 0)
+ return (ZS_PCT_NONE);
+
+ return (zs_ts_used_pct(&zone->zsz_share_time, &zone->zsz_cpu_usage,
+ B_FALSE));
+}
+
+static uint64_t
+zs_zone_physical_memory_cap(zs_zone_t *zone)
+{
+ return (zone->zsz_ram_cap);
+}
+
+static uint64_t
+zs_zone_virtual_memory_cap(zs_zone_t *zone)
+{
+ return (zone->zsz_vm_cap);
+}
+
+static uint64_t
+zs_zone_locked_memory_cap(zs_zone_t *zone)
+{
+ return (zone->zsz_locked_cap);
+}
+
+static uint64_t
+zs_zone_physical_memory_cap_used(zs_zone_t *zone)
+{
+ if (zone->zsz_ram_cap == ZS_LIMIT_NONE)
+ return (ZS_LIMIT_NONE);
+
+ return (zone->zsz_usage_ram);
+}
+
+static uint64_t
+zs_zone_virtual_memory_cap_used(zs_zone_t *zone)
+{
+ if (zone->zsz_vm_cap == ZS_LIMIT_NONE)
+ return (ZS_LIMIT_NONE);
+
+ return (zone->zsz_usage_vm);
+}
+
+static uint64_t
+zs_zone_locked_memory_cap_used(zs_zone_t *zone)
+{
+ if (zone->zsz_locked_cap == ZS_LIMIT_NONE)
+ return (ZS_LIMIT_NONE);
+
+ return (zone->zsz_usage_locked);
+}
+
+static int
+zs_pset_name(zs_pset_t *pset, char *name, size_t len)
+{
+ return (strlcpy(name, pset->zsp_name, len));
+}
+
+static psetid_t
+zs_pset_id(zs_pset_t *pset)
+{
+ return (pset->zsp_id);
+}
+
+static uint64_t
+zs_pset_size(zs_pset_t *pset)
+{
+ return (pset->zsp_size);
+}
+
+static uint64_t
+zs_pset_online(zs_pset_t *pset)
+{
+ return (pset->zsp_online);
+}
+
+uint64_t
+zs_pset_min(zs_pset_t *pset)
+{
+ return (pset->zsp_min);
+}
+
+uint64_t
+zs_pset_max(zs_pset_t *pset)
+{
+ return (pset->zsp_max);
+}
+
+static uint_t
+zs_pset_schedulers(zs_pset_t *pset)
+{
+ return (pset->zsp_scheds);
+}
+
+static uint_t
+zs_pset_zone_schedulers(zs_pset_zone_t *pz)
+{
+ return (pz->zspz_scheds);
+}
+
+static uint64_t
+zs_pset_cpu_shares(zs_pset_t *pset)
+{
+ if (!(pset->zsp_scheds & ZS_SCHED_FSS))
+ return (ZS_LIMIT_NONE);
+
+ return (pset->zsp_cpu_shares);
+}
+
+static uint64_t
+zs_pset_zone_cpu_shares(zs_pset_zone_t *pz)
+{
+ if (!(pz->zspz_scheds & ZS_SCHED_FSS))
+ return (ZS_LIMIT_NONE);
+
+ return (pz->zspz_cpu_shares);
+}
+
+static uint_t
+zs_pset_cputype(zs_pset_t *pset)
+{
+ return (pset->zsp_cputype);
+}
+
+static void
+zs_pset_usage_all(zs_pset_t *pset, timestruc_t *ts)
+{
+ timestruc_t tot;
+
+ tot = pset->zsp_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, pset->zsp_usage_zones);
+ *ts = tot;
+}
+
+static void
+zs_pset_usage_idle(zs_pset_t *pset, timestruc_t *ts)
+{
+ timestruc_t tot, time, idle;
+
+ tot = pset->zsp_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, pset->zsp_usage_zones);
+ time = pset->zsp_total_time;
+ TIMESTRUC_DELTA(idle, time, tot);
+ *ts = idle;
+}
+
+static void
+zs_pset_usage_kernel(zs_pset_t *pset, timestruc_t *ts)
+{
+ *ts = pset->zsp_usage_kern;
+}
+
+static void
+zs_pset_usage_zones(zs_pset_t *pset, timestruc_t *ts)
+{
+ *ts = pset->zsp_usage_zones;
+}
+
+static uint_t
+zs_pset_usage_all_pct(zs_pset_t *pset)
+{
+ timestruc_t tot;
+
+ tot = pset->zsp_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, pset->zsp_usage_zones);
+
+ return (zs_ts_used_pct(&pset->zsp_total_time, &tot, B_TRUE));
+}
+
+static uint_t
+zs_pset_usage_idle_pct(zs_pset_t *pset)
+{
+ timestruc_t tot, idle;
+
+ tot = pset->zsp_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, pset->zsp_usage_zones);
+ TIMESTRUC_DELTA(idle, pset->zsp_total_time, tot);
+
+ return (zs_ts_used_pct(&pset->zsp_total_time, &idle, B_TRUE));
+}
+
+static uint_t
+zs_pset_usage_kernel_pct(zs_pset_t *pset)
+{
+ return (zs_ts_used_pct(&pset->zsp_total_time, &pset->zsp_usage_kern,
+ B_TRUE));
+}
+
+static uint_t
+zs_pset_usage_zones_pct(zs_pset_t *pset)
+{
+ return (zs_ts_used_pct(&pset->zsp_total_time, &pset->zsp_usage_zones,
+ B_TRUE));
+}
+
+static uint_t
+zs_pset_usage_all_cpus(zs_pset_t *pset)
+{
+ timestruc_t tot;
+
+ tot = pset->zsp_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, pset->zsp_usage_zones);
+ return (zs_ts_used_cpus(&pset->zsp_total_time, &tot, pset->zsp_online,
+ B_TRUE));
+}
+
+static uint_t
+zs_pset_usage_idle_cpus(zs_pset_t *pset)
+{
+ timestruc_t tot, idle;
+
+ tot = pset->zsp_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, pset->zsp_usage_zones);
+ TIMESTRUC_DELTA(idle, pset->zsp_total_time, tot);
+
+ return (zs_ts_used_cpus(&pset->zsp_total_time, &tot, pset->zsp_online,
+ B_TRUE));
+}
+
+static uint_t
+zs_pset_usage_kernel_cpus(zs_pset_t *pset)
+{
+ return (zs_ts_used_cpus(&pset->zsp_total_time, &pset->zsp_usage_kern,
+ pset->zsp_online, B_TRUE));
+}
+
+static uint64_t
+zs_pset_usage_zones_cpus(zs_pset_t *pset)
+{
+ return (zs_ts_used_cpus(&pset->zsp_total_time, &pset->zsp_usage_zones,
+ pset->zsp_online, B_TRUE));
+}
+
+static void
+zs_pset_zone_usage_time(zs_pset_zone_t *pz, timestruc_t *t)
+{
+ *t = pz->zspz_cpu_usage;
+}
+
+static uint_t
+zs_pset_zone_usage_cpus(zs_pset_zone_t *pz)
+{
+ return (zs_ts_used_cpus(&pz->zspz_pset->zsp_total_time,
+ &pz->zspz_cpu_usage, pz->zspz_pset->zsp_online, B_TRUE));
+}
+
+static uint_t
+zs_pset_zone_usage_pct_pset(zs_pset_zone_t *pz)
+{
+ return (zs_ts_used_pct(&pz->zspz_pset->zsp_total_time,
+ &pz->zspz_cpu_usage, B_TRUE));
+}
+
+static uint64_t
+zs_pset_zone_cpu_cap(zs_pset_zone_t *pz)
+{
+ return (pz->zspz_zone->zsz_cpu_cap);
+}
+
+static uint_t
+zs_pset_zone_usage_pct_cpu_cap(zs_pset_zone_t *pz)
+{
+ zs_zone_t *zone = pz->zspz_zone;
+
+ if (zone->zsz_cpu_cap == ZS_LIMIT_NONE) {
+ return (ZS_PCT_NONE);
+ }
+ return (zs_ts_used_pct(&zone->zsz_cap_time,
+ &pz->zspz_cpu_usage, B_TRUE));
+}
+
+/*
+ * Return the fraction of total shares for a pset allocated to the zone.
+ */
+static uint_t
+zs_pset_zone_usage_pct_pset_shares(zs_pset_zone_t *pz)
+{
+ zs_pset_t *pset = pz->zspz_pset;
+
+ if (!(pz->zspz_scheds & ZS_SCHED_FSS))
+ return (ZS_PCT_NONE);
+
+ if (pz->zspz_cpu_shares == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (pz->zspz_cpu_shares == ZS_SHARES_UNLIMITED)
+ return (ZS_PCT_NONE);
+
+ if (pz->zspz_pset->zsp_cpu_shares == 0)
+ return (0);
+
+ if (pz->zspz_cpu_shares == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(pset->zsp_cpu_shares, pz->zspz_cpu_shares,
+ B_TRUE));
+}
+
+/*
+ * Of a zones shares, what percent of cpu time is it using. For instance,
+ * if a zone has 50% of shares, and is using 50% of the cpu time, then it is
+ * using 100% of its share.
+ */
+static uint_t
+zs_pset_zone_usage_pct_cpu_shares(zs_pset_zone_t *pz)
+{
+ timestruc_t tot, time;
+ double sharefactor;
+ double total;
+ double used;
+ double pct;
+
+ if (!(pz->zspz_scheds & ZS_SCHED_FSS))
+ return (ZS_PCT_NONE);
+
+ if (pz->zspz_cpu_shares == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (pz->zspz_cpu_shares == ZS_SHARES_UNLIMITED)
+ return (ZS_PCT_NONE);
+
+ if (pz->zspz_cpu_shares == 0)
+ return (ZS_PCT_NONE);
+
+ sharefactor = (double)zs_pset_zone_usage_pct_pset_shares(pz);
+
+ /* Common scaling function won't do sharefactor. */
+ time = pz->zspz_pset->zsp_total_time;
+ tot = pz->zspz_cpu_usage;
+
+ total = (double)time.tv_sec +
+ ((double)time.tv_nsec / (double)NANOSEC);
+ total = total * (sharefactor / ZSD_PCT_DOUBLE);
+ used = (double)tot.tv_sec +
+ ((double)tot.tv_nsec / (double)NANOSEC);
+
+ pct = used / total * ZSD_PCT_DOUBLE;
+ /* Allow percent of share used to exceed 100% */
+ return ((uint_t)pct);
+}
+
+static void
+zs_cpu_total_time(zs_usage_t *usage, timestruc_t *ts)
+{
+ *ts = usage->zsu_system->zss_cpu_total_time;
+}
+
+static void
+zs_cpu_usage_all(zs_usage_t *usage, timestruc_t *ts)
+{
+ timestruc_t tot;
+
+ tot.tv_sec = 0;
+ tot.tv_nsec = 0;
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_kern);
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_zones);
+ *ts = tot;
+}
+
+static void
+zs_cpu_usage_idle(zs_usage_t *usage, timestruc_t *ts)
+{
+ timestruc_t tot, time, idle;
+
+ tot.tv_sec = 0;
+ tot.tv_nsec = 0;
+ tot = usage->zsu_system->zss_cpu_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_zones);
+ time = usage->zsu_system->zss_cpu_total_time;
+ TIMESTRUC_DELTA(idle, time, tot);
+ *ts = idle;
+}
+
+static uint_t
+zs_cpu_usage_all_pct(zs_usage_t *usage)
+{
+ timestruc_t tot;
+
+ tot = usage->zsu_system->zss_cpu_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_zones);
+
+ return (zs_ts_used_pct(&usage->zsu_system->zss_cpu_total_time,
+ &tot, B_TRUE));
+}
+
+
+static uint_t
+zs_cpu_usage_idle_pct(zs_usage_t *usage)
+{
+ timestruc_t tot, idle;
+
+ tot = usage->zsu_system->zss_cpu_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_zones);
+ TIMESTRUC_DELTA(idle, usage->zsu_system->zss_cpu_total_time, tot);
+
+ return (zs_ts_used_pct(&usage->zsu_system->zss_cpu_total_time,
+ &idle, B_TRUE));
+}
+
+static void
+zs_cpu_usage_kernel(zs_usage_t *usage, timestruc_t *ts)
+{
+ *ts = usage->zsu_system->zss_cpu_usage_kern;
+}
+
+static uint_t
+zs_cpu_usage_kernel_pct(zs_usage_t *usage)
+{
+ return (zs_ts_used_pct(&usage->zsu_system->zss_cpu_total_time,
+ &usage->zsu_system->zss_cpu_usage_kern, B_TRUE));
+}
+
+static void
+zs_cpu_usage_zones(zs_usage_t *usage, timestruc_t *ts)
+{
+ *ts = usage->zsu_system->zss_cpu_usage_zones;
+}
+
+
+static uint_t
+zs_cpu_usage_zones_pct(zs_usage_t *usage)
+{
+ return (zs_ts_used_pct(&usage->zsu_system->zss_cpu_total_time,
+ &usage->zsu_system->zss_cpu_usage_zones, B_TRUE));
+}
+
+
+static void
+zs_cpu_usage_zone(zs_zone_t *zone, timestruc_t *ts)
+{
+ *ts = zone->zsz_cpu_usage;
+}
+
+static uint64_t
+zs_cpu_total_cpu(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ncpus_online * ZSD_ONE_CPU);
+}
+
+static uint64_t
+zs_cpu_usage_all_cpu(zs_usage_t *usage)
+{
+ timestruc_t tot;
+
+ tot = usage->zsu_system->zss_cpu_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_zones);
+
+ return (zs_ts_used_cpus(&usage->zsu_system->zss_cpu_total_time,
+ &tot, usage->zsu_system->zss_ncpus_online, B_TRUE));
+}
+
+static uint64_t
+zs_cpu_usage_idle_cpu(zs_usage_t *usage)
+{
+ timestruc_t tot, idle;
+
+ tot = usage->zsu_system->zss_cpu_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(tot, usage->zsu_system->zss_cpu_usage_zones);
+ TIMESTRUC_DELTA(idle, usage->zsu_system->zss_cpu_total_time, tot);
+
+ return (zs_ts_used_cpus(&usage->zsu_system->zss_cpu_total_time,
+ &idle, usage->zsu_system->zss_ncpus_online, B_TRUE));
+}
+
+static uint64_t
+zs_cpu_usage_kernel_cpu(zs_usage_t *usage)
+{
+ return (zs_ts_used_cpus(&usage->zsu_system->zss_cpu_total_time,
+ &usage->zsu_system->zss_cpu_usage_kern,
+ usage->zsu_system->zss_ncpus_online, B_TRUE));
+}
+
+static uint64_t
+zs_cpu_usage_zones_cpu(zs_usage_t *usage)
+{
+ return (zs_ts_used_cpus(&usage->zsu_system->zss_cpu_total_time,
+ &usage->zsu_system->zss_cpu_usage_kern,
+ usage->zsu_system->zss_ncpus_online, B_TRUE));
+}
+
+static uint64_t
+zs_cpu_usage_zone_cpu(zs_zone_t *zone)
+{
+ return (zs_ts_used_cpus(&zone->zsz_pset_time, &zone->zsz_cpu_usage,
+ zone->zsz_cpus_online, B_TRUE));
+}
+
+static uint_t
+zs_cpu_usage_zone_pct(zs_zone_t *zone)
+{
+ return (zs_ts_used_pct(&zone->zsz_pset_time, &zone->zsz_cpu_usage,
+ B_TRUE));
+}
+
+static uint64_t
+zs_physical_memory_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_total);
+}
+
+
+static uint64_t
+zs_physical_memory_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_kern +
+ usage->zsu_system->zss_ram_zones);
+}
+
+static uint_t
+zs_physical_memory_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ (system->zss_ram_kern + system->zss_ram_zones), B_TRUE));
+}
+
+static uint64_t
+zs_physical_memory_usage_free(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_total -
+ (usage->zsu_system->zss_ram_kern +
+ usage->zsu_system->zss_ram_zones));
+}
+
+static uint_t
+zs_physical_memory_usage_free_pct(zs_usage_t *usage)
+{
+ return (ZSD_PCT_INT - zs_physical_memory_usage_all_pct(usage));
+}
+
+static uint64_t
+zs_physical_memory_usage_kernel(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_kern);
+}
+
+static uint_t
+zs_physical_memory_usage_kernel_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ system->zss_ram_kern, B_TRUE));
+}
+
+static uint64_t
+zs_physical_memory_usage_zones(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_zones);
+}
+
+static uint_t
+zs_physical_memory_usage_zones_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ system->zss_ram_zones, B_TRUE));
+}
+
+static uint64_t
+zs_physical_memory_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_usage_ram);
+}
+
+static uint_t
+zs_physical_memory_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ zone->zsz_usage_ram, B_TRUE));
+}
+
+static uint_t
+zs_zone_physical_memory_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_ram_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_ram_cap == 0) {
+ return (0);
+ }
+
+ /* Allow ram cap to exeed 100% */
+ return (zs_uint64_used_pct(zone->zsz_ram_cap,
+ zone->zsz_usage_ram, B_FALSE));
+}
+static uint64_t
+zs_virtual_memory_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_vm_total);
+}
+
+static uint64_t
+zs_virtual_memory_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_vm_kern +
+ usage->zsu_system->zss_vm_zones);
+}
+static uint64_t
+zs_virtual_memory_usage_free(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_vm_total -
+ (usage->zsu_system->zss_vm_kern +
+ usage->zsu_system->zss_vm_zones));
+}
+static uint_t
+zs_virtual_memory_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_vm_total,
+ (system->zss_vm_kern + system->zss_vm_zones), B_TRUE));
+
+}
+
+static uint_t
+zs_virtual_memory_usage_free_pct(zs_usage_t *usage)
+{
+ return (ZSD_PCT_INT - zs_virtual_memory_usage_all_pct(usage));
+
+}
+static uint64_t
+zs_virtual_memory_usage_kernel(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_vm_kern);
+}
+
+static uint_t
+zs_virtual_memory_usage_kernel_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_vm_total,
+ system->zss_vm_kern, B_TRUE));
+}
+
+static uint64_t
+zs_virtual_memory_usage_zones(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_vm_zones);
+}
+
+static uint_t
+zs_virtual_memory_usage_zones_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_vm_total,
+ system->zss_vm_zones, B_TRUE));
+}
+
+static uint64_t
+zs_virtual_memory_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_usage_vm);
+}
+
+static uint_t
+zs_virtual_memory_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_vm_total,
+ zone->zsz_usage_vm, B_TRUE));
+
+}
+
+static uint_t
+zs_zone_virtual_memory_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_vm_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_vm_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_vm_cap,
+ zone->zsz_usage_vm, B_TRUE));
+}
+
+static uint64_t
+zs_locked_memory_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_total);
+}
+
+static uint64_t
+zs_locked_memory_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_locked_kern +
+ usage->zsu_system->zss_locked_zones);
+}
+static uint64_t
+zs_locked_memory_usage_free(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_ram_total -
+ (usage->zsu_system->zss_locked_kern +
+ usage->zsu_system->zss_locked_zones));
+}
+
+static uint_t
+zs_locked_memory_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ (system->zss_locked_kern + system->zss_locked_zones), B_TRUE));
+}
+
+static uint_t
+zs_locked_memory_usage_free_pct(zs_usage_t *usage)
+{
+ return (ZSD_PCT_INT - zs_locked_memory_usage_all_pct(usage));
+
+}
+
+static uint64_t
+zs_locked_memory_usage_kernel(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_locked_kern);
+}
+
+static uint_t
+zs_locked_memory_usage_kernel_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ system->zss_locked_kern, B_TRUE));
+}
+
+static uint64_t
+zs_locked_memory_usage_zones(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_locked_zones);
+}
+
+static uint_t
+zs_locked_memory_usage_zones_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ system->zss_locked_zones, B_TRUE));
+}
+
+static uint64_t
+zs_locked_memory_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_usage_locked);
+}
+
+static uint_t
+zs_locked_memory_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_ram_total,
+ zone->zsz_usage_locked, B_TRUE));
+}
+
+static uint_t
+zs_zone_locked_memory_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_locked_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_locked_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_locked_cap,
+ zone->zsz_usage_locked, B_TRUE));
+
+}
+static uint64_t
+zs_disk_swap_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_swap_total);
+}
+
+static uint64_t
+zs_disk_swap_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_swap_used);
+}
+
+static uint_t
+zs_disk_swap_usage_all_pct(zs_usage_t *usage)
+{
+ return (zs_uint64_used_pct(usage->zsu_system->zss_swap_total,
+ usage->zsu_system->zss_swap_used, B_TRUE));
+}
+
+static uint64_t
+zs_disk_swap_usage_free(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_swap_total -
+ usage->zsu_system->zss_swap_used);
+}
+
+static uint_t
+zs_disk_swap_usage_free_pct(zs_usage_t *usage)
+{
+ return (ZSD_PCT_INT - zs_disk_swap_usage_all_pct(usage));
+}
+
+static uint64_t
+zs_processes_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_processes_max);
+}
+
+static uint64_t
+zs_lwps_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_lwps_max);
+}
+
+static uint64_t
+zs_shm_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_shm_max);
+}
+
+static uint64_t
+zs_shmids_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_shmids_max);
+}
+
+static uint64_t
+zs_semids_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_semids_max);
+}
+
+static uint64_t
+zs_msgids_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_msgids_max);
+}
+
+static uint64_t
+zs_lofi_total(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_lofi_max);
+}
+
+static uint64_t
+zs_processes_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_processes);
+}
+
+static uint64_t
+zs_lwps_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_lwps);
+}
+
+static uint64_t
+zs_shm_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_shm);
+}
+
+static uint64_t
+zs_shmids_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_shmids);
+}
+
+static uint64_t
+zs_semids_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_semids);
+}
+
+static uint64_t
+zs_msgids_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_msgids);
+}
+
+static uint64_t
+zs_lofi_usage_all(zs_usage_t *usage)
+{
+ return (usage->zsu_system->zss_lofi);
+}
+static uint64_t
+zs_processes_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_processes_max,
+ system->zss_processes, B_TRUE));
+}
+
+static uint_t
+zs_lwps_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_lwps_max,
+ system->zss_lwps, B_TRUE));
+}
+
+static uint_t
+zs_shm_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_shm_max,
+ system->zss_shm, B_TRUE));
+}
+
+static uint_t
+zs_shmids_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_shmids_max,
+ system->zss_shmids, B_TRUE));
+}
+
+static uint64_t
+zs_semids_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_semids_max,
+ system->zss_semids, B_TRUE));
+}
+
+static uint64_t
+zs_msgids_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_msgids_max,
+ system->zss_msgids, B_TRUE));
+}
+
+static uint64_t
+zs_lofi_usage_all_pct(zs_usage_t *usage)
+{
+ zs_system_t *system = usage->zsu_system;
+
+ return (zs_uint64_used_pct(system->zss_lofi_max,
+ system->zss_lofi, B_TRUE));
+}
+
+static uint64_t
+zs_processes_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_processes);
+}
+
+static uint64_t
+zs_lwps_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_lwps);
+}
+
+static uint64_t
+zs_shm_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_shm);
+}
+
+static uint64_t
+zs_shmids_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_shmids);
+}
+
+static uint64_t
+zs_semids_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_semids);
+}
+
+static uint64_t
+zs_msgids_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_msgids);
+}
+
+static uint64_t
+zs_lofi_usage_zone(zs_zone_t *zone)
+{
+ return (zone->zsz_lofi);
+}
+
+static uint_t
+zs_processes_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_processes_max,
+ zone->zsz_processes, B_TRUE));
+}
+
+static uint_t
+zs_lwps_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_lwps_max,
+ zone->zsz_lwps, B_TRUE));
+}
+
+static uint_t
+zs_shm_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_shm_max,
+ zone->zsz_shm, B_TRUE));
+}
+
+static uint_t
+zs_shmids_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_shmids_max,
+ zone->zsz_shmids, B_TRUE));
+}
+
+static uint_t
+zs_semids_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_semids_max,
+ zone->zsz_semids, B_TRUE));
+}
+
+static uint_t
+zs_msgids_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_msgids_max,
+ zone->zsz_msgids, B_TRUE));
+}
+
+static uint_t
+zs_lofi_usage_zone_pct(zs_zone_t *zone)
+{
+ zs_system_t *system = zone->zsz_system;
+
+ return (zs_uint64_used_pct(system->zss_lofi_max,
+ zone->zsz_lofi, B_TRUE));
+}
+
+static uint_t
+zs_processes_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_processes_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_processes_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_processes_cap,
+ zone->zsz_processes, B_TRUE));
+}
+
+static uint_t
+zs_lwps_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_lwps_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_lwps_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_lwps_cap, zone->zsz_lwps, B_TRUE));
+}
+
+static uint_t
+zs_shm_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_shm_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_shm_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_shm_cap, zone->zsz_shm, B_TRUE));
+}
+
+static uint_t
+zs_shmids_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_shmids_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_shmids_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_shmids_cap, zone->zsz_shmids,
+ B_TRUE));
+}
+
+static uint_t
+zs_semids_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_semids_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_semids_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_semids_cap, zone->zsz_semids,
+ B_TRUE));
+}
+
+static uint_t
+zs_msgids_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_msgids_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_msgids_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_msgids_cap, zone->zsz_msgids,
+ B_TRUE));
+}
+
+static uint_t
+zs_lofi_zone_cap_pct(zs_zone_t *zone)
+{
+ if (zone->zsz_lofi_cap == ZS_LIMIT_NONE)
+ return (ZS_PCT_NONE);
+
+ if (zone->zsz_lofi_cap == 0)
+ return (0);
+
+ return (zs_uint64_used_pct(zone->zsz_lofi_cap, zone->zsz_lofi,
+ B_TRUE));
+}
+
+/* All funcs this line should be static */
+
+void
+zs_close(zs_ctl_t *ctl)
+{
+ (void) close(ctl->zsctl_door);
+ zs_usage_free(ctl->zsctl_start);
+ free(ctl);
+}
+
+/*
+ * ERRORS
+ *
+ * EINTR signal received, process forked, or zonestatd exited
+ * ESRCH zonestatd not responding
+ */
+static zs_usage_t *
+zs_usage_read_internal(zs_ctl_t *ctl, int init)
+{
+ int fd = -1;
+ uint_t i, j;
+ zs_usage_t *usage;
+ zs_zone_t *zone = NULL;
+ zs_pset_t *pset = NULL;
+ zs_pset_zone_t *pz;
+ char *next;
+ uint64_t cmd[2];
+ door_arg_t params;
+
+ fd = ctl->zsctl_door;
+ cmd[0] = ZSD_CMD_READ;
+ cmd[1] = ctl->zsctl_gen;
+ params.data_ptr = (char *)cmd;
+ params.data_size = sizeof (cmd);
+ params.desc_ptr = NULL;
+ params.desc_num = 0;
+ params.rbuf = NULL;
+ params.rsize = 0;
+
+ if (door_call(fd, &params) != 0) {
+ if (errno != EINTR)
+ errno = ESRCH;
+ return (NULL);
+ }
+
+ if (params.rbuf == NULL) {
+ errno = ESRCH;
+ return (NULL);
+ }
+ /* LINTED */
+ usage = (zs_usage_t *)params.data_ptr;
+ ctl->zsctl_gen = usage->zsu_gen;
+ usage->zsu_mmap = B_TRUE;
+ usage->zsu_intervals = 0;
+
+ list_create(&usage->zsu_zone_list, sizeof (zs_zone_t),
+ offsetof(zs_zone_t, zsz_next));
+ list_create(&usage->zsu_pset_list, sizeof (zs_pset_t),
+ offsetof(zs_pset_t, zsp_next));
+
+ /* Fix up next pointers inside usage_t */
+ next = (char *)usage;
+ next += sizeof (zs_usage_t);
+
+ /* LINTED */
+ usage->zsu_system = (zs_system_t *)next;
+ next += sizeof (zs_system_t);
+
+ for (i = 0; i < usage->zsu_nzones; i++) {
+ /* LINTED */
+ zone = (zs_zone_t *)next;
+ list_insert_tail(&usage->zsu_zone_list, zone);
+ next += sizeof (zs_zone_t);
+ zone->zsz_system = usage->zsu_system;
+ zone->zsz_intervals = 0;
+ }
+
+ for (i = 0; i < usage->zsu_npsets; i++) {
+ /* LINTED */
+ pset = (zs_pset_t *)next;
+ list_insert_tail(&usage->zsu_pset_list, pset);
+ next += sizeof (zs_pset_t);
+ list_create(&pset->zsp_usage_list, sizeof (zs_pset_zone_t),
+ offsetof(zs_pset_zone_t, zspz_next));
+ for (j = 0; j < pset->zsp_nusage; j++) {
+ /* LINTED */
+ pz = (zs_pset_zone_t *)next;
+ list_insert_tail(&pset->zsp_usage_list, pz);
+ next += sizeof (zs_pset_zone_t);
+ pz->zspz_pset = pset;
+ pz->zspz_zone =
+ zs_lookup_zone_byid(usage, pz->zspz_zoneid);
+ assert(pz->zspz_zone != NULL);
+ pz->zspz_intervals = 0;
+ }
+ pset->zsp_intervals = 0;
+ }
+ if (init)
+ return (usage);
+
+ /*
+ * If current usage tracking started after start usage, then
+ * no need to subtract start usage. This really can't happen,
+ * as zonestatd should never start over while this client is
+ * connected.
+ */
+ if (usage->zsu_hrstart > ctl->zsctl_start->zsu_hrtime) {
+ return (usage);
+ }
+
+ /*
+ * Compute usage relative to first open. Usage returned by
+ * zonestatd starts at an arbitrary point in the past.
+ *
+ */
+
+ (void) zs_usage_compute(usage, ctl->zsctl_start, usage,
+ ZS_COMPUTE_USAGE_INTERVAL);
+
+ return (usage);
+}
+
+zs_usage_t *
+zs_usage_read(zs_ctl_t *ctl)
+{
+ return (zs_usage_read_internal(ctl, B_FALSE));
+}
+
+/*
+ * Open connection to zonestatd. NULL of failure, with errno set:
+ *
+ * EPERM: Insufficent privilege (no PRIV_PROC_INFO)
+ * ESRCH: Zones monitoring service not available or responding
+ * ENOTSUP: Incompatiable zones monitoring service version.
+ * EINTR: Server exited or client forked.
+ * ENOMEM: as malloc(3c)
+ * EAGAIN: asl malloc(3c)
+ *
+ */
+zs_ctl_t *
+zs_open()
+{
+ zs_ctl_t *ctl;
+ int cmd[2];
+ int *res;
+ int fd;
+ door_arg_t params;
+ door_desc_t *door;
+ int errno_save;
+
+ ctl = calloc(sizeof (zs_ctl_t), 1);
+ if (ctl == NULL)
+ return (NULL);
+
+ fd = zs_connect_zonestatd();
+ if (fd < 0) {
+ free(ctl);
+ errno = ESRCH;
+ return (NULL);
+ }
+
+ cmd[0] = ZSD_CMD_CONNECT;
+ cmd[1] = ZS_VERSION;
+ params.data_ptr = (char *)cmd;
+ params.data_size = sizeof (cmd);
+ params.desc_ptr = NULL;
+ params.desc_num = 0;
+ params.rbuf = NULL;
+ params.rsize = 0;
+ if (door_call(fd, &params) != 0) {
+ errno_save = errno;
+ free(ctl);
+ (void) close(fd);
+ if (errno_save == EINTR)
+ errno = EINTR;
+ else
+ errno = ESRCH;
+ return (NULL);
+ }
+ (void) close(fd);
+ /* LINTED */
+ res = (int *)params.data_ptr;
+ if (res[1] == ZSD_STATUS_VERSION_MISMATCH) {
+ free(ctl);
+ errno = ENOTSUP;
+ return (NULL);
+ }
+ if (res[1] == ZSD_STATUS_PERMISSION) {
+ free(ctl);
+ errno = EPERM;
+ return (NULL);
+ }
+ if (res[1] != ZSD_STATUS_OK) {
+ free(ctl);
+ errno = ESRCH;
+ return (NULL);
+ }
+
+ door = params.desc_ptr;
+ if (door == NULL) {
+ free(ctl);
+ return (NULL);
+ }
+ ctl->zsctl_door = door->d_data.d_desc.d_descriptor;
+
+ if (params.data_ptr != (char *)cmd)
+ (void) munmap(params.data_ptr, params.data_size);
+
+
+ /*
+ * Get the initial usage from zonestatd. This creates a
+ * zero-point on which to base future usages returned by
+ * zs_read().
+ */
+ ctl->zsctl_start = zs_usage_read_internal(ctl, B_TRUE);
+ if (ctl->zsctl_start == NULL) {
+ errno_save = errno;
+ (void) close(ctl->zsctl_door);
+ free(ctl);
+ if (errno_save == EINTR)
+ errno = EINTR;
+ else
+ errno = ESRCH;
+ return (NULL);
+ }
+ return (ctl);
+}
+
+/*
+ * Return NULL on error.
+ *
+ * ERRORS:
+ * EINVAL: Invalid function.
+ */
+zs_usage_t *
+zs_usage_compute(zs_usage_t *ures, zs_usage_t *uold, zs_usage_t *unew,
+ int func)
+{
+ zs_system_t *sold, *snew, *sres;
+ boolean_t alloced = B_FALSE;
+
+ if (func != ZS_COMPUTE_USAGE_INTERVAL &&
+ func != ZS_COMPUTE_USAGE_TOTAL &&
+ func != ZS_COMPUTE_USAGE_AVERAGE &&
+ func != ZS_COMPUTE_USAGE_HIGH)
+ assert(0);
+
+ if (ures == NULL) {
+ alloced = B_TRUE;
+ ures = zs_usage_alloc();
+ if (ures == NULL)
+ return (NULL);
+ }
+
+ sres = ures->zsu_system;
+ sold = uold->zsu_system;
+ snew = unew->zsu_system;
+
+ switch (func) {
+ case ZS_COMPUTE_USAGE_INTERVAL:
+ /* Use system totals from newer interval */
+ if (sres != snew)
+ *sres = *snew;
+
+ TIMESTRUC_DELTA(sres->zss_cpu_total_time,
+ snew->zss_cpu_total_time, sold->zss_cpu_total_time);
+ TIMESTRUC_DELTA(sres->zss_cpu_usage_kern,
+ snew->zss_cpu_usage_kern, sold->zss_cpu_usage_kern);
+ TIMESTRUC_DELTA(sres->zss_cpu_usage_zones,
+ snew->zss_cpu_usage_zones, sold->zss_cpu_usage_zones);
+ break;
+ case ZS_COMPUTE_USAGE_HIGH:
+
+ /* Find max cpus */
+ sres->zss_ncpus = MAX(sold->zss_ncpus, snew->zss_ncpus);
+ sres->zss_ncpus_online = MAX(sold->zss_ncpus_online,
+ snew->zss_ncpus_online);
+
+ /* Find max cpu times */
+ sres->zss_cpu_total_time = ZS_MAXTS(sold->zss_cpu_total_time,
+ snew->zss_cpu_total_time);
+ sres->zss_cpu_usage_kern = ZS_MAXTS(sold->zss_cpu_usage_kern,
+ snew->zss_cpu_usage_kern);
+ sres->zss_cpu_usage_zones = ZS_MAXTS(sold->zss_cpu_usage_zones,
+ snew->zss_cpu_usage_zones);
+
+ /* These don't change */
+ sres->zss_processes_max = snew->zss_processes_max;
+ sres->zss_lwps_max = snew->zss_lwps_max;
+ sres->zss_shm_max = snew->zss_shm_max;
+ sres->zss_shmids_max = snew->zss_shmids_max;
+ sres->zss_semids_max = snew->zss_semids_max;
+ sres->zss_msgids_max = snew->zss_msgids_max;
+ sres->zss_lofi_max = snew->zss_lofi_max;
+ /*
+ * Add in memory values and limits. Scale memory to
+ * avoid overflow.
+ */
+ sres->zss_ram_total = MAX(sold->zss_ram_total,
+ snew->zss_ram_total);
+ sres->zss_ram_kern = MAX(sold->zss_ram_kern,
+ snew->zss_ram_kern);
+ sres->zss_ram_zones = MAX(sold->zss_ram_zones,
+ snew->zss_ram_zones);
+ sres->zss_locked_kern = MAX(sold->zss_locked_kern,
+ snew->zss_locked_kern);
+ sres->zss_locked_zones = MAX(sold->zss_locked_zones,
+ snew->zss_locked_zones);
+ sres->zss_vm_total = MAX(sold->zss_vm_total,
+ snew->zss_vm_total);
+ sres->zss_vm_kern = MAX(sold->zss_vm_kern,
+ snew->zss_vm_kern);
+ sres->zss_vm_zones = MAX(sold->zss_vm_zones,
+ snew->zss_vm_zones);
+ sres->zss_swap_total = MAX(sold->zss_swap_total,
+ snew->zss_swap_total);
+ sres->zss_swap_used = MAX(sold->zss_swap_used,
+ snew->zss_swap_used);
+
+ sres->zss_processes = MAX(sold->zss_processes,
+ snew->zss_processes);
+ sres->zss_lwps = MAX(sold->zss_lwps, snew->zss_lwps);
+ sres->zss_shm = MAX(sold->zss_shm, snew->zss_shm);
+ sres->zss_shmids = MAX(sold->zss_shmids, snew->zss_shmids);
+ sres->zss_semids = MAX(sold->zss_semids, snew->zss_semids);
+ sres->zss_msgids = MAX(sold->zss_msgids, snew->zss_msgids);
+ sres->zss_lofi = MAX(sold->zss_msgids, snew->zss_lofi);
+ break;
+ case ZS_COMPUTE_USAGE_TOTAL:
+ /* FALLTHROUGH */
+ case ZS_COMPUTE_USAGE_AVERAGE:
+ ures->zsu_intervals++;
+
+ /*
+ * Add cpus. The total report will divide this by the
+ * number of intervals to give the average number of cpus
+ * over all intervals.
+ */
+ sres->zss_ncpus = sold->zss_ncpus + snew->zss_ncpus;
+ sres->zss_ncpus_online = sold->zss_ncpus_online +
+ snew->zss_ncpus_online;
+
+ /* Add in cpu times */
+ sres->zss_cpu_total_time = sold->zss_cpu_total_time;
+ TIMESTRUC_ADD_TIMESTRUC(sres->zss_cpu_total_time,
+ snew->zss_cpu_total_time);
+ sres->zss_cpu_usage_kern = sold->zss_cpu_usage_kern;
+ TIMESTRUC_ADD_TIMESTRUC(sres->zss_cpu_usage_kern,
+ snew->zss_cpu_usage_kern);
+ sres->zss_cpu_usage_zones = sold->zss_cpu_usage_zones;
+ TIMESTRUC_ADD_TIMESTRUC(sres->zss_cpu_usage_zones,
+ snew->zss_cpu_usage_zones);
+
+ /* These don't change */
+ sres->zss_processes_max = snew->zss_processes_max;
+ sres->zss_lwps_max = snew->zss_lwps_max;
+ sres->zss_shm_max = snew->zss_shm_max;
+ sres->zss_shmids_max = snew->zss_shmids_max;
+ sres->zss_semids_max = snew->zss_semids_max;
+ sres->zss_msgids_max = snew->zss_msgids_max;
+ sres->zss_lofi_max = snew->zss_lofi_max;
+ /*
+ * Add in memory values and limits. Scale memory to
+ * avoid overflow.
+ */
+ if (sres != sold) {
+ sres->zss_ram_total = sold->zss_ram_total / 1024;
+ sres->zss_ram_kern = sold->zss_ram_kern / 1024;
+ sres->zss_ram_zones = sold->zss_ram_zones / 1024;
+ sres->zss_locked_kern = sold->zss_locked_kern / 1024;
+ sres->zss_locked_zones = sold->zss_locked_zones / 1024;
+ sres->zss_vm_total = sold->zss_vm_total / 1024;
+ sres->zss_vm_kern = sold->zss_vm_kern / 1024;
+ sres->zss_vm_zones = sold->zss_vm_zones / 1024;
+ sres->zss_swap_total = sold->zss_swap_total / 1024;
+ sres->zss_swap_used = sold->zss_swap_used / 1024;
+
+ sres->zss_processes = sold->zss_processes;
+ sres->zss_lwps = sold->zss_lwps;
+ sres->zss_shm = sold->zss_shm / 1024;
+ sres->zss_shmids = sold->zss_shmids;
+ sres->zss_semids = sold->zss_semids;
+ sres->zss_msgids = sold->zss_msgids;
+ sres->zss_lofi = sold->zss_lofi;
+ }
+ /* Add in new values. */
+ sres->zss_ram_total += (snew->zss_ram_total / 1024);
+ sres->zss_ram_kern += (snew->zss_ram_kern / 1024);
+ sres->zss_ram_zones += (snew->zss_ram_zones / 1024);
+ sres->zss_locked_kern += (snew->zss_locked_kern / 1024);
+ sres->zss_locked_zones += (snew->zss_locked_zones / 1024);
+ sres->zss_vm_total += (snew->zss_vm_total / 1024);
+ sres->zss_vm_kern += (snew->zss_vm_kern / 1024);
+ sres->zss_vm_zones += (snew->zss_vm_zones / 1024);
+ sres->zss_swap_total += (snew->zss_swap_total / 1024);
+ sres->zss_swap_used += (snew->zss_swap_used / 1024);
+ sres->zss_processes += snew->zss_processes;
+ sres->zss_lwps += snew->zss_lwps;
+ sres->zss_shm += (snew->zss_shm / 1024);
+ sres->zss_shmids += snew->zss_shmids;
+ sres->zss_semids += snew->zss_semids;
+ sres->zss_msgids += snew->zss_msgids;
+ sres->zss_lofi += snew->zss_lofi;
+ break;
+ default:
+ if (alloced)
+ zs_usage_free(ures);
+ assert(0);
+ }
+ if (zs_usage_compute_zones(ures, uold, unew, func) != 0)
+ goto err;
+
+ if (zs_usage_compute_psets(ures, uold, unew, func) != 0)
+ goto err;
+
+ return (ures);
+err:
+ if (alloced)
+ zs_usage_free(ures);
+ return (NULL);
+}
+
+void
+zs_usage_free(zs_usage_t *usage)
+{
+ zs_zone_t *zone, *ztmp;
+ zs_pset_t *pset, *ptmp;
+ zs_pset_zone_t *pz, *pztmp;
+
+ if (usage->zsu_mmap) {
+ (void) munmap((void *)usage, usage->zsu_size);
+ return;
+ }
+ free(usage->zsu_system);
+ zone = list_head(&usage->zsu_zone_list);
+ while (zone != NULL) {
+ ztmp = zone;
+ zone = list_next(&usage->zsu_zone_list, zone);
+ free(ztmp);
+ }
+ pset = list_head(&usage->zsu_pset_list);
+ while (pset != NULL) {
+ pz = list_head(&pset->zsp_usage_list);
+ while (pz != NULL) {
+ pztmp = pz;
+ pz = list_next(&pset->zsp_usage_list, pz);
+ free(pztmp);
+ }
+ ptmp = pset;
+ pset = list_next(&usage->zsu_pset_list, pset);
+ free(ptmp);
+ }
+ free(usage);
+}
+
+zs_usage_set_t *
+zs_usage_set_alloc()
+{
+ zs_usage_set_t *set;
+
+ set = calloc(sizeof (zs_usage_set_t), 1);
+ if (set == NULL)
+ return (NULL);
+
+ if ((set->zsus_total = zs_usage_alloc()) == NULL)
+ goto err;
+ if ((set->zsus_avg = zs_usage_alloc()) == NULL)
+ goto err;
+ if ((set->zsus_high = zs_usage_alloc()) == NULL)
+ goto err;
+
+ return (set);
+
+err:
+ if (set->zsus_total != NULL)
+ free(set->zsus_total);
+ if (set->zsus_avg != NULL)
+ free(set->zsus_avg);
+ if (set->zsus_high != NULL)
+ free(set->zsus_high);
+
+ return (NULL);
+}
+
+void
+zs_usage_set_free(zs_usage_set_t *set)
+{
+ zs_usage_free(set->zsus_total);
+ zs_usage_free(set->zsus_avg);
+ zs_usage_free(set->zsus_high);
+ free(set);
+}
+
+int
+zs_usage_set_add(zs_usage_set_t *set, zs_usage_t *usage)
+{
+
+ /* Compute ongoing functions for usage set */
+ (void) zs_usage_compute(set->zsus_high, set->zsus_high, usage,
+ ZS_COMPUTE_USAGE_HIGH);
+
+ (void) zs_usage_compute(set->zsus_total, set->zsus_total, usage,
+ ZS_COMPUTE_USAGE_TOTAL);
+
+ (void) zs_usage_compute(set->zsus_avg, set->zsus_avg, usage,
+ ZS_COMPUTE_USAGE_AVERAGE);
+
+ set->zsus_count++;
+ zs_usage_free(usage);
+ return (0);
+}
+
+int
+zs_usage_set_count(zs_usage_set_t *set)
+{
+ return (set->zsus_count);
+}
+
+zs_usage_t *
+zs_usage_set_compute(zs_usage_set_t *set, int func)
+{
+ zs_usage_t *u;
+ zs_system_t *s;
+ zs_zone_t *z;
+ zs_pset_t *p;
+ zs_pset_zone_t *pz;
+ uint_t intervals;
+ boolean_t average;
+
+ switch (func) {
+ case ZS_COMPUTE_SET_HIGH:
+ return (set->zsus_high);
+ case ZS_COMPUTE_SET_TOTAL:
+ u = set->zsus_total;
+ average = B_FALSE;
+ break;
+ case ZS_COMPUTE_SET_AVERAGE:
+ u = set->zsus_avg;
+ average = B_TRUE;
+ break;
+ default:
+ assert(0);
+ }
+
+ s = u->zsu_system;
+
+ s->zss_ram_total /= u->zsu_intervals;
+ s->zss_ram_total *= 1024;
+ s->zss_ram_kern /= u->zsu_intervals;
+ s->zss_ram_kern *= 1024;
+ s->zss_ram_zones /= u->zsu_intervals;
+ s->zss_ram_zones *= 1024;
+ s->zss_locked_kern /= u->zsu_intervals;
+ s->zss_locked_kern *= 1024;
+ s->zss_locked_zones /= u->zsu_intervals;
+ s->zss_locked_zones *= 1024;
+ s->zss_vm_total /= u->zsu_intervals;
+ s->zss_vm_total *= 1024;
+ s->zss_vm_kern /= u->zsu_intervals;
+ s->zss_vm_kern *= 1024;
+ s->zss_vm_zones /= u->zsu_intervals;
+ s->zss_vm_zones *= 1024;
+ s->zss_swap_total /= u->zsu_intervals;
+ s->zss_swap_total *= 1024;
+ s->zss_swap_used /= u->zsu_intervals;
+ s->zss_swap_used *= 1024;
+ s->zss_processes /= u->zsu_intervals;
+ s->zss_lwps /= u->zsu_intervals;
+ s->zss_shm /= u->zsu_intervals;
+ s->zss_shm *= 1024;
+ s->zss_shmids /= u->zsu_intervals;
+ s->zss_semids /= u->zsu_intervals;
+ s->zss_msgids /= u->zsu_intervals;
+ s->zss_lofi /= u->zsu_intervals;
+
+ s->zss_ncpus /= u->zsu_intervals;
+ s->zss_ncpus_online /= u->zsu_intervals;
+
+ for (z = list_head(&u->zsu_zone_list); z != NULL;
+ z = list_next(&u->zsu_zone_list, z)) {
+
+ if (average) {
+ intervals = z->zsz_intervals;
+ } else {
+ assert(z->zsz_intervals == 0);
+ intervals = u->zsu_intervals;
+ }
+
+ if (z->zsz_cpu_cap != ZS_LIMIT_NONE)
+ z->zsz_cpu_cap /= z->zsz_intervals;
+ if (z->zsz_ram_cap != ZS_LIMIT_NONE)
+ z->zsz_ram_cap /= z->zsz_intervals;
+ if (z->zsz_vm_cap != ZS_LIMIT_NONE)
+ z->zsz_vm_cap /= z->zsz_intervals;
+ if (z->zsz_locked_cap != ZS_LIMIT_NONE)
+ z->zsz_locked_cap /= z->zsz_intervals;
+ if (z->zsz_processes_cap != ZS_LIMIT_NONE)
+ z->zsz_processes_cap /= z->zsz_intervals;
+ if (z->zsz_lwps_cap != ZS_LIMIT_NONE)
+ z->zsz_lwps_cap /= z->zsz_intervals;
+ if (z->zsz_shm_cap != ZS_LIMIT_NONE)
+ z->zsz_shm_cap /= z->zsz_intervals;
+ if (z->zsz_shmids_cap != ZS_LIMIT_NONE)
+ z->zsz_shmids_cap /= z->zsz_intervals;
+ if (z->zsz_semids_cap != ZS_LIMIT_NONE)
+ z->zsz_semids_cap /= z->zsz_intervals;
+ if (z->zsz_msgids_cap != ZS_LIMIT_NONE)
+ z->zsz_msgids_cap /= z->zsz_intervals;
+ if (z->zsz_lofi_cap != ZS_LIMIT_NONE)
+ z->zsz_lofi_cap /= z->zsz_intervals;
+
+ z->zsz_usage_ram /= intervals;
+ z->zsz_usage_locked /= intervals;
+ z->zsz_usage_vm /= intervals;
+ z->zsz_processes /= intervals;
+ z->zsz_lwps /= intervals;
+ z->zsz_shm /= intervals;
+ z->zsz_shmids /= intervals;
+ z->zsz_semids /= intervals;
+ z->zsz_msgids /= intervals;
+ z->zsz_lofi /= intervals;
+ z->zsz_cpus_online /= intervals;
+ z->zsz_cpu_shares /= intervals;
+ }
+ for (p = list_head(&u->zsu_pset_list); p != NULL;
+ p = list_next(&u->zsu_pset_list, p)) {
+
+ intervals = p->zsp_intervals;
+
+ p->zsp_online /= intervals;
+ p->zsp_size /= intervals;
+ p->zsp_min /= intervals;
+ p->zsp_max /= intervals;
+ p->zsp_importance /= intervals;
+ p->zsp_cpu_shares /= intervals;
+
+ for (pz = list_head(&p->zsp_usage_list); pz != NULL;
+ pz = list_next(&p->zsp_usage_list, pz)) {
+
+ if (average) {
+ intervals = pz->zspz_intervals;
+ } else {
+ assert(pz->zspz_intervals == 0);
+ intervals = p->zsp_intervals;
+ }
+ pz->zspz_cpu_shares /= intervals;
+ }
+ }
+ return (u);
+}
+
+/*
+ * Returns 0 on success. Trips assert on invalid property.
+ */
+void
+zs_resource_property(zs_usage_t *u, int res, int prop, zs_property_t *p)
+{
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ switch (prop) {
+ case ZS_RESOURCE_PROP_CPU_TOTAL:
+ p->zsp_id = prop;
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_v.zsv_uint64 = u->zsu_system->zss_ncpus;
+ break;
+ case ZS_RESOURCE_PROP_CPU_ONLINE:
+ p->zsp_id = prop;
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_v.zsv_uint64 = u->zsu_system->zss_ncpus_online;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ case ZS_RESOURCE_RAM_LOCKED:
+ case ZS_RESOURCE_VM:
+ case ZS_RESOURCE_DISK_SWAP:
+ case ZS_RESOURCE_LWPS:
+ case ZS_RESOURCE_PROCESSES:
+ case ZS_RESOURCE_SHM_MEMORY:
+ case ZS_RESOURCE_SHM_IDS:
+ case ZS_RESOURCE_SEM_IDS:
+ case ZS_RESOURCE_MSG_IDS:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * Returns one of ZS_RESOURCE_TYPE_* on success. Asserts on invalid
+ * resource.
+ */
+int
+zs_resource_type(int res)
+{
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ return (ZS_RESOURCE_TYPE_TIME);
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ case ZS_RESOURCE_RAM_LOCKED:
+ case ZS_RESOURCE_VM:
+ case ZS_RESOURCE_DISK_SWAP:
+ case ZS_RESOURCE_SHM_MEMORY:
+ return (ZS_RESOURCE_TYPE_BYTES);
+ break;
+ case ZS_RESOURCE_LWPS:
+ case ZS_RESOURCE_PROCESSES:
+ case ZS_RESOURCE_SHM_IDS:
+ case ZS_RESOURCE_SEM_IDS:
+ case ZS_RESOURCE_MSG_IDS:
+ return (ZS_RESOURCE_TYPE_COUNT);
+ break;
+ default:
+ assert(0);
+ return (0);
+ }
+}
+
+/*
+ * Get total available resource on system
+ */
+uint64_t
+zs_resource_total_uint64(zs_usage_t *u, int res)
+{
+ uint64_t v;
+
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ v = zs_cpu_total_cpu(u);
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ v = zs_physical_memory_total(u);
+ break;
+ case ZS_RESOURCE_RAM_LOCKED:
+ v = zs_locked_memory_total(u);
+ break;
+ case ZS_RESOURCE_VM:
+ v = zs_virtual_memory_total(u);
+ break;
+ case ZS_RESOURCE_DISK_SWAP:
+ v = zs_disk_swap_total(u);
+ break;
+ case ZS_RESOURCE_LWPS:
+ v = zs_lwps_total(u);
+ break;
+ case ZS_RESOURCE_PROCESSES:
+ v = zs_processes_total(u);
+ break;
+ case ZS_RESOURCE_SHM_MEMORY:
+ v = zs_shm_total(u);
+ break;
+ case ZS_RESOURCE_SHM_IDS:
+ v = zs_shmids_total(u);
+ break;
+ case ZS_RESOURCE_SEM_IDS:
+ v = zs_semids_total(u);
+ break;
+ case ZS_RESOURCE_MSG_IDS:
+ v = zs_msgids_total(u);
+ break;
+ case ZS_RESOURCE_LOFI:
+ v = zs_lofi_total(u);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * Get amount of used resource.
+ */
+uint64_t
+zs_resource_used_uint64(zs_usage_t *u, int res, int user)
+{
+ uint64_t v;
+
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_cpu_usage_all_cpu(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_cpu_usage_kernel_cpu(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_cpu_usage_zones_cpu(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_cpu_usage_idle_cpu(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_physical_memory_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_physical_memory_usage_kernel(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_physical_memory_usage_zones(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_physical_memory_usage_free(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_RAM_LOCKED:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_locked_memory_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_locked_memory_usage_kernel(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_locked_memory_usage_zones(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_locked_memory_usage_free(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_VM:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_virtual_memory_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_virtual_memory_usage_kernel(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_virtual_memory_usage_zones(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_virtual_memory_usage_free(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_DISK_SWAP:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_disk_swap_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_disk_swap_usage_free(u);
+ break;
+ case ZS_USER_KERNEL:
+ case ZS_USER_ZONES:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_LWPS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_lwps_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_lwps_total(u) - zs_lwps_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_PROCESSES:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_processes_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_processes_total(u) - zs_processes_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_SHM_MEMORY:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_shm_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_shm_total(u) -
+ zs_shm_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_SHM_IDS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_shmids_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_shmids_total(u) - zs_shmids_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_SEM_IDS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_semids_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_semids_total(u) - zs_semids_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_MSG_IDS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_msgids_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_msgids_total(u) - zs_msgids_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_LOFI:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_lofi_usage_all(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_lofi_total(u) - zs_lofi_usage_all(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * Get used resource as a percent of total resource.
+ */
+uint_t
+zs_resource_used_pct(zs_usage_t *u, int res, int user)
+{
+ uint64_t v;
+
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_cpu_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_cpu_usage_kernel_pct(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_cpu_usage_zones_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_cpu_usage_idle_pct(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_physical_memory_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_physical_memory_usage_kernel_pct(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_physical_memory_usage_zones_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_physical_memory_usage_free_pct(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_RAM_LOCKED:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_locked_memory_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_locked_memory_usage_kernel_pct(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_locked_memory_usage_zones_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_locked_memory_usage_free_pct(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_VM:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_virtual_memory_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_virtual_memory_usage_kernel_pct(u);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_virtual_memory_usage_zones_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_virtual_memory_usage_free_pct(u);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_DISK_SWAP:
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_disk_swap_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = zs_disk_swap_usage_free_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ case ZS_USER_ZONES:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_LWPS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_lwps_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_lwps_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_PROCESSES:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_processes_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_processes_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_SHM_MEMORY:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_shm_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_shm_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_SHM_IDS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_shmids_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_shmids_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_SEM_IDS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_semids_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_semids_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_MSG_IDS:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_msgids_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_msgids_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_LOFI:
+ switch (user) {
+ case ZS_USER_ALL:
+ case ZS_USER_ZONES:
+ v = zs_lofi_usage_all_pct(u);
+ break;
+ case ZS_USER_FREE:
+ v = ZSD_PCT_INT - zs_lofi_usage_all_pct(u);
+ break;
+ case ZS_USER_KERNEL:
+ v = 0;
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ return (v);
+}
+
+/*
+ * Get resource used by individual zone.
+ */
+uint64_t
+zs_resource_used_zone_uint64(zs_zone_t *z, int res)
+{
+ uint64_t v;
+
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ v = zs_cpu_usage_zone_cpu(z);
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ v = zs_physical_memory_usage_zone(z);
+ break;
+ case ZS_RESOURCE_RAM_LOCKED:
+ v = zs_locked_memory_usage_zone(z);
+ break;
+ case ZS_RESOURCE_VM:
+ v = zs_virtual_memory_usage_zone(z);
+ break;
+ case ZS_RESOURCE_DISK_SWAP:
+ assert(0);
+ break;
+ case ZS_RESOURCE_LWPS:
+ v = zs_lwps_usage_zone(z);
+ break;
+ case ZS_RESOURCE_PROCESSES:
+ v = zs_processes_usage_zone(z);
+ break;
+ case ZS_RESOURCE_SHM_MEMORY:
+ v = zs_shm_usage_zone(z);
+ break;
+ case ZS_RESOURCE_SHM_IDS:
+ v = zs_shmids_usage_zone(z);
+ break;
+ case ZS_RESOURCE_SEM_IDS:
+ v = zs_semids_usage_zone(z);
+ break;
+ case ZS_RESOURCE_MSG_IDS:
+ v = zs_msgids_usage_zone(z);
+ break;
+ case ZS_RESOURCE_LOFI:
+ v = zs_lofi_usage_zone(z);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * Get resource used by individual zone as percent
+ */
+uint_t
+zs_resource_used_zone_pct(zs_zone_t *z, int res)
+{
+ uint_t v;
+
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ v = zs_cpu_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ v = zs_physical_memory_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_RAM_LOCKED:
+ v = zs_locked_memory_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_VM:
+ v = zs_virtual_memory_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_DISK_SWAP:
+ assert(0);
+ break;
+ case ZS_RESOURCE_LWPS:
+ v = zs_lwps_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_PROCESSES:
+ v = zs_processes_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_SHM_MEMORY:
+ v = zs_shm_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_SHM_IDS:
+ v = zs_shmids_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_SEM_IDS:
+ v = zs_semids_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_MSG_IDS:
+ v = zs_msgids_usage_zone_pct(z);
+ break;
+ case ZS_RESOURCE_LOFI:
+ v = zs_lofi_usage_zone_pct(z);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * Get total time available for a resource
+ */
+void
+zs_resource_total_time(zs_usage_t *u, int res, timestruc_t *t)
+{
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ zs_cpu_total_time(u, t);
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ case ZS_RESOURCE_RAM_LOCKED:
+ case ZS_RESOURCE_VM:
+ case ZS_RESOURCE_DISK_SWAP:
+ case ZS_RESOURCE_LWPS:
+ case ZS_RESOURCE_PROCESSES:
+ case ZS_RESOURCE_SHM_MEMORY:
+ case ZS_RESOURCE_SHM_IDS:
+ case ZS_RESOURCE_SEM_IDS:
+ case ZS_RESOURCE_MSG_IDS:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * Get total time used for a resource
+ */
+void
+zs_resource_used_time(zs_usage_t *u, int res, int user, timestruc_t *t)
+{
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ switch (user) {
+ case ZS_USER_ALL:
+ zs_cpu_usage_all(u, t);
+ break;
+ case ZS_USER_KERNEL:
+ zs_cpu_usage_kernel(u, t);
+ break;
+ case ZS_USER_ZONES:
+ zs_cpu_usage_zones(u, t);
+ break;
+ case ZS_USER_FREE:
+ zs_cpu_usage_idle(u, t);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ case ZS_RESOURCE_RAM_LOCKED:
+ case ZS_RESOURCE_VM:
+ case ZS_RESOURCE_DISK_SWAP:
+ case ZS_RESOURCE_LWPS:
+ case ZS_RESOURCE_PROCESSES:
+ case ZS_RESOURCE_SHM_MEMORY:
+ case ZS_RESOURCE_SHM_IDS:
+ case ZS_RESOURCE_SEM_IDS:
+ case ZS_RESOURCE_MSG_IDS:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * Get total resource time used for a particular zone
+ */
+void
+zs_resource_used_zone_time(zs_zone_t *z, int res, timestruc_t *t)
+{
+ switch (res) {
+ case ZS_RESOURCE_CPU:
+ zs_cpu_usage_zone(z, t);
+ break;
+ case ZS_RESOURCE_RAM_RSS:
+ case ZS_RESOURCE_RAM_LOCKED:
+ case ZS_RESOURCE_VM:
+ case ZS_RESOURCE_DISK_SWAP:
+ case ZS_RESOURCE_SHM_MEMORY:
+ case ZS_RESOURCE_LWPS:
+ case ZS_RESOURCE_PROCESSES:
+ case ZS_RESOURCE_SHM_IDS:
+ case ZS_RESOURCE_SEM_IDS:
+ case ZS_RESOURCE_MSG_IDS:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+
+int
+zs_zone_list(zs_usage_t *usage, zs_zone_t **zonelist, int num)
+{
+ int i = 0;
+ zs_zone_t *zone, *tmp;
+
+ /* copy what fits of the zone list into the buffer */
+ for (zone = list_head(&usage->zsu_zone_list); zone != NULL;
+ zone = list_next(&usage->zsu_zone_list, zone)) {
+
+ /* put the global zone at the first position */
+ if (i < num) {
+ if (zone->zsz_id == GLOBAL_ZONEID) {
+ tmp = zonelist[0];
+ zonelist[i] = tmp;
+ zonelist[0] = zone;
+ } else {
+ zonelist[i] = zone;
+ }
+ }
+ i++;
+ }
+ return (i);
+}
+
+zs_zone_t *
+zs_zone_first(zs_usage_t *usage)
+{
+ return (list_head(&usage->zsu_zone_list));
+}
+
+zs_zone_t *
+zs_zone_next(zs_usage_t *usage, zs_zone_t *zone)
+{
+ return (list_next(&usage->zsu_zone_list, zone));
+}
+
+
+/*
+ * Gets a zone property
+ */
+void
+zs_zone_property(zs_zone_t *zone, int prop, zs_property_t *p)
+{
+ switch (prop) {
+ case ZS_ZONE_PROP_NAME:
+ p->zsp_type = ZS_PROP_TYPE_STRING;
+ p->zsp_id = prop;
+ (void) zs_zone_name(zone, p->zsp_v.zsv_string,
+ sizeof (p->zsp_v.zsv_string));
+ break;
+ case ZS_ZONE_PROP_ID:
+ p->zsp_type = ZS_PROP_TYPE_INT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_int = zs_zone_id(zone);
+ break;
+ case ZS_ZONE_PROP_IPTYPE:
+ p->zsp_type = ZS_PROP_TYPE_UINT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint = zs_zone_iptype(zone);
+ break;
+ case ZS_ZONE_PROP_CPUTYPE:
+ p->zsp_type = ZS_PROP_TYPE_UINT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint = zs_zone_cputype(zone);
+ break;
+ case ZS_ZONE_PROP_SCHEDULERS:
+ p->zsp_type = ZS_PROP_TYPE_UINT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint = zs_zone_schedulers(zone);
+ break;
+ case ZS_ZONE_PROP_CPU_SHARES:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = zs_zone_cpu_shares(zone);
+ break;
+ case ZS_ZONE_PROP_POOLNAME:
+ p->zsp_type = ZS_PROP_TYPE_STRING;
+ p->zsp_id = prop;
+ (void) zs_zone_poolname(zone, p->zsp_v.zsv_string,
+ sizeof (p->zsp_v.zsv_string));
+ break;
+ case ZS_ZONE_PROP_PSETNAME:
+ p->zsp_type = ZS_PROP_TYPE_STRING;
+ p->zsp_id = prop;
+ (void) zs_zone_psetname(zone, p->zsp_v.zsv_string,
+ sizeof (p->zsp_v.zsv_string));
+ break;
+ /* Not implemented */
+ case ZS_ZONE_PROP_DEFAULT_SCHED:
+ case ZS_ZONE_PROP_UPTIME:
+ case ZS_ZONE_PROP_BOOTTIME:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+int
+zs_zone_limit_type(int limit)
+{
+ switch (limit) {
+ case ZS_LIMIT_CPU:
+ case ZS_LIMIT_CPU_SHARES:
+ return (ZS_LIMIT_TYPE_TIME);
+ case ZS_LIMIT_RAM_RSS:
+ case ZS_LIMIT_RAM_LOCKED:
+ case ZS_LIMIT_VM:
+ case ZS_LIMIT_SHM_MEMORY:
+ return (ZS_LIMIT_TYPE_BYTES);
+ case ZS_LIMIT_LWPS:
+ case ZS_LIMIT_PROCESSES:
+ case ZS_LIMIT_SHM_IDS:
+ case ZS_LIMIT_MSG_IDS:
+ case ZS_LIMIT_SEM_IDS:
+ return (ZS_LIMIT_TYPE_COUNT);
+ default:
+ assert(0);
+ return (0);
+ }
+}
+/*
+ * Gets the zones limit. Returns ZS_LIMIT_NONE if no limit set.
+ */
+uint64_t
+zs_zone_limit_uint64(zs_zone_t *z, int limit)
+{
+ uint64_t v;
+
+ switch (limit) {
+ case ZS_LIMIT_CPU:
+ v = zs_zone_cpu_cap(z);
+ break;
+ case ZS_LIMIT_CPU_SHARES:
+ v = zs_zone_cpu_shares(z);
+ break;
+ case ZS_LIMIT_RAM_RSS:
+ v = zs_zone_physical_memory_cap(z);
+ break;
+ case ZS_LIMIT_RAM_LOCKED:
+ v = zs_zone_locked_memory_cap(z);
+ break;
+ case ZS_LIMIT_VM:
+ v = zs_zone_virtual_memory_cap(z);
+ break;
+ case ZS_LIMIT_LWPS:
+ v = z->zsz_lwps_cap;
+ break;
+ case ZS_LIMIT_PROCESSES:
+ v = z->zsz_processes_cap;
+ break;
+ case ZS_LIMIT_SHM_MEMORY:
+ v = z->zsz_shm_cap;
+ break;
+ case ZS_LIMIT_SHM_IDS:
+ v = z->zsz_shmids_cap;
+ break;
+ case ZS_LIMIT_SEM_IDS:
+ v = z->zsz_semids_cap;
+ break;
+ case ZS_LIMIT_MSG_IDS:
+ v = z->zsz_msgids_cap;
+ break;
+ case ZS_LIMIT_LOFI:
+ v = z->zsz_lofi_cap;
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * Gets the amount of resource used for a limit. Returns ZS_LIMIT_NONE if
+ * no limit configured.
+ */
+uint64_t
+zs_zone_limit_used_uint64(zs_zone_t *z, int limit)
+{
+ uint64_t v;
+
+ switch (limit) {
+ case ZS_LIMIT_CPU:
+ v = zs_zone_cpu_cap_used(z);
+ break;
+ case ZS_LIMIT_CPU_SHARES:
+ v = zs_zone_cpu_shares_used(z);
+ break;
+ case ZS_LIMIT_RAM_RSS:
+ v = zs_zone_physical_memory_cap_used(z);
+ break;
+ case ZS_LIMIT_RAM_LOCKED:
+ v = zs_zone_locked_memory_cap_used(z);
+ break;
+ case ZS_LIMIT_VM:
+ v = zs_zone_virtual_memory_cap_used(z);
+ break;
+ case ZS_LIMIT_LWPS:
+ v = z->zsz_lwps;
+ break;
+ case ZS_LIMIT_PROCESSES:
+ v = z->zsz_processes;
+ break;
+ case ZS_LIMIT_SHM_MEMORY:
+ v = z->zsz_shm;
+ break;
+ case ZS_LIMIT_SHM_IDS:
+ v = z->zsz_shmids;
+ break;
+ case ZS_LIMIT_SEM_IDS:
+ v = z->zsz_semids;
+ break;
+ case ZS_LIMIT_MSG_IDS:
+ v = z->zsz_msgids;
+ break;
+ case ZS_LIMIT_LOFI:
+ v = z->zsz_lofi;
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * Gets time used under limit. Time is zero if no limit is configured
+ */
+void
+zs_zone_limit_time(zs_zone_t *z, int limit, timestruc_t *v)
+{
+ switch (limit) {
+ case ZS_LIMIT_CPU:
+ if (z->zsz_cpu_cap == ZS_LIMIT_NONE) {
+ v->tv_sec = 0;
+ v->tv_nsec = 0;
+ break;
+ }
+ zs_zone_cpu_cap_time(z, v);
+ break;
+ case ZS_LIMIT_CPU_SHARES:
+ if (z->zsz_cpu_shares == ZS_LIMIT_NONE ||
+ z->zsz_cpu_shares == ZS_SHARES_UNLIMITED ||
+ z->zsz_cpu_shares == 0 ||
+ (z->zsz_scheds & ZS_SCHED_FSS) == 0) {
+ v->tv_sec = 0;
+ v->tv_nsec = 0;
+ break;
+ }
+ zs_zone_cpu_share_time(z, v);
+ break;
+ case ZS_LIMIT_RAM_RSS:
+ case ZS_LIMIT_RAM_LOCKED:
+ case ZS_LIMIT_VM:
+ case ZS_LIMIT_SHM_MEMORY:
+ case ZS_LIMIT_LWPS:
+ case ZS_LIMIT_PROCESSES:
+ case ZS_LIMIT_SHM_IDS:
+ case ZS_LIMIT_MSG_IDS:
+ case ZS_LIMIT_SEM_IDS:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * Errno is set on error:
+ *
+ * EINVAL: No such property
+ * ENOENT: No time value for the specified limit.
+ * ESRCH: No limit is configured.
+ *
+ * If no limit is configured, the value will be ZS_PCT_NONE
+ */
+void
+zs_zone_limit_used_time(zs_zone_t *z, int limit, timestruc_t *t)
+{
+ switch (limit) {
+ case ZS_LIMIT_CPU:
+ if (z->zsz_cpu_cap == ZS_LIMIT_NONE) {
+ t->tv_sec = 0;
+ t->tv_nsec = 0;
+ break;
+ }
+ zs_zone_cpu_cap_time_used(z, t);
+ break;
+ case ZS_LIMIT_CPU_SHARES:
+ if (z->zsz_cpu_shares == ZS_LIMIT_NONE ||
+ z->zsz_cpu_shares == ZS_SHARES_UNLIMITED ||
+ z->zsz_cpu_shares == 0 ||
+ (z->zsz_scheds & ZS_SCHED_FSS) == 0) {
+ t->tv_sec = 0;
+ t->tv_nsec = 0;
+ break;
+ }
+ zs_zone_cpu_share_time_used(z, t);
+ break;
+ case ZS_LIMIT_RAM_RSS:
+ case ZS_LIMIT_RAM_LOCKED:
+ case ZS_LIMIT_VM:
+ case ZS_LIMIT_SHM_MEMORY:
+ case ZS_LIMIT_LWPS:
+ case ZS_LIMIT_PROCESSES:
+ case ZS_LIMIT_SHM_IDS:
+ case ZS_LIMIT_MSG_IDS:
+ case ZS_LIMIT_SEM_IDS:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * Get a zones usage as a percent of the limit. Return ZS_PCT_NONE if
+ * no limit is configured.
+ */
+uint_t
+zs_zone_limit_used_pct(zs_zone_t *z, int limit)
+{
+ uint_t v;
+
+ switch (limit) {
+ case ZS_LIMIT_CPU:
+ v = zs_zone_cpu_cap_pct(z);
+ break;
+ case ZS_LIMIT_CPU_SHARES:
+ v = zs_zone_cpu_shares_pct(z);
+ break;
+ case ZS_LIMIT_RAM_RSS:
+ v = zs_zone_physical_memory_cap_pct(z);
+ break;
+ case ZS_LIMIT_RAM_LOCKED:
+ v = zs_zone_locked_memory_cap_pct(z);
+ break;
+ case ZS_LIMIT_VM:
+ v = zs_zone_virtual_memory_cap_pct(z);
+ break;
+ case ZS_LIMIT_LWPS:
+ v = zs_lwps_zone_cap_pct(z);
+ break;
+ case ZS_LIMIT_PROCESSES:
+ v = zs_processes_zone_cap_pct(z);
+ break;
+ case ZS_LIMIT_SHM_MEMORY:
+ v = zs_shm_zone_cap_pct(z);
+ break;
+ case ZS_LIMIT_SHM_IDS:
+ v = zs_shmids_zone_cap_pct(z);
+ break;
+ case ZS_LIMIT_SEM_IDS:
+ v = zs_semids_zone_cap_pct(z);
+ break;
+ case ZS_LIMIT_MSG_IDS:
+ v = zs_msgids_zone_cap_pct(z);
+ break;
+ case ZS_LIMIT_LOFI:
+ v = zs_lofi_zone_cap_pct(z);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+int
+zs_pset_list(zs_usage_t *usage, zs_pset_t **psetlist, int num)
+{
+ int i = 0;
+ zs_pset_t *pset, *tmp;
+
+ /* copy what fits of the pset list into the buffer */
+ for (pset = list_head(&usage->zsu_pset_list); pset != NULL;
+ pset = list_next(&usage->zsu_pset_list, pset)) {
+
+ /* put the default pset at the first position */
+ if (i < num) {
+ if (pset->zsp_id == ZS_PSET_DEFAULT) {
+ tmp = psetlist[0];
+ psetlist[i] = tmp;
+ psetlist[0] = pset;
+ } else {
+ psetlist[i] = pset;
+ }
+ }
+ i++;
+ }
+ return (i);
+}
+
+zs_pset_t *
+zs_pset_first(zs_usage_t *usage)
+{
+ return (list_head(&usage->zsu_pset_list));
+}
+
+zs_pset_t *
+zs_pset_next(zs_usage_t *usage, zs_pset_t *pset)
+{
+ return (list_next(&usage->zsu_pset_list, pset));
+}
+
+/*
+ * Get various properties on a pset.
+ */
+void
+zs_pset_property(zs_pset_t *pset, int prop, zs_property_t *p)
+{
+ switch (prop) {
+
+ case ZS_PSET_PROP_NAME:
+ p->zsp_type = ZS_PROP_TYPE_STRING;
+ p->zsp_id = prop;
+ (void) zs_pset_name(pset, p->zsp_v.zsv_string,
+ sizeof (p->zsp_v.zsv_string));
+ break;
+ case ZS_PSET_PROP_ID:
+ p->zsp_type = ZS_PROP_TYPE_INT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_int = zs_pset_id(pset);
+ break;
+ case ZS_PSET_PROP_CPUTYPE:
+ p->zsp_type = ZS_PROP_TYPE_UINT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint = zs_pset_cputype(pset);
+ break;
+ case ZS_PSET_PROP_SIZE:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = zs_pset_size(pset);
+ break;
+ case ZS_PSET_PROP_ONLINE:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = zs_pset_online(pset);
+ break;
+ case ZS_PSET_PROP_MIN:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = zs_pset_min(pset);
+ break;
+ case ZS_PSET_PROP_MAX:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = zs_pset_max(pset);
+ break;
+ case ZS_PSET_PROP_CPU_SHARES:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = zs_pset_cpu_shares(pset);
+ break;
+ case ZS_PSET_PROP_SCHEDULERS:
+ p->zsp_type = ZS_PROP_TYPE_UINT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint = zs_pset_schedulers(pset);
+ break;
+ /* Not implemented */
+ case ZS_PSET_PROP_CREATETIME:
+ case ZS_PSET_PROP_LOAD_1MIN:
+ case ZS_PSET_PROP_LOAD_5MIN:
+ case ZS_PSET_PROP_LOAD_15MIN:
+ /* FALLTHROUGH */
+ default:
+ assert(0);
+ }
+}
+
+void
+zs_pset_total_time(zs_pset_t *pset, timestruc_t *t)
+{
+ *t = pset->zsp_total_time;
+}
+
+uint64_t
+zs_pset_total_cpus(zs_pset_t *pset)
+{
+ return (pset->zsp_online * ZSD_ONE_CPU);
+}
+
+/*
+ * Get total time used for pset
+ */
+void
+zs_pset_used_time(zs_pset_t *pset, int user, timestruc_t *t)
+{
+ switch (user) {
+ case ZS_USER_ALL:
+ zs_pset_usage_all(pset, t);
+ break;
+ case ZS_USER_KERNEL:
+ zs_pset_usage_kernel(pset, t);
+ break;
+ case ZS_USER_ZONES:
+ zs_pset_usage_zones(pset, t);
+ break;
+ case ZS_USER_FREE:
+ zs_pset_usage_idle(pset, t);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * Returns 0 on success. -1 on failure.
+ *
+ * ERRORS
+ * EINVAL: Invalid user.
+ *
+ */
+uint64_t
+zs_pset_used_cpus(zs_pset_t *pset, int user)
+{
+ uint_t v;
+
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_pset_usage_all_cpus(pset);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_pset_usage_kernel_cpus(pset);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_pset_usage_zones_cpus(pset);
+ break;
+ case ZS_USER_FREE:
+ v = zs_pset_usage_idle_cpus(pset);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+/*
+ * Get percent of pset cpu time used
+ */
+uint_t
+zs_pset_used_pct(zs_pset_t *pset, int user)
+{
+ uint_t v;
+
+ switch (user) {
+ case ZS_USER_ALL:
+ v = zs_pset_usage_all_pct(pset);
+ break;
+ case ZS_USER_KERNEL:
+ v = zs_pset_usage_kernel_pct(pset);
+ break;
+ case ZS_USER_ZONES:
+ v = zs_pset_usage_zones_pct(pset);
+ break;
+ case ZS_USER_FREE:
+ v = zs_pset_usage_idle_pct(pset);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+int
+zs_pset_zone_list(zs_pset_t *pset, zs_pset_zone_t **zonelist, int num)
+{
+ int i = 0;
+ zs_pset_zone_t *zone, *tmp;
+
+ /* copy what fits of the pset's zone list into the buffer */
+ for (zone = list_head(&pset->zsp_usage_list); zone != NULL;
+ zone = list_next(&pset->zsp_usage_list, zone)) {
+
+ /* put the global zone at the first position */
+ if (i < num) {
+ if (zone->zspz_zone->zsz_id == GLOBAL_ZONEID) {
+ tmp = zonelist[0];
+ zonelist[i] = tmp;
+ zonelist[0] = zone;
+ } else {
+ zonelist[i] = zone;
+ }
+ }
+ i++;
+ }
+ return (i);
+}
+
+zs_pset_zone_t *
+zs_pset_zone_first(zs_pset_t *pset)
+{
+ return (list_head(&pset->zsp_usage_list));
+}
+
+zs_pset_zone_t *
+zs_pset_zone_next(zs_pset_t *pset, zs_pset_zone_t *pz)
+{
+ return (list_next(&pset->zsp_usage_list, pz));
+}
+
+zs_pset_t *
+zs_pset_zone_get_pset(zs_pset_zone_t *pz)
+{
+ return (pz->zspz_pset);
+}
+
+zs_zone_t *
+zs_pset_zone_get_zone(zs_pset_zone_t *pz)
+{
+ return (pz->zspz_zone);
+}
+
+/*
+ * Get a property describing a zone's usage of a pset
+ */
+void
+zs_pset_zone_property(zs_pset_zone_t *pz, int prop, zs_property_t *p)
+{
+ switch (prop) {
+
+ case ZS_PZ_PROP_CPU_CAP:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = (int)zs_pset_zone_cpu_cap(pz);
+ break;
+ case ZS_PZ_PROP_CPU_SHARES:
+ p->zsp_type = ZS_PROP_TYPE_UINT64;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint64 = (int)zs_pset_zone_cpu_shares(pz);
+ break;
+ case ZS_PZ_PROP_SCHEDULERS:
+ p->zsp_type = ZS_PROP_TYPE_UINT;
+ p->zsp_id = prop;
+ p->zsp_v.zsv_uint = (int)zs_pset_zone_schedulers(pz);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void
+zs_pset_zone_used_time(zs_pset_zone_t *pz, timestruc_t *t)
+{
+ zs_pset_zone_usage_time(pz, t);
+}
+
+uint64_t
+zs_pset_zone_used_cpus(zs_pset_zone_t *pz)
+{
+ return (zs_pset_zone_usage_cpus(pz));
+}
+
+/*
+ * Get percent of a psets cpus used by a zone
+ */
+uint_t
+zs_pset_zone_used_pct(zs_pset_zone_t *pz, int type)
+{
+ uint_t v;
+
+ switch (type) {
+ case ZS_PZ_PCT_PSET:
+ v = zs_pset_zone_usage_pct_pset(pz);
+ break;
+ case ZS_PZ_PCT_CPU_CAP:
+ v = zs_pset_zone_usage_pct_cpu_cap(pz);
+ break;
+ case ZS_PZ_PCT_PSET_SHARES:
+ v = zs_pset_zone_usage_pct_pset_shares(pz);
+ break;
+ case ZS_PZ_PCT_CPU_SHARES:
+ v = zs_pset_zone_usage_pct_cpu_shares(pz);
+ break;
+ default:
+ assert(0);
+ }
+ return (v);
+}
+
+/*
+ * returns similar to malloc
+ */
+zs_property_t *
+zs_property_alloc()
+{
+ return ((zs_property_t *)malloc(sizeof (zs_property_t)));
+}
+
+size_t
+zs_property_size()
+{
+ return (sizeof (zs_property_t));
+}
+
+void
+zs_property_free(zs_property_t *p)
+{
+ free(p);
+}
+
+int
+zs_property_type(zs_property_t *p)
+{
+ return (p->zsp_type);
+}
+
+int
+zs_property_id(zs_property_t *p)
+{
+ return (p->zsp_id);
+}
+
+char *
+zs_property_string(zs_property_t *p)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_STRING);
+ return (p->zsp_v.zsv_string);
+}
+
+double
+zs_property_double(zs_property_t *p)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_DOUBLE);
+ return (p->zsp_v.zsv_double);
+}
+
+void
+zs_property_time(zs_property_t *p, timestruc_t *t)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_TIME);
+ *t = p->zsp_v.zsv_ts;
+}
+
+uint64_t
+zs_property_uint64(zs_property_t *p)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_UINT64);
+ return (p->zsp_v.zsv_uint64);
+}
+
+int64_t
+zs_property_int64(zs_property_t *p)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_INT64);
+ return (p->zsp_v.zsv_int64);
+}
+
+uint_t
+zs_property_uint(zs_property_t *p)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_UINT);
+ return (p->zsp_v.zsv_uint);
+}
+
+int
+zs_property_int(zs_property_t *p)
+{
+ assert(p->zsp_type == ZS_PROP_TYPE_INT);
+ return (p->zsp_v.zsv_uint);
+}
diff --git a/usr/src/lib/libzonestat/common/llib-lzonestat b/usr/src/lib/libzonestat/common/llib-lzonestat
new file mode 100644
index 0000000000..189214ce22
--- /dev/null
+++ b/usr/src/lib/libzonestat/common/llib-lzonestat
@@ -0,0 +1,30 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <zonestat.h>
+
diff --git a/usr/src/lib/libzonestat/common/mapfile-vers b/usr/src/lib/libzonestat/common/mapfile-vers
new file mode 100644
index 0000000000..34fa3d6da8
--- /dev/null
+++ b/usr/src/lib/libzonestat/common/mapfile-vers
@@ -0,0 +1,101 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ global:
+ zs_close;
+ zs_open;
+ zs_property_int;
+ zs_property_size;
+ zs_property_string;
+ zs_property_uint;
+ zs_property_uint64;
+ zs_property_alloc;
+ zs_property_double;
+ zs_property_free;
+ zs_property_id;
+ zs_property_time;
+ zs_property_type;
+ zs_pset_first;
+ zs_pset_list;
+ zs_pset_next;
+ zs_pset_property;
+ zs_pset_total_time;
+ zs_pset_used_time;
+ zs_pset_total_cpus;
+ zs_pset_used_cpus;
+ zs_pset_used_pct;
+ zs_pset_zone_first;
+ zs_pset_zone_get_pset;
+ zs_pset_zone_get_zone;
+ zs_pset_zone_list;
+ zs_pset_zone_next;
+ zs_pset_zone_property;
+ zs_pset_zone_used_cpus;
+ zs_pset_zone_used_pct;
+ zs_pset_zone_used_time;
+ zs_resource_property;
+ zs_resource_type;
+ zs_resource_total_time;
+ zs_resource_used_time;
+ zs_resource_used_zone_time;
+ zs_resource_total_uint64;
+ zs_resource_used_uint64;
+ zs_resource_used_zone_uint64;
+ zs_resource_used_pct;
+ zs_resource_used_zone_pct;
+ zs_usage_compute;
+ zs_usage_free;
+ zs_usage_read;
+ zs_usage_set_add;
+ zs_usage_set_alloc;
+ zs_usage_set_compute;
+ zs_usage_set_count;
+ zs_usage_set_free;
+ zs_zone_limit_uint64;
+ zs_zone_limit_used_pct;
+ zs_zone_limit_used_time;
+ zs_zone_list;
+ zs_zone_first;
+ zs_zone_next;
+ zs_zone_property;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libzonestat/common/zonestat.h b/usr/src/lib/libzonestat/common/zonestat.h
new file mode 100644
index 0000000000..d717bdf0ad
--- /dev/null
+++ b/usr/src/lib/libzonestat/common/zonestat.h
@@ -0,0 +1,277 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _ZONESTAT_H
+#define _ZONESTAT_H
+
+
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZS_IPTYPE_SHARED 1
+#define ZS_IPTYPE_EXCLUSIVE 2
+
+#define ZS_CPUTYPE_DEFAULT_PSET 1
+#define ZS_CPUTYPE_POOL_PSET 2
+#define ZS_CPUTYPE_PSRSET_PSET 3
+#define ZS_CPUTYPE_DEDICATED 4
+
+
+#define ZS_LIMIT_NONE (UINT64_MAX)
+#define ZS_PCT_NONE (UINT_MAX)
+#define ZS_SHARES_UNLIMITED (UINT16_MAX)
+
+#define ZS_ZONENAME_MAX ZONENAME_MAX
+#define ZS_PSETNAME_MAX (1024 + 1)
+#define ZS_POOLNAME_MAX (1024 + 1)
+
+#define ZS_RESOURCE_TYPE_TIME 1
+#define ZS_RESOURCE_TYPE_COUNT 2
+#define ZS_RESOURCE_TYPE_BYTES 3
+
+#define ZS_LIMIT_TYPE_TIME 1
+#define ZS_LIMIT_TYPE_COUNT 2
+#define ZS_LIMIT_TYPE_BYTES 3
+
+#define ZS_PROP_TYPE_STRING 1
+#define ZS_PROP_TYPE_TIME 2
+#define ZS_PROP_TYPE_UINT64 3
+#define ZS_PROP_TYPE_INT64 4
+#define ZS_PROP_TYPE_UINT 5
+#define ZS_PROP_TYPE_INT 6
+#define ZS_PROP_TYPE_DOUBLE 7
+
+#define ZS_SCHED_TS 0x1
+#define ZS_SCHED_IA 0x2
+#define ZS_SCHED_RT 0x4
+#define ZS_SCHED_FX 0x8
+#define ZS_SCHED_FX_60 0x10
+#define ZS_SCHED_FSS 0x20
+#define ZS_SCHED_CONFLICT 0x40
+
+#define ZS_RESOURCE_PROP_CPU_TOTAL 1
+#define ZS_RESOURCE_PROP_CPU_ONLINE 2
+#define ZS_RESOURCE_PROP_CPU_LOAD_1MIN 3
+#define ZS_RESOURCE_PROP_CPU_LOAD_5MIN 4
+#define ZS_RESOURCE_PROP_CPU_LOAD_15MIN 5
+
+#define ZS_RESOURCE_CPU 1
+#define ZS_RESOURCE_RAM_RSS 2
+#define ZS_RESOURCE_RAM_LOCKED 3
+#define ZS_RESOURCE_VM 4
+#define ZS_RESOURCE_DISK_SWAP 5
+#define ZS_RESOURCE_LWPS 6
+#define ZS_RESOURCE_PROCESSES 7
+#define ZS_RESOURCE_SHM_MEMORY 8
+#define ZS_RESOURCE_SHM_IDS 9
+#define ZS_RESOURCE_SEM_IDS 10
+#define ZS_RESOURCE_MSG_IDS 11
+#define ZS_RESOURCE_LOFI 12
+
+#define ZS_USER_ALL 1
+#define ZS_USER_KERNEL 2
+#define ZS_USER_ZONES 3
+#define ZS_USER_FREE 4
+
+#define ZS_LIMIT_CPU 1
+#define ZS_LIMIT_CPU_SHARES 2
+#define ZS_LIMIT_RAM_RSS 3
+#define ZS_LIMIT_RAM_LOCKED 4
+#define ZS_LIMIT_VM 5
+#define ZS_LIMIT_LWPS 6
+#define ZS_LIMIT_PROCESSES 7
+#define ZS_LIMIT_SHM_MEMORY 8
+#define ZS_LIMIT_SHM_IDS 9
+#define ZS_LIMIT_MSG_IDS 10
+#define ZS_LIMIT_SEM_IDS 11
+#define ZS_LIMIT_LOFI 12
+
+#define ZS_ZONE_PROP_NAME 1
+#define ZS_ZONE_PROP_ID 2
+#define ZS_ZONE_PROP_IPTYPE 3
+#define ZS_ZONE_PROP_CPUTYPE 4
+#define ZS_ZONE_PROP_DEFAULT_SCHED 5
+#define ZS_ZONE_PROP_SCHEDULERS 6
+#define ZS_ZONE_PROP_CPU_SHARES 7
+#define ZS_ZONE_PROP_POOLNAME 8
+#define ZS_ZONE_PROP_PSETNAME 9
+#define ZS_ZONE_PROP_UPTIME 10
+#define ZS_ZONE_PROP_BOOTTIME 11
+
+#define ZS_PSET_PROP_NAME 1
+#define ZS_PSET_PROP_ID 2
+#define ZS_PSET_PROP_CPUTYPE 3
+#define ZS_PSET_PROP_SIZE 4
+#define ZS_PSET_PROP_ONLINE 5
+#define ZS_PSET_PROP_MIN 6
+#define ZS_PSET_PROP_MAX 7
+#define ZS_PSET_PROP_CPU_SHARES 8
+#define ZS_PSET_PROP_SCHEDULERS 9
+#define ZS_PSET_PROP_CREATETIME 10
+#define ZS_PSET_PROP_LOAD_1MIN 11
+#define ZS_PSET_PROP_LOAD_5MIN 12
+#define ZS_PSET_PROP_LOAD_15MIN 13
+
+#define ZS_PZ_PROP_SCHEDULERS 1
+#define ZS_PZ_PROP_CPU_SHARES 2
+#define ZS_PZ_PROP_CPU_CAP 4
+
+#define ZS_COMPUTE_USAGE_INTERVAL 1
+#define ZS_COMPUTE_USAGE_TOTAL 2
+#define ZS_COMPUTE_USAGE_AVERAGE 3
+#define ZS_COMPUTE_USAGE_HIGH 4
+
+#define ZS_COMPUTE_SET_TOTAL 1
+#define ZS_COMPUTE_SET_AVERAGE 2
+#define ZS_COMPUTE_SET_HIGH 3
+
+#define ZS_PZ_PCT_PSET 1
+#define ZS_PZ_PCT_CPU_CAP 2
+#define ZS_PZ_PCT_PSET_SHARES 3
+#define ZS_PZ_PCT_CPU_SHARES 4
+
+
+/* Per-client handle to libzonestat */
+typedef struct zs_ctl zs_ctl_t;
+
+/*
+ * These usage structure contains the system's utilization (overall, zones,
+ * psets, memory) at a given point in time.
+ */
+typedef struct zs_usage zs_usage_t;
+
+/*
+ * The usage set is for computations on multiple usage structures to describe
+ * a range of time.
+ */
+typedef struct zs_usage_set zs_usage_set_t;
+
+/*
+ * The following structures desribe each zone, pset, and each zone's usage
+ * of each pset. Each usage structure (above) contains lists of these that
+ * can be traversed.
+ */
+typedef struct zs_zone zs_zone_t;
+typedef struct zs_pset zs_pset_t;
+typedef struct zs_pset_zone zs_pset_zone_t;
+
+/*
+ * Opaque structure for properties.
+ */
+typedef struct zs_property zs_property_t;
+
+
+/* functions for opening/closing a handle for reading current usage */
+zs_ctl_t *zs_open();
+void zs_close(zs_ctl_t *);
+
+/* function for reading current resource usage */
+zs_usage_t *zs_usage_read(zs_ctl_t *);
+
+/* functions for manimulating usage data: zs_usage */
+zs_usage_t *zs_usage_compute(zs_usage_t *, zs_usage_t *, zs_usage_t *, int);
+void zs_usage_free(zs_usage_t *);
+
+/* functions for manipulating sets of usage data: zs_usage_set */
+zs_usage_set_t *zs_usage_set_alloc();
+void zs_usage_set_free(zs_usage_set_t *);
+int zs_usage_set_add(zs_usage_set_t *, zs_usage_t *);
+int zs_usage_set_count(zs_usage_set_t *);
+zs_usage_t *zs_usage_set_compute(zs_usage_set_t *, int);
+
+/* functions for overall system resources: zs_resource */
+void zs_resource_property(zs_usage_t *, int, int, zs_property_t *);
+int zs_resource_type(int);
+uint64_t zs_resource_total_uint64(zs_usage_t *, int);
+uint64_t zs_resource_used_uint64(zs_usage_t *, int, int);
+uint64_t zs_resource_used_zone_uint64(zs_zone_t *, int);
+void zs_resource_total_time(zs_usage_t *, int, timestruc_t *);
+void zs_resource_used_time(zs_usage_t *, int, int, timestruc_t *);
+void zs_resource_used_zone_time(zs_zone_t *, int, timestruc_t *);
+uint_t zs_resource_used_pct(zs_usage_t *, int, int);
+uint_t zs_resource_used_zone_pct(zs_zone_t *, int);
+
+/* functions for individual zone usage: zs_zone */
+int zs_zone_list(zs_usage_t *, zs_zone_t **, int);
+zs_zone_t *zs_zone_first(zs_usage_t *);
+zs_zone_t *zs_zone_next(zs_usage_t *, zs_zone_t *);
+void zs_zone_property(zs_zone_t *, int, zs_property_t *);
+int zs_zone_limit_type(int);
+uint64_t zs_zone_limit_uint64(zs_zone_t *, int);
+uint64_t zs_zone_limit_used_uint64(zs_zone_t *, int);
+void zs_zone_limit_time(zs_zone_t *, int, timestruc_t *);
+void zs_zone_limit_used_time(zs_zone_t *, int, timestruc_t *);
+uint_t zs_zone_limit_used_pct(zs_zone_t *, int);
+
+/* functions for individual psets: zs_pset_list */
+int zs_pset_list(zs_usage_t *, zs_pset_t **, int);
+zs_pset_t *zs_pset_first(zs_usage_t *);
+zs_pset_t *zs_pset_next(zs_usage_t *, zs_pset_t *);
+void zs_pset_property(zs_pset_t *, int, zs_property_t *);
+void zs_pset_total_time(zs_pset_t *, timestruc_t *);
+uint64_t zs_pset_total_cpus(zs_pset_t *);
+void zs_pset_used_time(zs_pset_t *, int, timestruc_t *);
+uint64_t zs_pset_used_cpus(zs_pset_t *, int);
+uint_t zs_pset_used_pct(zs_pset_t *, int);
+
+/* functions for a pset's per-zone usage: zs_pset_zone */
+int zs_pset_zone_list(zs_pset_t *, zs_pset_zone_t **, int);
+zs_pset_zone_t *zs_pset_zone_first(zs_pset_t *);
+zs_pset_zone_t *zs_pset_zone_next(zs_pset_t *, zs_pset_zone_t *);
+zs_zone_t *zs_pset_zone_get_zone(zs_pset_zone_t *);
+zs_pset_t *zs_pset_zone_get_pset(zs_pset_zone_t *);
+void zs_pset_zone_property(zs_pset_zone_t *, int, zs_property_t *);
+void zs_pset_zone_used_time(zs_pset_zone_t *, timestruc_t *);
+uint64_t zs_pset_zone_used_cpus(zs_pset_zone_t *);
+uint_t zs_pset_zone_used_pct(zs_pset_zone_t *, int);
+
+/* functions for accessing properties */
+zs_property_t *zs_property_alloc();
+size_t zs_property_size();
+void zs_property_free(zs_property_t *);
+int zs_property_type(zs_property_t *);
+int zs_property_id(zs_property_t *);
+char *zs_property_string(zs_property_t *);
+double zs_property_double(zs_property_t *);
+void zs_property_time(zs_property_t *, timestruc_t *);
+uint64_t zs_property_uint64(zs_property_t *);
+int64_t zs_property_int64(zs_property_t *);
+uint_t zs_property_uint(zs_property_t *);
+int zs_property_int(zs_property_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZONESTAT_H */
diff --git a/usr/src/lib/libzonestat/common/zonestat_impl.h b/usr/src/lib/libzonestat/common/zonestat_impl.h
new file mode 100644
index 0000000000..e8f501a65a
--- /dev/null
+++ b/usr/src/lib/libzonestat/common/zonestat_impl.h
@@ -0,0 +1,280 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _ZONESTAT_IMPL_H
+#define _ZONESTAT_IMPL_H
+
+#include <zonestat.h>
+#include <sys/list.h>
+#include <sys/priv_const.h>
+
+#ifdef __cplusplus
+extern "C" {
+
+#endif
+
+#define ZS_VERSION 1
+
+#define ZS_PSET_DEFAULT PS_NONE
+#define ZS_PSET_MULTI PS_MYID
+#define ZS_PSET_ERROR PS_QUERY
+
+#define ZS_DOOR_PATH "/etc/svc/volatile/zonestat_door"
+
+#define ZSD_CMD_READ 1
+#define ZSD_CMD_CONNECT 2
+#define ZSD_CMD_NEW_ZONE 3
+
+/* The following read commands are unimplemented */
+#define ZSD_CMD_READ_TIME 3
+#define ZSD_CMD_READ_SET 4
+#define ZSD_CMD_READ_SET_TIME 5
+
+#define ZSD_STATUS_OK 0
+#define ZSD_STATUS_VERSION_MISMATCH 1
+#define ZSD_STATUS_PERMISSION 2
+#define ZSD_STATUS_INTERNAL_ERROR 3
+
+#define TIMESTRUC_ADD_NANOSEC(ts, nsec) \
+ { \
+ (ts).tv_sec += (time_t)((nsec) / NANOSEC); \
+ (ts).tv_nsec += (long)((nsec) % NANOSEC); \
+ if ((ts).tv_nsec > NANOSEC) { \
+ (ts).tv_sec += (ts).tv_nsec / NANOSEC; \
+ (ts).tv_nsec = (ts).tv_nsec % NANOSEC; \
+ } \
+ }
+
+#define TIMESTRUC_ADD_TIMESTRUC(ts, add) \
+ { \
+ (ts).tv_sec += (add).tv_sec; \
+ (ts).tv_nsec += (add).tv_nsec; \
+ if ((ts).tv_nsec > NANOSEC) { \
+ (ts).tv_sec += (ts).tv_nsec / NANOSEC; \
+ (ts).tv_nsec = (ts).tv_nsec % NANOSEC; \
+ } \
+ }
+
+#define TIMESTRUC_DELTA(delta, new, old) \
+ { \
+ (delta).tv_sec = (new).tv_sec - (old).tv_sec; \
+ (delta).tv_nsec = (new).tv_nsec - (old).tv_nsec;\
+ if ((delta).tv_nsec < 0) { \
+ delta.tv_nsec += NANOSEC; \
+ delta.tv_sec -= 1; \
+ } \
+ if ((delta).tv_sec < 0) { \
+ delta.tv_sec = 0; \
+ delta.tv_nsec = 0; \
+ } \
+ }
+
+typedef struct zs_system {
+
+ uint64_t zss_ram_total;
+ uint64_t zss_ram_kern;
+ uint64_t zss_ram_zones;
+
+ uint64_t zss_locked_kern;
+ uint64_t zss_locked_zones;
+
+ uint64_t zss_vm_total;
+ uint64_t zss_vm_kern;
+ uint64_t zss_vm_zones;
+
+ uint64_t zss_swap_total;
+ uint64_t zss_swap_used;
+
+ timestruc_t zss_cpu_total_time;
+ timestruc_t zss_cpu_usage_kern;
+ timestruc_t zss_cpu_usage_zones;
+
+ uint64_t zss_processes_max;
+ uint64_t zss_lwps_max;
+ uint64_t zss_shm_max;
+ uint64_t zss_shmids_max;
+ uint64_t zss_semids_max;
+ uint64_t zss_msgids_max;
+ uint64_t zss_lofi_max;
+
+ uint64_t zss_processes;
+ uint64_t zss_lwps;
+ uint64_t zss_shm;
+ uint64_t zss_shmids;
+ uint64_t zss_semids;
+ uint64_t zss_msgids;
+ uint64_t zss_lofi;
+
+ uint64_t zss_ncpus;
+ uint64_t zss_ncpus_online;
+
+} zs_system_t;
+
+struct zs_pset_zone {
+
+ list_node_t zspz_next;
+ struct zs_pset *zspz_pset;
+ struct zs_zone *zspz_zone;
+ zoneid_t zspz_zoneid;
+ time_t zspz_start;
+ hrtime_t zspz_hrstart;
+ uint_t zspz_intervals;
+
+ uint64_t zspz_cpu_shares;
+ uint_t zspz_scheds;
+
+ timestruc_t zspz_cpu_usage;
+
+};
+
+struct zs_ctl {
+ int zsctl_door;
+ uint64_t zsctl_gen;
+ zs_usage_t *zsctl_start;
+};
+
+struct zs_zone {
+ list_node_t zsz_next;
+ struct zs_system *zsz_system;
+ char zsz_name[ZS_ZONENAME_MAX];
+ char zsz_pool[ZS_POOLNAME_MAX];
+ char zsz_pset[ZS_PSETNAME_MAX];
+ zoneid_t zsz_id;
+ uint_t zsz_cputype;
+ uint_t zsz_iptype;
+ time_t zsz_start;
+ hrtime_t zsz_hrstart;
+ uint_t zsz_intervals;
+
+ uint_t zsz_scheds;
+ uint64_t zsz_cpu_shares;
+ uint64_t zsz_cpu_cap;
+ uint64_t zsz_ram_cap;
+ uint64_t zsz_vm_cap;
+ uint64_t zsz_locked_cap;
+
+ uint64_t zsz_cpus_online;
+ timestruc_t zsz_cpu_usage;
+ timestruc_t zsz_pset_time;
+ timestruc_t zsz_cap_time;
+ timestruc_t zsz_share_time;
+
+ uint64_t zsz_usage_ram;
+ uint64_t zsz_usage_locked;
+ uint64_t zsz_usage_vm;
+
+ uint64_t zsz_processes_cap;
+ uint64_t zsz_lwps_cap;
+ uint64_t zsz_shm_cap;
+ uint64_t zsz_shmids_cap;
+ uint64_t zsz_semids_cap;
+ uint64_t zsz_msgids_cap;
+ uint64_t zsz_lofi_cap;
+
+ uint64_t zsz_processes;
+ uint64_t zsz_lwps;
+ uint64_t zsz_shm;
+ uint64_t zsz_shmids;
+ uint64_t zsz_semids;
+ uint64_t zsz_msgids;
+ uint64_t zsz_lofi;
+
+};
+
+struct zs_pset {
+ list_node_t zsp_next;
+ char zsp_name[ZS_PSETNAME_MAX];
+ psetid_t zsp_id;
+ uint_t zsp_cputype;
+ time_t zsp_start;
+ hrtime_t zsp_hrstart;
+ uint_t zsp_intervals;
+
+ uint64_t zsp_online;
+ uint64_t zsp_size;
+ uint64_t zsp_min;
+ uint64_t zsp_max;
+ int64_t zsp_importance;
+
+ uint_t zsp_scheds;
+ uint64_t zsp_cpu_shares;
+ timestruc_t zsp_total_time;
+ timestruc_t zsp_usage_kern;
+ timestruc_t zsp_usage_zones;
+
+ uint_t zsp_nusage;
+ list_t zsp_usage_list;
+};
+
+struct zs_usage {
+ time_t zsu_start;
+ hrtime_t zsu_hrstart;
+ time_t zsu_time;
+ hrtime_t zsu_hrtime;
+ uint64_t zsu_size;
+ uint_t zsu_intervals;
+ uint64_t zsu_gen;
+ boolean_t zsu_mmap;
+ uint_t zsu_nzones;
+ uint_t zsu_npsets;
+ zs_system_t *zsu_system;
+ list_t zsu_zone_list;
+ list_t zsu_pset_list;
+};
+
+struct zs_usage_set {
+ struct zs_usage *zsus_total;
+ struct zs_usage *zsus_avg;
+ struct zs_usage *zsus_high;
+ uint_t zsus_count;
+};
+
+struct zs_property {
+ int zsp_type;
+ int zsp_id;
+ union zsp_value_union {
+ char zsv_string[ZS_PSETNAME_MAX];
+ timestruc_t zsv_ts;
+ double zsv_double;
+ uint64_t zsv_uint64;
+ int64_t zsv_int64;
+ uint_t zsv_uint;
+ int zsv_int;
+ } zsp_v;
+};
+
+typedef struct zs_usage_cache {
+ int zsuc_ref;
+ uint_t zsuc_size;
+ uint64_t zsuc_gen;
+ zs_usage_t *zsuc_usage;
+} zs_usage_cache_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZONESTAT_IMPL_H */
diff --git a/usr/src/lib/libzonestat/i386/Makefile b/usr/src/lib/libzonestat/i386/Makefile
new file mode 100644
index 0000000000..f9373d2096
--- /dev/null
+++ b/usr/src/lib/libzonestat/i386/Makefile
@@ -0,0 +1,26 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzonestat/sparc/Makefile b/usr/src/lib/libzonestat/sparc/Makefile
new file mode 100644
index 0000000000..f9373d2096
--- /dev/null
+++ b/usr/src/lib/libzonestat/sparc/Makefile
@@ -0,0 +1,26 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzonestat/sparcv9/Makefile b/usr/src/lib/libzonestat/sparcv9/Makefile
new file mode 100644
index 0000000000..9eeb4ef71d
--- /dev/null
+++ b/usr/src/lib/libzonestat/sparcv9/Makefile
@@ -0,0 +1,27 @@
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf
index 59563bb1cd..6d0b70fcec 100644
--- a/usr/src/pkg/manifests/system-header.mf
+++ b/usr/src/pkg/manifests/system-header.mf
@@ -1682,6 +1682,7 @@ file path=usr/include/wordexp.h
file path=usr/include/xti.h
file path=usr/include/xti_inet.h
file path=usr/include/zone.h
+file path=usr/include/zonestat.h
$(i386_ONLY)file path=usr/platform/i86pc/include/sys/acpidev.h
$(i386_ONLY)file path=usr/platform/i86pc/include/sys/amd_iommu.h
$(i386_ONLY)file path=usr/platform/i86pc/include/sys/asm_misc.h
diff --git a/usr/src/pkg/manifests/system-zones-internal.mf b/usr/src/pkg/manifests/system-zones-internal.mf
index 07336c4fae..40598c0012 100644
--- a/usr/src/pkg/manifests/system-zones-internal.mf
+++ b/usr/src/pkg/manifests/system-zones-internal.mf
@@ -36,17 +36,23 @@ dir path=usr/lib/$(ARCH64)
file path=usr/include/libbrand.h
file path=usr/include/libuutil.h
file path=usr/include/libzonecfg.h
+file path=usr/include/zonestat_impl.h
file path=usr/lib/$(ARCH64)/llib-lbrand.ln
file path=usr/lib/$(ARCH64)/llib-lzonecfg.ln
+file path=usr/lib/$(ARCH64)/llib-lzonestat.ln
file path=usr/lib/llib-lbrand
file path=usr/lib/llib-lbrand.ln
file path=usr/lib/llib-lzonecfg
file path=usr/lib/llib-lzonecfg.ln
+file path=usr/lib/llib-lzonestat
+file path=usr/lib/llib-lzonestat.ln
legacy pkg=SUNWzoneint desc="Solaris Zones internal files" \
name="Solaris Zones Internal Files" version=11.11,REV=2009.10.13
license cr_Sun license=cr_Sun
license lic_CDDL license=lic_CDDL
link path=usr/lib/$(ARCH64)/libbrand.so target=./libbrand.so.1
link path=usr/lib/$(ARCH64)/libzonecfg.so target=./libzonecfg.so.1
+link path=usr/lib/$(ARCH64)/libzonestat.so target=./libzonestat.so.1
link path=usr/lib/libbrand.so target=./libbrand.so.1
link path=usr/lib/libzonecfg.so target=./libzonecfg.so.1
+link path=usr/lib/libzonestat.so target=./libzonestat.so.1
diff --git a/usr/src/pkg/manifests/system-zones.mf b/usr/src/pkg/manifests/system-zones.mf
index dd156b4ad3..e2a45c43ee 100644
--- a/usr/src/pkg/manifests/system-zones.mf
+++ b/usr/src/pkg/manifests/system-zones.mf
@@ -62,13 +62,19 @@ file path=lib/svc/manifest/system/resource-mgmt.xml group=sys mode=0444 \
variant.opensolaris.zone=global
file path=lib/svc/manifest/system/zones.xml group=sys mode=0444 \
variant.opensolaris.zone=global
+file path=lib/svc/manifest/system/zonestat.xml group=sys mode=0444 \
+ variant.opensolaris.zone=global
file path=lib/svc/method/svc-resource-mgmt mode=0555 \
variant.opensolaris.zone=global
file path=lib/svc/method/svc-zones mode=0555 variant.opensolaris.zone=global
+file path=lib/svc/method/svc-zonestat mode=0555 \
+ variant.opensolaris.zone=global
+file path=usr/bin/zonestat mode=0555
file path=usr/kernel/drv/$(ARCH64)/zcons group=sys
$(i386_ONLY)file path=usr/kernel/drv/zcons group=sys
file path=usr/lib/$(ARCH64)/libbrand.so.1
file path=usr/lib/$(ARCH64)/libzonecfg.so.1
+file path=usr/lib/$(ARCH64)/libzonestat.so.1
file path=usr/lib/brand/ipkg/config.xml mode=0444
file path=usr/lib/brand/ipkg/platform.xml mode=0444
file path=usr/lib/brand/shared/common.ksh mode=0444
@@ -76,7 +82,9 @@ file path=usr/lib/brand/shared/query mode=0755
file path=usr/lib/brand/shared/uninstall.ksh mode=0444
file path=usr/lib/libbrand.so.1
file path=usr/lib/libzonecfg.so.1
+file path=usr/lib/libzonestat.so.1
file path=usr/lib/zones/zoneadmd mode=0555
+file path=usr/lib/zones/zonestatd mode=0555 variant.opensolaris.zone=global
file path=usr/sbin/zlogin mode=0555
file path=usr/sbin/zoneadm mode=0555
file path=usr/sbin/zonecfg mode=0555