changeset 13134:61fe7fb74c94

PSARC/2010/291 zonestat 6871288 integrate zones observability phase 1 6976077 zoneadmd dumps core after restart
author Steve Lawrence <Stephen.Lawrence@oracle.COM>
date Mon, 16 Aug 2010 15:11:00 -0700
parents 8c553b17498a
children 9efd3d43accd
files exception_lists/keywords usr/src/cmd/Makefile usr/src/cmd/Makefile.check usr/src/cmd/zoneadmd/vplat.c usr/src/cmd/zoneadmd/zoneadmd.c usr/src/cmd/zoneadmd/zoneadmd.h usr/src/cmd/zonestat/Makefile usr/src/cmd/zonestat/Makefile.subdirs usr/src/cmd/zonestat/zonestat/Makefile usr/src/cmd/zonestat/zonestat/zonestat.c usr/src/cmd/zonestat/zonestatd/Makefile usr/src/cmd/zonestat/zonestatd/svc-zonestat usr/src/cmd/zonestat/zonestatd/zonestat.xml usr/src/cmd/zonestat/zonestatd/zonestatd.c usr/src/lib/Makefile usr/src/lib/libzonestat/Makefile usr/src/lib/libzonestat/Makefile.com usr/src/lib/libzonestat/amd64/Makefile usr/src/lib/libzonestat/common/libzonestat.c usr/src/lib/libzonestat/common/llib-lzonestat usr/src/lib/libzonestat/common/mapfile-vers usr/src/lib/libzonestat/common/zonestat.h usr/src/lib/libzonestat/common/zonestat_impl.h usr/src/lib/libzonestat/i386/Makefile usr/src/lib/libzonestat/sparc/Makefile usr/src/lib/libzonestat/sparcv9/Makefile usr/src/pkg/manifests/system-header.mf usr/src/pkg/manifests/system-zones-internal.mf usr/src/pkg/manifests/system-zones.mf
diffstat 29 files changed, 12996 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/exception_lists/keywords	Mon Aug 16 15:11:00 2010 -0700
@@ -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
--- a/usr/src/cmd/Makefile	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/cmd/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -456,6 +456,7 @@
 	zonename	\
 	zpool		\
 	zlook		\
+	zonestat	\
 	zstreamdump	\
 	ztest
 
@@ -744,7 +745,8 @@
 	zoneadmd	\
 	zonecfg		\
 	zonename	\
-	zpool
+	zpool		\
+	zonestat
 
 $(CLOSED_BUILD)MSGSUBDIRS += \
 	$(CLOSED)/cmd/iconv	\
--- a/usr/src/cmd/Makefile.check	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/cmd/Makefile.check	Mon Aug 16 15:11:00 2010 -0700
@@ -144,7 +144,8 @@
 	vscan/vscand			\
 	xvm/ipagent			\
 	ypcmd/yppasswd			\
-	ypcmd/ypupdated
+	ypcmd/ypupdated			\
+	zonestat/zonestatd
 
 $(CLOSED_BUILD)MANIFEST_SUBDIRS +=		\
 	$(CLOSED)/cmd/cmd-inet/usr.lib/in.iked
--- a/usr/src/cmd/zoneadmd/vplat.c	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/cmd/zoneadmd/vplat.c	Mon Aug 16 15:11:00 2010 -0700
@@ -2471,6 +2471,7 @@
 {
 	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 @@
 	}
 
 	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 @@
 			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 @@
 		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 @@
 		}
 	}
 
-	free(pool_name);
-
 	remove_mlps(zlogp, zoneid);
 
 	if (zone_destroy(zoneid) != 0) {
--- a/usr/src/cmd/zoneadmd/zoneadmd.c	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/cmd/zoneadmd/zoneadmd.c	Mon Aug 16 15:11:00 2010 -0700
@@ -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 @@
 }
 
 /*
+ * 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 @@
 		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;
--- a/usr/src/cmd/zoneadmd/zoneadmd.h	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/cmd/zoneadmd/zoneadmd.h	Mon Aug 16 15:11:00 2010 -0700
@@ -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 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];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/Makefile.subdirs	Mon Aug 16 15:11:00 2010 -0700
@@ -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:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/zonestat/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/zonestat/zonestat.c	Mon Aug 16 15:11:00 2010 -0700
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/zonestatd/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/zonestatd/svc-zonestat	Mon Aug 16 15:11:00 2010 -0700
@@ -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
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/zonestatd/zonestat.xml	Mon Aug 16 15:11:00 2010 -0700
@@ -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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/zonestat/zonestatd/zonestatd.c	Mon Aug 16 15:11:00 2010 -0700
@@ -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);
+}
--- a/usr/src/lib/Makefile	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/lib/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -209,6 +209,7 @@
 	libbrand	.WAIT   \
 	libzonecfg	\
 	libzoneinfo	\
+	libzonestat	\
 	libtsnet	\
 	libtsol		\
 	gss_mechs/mech_spnego	\
@@ -479,6 +480,7 @@
 	libzfs		\
 	libzfs_jni	\
 	libzoneinfo	\
+	libzonestat	\
 	hal		\
 	policykit	\
 	lvm		\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/Makefile.com	Mon Aug 16 15:11:00 2010 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/amd64/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/common/libzonestat.c	Mon Aug 16 15:11:00 2010 -0700
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/common/llib-lzonestat	Mon Aug 16 15:11:00 2010 -0700
@@ -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>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/common/mapfile-vers	Mon Aug 16 15:11:00 2010 -0700
@@ -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:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/common/zonestat.h	Mon Aug 16 15:11:00 2010 -0700
@@ -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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/common/zonestat_impl.h	Mon Aug 16 15:11:00 2010 -0700
@@ -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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/i386/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/sparc/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libzonestat/sparcv9/Makefile	Mon Aug 16 15:11:00 2010 -0700
@@ -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)
--- a/usr/src/pkg/manifests/system-header.mf	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/pkg/manifests/system-header.mf	Mon Aug 16 15:11:00 2010 -0700
@@ -1682,6 +1682,7 @@
 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
--- a/usr/src/pkg/manifests/system-zones-internal.mf	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/pkg/manifests/system-zones-internal.mf	Mon Aug 16 15:11:00 2010 -0700
@@ -36,17 +36,23 @@
 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
--- a/usr/src/pkg/manifests/system-zones.mf	Mon Aug 16 14:56:26 2010 -0700
+++ b/usr/src/pkg/manifests/system-zones.mf	Mon Aug 16 15:11:00 2010 -0700
@@ -62,13 +62,19 @@
     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/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