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