summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorakolb <none@none>2006-09-06 11:54:18 -0700
committerakolb <none@none>2006-09-06 11:54:18 -0700
commitc64027834c5ffc60c557c2b12555e0cd4d30320c (patch)
tree795abc626b4f567a60b01b6e1ae7a867f8a3a2fb /usr/src
parent8a16ae8f6efb5a6c02ab0aec860e229706335bb9 (diff)
downloadillumos-joyent-c64027834c5ffc60c557c2b12555e0cd4d30320c.tar.gz
PSARC 2006/421 MPO Control And Observability Tools
4698370 Want tool for applying advice to affect memory allocation via madvise(3C) 5048416 Want to be able to observe lgroup memory placement via pmap(1) 5048789 Want tool for observing and affecting thread placement on lgroups 6424359 Want Perl interface to liblgrp(3LIB) library 6424363 Want tool for observing lgroup information 6424366 Want walkers for lgroup resources and lineage 6424384 Want ps(1) to be lgroup-aware 6424387 Want prstat(1M) to be lgroup-aware 6455612 DTrace proc provider should have inline for thread home lgroup 6455724 Want thread home lgroup visibility via /proc 6457390 threads should rehome to lgroup with same (or more affinity) than existing home lgroup 6457640 Want to expose lgroup load average scale to user-land
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/Makefile3
-rw-r--r--usr/src/cmd/lgrpinfo/Makefile57
-rwxr-xr-xusr/src/cmd/lgrpinfo/lgrpinfo.pl756
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c14
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/lgrp.c275
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/lgrp.h16
-rw-r--r--usr/src/cmd/perl/5.6.1/contrib/Makefile3
-rw-r--r--usr/src/cmd/perl/5.8.4/contrib/Makefile3
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes30
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm302
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs397
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST10
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL89
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README57
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON47
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod738
-rwxr-xr-xusr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t308
-rwxr-xr-xusr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t323
-rw-r--r--usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap37
-rw-r--r--usr/src/cmd/prstat/prstat.c91
-rw-r--r--usr/src/cmd/prstat/prstat.h14
-rw-r--r--usr/src/cmd/prstat/prutil.c4
-rw-r--r--usr/src/cmd/ps/ps.c104
-rw-r--r--usr/src/cmd/ptools/Makefile33
-rw-r--r--usr/src/cmd/ptools/Makefile.bld11
-rw-r--r--usr/src/cmd/ptools/plgrp/plgrp.c1591
-rw-r--r--usr/src/cmd/ptools/pmadvise/pmadvise.c1269
-rw-r--r--usr/src/cmd/ptools/pmap/pmap.c800
-rw-r--r--usr/src/lib/libdtrace/common/procfs.d.in3
-rw-r--r--usr/src/lib/libproc/common/P32ton.c9
-rw-r--r--usr/src/pkgdefs/SUNWesu/prototype_com3
-rw-r--r--usr/src/pkgdefs/SUNWesu/prototype_i38612
-rw-r--r--usr/src/pkgdefs/SUNWesu/prototype_sparc11
-rw-r--r--usr/src/pkgdefs/SUNWperl584core/prototype_com9
-rw-r--r--usr/src/pkgdefs/SUNWperl584core/prototype_i38613
-rw-r--r--usr/src/pkgdefs/SUNWperl584core/prototype_sparc13
-rw-r--r--usr/src/pkgdefs/SUNWpl5u/prototype_com11
-rw-r--r--usr/src/pkgdefs/SUNWpl5u/prototype_i38613
-rw-r--r--usr/src/pkgdefs/SUNWpl5u/prototype_sparc11
-rw-r--r--usr/src/uts/common/fs/proc/prsubr.c2
-rw-r--r--usr/src/uts/common/os/lgrp.c14
-rw-r--r--usr/src/uts/common/sys/lgrp.h14
-rw-r--r--usr/src/uts/common/sys/procfs.h15
-rw-r--r--usr/src/uts/common/syscall/lgrpsys.c55
44 files changed, 7364 insertions, 226 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 6868fa894a..fd41167eda 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -211,6 +211,7 @@ COMMON_SUBDIRS= \
lastcomm \
ldap \
ldapcachemgr \
+ lgrpinfo \
line \
link \
listen \
@@ -562,6 +563,7 @@ MSGSUBDIRS= \
last \
ldap \
ldapcachemgr \
+ lgrpinfo \
locale \
lofiadm \
logadm \
@@ -605,6 +607,7 @@ MSGSUBDIRS= \
prtdiag \
ps \
psrinfo \
+ ptools \
pwconv \
pwd \
raidctl \
diff --git a/usr/src/cmd/lgrpinfo/Makefile b/usr/src/cmd/lgrpinfo/Makefile
new file mode 100644
index 0000000000..9178046633
--- /dev/null
+++ b/usr/src/cmd/lgrpinfo/Makefile
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= lgrpinfo
+
+include ../Makefile.cmd
+
+OWNER = root
+GROUP = sys
+
+TARGET= all
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all .WAIT $(ROOTPROG)
+
+clean:
+
+%: %.pl
+ $(SED) -e "s@TEXT_DOMAIN@\"$(TEXT_DOMAIN)\"@" $< > $@
+
+$(ROOTBINPROG): $(PROG)
+ $(INS.file)
+
+$(PROG).po: $(PROG).pl
+ $(XGETTEXT) -d $(PROG) $(PROG).pl
+
+lint:
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/lgrpinfo/lgrpinfo.pl b/usr/src/cmd/lgrpinfo/lgrpinfo.pl
new file mode 100755
index 0000000000..5e641ef189
--- /dev/null
+++ b/usr/src/cmd/lgrpinfo/lgrpinfo.pl
@@ -0,0 +1,756 @@
+#! /usr/perl5/bin/perl
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# lgrpinfo: display information about locality groups.
+#
+
+require 5.6.1;
+use warnings;
+use strict;
+use Getopt::Long qw(:config no_ignore_case bundling auto_version);
+use File::Basename;
+# Sun::Solaris::Kstat is used to extract per-lgroup load average.
+use Sun::Solaris::Kstat;
+use POSIX qw(locale_h);
+use Sun::Solaris::Utils qw(textdomain gettext);
+use Sun::Solaris::Lgrp ':CONSTANTS';
+
+use constant KB => 1024;
+
+#
+# Amount of load contributed by a single thread. The value is exported by the
+# kernel in the 'loadscale' variable of lgroup kstat, but in case it is missing
+# we use the current default value as the best guess.
+#
+use constant LGRP_LOADAVG_THREAD_MAX => 65516;
+
+# Get script name
+our $cmdname = basename($0, ".pl");
+
+# Get liblgrp version
+my $version = Sun::Solaris::Lgrp::lgrp_version();
+
+our $VERSION = "%I% (liblgrp version $version)";
+
+# The $loads hash keeps per-lgroup load average.
+our $loads = {};
+
+########################################
+# Main body
+##
+
+# Set message locale
+setlocale(LC_ALL, "");
+textdomain(TEXT_DOMAIN);
+
+# Parse command-line options
+our($opt_a, $opt_l, $opt_m, $opt_c, $opt_C, $opt_e, $opt_t, $opt_h, $opt_u,
+ $opt_r, $opt_L, $opt_P, $opt_I, $opt_T, $opt_G);
+
+GetOptions("a" => \$opt_a,
+ "c" => \$opt_c,
+ "C" => \$opt_C,
+ "e" => \$opt_e,
+ "G" => \$opt_G,
+ "h|?" => \$opt_h,
+ "l" => \$opt_l,
+ "L" => \$opt_L,
+ "I" => \$opt_I,
+ "m" => \$opt_m,
+ "r" => \$opt_r,
+ "t" => \$opt_t,
+ "T" => \$opt_T,
+ "u=s" => \$opt_u,
+ "P" => \$opt_P) || usage(3);
+
+usage(0) if $opt_h;
+
+# Check for conflicting options
+my $nfilters = 0;
+$nfilters++ if $opt_C;
+$nfilters++ if $opt_P;
+$nfilters++ if $opt_T;
+
+if ($nfilters > 1) {
+ printf STDERR
+ gettext("%s: Options -C, -T and -P can not be used together\n"),
+ $cmdname;
+ usage(3);
+}
+
+if ($opt_T && ($opt_I || $opt_t)) {
+ printf STDERR
+ gettext("%s: Option -T can not be used with -I, -t\n"),
+ $cmdname;
+ usage(3);
+}
+
+if ($opt_T && scalar @ARGV) {
+ printf STDERR
+ gettext("%s: Warning: with '-T' all lgroups on the command line "),
+ $cmdname;
+ printf STDERR gettext("are ignored\n\n");
+}
+
+if ($opt_L && $opt_I) {
+ printf STDERR gettext("%s: Option -I can not be used with -L\n"),
+ $cmdname;
+ usage(3);
+}
+
+# Figure out what to do based on options
+my $do_default = 1 unless
+ $opt_a || $opt_l || $opt_m || $opt_c || $opt_e || $opt_t || $opt_r;
+
+
+my $l = Sun::Solaris::Lgrp->new($opt_G ? LGRP_VIEW_OS : LGRP_VIEW_CALLER) or
+ die(gettext("$cmdname: can not get lgroup information from the system\n"));
+
+
+# Get list of all lgroups, the root and the list of intermediates
+my @lgrps = nsort($l->lgrps);
+my $root = $l->root;
+my @intermediates = grep { $_ != $root && !$l->isleaf($_) } @lgrps;
+my $is_uma = (scalar @lgrps == 1);
+
+# Print everything if -a is specified or it is default without -T
+my $do_all = 1 if $opt_a || ($do_default && !($opt_T || $opt_L));
+
+# Print individual information if do_all or requested specific print
+my $do_lat = 1 if $do_all || $opt_l;
+my $do_memory = 1 if $do_all || $opt_m;
+my $do_cpu = 1 if $do_all || $opt_c;
+my $do_topo = 1 if $do_all || $opt_t;
+my $do_rsrc = 1 if $do_all || $opt_r;
+my $do_load = 1 if $do_all || $opt_e;
+my $do_table = 1 if $opt_a || $opt_L;
+my $do_something = ($do_lat || $do_memory || $do_cpu || $do_topo ||
+ $do_rsrc || $do_load);
+
+# Does the liblgrp(3LIB) has enough capabilities to support resource view?
+if ($do_rsrc && LGRP_VER_CURRENT == 1) {
+ if ($opt_r) {
+ printf STDERR
+ gettext("%s: sorry, your system does not support"),
+ $cmdname;
+ printf STDERR " lgrp_resources(3LGRP)\n";
+ }
+ $do_rsrc = 0;
+}
+
+# Get list of lgrps from arguments, expanding symbolic names like
+# "root" and "leaves"
+# Use all lgroups if none are specified on the command line
+my @lgrp_list = (scalar (@ARGV) && !$opt_T) ? lgrp_expand($l, @ARGV) : @lgrps;
+
+# Apply 'Parent' or 'Children' operations if requested
+@lgrp_list = map { $l->parents($_) } @lgrp_list if $opt_P;
+@lgrp_list = map { $l->children($_) } @lgrp_list if $opt_C;
+
+# Drop repeating elements and sort lgroups numerically.
+@lgrp_list = uniqsort(@lgrp_list);
+
+# If both -L and -c are specified, just print list of CPUs.
+if ($opt_c && $opt_I) {
+ my @cpus = uniqsort(map { $l->cpus($_, LGRP_CONTENT_HIERARCHY) }
+ @lgrp_list);
+ print "@cpus\n";
+ exit(0);
+}
+
+my $unit_str = "K";
+my $units = KB;
+
+# Convert units to canonical numeric and string formats.
+if ($opt_u) {
+ if ($opt_u =~ /^b$/i) {
+ $units = 1;
+ $unit_str = "B";
+ } elsif ($opt_u =~ /^k$/i) {
+ $units = KB;
+ $unit_str = "K";
+ } elsif ($opt_u =~ /^m$/i) {
+ $units = KB * KB;
+ $unit_str = "M";
+ } elsif ($opt_u =~ /^g$/i) {
+ $units = KB * KB * KB;
+ $unit_str = "G";
+ } elsif ($opt_u =~ /^t$/i) {
+ $units = KB * KB * KB * KB;
+ $unit_str = "T";
+ } elsif ($opt_u =~ /^p$/i) {
+ $units = KB * KB * KB * KB * KB;
+ $unit_str = "P";
+ } elsif ($opt_u =~ /^e$/i) {
+ $units = KB * KB * KB * KB * KB * KB;
+ $unit_str = "E";
+ } elsif (! ($opt_u =~ /^m$/i)) {
+ printf STDERR
+ gettext("%s: invalid unit '$opt_u', should be [b|k|m|g|t|p|e]"),
+ $cmdname;
+ printf STDERR gettext(", using the default.\n\n");
+ $opt_u = 0;
+ }
+}
+
+# Collect load average data if requested.
+$loads = get_lav() if $do_load;
+
+# Get latency values for each lgroup.
+my %self_latencies;
+map { $self_latencies{$_} = $l->latency($_, $_) } @lgrps;
+
+# If -T is specified, just print topology and return.
+if ($opt_T) {
+ lgrp_prettyprint($l);
+ print_latency_table(\@lgrps, \@lgrps) if $do_table;
+ exit(0);
+}
+
+if (!scalar @lgrp_list) {
+ printf STDERR gettext("%s: No matching lgroups found!\n"), $cmdname;
+ exit(2);
+}
+
+# Just print list of lgrps if doing just filtering
+(print "@lgrp_list\n"), exit 0 if $opt_I;
+
+if ($do_something) {
+ # Walk through each requested lgrp and print whatever is requested.
+ foreach my $lgrp (@lgrp_list) {
+ my $is_leaf = $l->isleaf($lgrp);
+ my ($children, $parents, $cpus, $memstr, $rsrc);
+
+ my $prefix = ($lgrp == $root) ?
+ "root": $is_leaf ? gettext("leaf") : gettext("intermediate");
+ printf gettext("lgroup %d (%s):"), $lgrp, $prefix;
+
+ if ($do_topo) {
+ # Get children of this lgrp.
+ my @children = $l->children($lgrp);
+ $children = $is_leaf ?
+ gettext("Children: none") :
+ gettext("Children: ") . lgrp_collapse(@children);
+ # Are there any parents for this lgrp?
+ my @parents = $l->parents($lgrp);
+ $parents = @parents ?
+ gettext(", Parent: ") . "@parents" :
+ "";
+ }
+
+ if ($do_cpu) {
+ $cpus = lgrp_showcpus($lgrp, LGRP_CONTENT_HIERARCHY);
+ }
+ if ($do_memory) {
+ $memstr = lgrp_showmemory($lgrp, LGRP_CONTENT_HIERARCHY);
+ }
+ if ($do_rsrc) {
+ $rsrc = lgrp_showresources($lgrp);
+ }
+
+ # Print all the information about lgrp.
+ print "\n\t$children$parents" if $do_topo;
+ print "\n\t$cpus" if $do_cpu && $cpus;
+ print "\n\t$memstr" if $do_memory && $memstr;
+ print "\n\t$rsrc" if $do_rsrc;
+ print "\n\t$loads->{$lgrp}" if defined ($loads->{$lgrp});
+ if ($do_lat && defined($self_latencies{$lgrp})) {
+ printf gettext("\n\tLatency: %d"), $self_latencies{$lgrp};
+ }
+ print "\n";
+ }
+}
+
+print_latency_table(\@lgrps, \@lgrp_list) if $do_table;
+
+exit 0;
+
+#
+# usage(exit_status)
+# print usage message and exit with the specified exit status.
+#
+sub usage
+{
+ printf STDERR gettext("Usage:\t%s"), $cmdname;
+ print STDERR " [-aceGlLmrt] [-u unit] [-C|-P] [lgrp] ...\n";
+ print STDERR " \t$cmdname -I [-c] [-G] [-C|-P] [lgrp] ...\n";
+ print STDERR " \t$cmdname -T [-aceGlLmr] [-u unit]\n";
+ print STDERR " \t$cmdname -h\n\n";
+
+ printf STDERR
+ gettext(" Display information about locality groups\n\n" .
+ "\t-a: Equivalent to \"%s\" without -T and to \"%s\" with -T\n"),
+ "-celLmrt", "-celLmr";
+
+ print STDERR
+ gettext("\t-c: Print CPU information\n"),
+ gettext("\t-C: Children of the specified lgroups\n"),
+ gettext("\t-e: Print lgroup load average\n"),
+ gettext("\t-h: Print this message and exit\n"),
+ gettext("\t-I: Print lgroup or CPU IDs only\n"),
+ gettext("\t-l: Print information about lgroup latencies\n"),
+ gettext("\t-G: Print OS view of lgroup hierarchy\n"),
+ gettext("\t-L: Print lgroup latency table\n"),
+ gettext("\t-m: Print memory information\n"),
+ gettext("\t-P: Parent(s) of the specified lgroups\n"),
+ gettext("\t-r: Print lgroup resources\n"),
+ gettext("\t-t: Print information about lgroup topology\n"),
+ gettext("\t-T: Print the hierarchy tree\n"),
+ gettext("\t-u unit: Specify memory unit (b,k,m,g,t,p,e)\n\n\n");
+
+ print STDERR
+ gettext(" The lgrp may be specified as an lgroup ID,"),
+ gettext(" \"root\", \"all\",\n"),
+ gettext(" \"intermediate\" or \"leaves\".\n\n");
+
+ printf STDERR
+ gettext(" The default set of options is \"%s\"\n\n"),
+ "-celmrt all";
+
+ print STDERR
+ gettext(" Without any options print topology, CPU and memory " .
+ "information about each\n" .
+ " lgroup. If any lgroup IDs are specified on the " .
+ "command line only print\n" .
+ " information about the specified lgroup.\n\n");
+
+ exit(shift);
+}
+
+# Return the input list with duplicates removed.
+sub uniq
+{
+ my %seen;
+ return (grep { ++$seen{$_} == 1 } @_);
+}
+
+#
+# Sort the list numerically
+# Should be called in list context
+#
+sub nsort
+{
+ return (sort { $a <=> $b } @_);
+}
+
+#
+# Sort list numerically and remove duplicates
+# Should be called in list context
+#
+sub uniqsort
+{
+ return (sort { $a <=> $b } uniq(@_));
+}
+
+# Round values
+sub round
+{
+ my $val = shift;
+
+ return (int($val + 0.5));
+}
+
+#
+# Expand list of lgrps.
+# Translate 'root' to the root lgrp id
+# Translate 'all' to the list of all lgrps
+# Translate 'leaves' to the list of all lgrps'
+# Translate 'intermediate' to the list of intermediates.
+#
+sub lgrp_expand
+{
+ my $lobj = shift;
+ my %seen;
+ my @result;
+
+ # create a hash element for every element in @lgrps
+ map { $seen{$_}++ } @lgrps;
+
+ foreach my $lgrp (@_) {
+ push(@result, $lobj->root), next if $lgrp =~ m/^root$/i;
+ push(@result, @lgrps), next if $lgrp =~ m/^all$/i;
+ push(@result, $lobj->leaves), next if $lgrp =~ m/^leaves$/i;
+ push(@result, @intermediates),
+ next if $lgrp =~ m/^intermediate$/i;
+ push(@result, $lgrp),
+ next if $lgrp =~ m/^\d+$/ && $seen{$lgrp};
+ printf STDERR gettext("%s: skipping invalid lgrp $lgrp\n"),
+ $cmdname;
+ }
+
+ return @result;
+}
+
+#
+# lgrp_tree(class, node)
+#
+# Build the tree of the lgroup hierarchy starting with the specified node or
+# root if no initial node is specified. Calls itself recursively specifying each
+# of the children as a starting node. Builds a reference to the list with the
+# node in the end and each element being a subtree.
+#
+sub lgrp_tree
+{
+ my $c = shift;
+ my $lgrp = shift || $c->root;
+
+ # Call itself for each of the children and combine results in a list.
+ [ (map { lgrp_tree($c, $_) } $c->children($lgrp)), $lgrp ];
+}
+
+#
+# lgrp_pp(tree, prefix, childprefix, npeers)
+#
+# pretty-print the hierarchy tree.
+# Input Arguments:
+# Reference to the tree
+# Prefix for me to use
+# Prefix for my children to use
+# Number of peers left
+#
+sub lgrp_pp
+{
+ my $tree = shift;
+ my $myprefix = shift;
+ my $childprefix = shift;
+ my $npeers = shift;
+ my $el = pop @$tree;
+ my $nchildren = scalar @$tree;
+ my $printprefix = "$childprefix";
+ my $printpostfix = $npeers ? "| " : " ";
+
+ return unless defined ($el);
+
+ my $bar = $npeers ? "|" : "`";
+ print $childprefix ? $childprefix : "";
+ print $myprefix ? "$bar" . "-- " : "";
+ lgrp_print($el, "$printprefix$printpostfix");
+
+ my $new_prefix = $npeers ? $myprefix : " ";
+
+ # Pretty-print the subtree with a new offset.
+ map {
+ lgrp_pp($_, "| ", "$childprefix$new_prefix", --$nchildren)
+ } @$tree;
+}
+
+# Pretty print the whole tree
+sub lgrp_prettyprint
+{
+ my $c = shift;
+ my $tree = lgrp_tree $c;
+ lgrp_pp($tree, '', '', scalar $tree - 1);
+}
+
+sub lgrp_print
+{
+ my $lgrp = shift;
+ my $prefix = shift;
+ my ($cpus, $memstr, $rsrc);
+ my $is_interm = ($lgrp != $root && !$l->isleaf($lgrp));
+ my $not_root = $is_uma || $lgrp != $root;
+
+ print "$lgrp";
+
+ if ($do_cpu && $not_root) {
+ $cpus = lgrp_showcpus($lgrp, LGRP_CONTENT_HIERARCHY);
+ }
+ if ($do_memory && $not_root) {
+ $memstr = lgrp_showmemory($lgrp, LGRP_CONTENT_HIERARCHY);
+ }
+ if ($do_rsrc && ($is_uma || $is_interm)) {
+ $rsrc = lgrp_showresources($lgrp) if $do_rsrc;
+ }
+
+ # Print all the information about lgrp.
+
+ print "\n$prefix$cpus" if $cpus;
+ print "\n$prefix$memstr" if $memstr;
+ print "\n$prefix$rsrc" if $rsrc;
+ print "\n$prefix$loads->{$lgrp}" if defined ($loads->{$lgrp});
+
+ # Print latency information if requested.
+ if ($do_lat && $lgrp != $root && defined($self_latencies{$lgrp})) {
+ print "\n${prefix}";
+ printf gettext("Latency: %d"), $self_latencies{$lgrp};
+ }
+ print "\n";
+}
+
+# What CPUs are in this lgrp?
+sub lgrp_showcpus
+{
+ my $lgrp = shift;
+ my $hier = shift;
+
+ my @cpus = $l->cpus($lgrp, $hier);
+ my $ncpus = @cpus;
+ return 0 unless $ncpus;
+ # Sort CPU list if there is something to sort.
+ @cpus = nsort(@cpus) if ($ncpus > 1);
+ my $cpu_string = lgrp_collapse(@cpus);
+ return (($ncpus == 1) ?
+ gettext("CPU: ") . $cpu_string:
+ gettext("CPUs: ") . $cpu_string);
+}
+
+# How much memory does this lgrp contain?
+sub lgrp_showmemory
+{
+ my $lgrp = shift;
+ my $hier = shift;
+
+ my $memory = $l->mem_size($lgrp, LGRP_MEM_SZ_INSTALLED, $hier);
+ return (0) unless $memory;
+ my $freemem = $l->mem_size($lgrp, LGRP_MEM_SZ_FREE, $hier) || 0;
+
+ my $memory_r = memory_to_string($memory);
+ my $freemem_r = memory_to_string($freemem);
+ my $usedmem = memory_to_string($memory - $freemem);
+
+ my $memstr = sprintf(gettext("Memory: installed %s"),
+ $memory_r);
+ $memstr = $memstr . sprintf(gettext(", allocated %s"),
+ $usedmem);
+ $memstr = $memstr . sprintf(gettext(", free %s"),
+ $freemem_r);
+ return ($memstr);
+}
+
+# Get string containing lgroup resources
+sub lgrp_showresources
+{
+ my $lgrp = shift;
+ my $rsrc_prefix = gettext("Lgroup resources:");
+ # What resources does this lgroup contain?
+ my @resources_cpu = nsort($l->resources($lgrp, LGRP_RSRC_CPU));
+ my @resources_mem = nsort($l->resources($lgrp, LGRP_RSRC_MEM));
+ my $rsrc = @resources_cpu || @resources_mem ? "" : gettext("none");
+ $rsrc = $rsrc_prefix . $rsrc;
+ my $rsrc_cpu = lgrp_collapse(@resources_cpu);
+ my $rsrc_mem = lgrp_collapse(@resources_mem);
+ my $lcpu = gettext("CPU");
+ my $lmemory = gettext("memory");
+ $rsrc = "$rsrc $rsrc_cpu ($lcpu);" if scalar @resources_cpu;
+ $rsrc = "$rsrc $rsrc_mem ($lmemory)" if scalar @resources_mem;
+ return ($rsrc);
+}
+
+#
+# Consolidate consequtive ids as start-end
+# Input: list of ids
+# Output: string with space-sepated cpu values with ranges
+# collapsed as x-y
+#
+sub lgrp_collapse
+{
+ return ('') unless @_;
+ my @args = uniqsort(@_);
+ my $start = shift(@args);
+ my $result = '';
+ my $end = $start; # Initial range consists of the first element
+ foreach my $el (@args) {
+ if ($el == ($end + 1)) {
+ #
+ # Got consecutive ID, so extend end of range without
+ # printing anything since the range may extend further
+ #
+ $end = $el;
+ } else {
+ #
+ # Next ID is not consecutive, so print IDs gotten so
+ # far.
+ #
+ if ($end > $start + 1) { # range
+ $result = "$result $start-$end";
+ } elsif ($end > $start) { # different values
+ $result = "$result $start $end";
+ } else { # same value
+ $result = "$result $start";
+ }
+
+ # Try finding consecutive range starting from this ID
+ $start = $end = $el;
+ }
+ }
+
+ # Print last ID(s)
+ if ($end > $start + 1) {
+ $result = "$result $start-$end";
+ } elsif ($end > $start) {
+ $result = "$result $start $end";
+ } else {
+ $result = "$result $start";
+ }
+ # Remove any spaces in the beginning
+ $result =~ s/^\s+//;
+ return ($result);
+}
+
+# Print latency information if requested and the system has several lgroups.
+sub print_latency_table
+{
+ my ($lgrps1, $lgrps2) = @_;
+
+ return unless scalar @lgrps;
+
+ # Find maximum lgroup
+ my $max = $root;
+ map { $max = $_ if $max < $_ } @$lgrps1;
+
+ # Field width for lgroup - the width of the largest lgroup and 1 space
+ my $lgwidth = length($max) + 1;
+ # Field width for latency. Get the maximum latency and add 1 space.
+ my $width = length($l->latency($root, $root)) + 1;
+ # Make sure that width is enough to print lgroup itself.
+ $width = $lgwidth if $width < $lgwidth;
+
+ # Print table header
+ print gettext("\nLgroup latencies:\n");
+ # Print horizontal line
+ print "\n", "-" x ($lgwidth + 1);
+ map { print '-' x $width } @$lgrps1;
+ print "\n", " " x $lgwidth, "|";
+ map { printf("%${width}d", $_) } @$lgrps1;
+ print "\n", "-" x ($lgwidth + 1);
+ map { print '-' x $width } @$lgrps1;
+ print "\n";
+
+ # Print the latency table
+ foreach my $l1 (@$lgrps2) {
+ printf "%-${lgwidth}d|", $l1;
+ foreach my $l2 (@lgrps) {
+ my $latency = $l->latency($l1, $l2);
+ if (!defined ($latency)) {
+ printf "%${width}s", "-";
+ } else {
+ printf "%${width}d", $latency;
+ }
+ }
+ print "\n";
+ }
+
+ # Print table footer
+ print "-" x ($lgwidth + 1);
+ map { print '-' x $width } @lgrps;
+ print "\n";
+}
+
+#
+# Convert a number to a string representation
+# The number is scaled down until it is small enough to be in a good
+# human readable format i.e. in the range 0 thru 1023.
+# If it's smaller than 10 there's room enough to provide one decimal place.
+#
+sub number_to_scaled_string
+{
+ my $number = shift;
+
+ my $scale = KB;
+ my @measurement = ('K', 'M', 'G', 'T', 'P', 'E'); # Measurement
+ my $uom = shift(@measurement);
+ my $result;
+
+ # Get size in K.
+ $number /= KB;
+
+ my $save = $number;
+ while (($number >= $scale) && $uom ne 'E') {
+ $uom = shift(@measurement);
+ $save = $number;
+ $number = ($number + ($scale / 2)) / $scale;
+ }
+
+ # check if we should output a decimal place after the point
+ if ($save && (($save / $scale) < 10)) {
+ $result = sprintf("%2.1f", $save / $scale);
+ } else {
+ $result = round($number);
+ }
+ return ("$result$uom");
+}
+
+#
+# Convert memory size to the string representation
+#
+sub memory_to_string
+{
+ my $number = shift;
+
+ # Zero memory - just print 0
+ return ("0$unit_str") unless $number;
+
+ #
+ # Return memory size scaled to human-readable form unless -u is
+ # specified.
+ #
+ return (number_to_scaled_string($number)) unless $opt_u;
+
+ my $scaled = $number / $units;
+ my $result;
+
+ if ($scaled < 0.1) {
+ $result = sprintf("%2.1g", $scaled);
+ } elsif ($scaled < 10) {
+ $result = sprintf("%2.1f", $scaled);
+ } else {
+ $result = int($scaled + 0.5);
+ }
+ return ("$result$unit_str");
+}
+
+#
+# Read load averages from lgrp kstats Return hash reference indexed by lgroup ID
+# for each lgroup which has load information.
+#
+sub get_lav
+{
+ my $load = {};
+
+ my $ks = Sun::Solaris::Kstat->new(strip_strings => 1) or
+ warn(gettext("$cmdname: kstat_open() failed: %!\n")),
+ return $load;
+
+ my $lgrp_kstats = $ks->{lgrp} or
+ warn(gettext("$cmdname: can not read lgrp kstat\n)")),
+ return $load;
+
+ # Collect load for each lgroup
+ foreach my $i (keys %$lgrp_kstats) {
+ next unless $lgrp_kstats->{$i}->{"lgrp$i"};
+ my $lav = $lgrp_kstats->{$i}->{"lgrp$i"}->{"load average"};
+ # Skip this lgroup if can't find its load average
+ next unless defined $lav;
+ my $scale = $lgrp_kstats->{$i}->{"lgrp$i"}->{"loadscale"} ||
+ LGRP_LOADAVG_THREAD_MAX;
+ $load->{$i} = sprintf (gettext("Load: %4.3g"), $lav / $scale);
+ }
+ return $load;
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index 8dcfb87192..c291bd84b3 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -3356,6 +3356,7 @@ static const mdb_dcmd_t dcmds[] = {
/* from lgrp.c */
{ "lgrp", "?[-q] [-p | -Pih]", "display an lgrp", lgrp},
+ { "lgrp_set", "", "display bitmask of lgroups as a list", lgrp_set},
/* from log.c */
{ "msgbuf", "?[-v]", "print most recent console messages", msgbuf },
@@ -3679,11 +3680,16 @@ static const mdb_walker_t walkers[] = {
leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini },
/* from lgrp.c */
- { "lgrp_cpulist", "given an lgrp, walk cpus",
- lgrp_cpulist_walk_init, lgrp_cpulist_walk_step,
- NULL },
- { "lgrptbl", "walk the lgrp table",
+ { "lgrp_cpulist", "walk CPUs in a given lgroup",
+ lgrp_cpulist_walk_init, lgrp_cpulist_walk_step, NULL },
+ { "lgrptbl", "walk lgroup table",
lgrp_walk_init, lgrp_walk_step, NULL },
+ { "lgrp_parents", "walk up lgroup lineage from given lgroup",
+ lgrp_parents_walk_init, lgrp_parents_walk_step, NULL },
+ { "lgrp_rsrc_mem", "walk lgroup memory resources of given lgroup",
+ lgrp_rsrc_mem_walk_init, lgrp_set_walk_step, NULL },
+ { "lgrp_rsrc_cpu", "walk lgroup CPU resources of given lgroup",
+ lgrp_rsrc_cpu_walk_init, lgrp_set_walk_step, NULL },
/* from list.c */
{ LIST_WALK_NAME, LIST_WALK_DESC,
diff --git a/usr/src/cmd/mdb/common/modules/genunix/lgrp.c b/usr/src/cmd/mdb/common/modules/genunix/lgrp.c
index 95d9a98326..b79a4ef755 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/lgrp.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/lgrp.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -467,39 +467,278 @@ lgrp_walk_init(mdb_walk_state_t *wsp)
return (WALK_NEXT);
}
+
+/*
+ * Common routine for several walkers.
+ * Read lgroup from wsp->walk_addr and call wsp->walk_callback for it.
+ * Normally returns the result of the callback.
+ * Returns WALK_DONE if walk_addr is NULL and WALK_ERR if cannot read the
+ * lgroup.
+ */
+static int
+lgrp_walk_step_common(mdb_walk_state_t *wsp)
+{
+ lgrp_t lgrp;
+
+ if (wsp->walk_addr == NULL)
+ return (WALK_DONE);
+
+ if (mdb_vread(&lgrp, sizeof (lgrp_t), wsp->walk_addr) == -1) {
+ mdb_warn("unable to read lgrp at %p", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+
+ return (wsp->walk_callback(wsp->walk_addr, &lgrp, wsp->walk_cbdata));
+}
+
+/*
+ * Get one lgroup from the lgroup table and adjust lwd_iter to point to the next
+ * one.
+ */
int
lgrp_walk_step(mdb_walk_state_t *wsp)
{
lgrp_walk_data_t *lwd = wsp->walk_data;
+ int status = lgrp_walk_step_common(wsp);
+
+ if (status == WALK_NEXT) {
+ lwd->lwd_iter++;
+
+ if (lwd->lwd_iter >= lwd->lwd_nlgrps) {
+ status = WALK_DONE;
+ } else {
+ wsp->walk_addr = lwd->lwd_lgrp_tbl[lwd->lwd_iter];
+
+ if (wsp->walk_addr == NULL) {
+ mdb_warn("NULL lgrp pointer in lgrp_table[%d]",
+ lwd->lwd_iter);
+ return (WALK_ERR);
+ }
+ }
+ }
+
+ return (status);
+}
+
+/*
+ * Initialize walker to traverse parents of lgroups. Nothing to do here.
+ */
+/* ARGSUSED */
+int
+lgrp_parents_walk_init(mdb_walk_state_t *wsp)
+{
+ return (WALK_NEXT);
+}
+
+/*
+ * Call wsp callback on current lgroup in wsp and replace the lgroup with its
+ * parent.
+ */
+int
+lgrp_parents_walk_step(mdb_walk_state_t *wsp)
+{
lgrp_t lgrp;
int status;
+ if (wsp->walk_addr == NULL)
+ return (WALK_DONE);
- if (mdb_vread(&lgrp, sizeof (struct lgrp),
- wsp->walk_addr) == -1) {
- mdb_warn("unable to read lgrp at %p",
- wsp->walk_addr);
+ if (mdb_vread(&lgrp, sizeof (struct lgrp), wsp->walk_addr) == -1) {
+ mdb_warn("couldn't read 'lgrp' at %p", wsp->walk_addr);
return (WALK_ERR);
}
- status = wsp->walk_callback(wsp->walk_addr, &lgrp,
- wsp->walk_cbdata);
+ status = wsp->walk_callback(wsp->walk_addr, &lgrp, wsp->walk_cbdata);
- if (status != WALK_NEXT)
- return (status);
+ if (status == WALK_NEXT)
+ wsp->walk_addr = (uintptr_t)lgrp.lgrp_parent;
+
+ return (status);
+}
+
+/*
+ * Given the set return the ID of the first member of the set.
+ * Returns LGRP_NONE if the set has no elements smaller than max_lgrp.
+ */
+static lgrp_id_t
+lgrp_set_get_first(klgrpset_t set, int max_lgrp)
+{
+ lgrp_id_t id;
+ klgrpset_t bit = 1;
- lwd->lwd_iter++;
+ if (set == (klgrpset_t)0)
+ return (LGRP_NONE);
+
+ for (id = 0; (id < max_lgrp) && !(set & bit); id++, bit <<= 1)
+ ;
+
+ if (id >= max_lgrp)
+ id = LGRP_NONE;
+
+ return (id);
+}
- if (lwd->lwd_iter >= lwd->lwd_nlgrps)
+/*
+ * lgrp_set_walk_data is used to walk lgroups specified by a set.
+ * On every iteration one element is removed from the set.
+ */
+typedef struct lgrp_set_walk_data {
+ int lswd_nlgrps; /* Number of lgroups */
+ uintptr_t *lwsd_lgrp_tbl; /* Full lgroup table */
+ klgrpset_t lwsd_set; /* Set of lgroups to walk */
+} lgrp_set_walk_data_t;
+
+/*
+ * Initialize iterator for walkers over a set of lgroups
+ */
+static int
+lgrp_set_walk_init(mdb_walk_state_t *wsp, klgrpset_t set)
+{
+ lgrp_set_walk_data_t *lwsd;
+ int nlgrps;
+ lgrp_id_t id;
+ GElf_Sym sym;
+
+ /* Nothing to do if the set is empty */
+ if (set == (klgrpset_t)0)
return (WALK_DONE);
- wsp->walk_addr = lwd->lwd_lgrp_tbl[lwd->lwd_iter];
+ lwsd = mdb_zalloc(sizeof (lgrp_set_walk_data_t), UM_SLEEP | UM_GC);
+
+ /* Get the total number of lgroups */
+ if (mdb_readsym(&nlgrps, sizeof (int), "lgrp_alloc_max") == -1) {
+ mdb_warn("symbol 'lgrp_alloc_max' not found");
+ return (WALK_ERR);
+ }
+
+ if (nlgrps < 0) {
+ mdb_warn("lgrp_alloc_max of bounds (%d)\n", nlgrps);
+ return (WALK_ERR);
+ }
+
+ nlgrps++;
- if (wsp->walk_addr == NULL) {
- mdb_warn("NULL lgrp pointer in lgrp_table[%d]",
- lwd->lwd_iter);
+ /* Find ID of the first lgroup in the set */
+ if ((id = lgrp_set_get_first(set, nlgrps)) == LGRP_NONE) {
+ mdb_warn("No set elements within %d lgroups\n", nlgrps);
return (WALK_ERR);
}
+ /* Read lgroup_table and copy it to lwsd_lgrp_tbl */
+ if (mdb_lookup_by_name("lgrp_table", &sym) == -1) {
+ mdb_warn("failed to find 'lgrp_table'");
+ return (WALK_ERR);
+ }
+
+ /* Get number of valid entries in lgrp_table */
+ if (sym.st_size < nlgrps * sizeof (lgrp_t *)) {
+ mdb_warn("lgrp_table size inconsistent with lgrp_alloc_max");
+ return (WALK_ERR);
+ }
+
+ lwsd->lwsd_lgrp_tbl = mdb_alloc(sym.st_size, UM_SLEEP | UM_GC);
+ lwsd->lswd_nlgrps = nlgrps;
+
+ if (mdb_readsym(lwsd->lwsd_lgrp_tbl, nlgrps * sizeof (lgrp_t *),
+ "lgrp_table") == -1) {
+ mdb_warn("unable to read lgrp_table");
+ return (WALK_ERR);
+ }
+
+ wsp->walk_data = lwsd;
+
+ /* Save the first lgroup from the set and remove it from the set */
+ wsp->walk_addr = lwsd->lwsd_lgrp_tbl[id];
+ lwsd->lwsd_set = set & ~(1 << id);
+
return (WALK_NEXT);
}
+
+/*
+ * Get current lgroup and advance the lgroup to the next one in the lwsd_set.
+ */
+int
+lgrp_set_walk_step(mdb_walk_state_t *wsp)
+{
+ lgrp_id_t id = 0;
+ lgrp_set_walk_data_t *lwsd = wsp->walk_data;
+ int status = lgrp_walk_step_common(wsp);
+
+ if (status == WALK_NEXT) {
+ id = lgrp_set_get_first(lwsd->lwsd_set, lwsd->lswd_nlgrps);
+ if (id == LGRP_NONE) {
+ status = WALK_DONE;
+ } else {
+ /* Move to the next lgroup in the set */
+ wsp->walk_addr = lwsd->lwsd_lgrp_tbl[id];
+
+ /* Remove id from the set */
+ lwsd->lwsd_set = lwsd->lwsd_set & ~(1 << id);
+ }
+ }
+
+ return (status);
+}
+
+/*
+ * Initialize resource walker for a given lgroup and resource. The lgroup
+ * address is specified in walk_addr.
+ */
+static int
+lgrp_rsrc_walk_init(mdb_walk_state_t *wsp, int resource)
+{
+ lgrp_t lgrp;
+
+ if (mdb_vread(&lgrp, sizeof (struct lgrp), wsp->walk_addr) == -1) {
+ mdb_warn("couldn't read 'lgrp' at %p", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+
+ return (lgrp_set_walk_init(wsp, lgrp.lgrp_set[resource]));
+}
+
+/*
+ * Initialize CPU resource walker
+ */
+int
+lgrp_rsrc_cpu_walk_init(mdb_walk_state_t *wsp)
+{
+ return (lgrp_rsrc_walk_init(wsp, LGRP_RSRC_CPU));
+}
+
+/*
+ * Initialize memory resource walker
+ */
+int
+lgrp_rsrc_mem_walk_init(mdb_walk_state_t *wsp)
+{
+ return (lgrp_rsrc_walk_init(wsp, LGRP_RSRC_MEM));
+}
+
+/*
+ * Display bitmap as a list of integers
+ */
+/* ARGSUSED */
+int
+lgrp_set(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint64_t set = (uint64_t)addr;
+ uint64_t mask = 1;
+ int i = 0;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ return (DCMD_USAGE);
+ }
+
+ if (set == 0)
+ return (DCMD_OK);
+
+ for (; set != (uint64_t)0; i++, mask <<= 1) {
+ if (set & mask) {
+ mdb_printf("%d ", i);
+ set &= ~mask;
+ }
+ }
+ mdb_printf("\n");
+ return (DCMD_OK);
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/lgrp.h b/usr/src/cmd/mdb/common/modules/genunix/lgrp.h
index d6b3af1286..ed6d9f4aa9 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/lgrp.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/lgrp.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,7 +41,15 @@ extern int lgrp_cpulist_walk_step(mdb_walk_state_t *);
extern int lgrp_walk_init(mdb_walk_state_t *);
extern int lgrp_walk_step(mdb_walk_state_t *);
+extern int lgrp_parents_walk_init(mdb_walk_state_t *);
+extern int lgrp_parents_walk_step(mdb_walk_state_t *);
+
+extern int lgrp_rsrc_cpu_walk_init(mdb_walk_state_t *);
+extern int lgrp_rsrc_mem_walk_init(mdb_walk_state_t *);
+extern int lgrp_set_walk_step(mdb_walk_state_t *);
+
extern int lgrp(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int lgrp_set(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int print_range(int start, int end, int separator);
extern void print_cpuset_range(ulong_t *cs, int words, int width);
diff --git a/usr/src/cmd/perl/5.6.1/contrib/Makefile b/usr/src/cmd/perl/5.6.1/contrib/Makefile
index 983abcacad..498ed01b0b 100644
--- a/usr/src/cmd/perl/5.6.1/contrib/Makefile
+++ b/usr/src/cmd/perl/5.6.1/contrib/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -45,6 +45,7 @@ PERL_DYNAMIC_EXT = \
$(SUN_SOLARIS)/Task \
$(SUN_SOLARIS)/Exacct \
$(SUN_SOLARIS)/Privilege \
+ $(SUN_SOLARIS)/Lgrp \
$(SUN_SOLARIS)/Ucred
# Add any pure-perl extensions here.
diff --git a/usr/src/cmd/perl/5.8.4/contrib/Makefile b/usr/src/cmd/perl/5.8.4/contrib/Makefile
index 618d139432..32aa28c230 100644
--- a/usr/src/cmd/perl/5.8.4/contrib/Makefile
+++ b/usr/src/cmd/perl/5.8.4/contrib/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -49,6 +49,7 @@ PERL_DYNAMIC_EXT = \
$(SUN_SOLARIS)/Task \
$(SUN_SOLARIS)/Exacct \
$(SUN_SOLARIS)/Privilege \
+ $(SUN_SOLARIS)/Lgrp \
$(SUN_SOLARIS)/Ucred
# Add any pure-perl extensions here.
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes
new file mode 100644
index 0000000000..6f50cb517b
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Changes
@@ -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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+Version %I%
+Initial Public Source release.
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm
new file mode 100644
index 0000000000..c2ae106a96
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.pm
@@ -0,0 +1,302 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# Lgrp.pm provides procedural and object-oriented interface to the Solaris
+# liblgrp(3LIB) library.
+#
+
+#ident "%Z%%M% %I% %E% SMI"
+
+require 5.6.1;
+use strict;
+use warnings;
+use Carp;
+
+package Sun::Solaris::Lgrp;
+
+our $VERSION = '%I%';
+use XSLoader;
+XSLoader::load(__PACKAGE__, $VERSION);
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+our (@EXPORT_OK, %EXPORT_TAGS);
+
+# Things to export
+my @lgrp_constants = qw(LGRP_AFF_NONE LGRP_AFF_STRONG LGRP_AFF_WEAK
+ LGRP_CONTENT_DIRECT LGRP_CONTENT_HIERARCHY
+ LGRP_MEM_SZ_FREE LGRP_MEM_SZ_INSTALLED LGRP_VER_CURRENT
+ LGRP_VER_NONE LGRP_VIEW_CALLER
+ LGRP_VIEW_OS LGRP_NONE
+ LGRP_RSRC_CPU LGRP_RSRC_MEM
+ LGRP_CONTENT_ALL LGRP_LAT_CPU_TO_MEM
+);
+
+my @proc_constants = qw(P_PID P_LWPID P_MYID);
+
+my @constants = (@lgrp_constants, @proc_constants);
+
+my @functions = qw(lgrp_affinity_get lgrp_affinity_set
+ lgrp_children lgrp_cookie_stale lgrp_cpus lgrp_fini
+ lgrp_home lgrp_init lgrp_latency lgrp_latency_cookie
+ lgrp_mem_size lgrp_nlgrps lgrp_parents
+ lgrp_root lgrp_version lgrp_view lgrp_resources
+ lgrp_isleaf lgrp_lgrps lgrp_leaves);
+
+my @all = (@constants, @functions);
+
+# Define symbolic names for various subsets of export lists
+%EXPORT_TAGS = ('CONSTANTS' => \@constants,
+ 'LGRP_CONSTANTS' => \@lgrp_constants,
+ 'PROC_CONSTANTS' => \@proc_constants,
+ 'FUNCTIONS' => \@functions,
+ 'ALL' => \@all);
+
+# Define things that are ok ot export.
+@EXPORT_OK = ( @{ $EXPORT_TAGS{'ALL'} } );
+
+#
+# _usage(): print error message and terminate the program.
+#
+sub _usage
+{
+ my $msg = shift;
+ Carp::croak "Usage: Sun::Solaris::Lgrp::$msg";
+}
+
+#
+# lgrp_isleaf($cookie, $lgrp)
+# Returns T if lgrp is leaf, F otherwise.
+#
+sub lgrp_isleaf
+{
+ scalar @_ == 2 or _usage "lgrp_isleaf(cookie, lgrp)";
+ return (!lgrp_children(shift, shift));
+}
+
+#
+# lgrp_lgrps($cookie, [$lgrp])
+# Returns: list of lgrps in a subtree starting from $lgrp.
+# If $root is not specified, use lgrp_root.
+# undef on failure.
+sub lgrp_lgrps
+{
+ scalar @_ > 0 or _usage("lgrp_lgrps(cookie, [lgrp])");
+ my $cookie = shift;
+ my $root = shift;
+ $root = lgrp_root($cookie) unless defined $root;
+ return unless defined $root;
+ my @children = lgrp_children($cookie, $root);
+ my @result;
+
+ #
+ # Concatenate root with subtrees for every children. Every subtree is
+ # obtained by calling lgrp_lgrps recursively with each of the children
+ # as the argument.
+ #
+ @result = @children ?
+ ($root, map {lgrp_lgrps($cookie, $_)} @children) :
+ ($root);
+ return (wantarray ? @result : scalar @result);
+}
+
+#
+# lgrp_leaves($cookie, [$lgrp])
+# Returns: list of leaves in the hierarchy starting from $lgrp.
+# If $lgrp is not specified, use lgrp_root.
+# undef on failure.
+#
+sub lgrp_leaves
+{
+ scalar @_ > 0 or _usage("lgrp_leaves(cookie, [lgrp])");
+ my $cookie = shift;
+ my $root = shift;
+ $root = lgrp_root($cookie) unless defined $root;
+ return unless defined $root;
+ my @result = grep {
+ lgrp_isleaf($cookie, $_)
+ } lgrp_lgrps($cookie, $root);
+ return (wantarray ? @result : scalar @result);
+}
+
+######################################################################
+# Object-Oriented interface.
+######################################################################
+
+#
+# cookie: extract cookie from the argument.
+# If the argument is scalar, it is the cookie itself, otherwise it is the
+# reference to the object and the cookie value is in $self->{COOKIE}.
+#
+sub cookie
+{
+ my $self = shift;
+ return ((ref $self) ? $self->{COOKIE} : $self);
+}
+
+#
+# new: The object constructor
+#
+sub new
+{
+ my $class = shift;
+ my ($self, $view);
+ $view = shift;
+ $self->{COOKIE} = ($view ? lgrp_init($view) : lgrp_init()) or
+ croak("lgrp_init: $!\n"), return;
+ bless($self, $class) if defined($class);
+ bless($self) unless defined($class);
+ return ($self);
+}
+
+#
+# DESTROY: the object destructor.
+#
+sub DESTROY
+{
+ lgrp_fini(cookie(shift));
+}
+
+############################################################
+# Wrapper methods.
+#
+sub stale
+{
+ scalar @_ == 1 or _usage("stale(class)");
+ return (lgrp_cookie_stale(cookie(shift)));
+}
+
+sub view
+{
+ scalar @_ == 1 or _usage("view(class)");
+ return (lgrp_view(cookie(shift)));
+}
+
+sub root
+{
+ scalar @_ == 1 or _usage("root(class)");
+ return (lgrp_root(cookie(shift)));
+}
+
+sub nlgrps
+{
+ scalar @_ == 1 or _usage("nlgrps(class)");
+ return (lgrp_nlgrps(cookie(shift)));
+}
+
+sub lgrps
+{
+ scalar @_ > 0 or _usage("lgrps(class, [lgrp])");
+ return (lgrp_lgrps(cookie(shift), shift));
+}
+
+sub leaves
+{
+ scalar @_ > 0 or _usage("leaves(class, [lgrp])");
+ return (lgrp_leaves(cookie(shift), shift));
+}
+
+sub version
+{
+ scalar @_ > 0 or _usage("leaves(class, [version])");
+ shift;
+ return (lgrp_version(shift || 0));
+}
+
+sub children
+{
+ scalar @_ == 2 or _usage("children(class, lgrp)");
+ return (lgrp_children(cookie(shift), shift));
+}
+
+sub parents
+{
+ scalar @_ == 2 or _usage("parents(class, lgrp)");
+ return (lgrp_parents(cookie(shift), shift));
+}
+
+sub mem_size
+{
+ scalar @_ == 4 or _usage("mem_size(class, lgrp, type, content)");
+ return (lgrp_mem_size(cookie(shift), shift, shift, shift));
+}
+
+sub cpus
+{
+ scalar @_ == 3 or _usage("cpus(class, lgrp, content)");
+ return (lgrp_cpus(cookie(shift), shift, shift));
+}
+
+sub isleaf
+{
+ scalar @_ == 2 or _usage("isleaf(class, lgrp)");
+ lgrp_isleaf(cookie(shift), shift);
+}
+
+sub resources
+{
+ scalar @_ == 3 or _usage("resources(class, lgrp, resource)");
+ return (lgrp_resources(cookie(shift), shift, shift));
+}
+
+sub latency
+{
+ scalar @_ == 3 or _usage("latency(class, from, to)");
+ return (lgrp_latency_cookie(cookie(shift), shift, shift));
+}
+
+# Methods that do not require cookie
+sub home
+{
+ scalar @_ == 3 or _usage("home(class, idtype, id)");
+ shift;
+ return (lgrp_home(shift, shift));
+}
+
+sub affinity_get
+{
+ scalar @_ == 4 or _usage("affinity_get(class, idtype, id, lgrp)");
+ shift;
+ return (lgrp_affinity_get(shift, shift, shift));
+}
+
+sub affinity_set
+{
+ scalar @_ == 5 or
+ _usage("affinity_set(class, idtype, id, lgrp, affinity)");
+ shift;
+ return (lgrp_affinity_set(shift, shift, shift, shift));
+}
+
+1;
+
+__END__
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs
new file mode 100644
index 0000000000..a2df0fd073
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Lgrp.xs
@@ -0,0 +1,397 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Lgrp.xs contains XS wrappers for the system locality group library
+ * liblgrp(3LIB).
+ */
+
+#include <sys/errno.h>
+#include <sys/lgrp_user.h>
+
+/*
+ * On i386 Solaris defines SP, which conflicts with the perl definition of SP
+ * We don't need the Solaris one, so get rid of it to avoid warnings.
+ */
+#undef SP
+
+/* Perl XS includes. */
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+/* Return undef in scalar context and empty list in list context */
+#define LGRP_BADVAL() { \
+ if (GIMME_V == G_ARRAY) \
+ XSRETURN_EMPTY; \
+ else \
+ XSRETURN_UNDEF; \
+}
+
+/*
+ * Push all values from input array onto the perl return stack.
+ */
+#define PUSHARRAY(array, nitems) \
+{ \
+ int x; \
+ \
+ if (nitems < 0) { \
+ LGRP_BADVAL() \
+ } else if (nitems > 0) { \
+ EXTEND(SP, nitems); \
+ for (x = 0; x < nitems; x++) { \
+ PUSHs(sv_2mortal(newSVnv(array[x]))); \
+ } \
+ } \
+}
+
+/*
+ * Several constants are not present in the first version of the Lgrp API,
+ * we define them here.
+ *
+ * lgrp_resources() and lgrp_latency_cookie() only appear in API v2. If the
+ * module is linked with old version of liblgrp(3LIB) there is no lgrp_resources
+ * symbol in the library and perl wrapper returns empty list and sets errno to
+ * EINVAL.
+ *
+ * The lgrp_latency_cookie() is emulated using lgrp_latency().
+ */
+#if LGRP_VER_CURRENT == 1
+#define LGRP_CONTENT_ALL LGRP_CONTENT_HIERARCHY
+#define LGRP_LAT_CPU_TO_MEM 0
+#define LGRP_RSRC_CPU 0 /* CPU resources */
+#define LGRP_RSRC_MEM 1 /* memory resources */
+
+#define LGRP_RESOURCES(c, lgrp, type) \
+ { errno = EINVAL; LGRP_BADVAL(); }
+
+/*
+ * Simulate lgrp_latency_cookie() which just fails. This macro is never called
+ * and we just define it so that the C compiler will not complain about the
+ * missing symbol.
+ */
+#define lgrp_latency_cookie(c, f, t, b) (errno = EINVAL, -1)
+
+#else
+#define LGRP_RESOURCES(c, lgrp, type) { \
+ int nr; \
+ lgrp_id_t *lgrps; \
+ \
+ errno = 0; \
+ nr = lgrp_resources(c, lgrp, NULL, 0, type); \
+ if (nr < 0) \
+ LGRP_BADVAL(); \
+ if (GIMME_V == G_SCALAR) \
+ XSRETURN_IV(nr); \
+ if (nr == 0) { \
+ XSRETURN_EMPTY; \
+ } else if (New(0, lgrps, nr, lgrp_id_t) == NULL) { \
+ errno = ENOMEM; \
+ LGRP_BADVAL(); \
+ } else { \
+ nr = lgrp_resources(c, lgrp, lgrps, nr, type); \
+ PUSHARRAY(lgrps, nr); \
+ Safefree(lgrps); \
+ } \
+}
+#endif
+
+/*
+ * Special version of lgrp_latency_cookie(). Use lgrp_latency() for liblgrp V1
+ * and lgrp_latency_cookie for V2.
+ */
+static int
+_lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to,
+ int between)
+{
+ return (LGRP_VER_CURRENT < 2 ?
+ lgrp_latency(from, to) :
+ lgrp_latency_cookie(cookie, from, to, between));
+}
+
+/*
+ * Most functions in liblgrp return -1 on failure. The perl equivalent returns
+ * 'undef' instead. The macro should be call after the RETVAL is set to the
+ * return value of the function.
+ */
+#define RETURN_UNDEF_IF_FAIL { if (RETVAL < 0) XSRETURN_UNDEF; }
+
+/*
+ * End of C part, start of XS part.
+ *
+ * The XS code exported to perl is below here. Note that the XS preprocessor
+ * has its own commenting syntax, so all comments from this point on are in
+ * that form.
+ */
+
+MODULE = Sun::Solaris::Lgrp PACKAGE = Sun::Solaris::Lgrp
+PROTOTYPES: ENABLE
+
+ #
+ # Define any constants that need to be exported. By doing it this way we can
+ # avoid the overhead of using the DynaLoader package, and in addition constants
+ # defined using this mechanism are eligible for inlining by the perl
+ # interpreter at compile time.
+ #
+BOOT:
+ {
+ HV *stash;
+
+ stash = gv_stashpv("Sun::Solaris::Lgrp", TRUE);
+ newCONSTSUB(stash, "LGRP_AFF_NONE", newSViv(LGRP_AFF_NONE));
+ newCONSTSUB(stash, "LGRP_AFF_STRONG", newSViv(LGRP_AFF_STRONG));
+ newCONSTSUB(stash, "LGRP_AFF_WEAK", newSViv(LGRP_AFF_WEAK));
+ newCONSTSUB(stash, "LGRP_VER_CURRENT", newSViv(LGRP_VER_CURRENT));
+ newCONSTSUB(stash, "LGRP_VER_NONE", newSViv(LGRP_VER_NONE));
+ newCONSTSUB(stash, "LGRP_NONE", newSViv(LGRP_NONE));
+ newCONSTSUB(stash, "LGRP_RSRC_CPU", newSViv(LGRP_RSRC_CPU));
+ newCONSTSUB(stash, "LGRP_RSRC_MEM", newSViv(LGRP_RSRC_MEM));
+ newCONSTSUB(stash, "LGRP_CONTENT_HIERARCHY",
+ newSViv(LGRP_CONTENT_HIERARCHY));
+ newCONSTSUB(stash, "LGRP_CONTENT_DIRECT", newSViv(LGRP_CONTENT_DIRECT));
+ newCONSTSUB(stash, "LGRP_VIEW_CALLER", newSViv(LGRP_VIEW_CALLER));
+ newCONSTSUB(stash, "LGRP_VIEW_OS", newSViv(LGRP_VIEW_OS));
+ newCONSTSUB(stash, "LGRP_MEM_SZ_FREE", newSViv(LGRP_MEM_SZ_FREE));
+ newCONSTSUB(stash, "LGRP_MEM_SZ_INSTALLED",
+ newSViv(LGRP_MEM_SZ_INSTALLED));
+ newCONSTSUB(stash, "LGRP_CONTENT_ALL", newSViv(LGRP_CONTENT_ALL));
+ newCONSTSUB(stash, "LGRP_LAT_CPU_TO_MEM", newSViv(LGRP_LAT_CPU_TO_MEM));
+ newCONSTSUB(stash, "P_PID", newSViv(P_PID));
+ newCONSTSUB(stash, "P_LWPID", newSViv(P_LWPID));
+ newCONSTSUB(stash, "P_MYID", newSViv(P_MYID));
+ }
+
+ #
+ # The code below uses POSTCALL directive which allows to return 'undef'
+ # whenever a C function returns a negative value.
+ #
+
+
+ #
+ # lgrp_init([view])
+ # Use LGRP_VIEW_OS as the default view.
+ #
+lgrp_cookie_t
+lgrp_init(lgrp_view_t view = LGRP_VIEW_OS)
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+lgrp_view_t
+lgrp_view(cookie)
+ lgrp_cookie_t cookie
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+lgrp_affinity_t
+lgrp_affinity_get(idtype, id, lgrp)
+ idtype_t idtype;
+ id_t id;
+ lgrp_id_t lgrp;
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+int
+lgrp_affinity_set(idtype, id, lgrp, affinity)
+ idtype_t idtype;
+ id_t id;
+ lgrp_id_t lgrp;
+ lgrp_affinity_t affinity;
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+ XSRETURN_YES;
+
+int
+lgrp_cookie_stale(cookie)
+ lgrp_cookie_t cookie;
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+int
+lgrp_fini(cookie)
+ lgrp_cookie_t cookie;
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+ XSRETURN_YES;
+
+lgrp_id_t
+lgrp_home(idtype, id)
+ idtype_t idtype;
+ id_t id;
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+int
+lgrp_latency(lgrp_id_t from,lgrp_id_t to)
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+lgrp_mem_size_t
+lgrp_mem_size(cookie, lgrp, type, content)
+ lgrp_cookie_t cookie
+ lgrp_id_t lgrp
+ int type
+ lgrp_content_t content
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+int
+lgrp_nlgrps(cookie)
+ lgrp_cookie_t cookie;
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+lgrp_id_t
+lgrp_root(cookie)
+ lgrp_cookie_t cookie
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+
+int
+lgrp_version(int version = LGRP_VER_NONE)
+
+ #
+ # lgrp_latency_cookie calls our internal wrapper _lgrp_latency_cookie() which
+ # works for both old and new versions of liblgrp.
+ #
+int
+lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to, int between = 0)
+ CODE:
+ RETVAL = _lgrp_latency_cookie(cookie, from, to, between);
+ POSTCALL:
+ RETURN_UNDEF_IF_FAIL;
+ OUTPUT:
+ RETVAL
+
+ #
+ # Functions below convert C arrays into Perl lists. They use XS PPCODE
+ # directive to avoid implicit RETVAL assignments and manipulate perl
+ # stack directly.
+ #
+ # When called in scalar context functions return the number of elements
+ # in the list or undef on failure.
+ #
+ # The PUSHARRAY() macro defined above pushes all values from the C array to
+ # the perl stack.
+ #
+
+ #
+ # @children = lgrp_children($cookie, $parent).
+ #
+void
+lgrp_children(cookie, lgrp)
+ lgrp_cookie_t cookie;
+ lgrp_id_t lgrp;
+ PREINIT:
+ lgrp_id_t *lgrps;
+ int count;
+ PPCODE:
+ errno = 0;
+ if ((count = lgrp_children(cookie, lgrp, NULL, 0)) < 0)
+ LGRP_BADVAL();
+
+ if (GIMME_V == G_SCALAR)
+ XSRETURN_IV(count);
+
+ if (count > 0) {
+ if (New(0, lgrps, count, lgrp_id_t) == NULL) {
+ errno = ENOMEM;
+ LGRP_BADVAL();
+ } else {
+ count = lgrp_children(cookie, lgrp, lgrps, count);
+ PUSHARRAY(lgrps, count);
+ Safefree(lgrps);
+ }
+ }
+
+ #
+ # @parents = lgrp_parents($cookie, $lgrp).
+ #
+void
+lgrp_parents(cookie, lgrp)
+ lgrp_cookie_t cookie;
+ lgrp_id_t lgrp;
+ PREINIT:
+ lgrp_id_t *lgrps;
+ int count;
+ PPCODE:
+ errno = 0;
+ if ((count = lgrp_parents(cookie, lgrp, NULL, 0)) < 0)
+ LGRP_BADVAL();
+
+ if (GIMME_V == G_SCALAR)
+ XSRETURN_IV(count);
+
+ if (count > 0) {
+ if (New(0, lgrps, count, lgrp_id_t) == NULL) {
+ errno = ENOMEM;
+ LGRP_BADVAL();
+ } else {
+ count = lgrp_parents(cookie, lgrp, lgrps, count);
+ PUSHARRAY(lgrps, count);
+ Safefree(lgrps);
+ }
+ }
+
+ #
+ # @parents = lgrp_cpus($cookie, $lgrp, $content).
+ # Content should be LGRP_CONTENT_HIERARCHY or LGRP_CONTENT_ALL or
+ # LGRP_CONTENT_DIRECT
+void
+lgrp_cpus(cookie, lgrp, content)
+ lgrp_cookie_t cookie;
+ lgrp_id_t lgrp;
+ lgrp_content_t content;
+ PREINIT:
+ int ncpus;
+ processorid_t *cpus;
+ PPCODE:
+ errno = 0;
+ if ((ncpus = lgrp_cpus(cookie, lgrp, NULL, 0, content)) < 0)
+ LGRP_BADVAL();
+
+ if (GIMME_V == G_SCALAR)
+ XSRETURN_IV(ncpus);
+
+ if (ncpus > 0) {
+ if (New(0, cpus, ncpus, processorid_t) == NULL) {
+ errno = ENOMEM;
+ LGRP_BADVAL();
+ } else {
+ ncpus = lgrp_cpus(cookie, lgrp, cpus, ncpus, content);
+ PUSHARRAY(cpus, ncpus);
+ Safefree(cpus);
+ }
+ }
+
+void
+lgrp_resources(cookie, lgrp, type)
+ lgrp_cookie_t cookie;
+ lgrp_id_t lgrp;
+ int type;
+ PPCODE:
+ LGRP_RESOURCES(cookie, lgrp, type);
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST
new file mode 100644
index 0000000000..ea60af447b
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/MANIFEST
@@ -0,0 +1,10 @@
+Changes
+Makefile.PL
+MANIFEST
+Lgrp.pm
+Lgrp.xs
+README
+typemap
+pod/Lgrp.pod
+t/Lgrp.t
+t/Lgrp_api.t
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL
new file mode 100644
index 0000000000..821f43fb29
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/Makefile.PL
@@ -0,0 +1,89 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+require 5.6.1;
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+# #defines.
+my @defines = ( DEFINE => exists($ENV{RELEASE_BUILD}) ? '-DNDEBUG' : '' );
+
+# List of POD pages to install.
+my @man3pods = ( MAN3PODS => {} );
+
+
+#
+# If not building as part of ON.
+#
+if (! exists($ENV{CODEMGR_WS})) {
+
+ #
+ # Suppress the setting of LD_RUN_PATH. The ON build environment
+ # contains a modified MakeMaker that does this automatically, so we
+ # only need to do this if we are building outside of ON.
+ #
+ package MY;
+ no warnings qw(once);
+
+ # Override const_loadlibs to remove LD_RUN_PATH cruft.
+ *const_loadlibs = sub
+ {
+ my $self = shift(@_);
+ delete($self->{LD_RUN_PATH});
+ return($self->SUPER::const_loadlibs(@_));
+ };
+
+ # Override dynamic_lib to get rid of LD_RUN_PATH cruft.
+ *dynamic_lib = sub
+ {
+ my $self = shift(@_);
+ my $txt = $self->SUPER::dynamic_lib(@_);
+ $txt =~ s/LD_RUN_PATH=\S*\s*//;
+ return($txt);
+ };
+
+ # Turn off debugging.
+ @defines = ();
+
+ #
+ # Install the POD documentation for non-ON builds.
+ #
+ my $man3pfx = '$(INST_MAN3DIR)/Sun::Solaris::Lgrp';
+ @man3pods = (
+ MAN3PODS => { 'pod/Lgrp.pod' => $man3pfx . '.$(MAN3EXT)' }
+ );
+}
+
+WriteMakefile(
+ NAME => 'Sun::Solaris::Lgrp',
+ VERSION_FROM => 'Lgrp.pm',
+ LIBS => ['-llgrp '],
+ @defines,
+ @man3pods,
+);
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README
new file mode 100644
index 0000000000..c5c7d9ddfd
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+Licensing
+---------
+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.
+
+Availability
+------------
+This module is only available for Solaris 9u6 onwards.
+
+Description
+-----------
+This module provided access to the Solaris locality group library
+liblgrp(3LIB). It provides both functional and object-oriented interface to all
+functions defined in liblgrp(3LIB).
+
+The t directory contains two files: Lgrp.t and Lgrp_api.t which contain lots
+of examples (and serve as tests).
+
+Installation
+------------
+
+1. Uncompress and untar the archive
+2. cd to the module directory
+3. perl Makefile.PL; make test; make install
+
+If you are using gcc and wish to build this module against the perl shipped as
+part of Solaris, see the Solaris-PerlGcc module, also available from CPAN.
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON
new file mode 100644
index 0000000000..4e195006ca
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/README.ON
@@ -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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+This module is released as Public Source via the Comprehensive Perl Archive
+Network (http://www.cpan.org). If it is updated for any reason, a new
+release should be pushed to CPAN.
+
+The version number of this module is derived from the $VERSION variable in
+Lgrp.pm. This in turn contains the SID of the Lgrp.pm file, so if
+anything under this directory is modified, a delta should be made to Lgrp.pm
+to update the module version number correctly. If this is not done, it will
+not be possible to upload the generated archive to CPAN, as CPAN requires that
+uploaded archives have unique names.
+
+To prepare the contents of the directory for release, they should be packaged
+up as a .tar.gz archive. The procedure for this is:
+
+$ /usr/perl5/bin/perl Makefile.PL
+$ make tardist
+
+This will produce the file for upload to CPAN. Contact cpan-support@sun.com
+for details of how to do this.
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod
new file mode 100644
index 0000000000..00d07c0a9c
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/pod/Lgrp.pod
@@ -0,0 +1,738 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# Sun::Solaris::Lgrp documentation.
+#
+
+=head1 NAME
+
+Lgrp - Perl interface to Solaris liblgrp library.
+
+=head1 SYNOPSIS
+
+ use Sun::Solaris::Lgrp qw(:ALL);
+
+ # initialize lgroup interface
+ my $cookie = lgrp_init(LGRP_VIEW_OS | LGRP_VIEW_CALLER);
+ my $l = Sun::Solaris::Lgrp->new(LGRP_VIEW_OS | LGRP_VIEW_CALLER);
+
+ my $version = lgrp_version(LGRP_VER_CURRENT | LGRP_VER_NONE);
+ $version = $l->version(LGRP_VER_CURRENT | LGRP_VER_NONE);
+
+ $home = lgrp_home(P_PID, P_MYID);
+ $home = l->home(P_PID, P_MYID);
+
+ lgrp_affinity_set(P_PID, $pid, $lgrp,
+ LGRP_AFF_STRONG | LGRP_AFF_WEAK | LGRP_AFF_NONE);
+ $l->affinity_set(P_PID, $pid, $lgrp,
+ LGRP_AFF_STRONG | LGRP_AFF_WEAK | LGRP_AFF_NONE);
+
+ my $affinity = lgrp_affinity_get(P_PID, $pid, $lgrp);
+ $affinity = $l->affinity_get(P_PID, $pid, $lgrp);
+
+ my $nlgrps = lgrp_nlgrps($cookie);
+ $nlgrps = $l->nlgrps();
+
+ my $root = lgrp_root($cookie);
+ $root = l->root();
+
+ $latency = lgrp_latency($lgrp1, $lgrp2);
+ $latency = $l->latency($lgrp1, $lgrp2);
+
+ my @children = lgrp_children($cookie, $lgrp);
+ @children = l->children($lgrp);
+
+ my @parents = lgrp_parents($cookie, $lgrp);
+ @parents = l->parents($lgrp);
+
+ my @lgrps = lgrp_lgrps($cookie);
+ @lgrps = l->lgrps();
+
+ @lgrps = lgrp_lgrps($cookie, $lgrp);
+ @lgrps = l->lgrps($lgrp);
+
+ my @leaves = lgrp_leaves($cookie);
+ @leaves = l->leaves();
+
+ my $is_leaf = lgrp_isleaf($cookie, $lgrp);
+ $is_leaf = $l->is_leaf($lgrp);
+
+ my @cpus = lgrp_cpus($cookie, $lgrp,
+ LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT);
+ @cpus = l->cpus($lgrp, LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT);
+
+ my $memsize = lgrp_mem_size($cookie, $lgrp,
+ LGRP_MEM_SZ_INSTALLED | LGRP_MEM_SZ_FREE,
+ LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT);
+ $memsize = l->mem_size($lgrp,
+ LGRP_MEM_SZ_INSTALLED | LGRP_MEM_SZ_FREE,
+ LGRP_CONTENT_HIERARCHY | LGRP_CONTENT_DIRECT);
+
+ my $is_stale = lgrp_cookie_stale($cookie);
+ $stale = l->stale();
+
+ lgrp_fini($cookie);
+
+ # The following is available for API version greater than 1:
+
+ my @lgrps = lgrp_resources($cookie, $lgrp, LGRP_RSRC_CPU);
+
+ # Get latencies from cookie
+ $latency = lgrp_latency_cookie($cookie, $from, $to);
+
+=head1 DESCRIPTION
+
+This module provides access to the C<liblgrp(3LIB)> library and to various
+constants and functions defined in C<sys/lgrp_sys.h> header file. It provides
+both the procedural and object interface to the library. The procedural
+interface requires (in most cases) passing a transparent cookie around. The
+object interface hides all the cookie manipulations from the user.
+
+Functions returning scalar value indicate error by returning B<undef>. The
+caller may examine the B<$!> variable to get the C<errno> value.
+
+Functions returning list value return the number of elements in the list when
+called in scalar context. In case of error the empty list is return in the array
+context and B<undef> is returned in the scalar context.
+
+=head2 CONSTANTS
+
+The constants are exported with B<:CONSTANTS> or B<:ALL> tags:
+
+ use Sun::Solaris::Lgrp ':ALL';
+
+or
+
+ use Sun::Solaris::Lgrp ':CONSTANTS';
+
+The following constants are available for use in Perl programs:
+
+
+ LGRP_NONE
+
+ LGRP_VER_CURRENT
+ LGRP_VER_NONE
+
+ LGRP_VIEW_CALLER
+ LGRP_VIEW_OS
+
+ LGRP_AFF_NONE
+ LGRP_AFF_STRONG
+ LGRP_AFF_WEAK
+
+ LGRP_CONTENT_DIRECT
+ LGRP_CONTENT_HIERARCHY
+
+ LGRP_MEM_SZ_FREE
+ LGRP_MEM_SZ_INSTALLED
+
+ LGRP_RSRC_CPU (1)
+ LGRP_RSRC_MEM (1)
+ LGRP_CONTENT_ALL (1)
+ LGRP_LAT_CPU_TO_MEM(1)
+
+ P_PID
+ P_LWPID
+ P_MYID
+
+(1) Available for versions of the liblgrp(3LIB) API greater than 1.
+
+=head2 functions
+
+A detailed description of each function follows. Since this module is intended
+to provide a Perl interface to the routines in L<liblgrp(3LIB)>, a very short
+description is given for the corresponding functions in this module and a
+reference is given to the complete description in the L<liblgrp(3LIB)> man
+pages. Any differences or additional functionality in the Perl module are
+highlighted and fully documented here.
+
+=over
+
+=item lgrp_init([LGRP_VIEW_CALLER | LGRP_VIEW_OS])
+
+The function initializes the lgroup interface and takes a snapshot of the lgroup
+hierarchy with the given view. Given the view, L<lgrp_init()> returns a cookie
+representing this snapshot of the lgroup hierarchy. This cookie should be used
+with other routines in the lgroup interface needing the lgroup hierarchy. The
+L<lgrp_fini()> function should be called with the cookie when it is no longer
+needed. Unlike L<lgrp_init (3LGRP)>, C<LGRP_VIEW_OS> is assumed as the default if
+no view is provided.
+
+Upon successful completion, L<lgrp_init()> returns a cookie. Otherwise it returns
+B<undef> and sets B<$!> to indicate the error.
+
+See L<lgrp_init(3LGRP)> for more information.
+
+=item lgrp_fini($cookie)
+
+The function takes a cookie, frees the snapshot of the lgroup hierarchy created
+by L<lgrp_init()>, and cleans up anything else set up by L<lgrp_init()>. After
+this function is called, the cookie returned by the lgroup interface might no
+longer be valid and should not be used.
+
+Upon successful completion, 1 is returned. Otherwise, B<undef> is returned and
+B<$!> is set to indicate the error.
+
+See L<lgrp_fini(3LGRP)> for more information.
+
+=item lgrp_view($cookie)
+
+The function takes a cookie representing the snapshot of the lgroup hierarchy
+and returns the snapshot's view of the lgroup hierarchy.
+
+If the given view is C<LGRP_VIEW_CALLER>, the snapshot contains only the
+resources that are available to the caller (such as those with respect to
+processor sets). When the view is C<LGRP_VIEW_OS>, the snapshot contains what
+is available to the operating system.
+
+Upon succesful completion, the function returns the view for the snapshot of the
+lgroup hierarchy represented by the given cookie. Otherwise, B<undef> is
+returned and C<$!> is set.
+
+See L<lgrp_view(3LGRP)> for more information.
+
+=item lgrp_home($idtype, $id)
+
+Returns the home lgroup for the given process or thread. The B<$idtype> argument
+should be C<P_PID> to specify a process and the B<$id> argument should be its
+process id. Otherwise, the B<$idtype> argument should be C<P_LWPID> to specify a
+thread and the B<$id> argument should be its LWP id. The value C<P_MYID> can be
+used for the id argument to specify the current process or thread.
+
+Upon successful completion, C<lgrp_home()> returns the id of the home lgroup of
+the specified process or thread. Otherwise, B<undef> is returned and B<$!> is
+set to indicate the error.
+
+See L<lgrp_home(3LGRP)> for more information.
+
+=item lgrp_cookie_stale($cookie)
+
+Upon successful completion, the function returns whether the cookie is
+stale. Otherwise, it returns B<undef> and sets B<$!> to indicate the error.
+
+The L<lgrp_cookie_stale()> function will fail with C<EINVAL> if the cookie is
+not valid.
+
+See L<lgrp_cookie_stale(3LGRP)> for more information.
+
+=item lgrp_cpus($cookie, $lgrp, $context)
+
+The function takes a cookie representing a snapshot of the lgroup hierarchy and
+returns the list of CPUs in the lgroup specified by B<$lgrp>. The B<$context>
+argument should be set to one of the following values to specify whether the
+direct contents or everything in this lgroup including its children should be
+returned:
+
+=over
+
+=item LGRP_CONTENT_HIERARCHY
+
+Everything within this hierarchy.
+
+=item LGRP_CONTENT_DIRECT
+
+Directly contained in lgroup.
+
+=back
+
+When called in scalar context, L<lgrp_cpus()> function returns the number of
+CPUs, contained in the specified lgroup.
+
+In case of error B<undef> is returned in scalar context and B<$!> is set to
+indicate the error. In list context the empty list is returned and B<$!> is set.
+
+See L<lgrp_cpus(3LGRP)> for more information.
+
+=item lgrp_children($cookie, $lgrp)
+
+The function takes a cookie representing a snapshot of the lgroup hierarchy and
+returns the list of lgroups that are children of the specified lgroup.
+
+When called in scalar context, L<lgrp_children()> function returns the number of
+children lgroups for the specified lgroup.
+
+In case of error B<undef> or empty list is returned and B<$!> is set to indicate
+the error.
+
+See L<lgrp_children(3LGRP)> for more information.
+
+=item lgrp_parents($cookie, $lgrp)
+
+The function takes a cookie representing a snapshot of the lgroup hierarchy and
+returns the list of parent of the specified lgroup.
+
+When called in scalar context, L<lgrp_parents()> function returns the number of
+parent lgroups for the specified lgroup.
+
+In case of error B<undef> or empty list is returned and B<$!> is set to indicate
+the error.
+
+See L<lgrp_parents(3LGRP)> for more information.
+
+=item lgrp_nlgrps($cookie)
+
+The function takes a cookie representing a snapshot of the lgroup hierarchy. It
+returns the number of lgroups in the hierarchy where the number is always at
+least one.
+
+In case of error B<undef> is returned and B<$!> is set to EINVAL indicatng that
+the cookie is not valid.
+
+See L<lgrp_nlgrps(3LGRP)> for more information.
+
+=item lgrp_root($cookie)
+
+The function returns the root lgroup ID. In case of error B<undef> is returned
+and B<$!> is set to EINVAL indicatng that the cookie is not valid.
+
+See L<lgrp_root(3LGRP)> for more information.
+
+=item lgrp_mem_size($cookie, $lgrp, $type, $content)
+
+The function takes a cookie representing a snapshot of the lgroup hierarchy. The
+function returns the memory size of the given lgroup in bytes. The B<$type>
+argument should be set to one of the following values:
+
+=over
+
+=item LGRP_MEM_SZ_FREE
+
+Free memory.
+
+=item LGRP_MEM_SZ_INSTALLED
+
+Installed memory.
+
+=back
+
+The B<$content> argument should be set to one of the following values to specify
+whether the direct contents or everything in this lgroup including its children
+should be returned:
+
+=over
+
+=item LGRP_CONTENT_HIERARCHY
+
+Everything within this hierarchy.
+
+=item LGRP_CONTENT_DIRECT
+
+Directly contained in lgroup.
+
+=back
+
+The total sizes include all the memory in the lgroup including its children,
+while the others reflect only the memory contained directly in the given lgroup.
+
+Upon successful completion, the size in bytes is returned. Otherwise, B<undef>
+is returned and B<$!> is set to indicate the error.
+
+See L<lgrp_mem_size(3LGRP)> for more information.
+
+=item lgrp_version([$version])
+
+The function takes an interface version number, B$version>, as an argument and
+returns an lgroup interface version. The B<$version> argument should be the
+value of C<LGRP_VER_CURRENT> or C<LGRP_VER_NONE> to find out the current lgroup
+interface version on the running system.
+
+If B<$version> is still supported by the implementation, then L<lgrp_version()>
+returns the requested version. If C<LGRP_VER_NONE> is returned, the
+implementation cannot support the requested version.
+
+If B<$version> is C<LGRP_VER_NONE>, L<lgrp_version()> returns the current version of
+the library.
+
+The following example tests whether the version of the
+interface used by the caller is supported:
+
+ lgrp_version(LGRP_VER_CURRENT) == LGRP_VER_CURRENT or
+ die("Built with unsupported lgroup interface");
+
+See L<lgrp_version(3LGRP)> for more information.
+
+=item lgrp_affinity_set($idtype, $id, $lgrp, $affinity)
+
+The function sets of LWPs specified by the B<$idtype> and B<$id> arguments have
+for the given lgroup.
+
+The function sets the affinity that the LWP or set of LWPs specified by $idtype
+and $id have for the given lgroup. The lgroup affinity can be set to
+C<LGRP_AFF_STRONG>, C<LGRP_AFF_WEAK>, or C<LGRP_AFF_NONE>.
+
+If the B<$idtype> is C<P_PID>, the affinity is retrieved for one of the LWPs in
+the process or set for all the LWPs of the process with process id (PID) B<$id>.
+The affinity is retrieved or set for the LWP of the current process with LWP id
+$id if idtype is C<P_LWPID>. If $id is C<P_MYID>, then the current LWP or
+process is specified.
+
+There are different levels of affinity that can be specified by a thread for a
+particuliar lgroup. The levels of affinity are the following from strongest to
+weakest:
+
+=over
+
+=item LGRP_AFF_STRONG
+
+Strong affinity.
+
+=item LGRP_AFF_WEAK
+
+Weak affinity.
+
+=item LGRP_AFF_NONE
+
+No affinity.
+
+=back
+
+Upon successful completion, L<lgrp_affinity_set()> return 1. Otherwise, it
+returns B<undef> and set B<$!> to indicate the error.
+
+See L<lgrp_affinity_set(3LGRP)> for more information.
+
+=item lgrp_affinity_get($idtype, $id, $lgrp)
+
+The function returns the affinity that the LWP has to a given lgrp. See
+L<lgrp_affinity_get()> for detailed description.
+
+See L<lgrp_affinity_get(3LGRP)> for more information.
+
+=item lgrp_latency_cookie($cookie, $from, $to, [$between=LGRP_LAT_CPU_TO_MEM])
+
+The function takes a cookie representing a snapshot of the lgroup hierarchy and
+returns the latency value between a hardware resource in the $from lgroup to a
+hardware resource in the B<$to> lgroup. If B<$from> is the same lgroup as $to, the
+latency value within that lgroup is returned.
+
+The optional B<between> argument should be set to C<LGRP_LAT_CPU_TO_MEM> to specify
+between which hardware resources the latency should be measured. Currently the
+only valid value is C<LGRP_LAT_CPU_TO_MEM> which represents latency from CPU to
+memory.
+
+Upon successful completion, lgrp_latency_cookie() return 1. Otherwise, it
+returns B<undef> and set B<$!> to indicate the error. For LGRP API version 1 the
+L<lgrp_latency_cookie()> is an alias for L<lgrp_latency()>.
+
+See L<lgrp_latency_cookie(3LGRP)> for more information.
+
+=item lgrp_latency($from, $to)
+
+The function is similiar to the L<lgrp_latency_cookie()> function, but returns the
+latency between the given lgroups at the given instant in time. Since lgroups
+may be freed and reallocated, this function may not be able to provide a
+consistent answer across calls. For that reason, it is recommended that
+L<lgrp_latency_cookie()> function be used in its place.
+
+See L<lgrp_latency(3LGRP)> for more information.
+
+=item lgrp_resources($cookie, $lgrp, $type)
+
+Return the list of lgroups directly containing resources of the specified type.
+The resources are represented by a set of lgroups in which each lgroup directly
+contains CPU and/or memory resources.
+
+The type can be specified as
+
+=over
+
+=item C<LGRP_RSRC_CPU>
+
+CPU resources
+
+=item C<LGRP_RSRC_MEM>
+
+Memory resources
+
+=back
+
+In case of error B<undef> or empty list is returned and B<$!> is set to indicate
+the error.
+
+This function is only available for API version 2 and will return B<undef> or
+empty list for API version 1 and set $! to C<EINVAL>.
+
+See C<lgrp_resources(3LGRP)> for more information.
+
+=item lgrp_lgrps($cookie, [$lgrp])
+
+Returns list of all lgroups in a hierarchy starting from $lgrp. If B<$lgrp> is
+not specified, uses the value of lgrp_root($cookie). Returns the empty list on
+failure.
+
+When called in scalar context, returns the total number of lgroups in the
+system.
+
+=item lgrp_leaves($cookie, [$lgrp])
+
+Returns list of all leaf lgroups in a hierarchy starting from $lgrp. If $lgrp is
+not specified, uses the value of lgrp_root($cookie). Returns B<undef> or empty
+list on failure.
+
+When called in scalar context, returns the total number of leaf lgroups in the
+system.
+
+=item lgrp_isleaf($cookie, $lgrp)
+
+Returns B<True> if $lgrp is leaf (has no children), B<False> otherwise.
+
+=back
+
+=head2 Object Methods
+
+=over
+
+=item new([$view])
+
+Creates a new Sun::Solaris::Lgrp object. An optional argument is passed to
+L<lgrp_init()> function. By default uses C<LGRP_VIEW_OS>.
+
+=item cookie()
+
+Returns a transparent cookie that may be passed to functions accepting cookie.
+
+=item version([$version])
+
+Without the argument returns the current version of the L<liblgrp(3LIB)>
+library. This is a wrapper for L<lgrp_version()> with C<LGRP_VER_NONE> as the
+default version argument.
+
+=item stale()
+
+Returns B<T> if the lgroup information in the object is stale, B<F>
+otherwise. It is a wrapper for L<lgrp_cookie_stale()>.
+
+=item view()
+
+Returns the snapshot's view of the lgroup hierarchy. It is a wrapper for
+L<lgrp_view()>.
+
+=item root()
+
+Returns the root lgroup. It is a wrapper for L<lgrp_root()>.
+
+=item children($lgrp)
+
+Returns the list of lgroups that are children of the specified lgroup. This is a
+wrapper for L<lgrp_children()>.
+
+=item parents($lgrp)
+
+Returns the list of lgroups that are parents of the specified lgroup. This is a
+wrapper for L<lgrp_parents()>.
+
+=item nlgrps()
+
+Returns the number of lgroups in the hierarchy. This is a wrapper for
+L<lgrp_nlgrps()>.
+
+=item mem_size($lgrp, $type, $content)
+
+Returns the memory size of the given lgroup in bytes. This is a wrapper for
+L<lgrp_mem_size()>.
+
+=item cpus($lgrp, $context)
+
+Returns the list of CPUs in the lgroup specified by $lgrp. This is a wrapper for
+L<lgrp_cpus()>.
+
+=item resources($lgrp, $type)
+
+Returns the list of lgroups directly containing resources of the specified
+type. This is a wrapper for L<lgrp_resources()>.
+
+=item home($idtype, $id)
+
+Returns the home lgroup for the given process or thread. This is a wrapper for
+L<lgrp_home()>.
+
+=item affinity_get($idtype, $id, $lgrp)
+
+Returns the affinity that the LWP has to a given lgrp. This is a wrapper for
+L<lgrp_affinity_get()>.
+
+=item affinity_set($idtype, $id, $lgrp, $affinity)
+
+Sets of LWPs specified by the $idtype and $id arguments have for the given lgroup.
+This is a wrapper for L<lgrp_affinity_set()>.
+
+=item lgrps([$lgrp])
+
+Returns list of all lgroups in a hierarchy starting from $lgrp (or the
+L<lgrp_root()> if $lgrp is not specified). This is a wrapper for L<lgrp_lgrps()>.
+
+=item leaves([$lgrp])
+
+Returns list of all leaf lgroups in a hierarchy starting from B<$lgrp>. If $lgrp
+is not specified, uses the value of lgrp_root(). This is a wrapper for
+L<lgrp_leaves()>.
+
+=item isleaf($lgrp)
+
+Returns B<True> if B<$lgrp> is leaf (has no children), B<False> otherwise.
+This is a wrapper for L<lgrp_isleaf()>.
+
+=item latency($from, $to)
+
+Returns the latency value between a hardware resource in the $from lgroup to a
+hardware resource in the B<$to> lgroup. It will use L<lgrp_latency()> for
+version 1 of liblgrp(3LIB) and L<lgrp_latency_cookie()> for newer versions.
+
+=back
+
+=head2 EXPORTS
+
+By default nothing is exported from this module. The following tags can be used
+to selectively import constants and functions defined in this module:
+
+=over
+
+=item :LGRP_CONSTANTS
+
+LGRP_AFF_NONE, LGRP_AFF_STRONG, LGRP_AFF_WEAK, LGRP_CONTENT_DIRECT,
+LGRP_CONTENT_HIERARCHY, LGRP_MEM_SZ_FREE, LGRP_MEM_SZ_INSTALLED,
+LGRP_VER_CURRENT, LGRP_VER_NONE, LGRP_VIEW_CALLER, LGRP_VIEW_OS,
+LGRP_NONE, LGRP_RSRC_CPU, LGRP_RSRC_MEM, LGRP_CONTENT_ALL,
+LGRP_LAT_CPU_TO_MEM.
+
+=item :PROC_CONSTANTS
+
+P_PID, P_LWPID P_MYID
+
+=item :CONSTANTS
+
+:LGRP_CONSTANTS, :PROC_CONSTANTS
+
+=item :FUNCTIONS
+
+lgrp_affinity_get(), lgrp_affinity_set(), lgrp_children(), lgrp_cookie_stale(),
+lgrp_cpus(), lgrp_fini(), lgrp_home(), lgrp_init(), lgrp_latency(),
+lgrp_latency_cookie(), lgrp_mem_size(), lgrp_nlgrps(), lgrp_parents(),
+lgrp_root(), lgrp_version(), lgrp_view(), lgrp_resources(),
+lgrp_lgrps(), lgrp_leaves(), lgrp_isleaf(), lgrp_lgrps(), lgrp_leaves().
+
+=item :ALL
+
+:CONSTANTS, :FUNCTIONS
+
+=back
+
+=head2 Error values
+
+The functions in this module return B<undef> or an empty list when an underlying
+library function fails. The B<$!> is set to provide more information values for
+the error. The following error codes are possible:
+
+=over
+
+=item EINVAL
+
+The value supplied is not valid.
+
+=item ENOMEM
+
+There was not enough system memory to complete an operation.
+
+=item EPERM
+
+The effective user of the calling process does not have appropriate privileges,
+and its real or effective user ID does not match the real or effective user ID
+of one of the threads.
+
+=item ESRCH
+
+The specified process or thread was not found.
+
+=back
+
+=head2 Difference in the API versions
+
+The C<liblgrp(3LIB)> is versioned. The exact version which was used to compile a
+module is available through B<lgrp_version> function.
+
+Version 2 of the lgrpp_user API introduced the following constants and
+functions, nbot present in version 1:
+
+=over
+
+=item C<LGRP_RSRC_CPU> constant
+
+=item C<LGRP_RSRC_MEM> constant
+
+=item C<LGRP_CONTENT_ALL> constant
+
+=item C<LGRP_LAT_CPU_TO_MEM> constant
+
+=item C<lgrp_resources()> function
+
+=item C<lgrp_latency_cookie()> function
+
+=back
+
+The C<LGRP_RSRC_CPU> and C<LGRP_RSRC_MEM> are not defined for version 1. The
+L<lgrp_resources()> function is defined for version 1 but always returns empty
+list. The L<lgrp_latency_cookie()> function is an alias for lgrp_latency for
+version 1.
+
+=head1 ATTRIBUTES
+
+See L<attributes(5)> for descriptions of the following attributes:
+
+ ___________________________________________________________
+ | ATTRIBUTE TYPE | ATTRIBUTE VALUE |
+ |_____________________________|_____________________________|
+ | Availability | SUNWpl5u |
+ |_____________________________|_____________________________|
+ | Interface Stability | Unstable |
+ |_____________________________|_____________________________|
+
+
+=head1 SEE ALSO
+
+L<liblgrp(3LIB)>,
+L<lgrp_affinity_get(3LGRP)>,
+L<lgrp_affinity_set(3LGRP)>,
+L<lgrp_children(3LGRP)>,
+L<lgrp_cookie_stale(3LGRP)>,
+L<lgrp_cpus(3LGRP)>,
+L<lgrp_fini(3LGRP)>,
+L<lgrp_home(3LGRP)>,
+L<lgrp_init(3LGRP)>,
+L<lgrp_latency(3LGRP)>,
+L<lgrp_mem_size(3LGRP)>,
+L<lgrp_nlgrps(3LGRP)>,
+L<lgrp_parents(3LGRP)>,
+L<lgrp_root(3LGRP)>,
+L<lgrp_version(3LGRP)>,
+L<lgrp_view(3LGRP)>,
+L<lgrp_resources(3LGRP)>,
+L<lgrp_latency_cookie(3LGRP)>,
+L<attributes(5)>
+
+=cut
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t
new file mode 100755
index 0000000000..eb242a7499
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp.t
@@ -0,0 +1,308 @@
+#! /usr/perl5/bin/perl
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# Tests for Sun::Solaris::Lgrp API.
+#
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl Lgrp.t'
+#
+# The test uses Test module which is available on Perl 5.6 and later.
+#
+
+
+use strict;
+use warnings;
+use Test;
+
+# Tests to run
+BEGIN { plan tests => 63 }
+
+use Sun::Solaris::Lgrp ':ALL';
+
+#
+######################################################################
+
+my ($home, $fail);
+
+######################################################################
+# Check that all exported constants can be accessed.
+$fail = 0;
+foreach my $constname (qw(
+ LGRP_AFF_NONE LGRP_AFF_STRONG LGRP_AFF_WEAK LGRP_CONTENT_DIRECT
+ LGRP_CONTENT_HIERARCHY LGRP_MEM_SZ_FREE
+ LGRP_MEM_SZ_INSTALLED LGRP_VER_CURRENT LGRP_VER_NONE
+ LGRP_VIEW_CALLER LGRP_VIEW_OS LGRP_RSRC_CPU LGRP_RSRC_MEM
+ LGRP_CONTENT_ALL LGRP_LAT_CPU_TO_MEM)) {
+ next if (eval "my \$a = $constname; 1");
+ $fail++;
+}
+
+ok($fail, 0, 'All Constants defined' );
+
+#########################
+
+######################################################################
+# Verify lgrp_version
+##
+my $version = lgrp_version(-1);
+ok($version, LGRP_VER_NONE, 'incorrect lgrp version unsupported');
+
+$version = lgrp_version(LGRP_VER_NONE);
+ok($version, LGRP_VER_CURRENT, 'lgrp version is current');
+
+$version = lgrp_version(LGRP_VER_CURRENT);
+ok($version, LGRP_VER_CURRENT, 'support LGRP_VER_CURRENT version');
+#
+#######################################################################
+
+######################################################################
+# Verify that lgrp_init()/lgrp_fini work.
+##
+my $c = lgrp_init(LGRP_VIEW_CALLER);
+ok($c) or
+ die("lgrp_init: $!");
+
+my $view = lgrp_view($c);
+
+ok($view, LGRP_VIEW_CALLER, 'View is LGRP_VIEW_CALLER');
+
+my $fin = lgrp_fini($c);
+ok($fin);
+
+# Try to free it again, it should fail.
+$fin = lgrp_fini($c);
+ok($fin, undef, 'lgrp_fini second time should fail');
+
+$c = lgrp_init(LGRP_VIEW_OS);
+ok($c) or
+ die("lgrp_init: $!");
+
+$view = lgrp_view($c);
+
+ok($view, LGRP_VIEW_OS, 'View is LGRP_VIEW_OS');
+#
+######################################################################
+
+######################################################################
+# root should have ID 0.
+##
+my $root = lgrp_root($c);
+ok($root, 0, 'root should have id zero');
+#
+######################################################################
+# Verify lgrp_nlgrps()
+##
+my $nlgrps = lgrp_nlgrps($c);
+ok($nlgrps);
+
+my @lgrps = lgrp_lgrps($c);
+ok(@lgrps);
+ok(scalar @lgrps, $nlgrps, 'lgrp_nlgrps() should match number of lgrps');
+ok($nlgrps, lgrp_lgrps($c), 'lgrp_lgrps() in scalar context is sane');
+
+######################################################################
+# All root children should have root as their one and only one parent
+##
+$fail = 0;
+my @children = lgrp_children($c, $root);
+ok(scalar @children, lgrp_children($c, $root), 'lgrp_children as scalar');
+my @leaves = lgrp_leaves $c;
+ok(scalar @leaves);
+ok(scalar @leaves, lgrp_leaves $c);
+ok(scalar @children <= scalar @leaves);
+
+my @parents;
+
+my $fail_lgrp_parents = 0;
+
+foreach my $l (@children) {
+ @parents = lgrp_parents($c, $l) or
+ (print STDERR "# lgrp_parents: $!\n"), $fail++, last;
+ my $nparents = @parents;
+ my ($parent, @rest) = @parents;
+ $fail++ if $parent != $root;
+ $fail++ unless $nparents == 1;
+ $fail_lgrp_parents++ if $nparents != lgrp_parents($c, $l);
+}
+ok($fail, 0, 'correct parents for children');
+ok($fail_lgrp_parents, 0, 'correct lgrp_parents() as scalar');
+
+######################################################################
+# Illegal parents have no children
+##
+@children = lgrp_children($c, -1);
+my $nchildren = lgrp_children($c, -1);
+ok(scalar @children, 0, 'Illegal parents have no children');
+# Same in scalar context
+ok($nchildren, undef, 'No children means undef as scalar');
+
+######################################################################
+# root should have no parents.
+##
+@parents = lgrp_parents($c, $root);
+ok(scalar @parents, 0, 'root should have no parents');
+# Same in scalar context
+ok(lgrp_parents($c, $root), 0);
+#
+######################################################################
+# Illegal children have no paremts
+##
+@parents = lgrp_parents($c, -1);
+my $nparents = lgrp_parents($c, -1);
+ok(scalar @parents, 0, 'Illegal children have no paremts');
+# Same in scalar context
+ok($nparents, undef, 'No parents means undef as scalar');
+#
+######################################################################
+# Root should have non-zero CPUs and memory size
+##
+my @cpus = lgrp_cpus($c, $root, LGRP_CONTENT_HIERARCHY);
+my $ncpus = lgrp_cpus($c, $root, LGRP_CONTENT_HIERARCHY);
+ok(scalar @cpus, $ncpus);
+ok($ncpus);
+ok(lgrp_mem_size($c, $root, LGRP_MEM_SZ_INSTALLED, LGRP_CONTENT_HIERARCHY));
+my @ncpus_bad = lgrp_cpus($c, $root, -1);
+ok(scalar @ncpus_bad, 0, 'Bad argument to lgrp_cpus should return empty');
+my $ncpus_bad = lgrp_cpus($c, $root, -1);
+ok($ncpus_bad, undef, 'Bad argument to lgrp_cpus should return undef');
+#
+######################################################################
+
+######################################################################
+# The cookie should not be stale
+#
+ok(! lgrp_cookie_stale($c));
+#
+######################################################################
+
+######################################################################
+# Can we call lgrp_latency?
+# The latencies from lgrp_latency and lgrp_latency_cookie should match.
+##
+my $latency = lgrp_latency($root, $root);
+ok(defined $latency);
+
+my $latency1 = lgrp_latency_cookie($c, $root, $root);
+ok(defined $latency1);
+ok($latency, $latency1, 'Latencies should match');
+#
+######################################################################
+# Can we call lgrp_resources?
+##
+my @lgrps_c = lgrp_resources($c, $root, LGRP_RSRC_CPU);
+my $nresources = lgrp_resources($c, $root, LGRP_RSRC_CPU);
+ok(!defined $nresources) if $version < 2;
+ok(scalar @lgrps_c, 0) if $version < 2;
+ok($nresources) if $version >= 2;
+ok(@lgrps_c) if $version >= 2;
+
+##
+# lgrp_fini should always succeed.
+ok(lgrp_fini($c));
+
+
+######################################################################
+# Now test Object-Oriented interface.
+##
+$c = Sun::Solaris::Lgrp->new or
+ die "Lgrp->new(LGRP_VIEW_OS): $!";
+
+ok($c->view, LGRP_VIEW_OS);
+ok($c->stale, 0, 'cookie is not stale');
+ok($nlgrps, $c->nlgrps, 'nlgrps');
+my @lg1 = $c->lgrps;
+ok(@lgrps, @lg1);
+my@leaves1 = $c->leaves;
+ok(@leaves, @leaves1) or
+ print STDERR "# \@leaves: @leaves, \@leaves1: @leaves\n";
+ok($root, $c->root);
+@cpus = lgrp_cpus($c->cookie, $root, LGRP_CONTENT_HIERARCHY);
+my @cpus1 = $c->cpus($root, LGRP_CONTENT_HIERARCHY);
+ok(@cpus, @cpus1) or
+ print STDERR "# \@cpus: @cpus, \@cpus1: @cpus1\n";
+ok(lgrp_latency($root, $root), $c->latency($root, $root));
+my @lgrps_c1 = $c->resources($root, LGRP_RSRC_CPU);
+ok(@lgrps_c, @lgrps_c1);
+ok(lgrp_version(LGRP_VER_NONE), $c->version);
+
+#
+######################################################################
+# Can we call lgrp_home?
+##
+$home = lgrp_home(P_PID, P_MYID);
+ok(defined($home));
+my $home1 = $c->home(P_PID, P_MYID);
+ok($home1 == $home);
+$home1 = lgrp_home(P_LWPID, 1);
+ok($home1 == $home);
+$home1 = $c->home(P_LWPID, 1);
+ok($home1 == $home);
+
+#
+######################################################################
+# Can we call lgrp_affinity_set?
+##
+my $affinity;
+
+ok(LGRP_AFF_WEAK);
+ok(P_LWPID);
+
+$affinity = $c->affinity_set(P_PID, P_MYID, $home, LGRP_AFF_WEAK);
+ok($affinity);
+
+$affinity = $c->affinity_set(P_LWPID, 1, $home, LGRP_AFF_WEAK);
+ok($affinity);
+
+$affinity = lgrp_affinity_set(P_PID, P_MYID, $home, LGRP_AFF_WEAK);
+ok($affinity);
+
+$affinity = lgrp_affinity_set(P_LWPID, 1, $home, LGRP_AFF_WEAK);
+ok($affinity);
+
+#
+######################################################################
+# Can we call lgrp_affinity_get?
+##
+$affinity = lgrp_affinity_get(P_PID, P_MYID, $home);
+ok($affinity = LGRP_AFF_WEAK);
+
+$affinity = lgrp_affinity_get(P_LWPID, 1, $home);
+ok($affinity == LGRP_AFF_WEAK);
+
+$affinity = $c->affinity_get(P_PID, P_MYID, $home);
+ok($affinity == LGRP_AFF_WEAK);
+
+$affinity = $c->affinity_get(P_LWPID, 1, $home);
+ok($affinity == LGRP_AFF_WEAK);
+
+#
+######################################################################
+# THE END!
+#########
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t
new file mode 100755
index 0000000000..db08ddd495
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/t/Lgrp_api.t
@@ -0,0 +1,323 @@
+#! /usr/perl5/bin/perl
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+require 5.8.0;
+use strict;
+use warnings;
+
+######################################################################
+# Tests for Sun::Solaris::Lgrp API.
+#
+# This is an example script that demonstrates use of Sun::Solaris::Lgrp module.
+# It can be used to test the module itself, the liblgrp library or the in-kernel
+# implementation.
+######################################################################
+
+# Tests to run
+use Test::More tests => 33;
+
+# Verify that we can load the module
+BEGIN { use_ok('Sun::Solaris::Lgrp') };
+
+use Sun::Solaris::Lgrp ':ALL';
+
+my ($home, $fail);
+
+######################################################################
+# Verify that lgrp_init() works.
+##
+my $c = Sun::Solaris::Lgrp->new(LGRP_VIEW_OS);
+ok($c, 'lgrp_init') or die("lgrp_init: $!");
+#
+######################################################################
+
+######################################################################
+# root should have ID 0.
+##
+my $root = $c->root;
+is($root, 0, 'root should have id zero');
+
+#
+######################################################################
+# Verify lgrp_nlgrps()
+##
+my $nlgrps = $c->nlgrps;
+ok($nlgrps, 'lgrp_nlgrps') or
+ diag("lgrp_nlgrps: $!");
+
+my $is_numa = ($nlgrps > 1);
+
+my @lgrps = $c->lgrps;
+ok(scalar @lgrps, 'Can get lgrps list') or
+ diag("lgrp_lgrps: $!");
+
+is(scalar @lgrps, $nlgrps, 'lgrp_nlgrps() should match number of lgrps');
+
+######################################################################
+# All root children should have root as their one and only one parent
+##
+$fail = 0;
+my (@children) = $c->children($root);
+my @leaves = $c->leaves;
+ok(@leaves, 'There are some leaves');
+
+cmp_ok(@children, '<=', @leaves, 'Root should have nchildren <= nleaves');
+my @parents;
+
+foreach my $l (@children) {
+ (@parents) = $c->parents($l) or
+ diag("lgrp_parents: $!");
+ my $nparents = @parents;
+ my ($parent, @rest) = @parents;
+ $fail++ if $parent != $root;
+ $fail++ unless $nparents == 1;
+}
+is($fail, 0, 'correct parents for children');
+
+######################################################################
+# Each lgrp other than root should have a single parent and
+# root should have no parents.
+##
+
+$fail = 0;
+foreach my $l (lgrp_lgrps($c)) {
+ next if $l == $root;
+ my (@parents) = $c->parents($l) or
+ diag("lgrp_parents: $!");
+ my $nparents = @parents;
+ $fail++ unless $nparents == 1;
+}
+is($fail, 0, 'All non-leaf lgrps should have single parent');
+
+@parents = $c->parents($root);
+ok(!@parents, 'root should have no parents');
+#
+#######################################################################
+
+######################################################################
+# Lgrp affinity tests.
+#######################
+
+######################################################################
+# lgrp_affinity-set should change home lgrp.
+##
+SKIP: {
+ skip 'Test only valid on NUMA platform', 1 unless $is_numa;
+ my $leaf = $leaves[0]; # Pickup any non-root lgrp.
+ $home = $c->home(P_PID, P_MYID);
+
+ # Pickup any lgrp not equal to the current one.
+ my $lgrp = ($home == $root ? $leaf : $root);
+ # Set affinity to the new lgrp.
+ $c->affinity_set(P_PID, P_MYID, $lgrp, LGRP_AFF_STRONG) or
+ diag("lgrp_affinity_set(): $!");
+ # Our home should change to a new lgrp.
+ $home = $c->home(P_PID, P_MYID);
+ is($home, $lgrp, 'Home lgrp should change after strong affinity is set');
+ # Drop affinity to the lgrp.
+ $c->affinity_set(P_PID, P_MYID, $lgrp, LGRP_AFF_NONE) or
+ diag("lgrp_affinity_set(): $!");
+}
+
+######################################################################
+# Should be able to set affinity to any legal value
+##
+
+my @affs = (LGRP_AFF_WEAK, LGRP_AFF_STRONG, LGRP_AFF_NONE);
+
+foreach my $aff (@affs) {
+ $c->affinity_set(P_PID, P_MYID, $root, $aff) or
+ diag("lgrp_affinity_set(): $!");
+ my $affinity = $c->affinity_get(P_PID, $$, $root);
+ is($affinity, $aff, "affinity should be $aff");
+}
+
+#
+######################################################################
+
+######################################################################
+# Root should have non-zero CPUs and memory size
+# Also, its memory size should be consistent with the one reported by
+# sysconfig.
+##
+my @rcpus = $c->cpus($root, LGRP_CONTENT_HIERARCHY) or
+ die("lgrp_cpus: $!");
+my $ncpus = @rcpus;
+ok($ncpus, 'there are CPUs in the system');
+
+my $memsize = $c->mem_size($root,
+ LGRP_MEM_SZ_INSTALLED,
+ LGRP_CONTENT_HIERARCHY) or
+ diag("lgrp_mem_size(): $!");
+
+ok($memsize, 'memory size is non-zero');
+#
+######################################################################
+
+######################################################################
+# The cookie should not be stale
+is($c->stale, 0, 'Cookie should not be stale');
+#
+######################################################################
+
+######################################################################
+# Latency should be non-zero.
+my $latency = lgrp_latency($root, $root);
+ok(defined $latency, 'lgrp_latency() is working') or
+ diag("lgrp_latency: $!");
+
+my $latency1 = $c->latency($root, $root);
+ok(defined $latency1, 'lgrp_latency_cookie() is working') or
+ diag("lgrp_latency_cookie: $!");
+
+is($latency, $latency1, 'Latencies should match');
+#
+######################################################################
+
+######################################################################
+# Verify latency matrix.
+##
+SKIP: {
+ skip 'Test only valid on NUMA platform', 9 unless $is_numa;
+
+ cmp_ok($latency, '>', 0, "Latency from root to self should be positive");
+ my $latencies;
+ my $min_latency = 10000;
+ my $max_latency = 0;
+ my $badlatency = 0;
+ my $assymetrical = 0;
+ my $diagonalmin = 0;
+ my $badself = 0;
+ my $nlatencies;
+
+ foreach my $l1 (@lgrps) {
+ foreach my $l2 (@lgrps) {
+ $latencies->{$l1}{$l2} = $c->latency($l1, $l2);
+ $nlatencies++ if $latencies->{$l1}{$l2};
+ }
+ }
+
+ # There should be at least some lgroups which have latencies.
+ my @d_lgrps = grep { defined $latencies->{$_}{$_} } @leaves;
+ ok(@d_lgrps, 'There should be at least some lgroups which have latencies');
+
+ # All diagonal latencies should be the same.
+ my $lat_diag_lgrp = $d_lgrps[0];
+ my $lat_diag = $latencies->{$lat_diag_lgrp}{$lat_diag_lgrp};
+ my @badlatencies = grep { $latencies->{$_}{$_} != $lat_diag } @d_lgrps;
+ is(scalar @badlatencies, 0, 'All diagonal latencies should be the same') or
+ diag("diagonal latency: $lat_diag; bad latencies: @badlatencies");
+
+ my %l_cpus;
+ my %l_mem;
+ my $lgrps_nomem;
+ my $lgrps_nocpus;
+
+ foreach my $l1 (@lgrps) {
+ $l_cpus{$l1} = scalar $c->cpus($l1, LGRP_CONTENT_HIERARCHY);
+ $l_mem{$l1} = $c->mem_size($l1, LGRP_MEM_SZ_INSTALLED,
+ LGRP_CONTENT_HIERARCHY);
+ $lgrps_nomem++ unless $l_mem{$l1};
+ $lgrps_nocpus++ unless $c->cpus($l1, LGRP_CONTENT_HIERARCHY);
+ }
+
+ # Verify latencies consistency
+ foreach my $l1 (@lgrps) {
+ # Can't get latency if source doesn't have CPUs
+ next unless $l_cpus{$l1};
+ my $self_latency = $latencies->{$l1}{$l1};
+ $lat_diag = $self_latency if $self_latency;
+
+ foreach my $l2 (@lgrps) {
+ # Can't get latenciy if destination doesn't have memory
+ next unless $l_mem{$l2};
+
+ if (! $latencies->{$l1}{$l2}) {
+ $badlatency++;
+ diag("Invalid latency between $l1 and $l2");
+ next;
+ }
+
+ $max_latency = $latencies->{$l1}{$l2} if
+ $latencies->{$l1}{$l2} > $max_latency;
+ $min_latency = $latencies->{$l1}{$l2} if
+ $latencies->{$l1}{$l2} < $min_latency;
+
+ # Latencies should be symmetrical but only if they are valid.
+ if ($latencies->{$l2}{$l1} &&
+ $latencies->{$l1}{$l2} != $latencies->{$l2}{$l1}) {
+ $assymetrical++;
+ diag("latency($l1, $l2) != latency($l2, $l1)");
+ }
+
+ $diagonalmin++ if $c->isleaf($l1) && $c->isleaf($l2) &&
+ $self_latency && $self_latency > $latencies->{$l1}{$l2};
+ }
+ }
+
+ SKIP: {
+ skip 'Symmetry test only valid if all lgroups have memory and CPUs',
+ 1 if $lgrps_nomem || $lgrps_nocpus;
+ is($assymetrical, 0, 'Latencies should be symmetrical');
+ }
+
+ is($diagonalmin, 0, 'Latency should be minimal on diagonals');
+ is($badlatency, 0, 'Latency should be defined');
+ is($max_latency, $latencies->{$root}{$root},
+ 'Root should have maximum latencies');
+ cmp_ok($min_latency, '>', 0, 'Minimum latency should be positive') if
+ $nlatencies;
+ cmp_ok($min_latency, '<=', $max_latency,
+ 'Minimum latency should be less then maximum') if $nlatencies;
+}
+
+######################################################################
+# Verify lgrp_resources API
+##
+SKIP: {
+ skip 'lgrp_resources() is not supported', 3 if
+ ((LGRP_VER_CURRENT == 1) || !$is_numa);
+
+ my @lgrps_c = $c->resources($root, LGRP_RSRC_CPU);
+ ok(scalar @lgrps_c, 'there are CPU resources in the system');
+ $fail = 0;
+ my $nc = 0;
+ foreach my $l (@lgrps_c) {
+ $fail++ unless $c->isleaf($l);
+ my @cpu_l = $c->cpus($l, LGRP_CONTENT_DIRECT);
+ $nc += @cpu_l;
+ }
+ is($fail, 0, 'Each lgrp containing CPU resources should be leaf');
+ is($nc, $ncpus, 'Number of CPUs should match');
+}
+
+#
+######################################################################
+# THE END!
+#########
diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap
new file mode 100644
index 0000000000..eb4995900a
--- /dev/null
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Lgrp/typemap
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+TYPEMAP
+ id_t T_IV
+ idtype_t T_IV
+ lgrp_affinity_t T_IV
+ lgrp_content_t T_IV
+ lgrp_cookie_t T_UV
+ lgrp_id_t T_IV
+ lgrp_view_t T_UV
+ lgrp_mem_size_t T_IV
diff --git a/usr/src/cmd/prstat/prstat.c b/usr/src/cmd/prstat/prstat.c
index e87d2a9b84..743990ad2a 100644
--- a/usr/src/cmd/prstat/prstat.c
+++ b/usr/src/cmd/prstat/prstat.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -75,8 +75,12 @@
#define PSINFO_HEADER_PROC \
" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP "
+#define PSINFO_HEADER_PROC_LGRP \
+" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/NLWP "
#define PSINFO_HEADER_LWP \
" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/LWPID "
+#define PSINFO_HEADER_LWP_LGRP \
+" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/LWPID "
#define USAGE_HEADER_PROC \
" PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP "
#define USAGE_HEADER_LWP \
@@ -99,6 +103,8 @@
"ZONEID NLWP SIZE RSS MEMORY TIME CPU ZONE "
#define PSINFO_LINE \
"%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %-.16s/%d"
+#define PSINFO_LINE_LGRP \
+"%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %4d %-.16s/%d"
#define USAGE_LINE \
"%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
"%3.3s %-.12s/%d"
@@ -133,6 +139,7 @@ static table_t cpu_tbl = {0, 0, NULL}; /* selected processors */
static table_t set_tbl = {0, 0, NULL}; /* selected processor sets */
static table_t prj_tbl = {0, 0, NULL}; /* selected projects */
static table_t tsk_tbl = {0, 0, NULL}; /* selected tasks */
+static table_t lgr_tbl = {0, 0, NULL}; /* selected lgroups */
static zonetbl_t zone_tbl = {0, 0, NULL}; /* selected zones */
static nametbl_t euid_tbl = {0, 0, NULL}; /* selected effective users */
static nametbl_t ruid_tbl = {0, 0, NULL}; /* selected real users */
@@ -147,6 +154,7 @@ static list_t users; /* list of users */
static list_t tasks; /* list of tasks */
static list_t projects; /* list of projects */
static list_t zones; /* list of zones */
+static list_t lgroups; /* list of lgroups */
static volatile uint_t sigwinch = 0;
static volatile uint_t sigtstp = 0;
@@ -235,13 +243,21 @@ list_print(list_t *list)
break;
case LT_LWPS:
if (opts.o_outpmode & OPT_LWPS) {
- if (opts.o_outpmode & OPT_PSINFO)
- (void) printf(PSINFO_HEADER_LWP);
+ if (opts.o_outpmode & OPT_PSINFO) {
+ if (opts.o_outpmode & OPT_LGRP)
+ (void) printf(PSINFO_HEADER_LWP_LGRP);
+ else
+ (void) printf(PSINFO_HEADER_LWP);
+ }
if (opts.o_outpmode & OPT_MSACCT)
(void) printf(USAGE_HEADER_LWP);
} else {
- if (opts.o_outpmode & OPT_PSINFO)
- (void) printf(PSINFO_HEADER_PROC);
+ if (opts.o_outpmode & OPT_PSINFO) {
+ if (opts.o_outpmode & OPT_LGRP)
+ (void) printf(PSINFO_HEADER_PROC_LGRP);
+ else
+ (void) printf(PSINFO_HEADER_PROC);
+ }
if (opts.o_outpmode & OPT_MSACCT)
(void) printf(USAGE_HEADER_PROC);
}
@@ -341,10 +357,20 @@ list_print(list_t *list)
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
stripfname(lwp->li_info.pr_fname);
- (void) printf(PSINFO_LINE,
- (int)lwp->li_info.pr_pid, pname,
- psize, prssize, pstate, ppri, pnice,
- ptime, pcpu, lwp->li_info.pr_fname, lwpid);
+ if (opts.o_outpmode & OPT_LGRP) {
+ (void) printf(PSINFO_LINE_LGRP,
+ (int)lwp->li_info.pr_pid, pname,
+ psize, prssize, pstate, ppri, pnice,
+ ptime, pcpu,
+ (int)lwp->li_info.pr_lwp.pr_lgrp,
+ lwp->li_info.pr_fname, lwpid);
+ } else {
+ (void) printf(PSINFO_LINE,
+ (int)lwp->li_info.pr_pid, pname,
+ psize, prssize, pstate, ppri, pnice,
+ ptime, pcpu,
+ lwp->li_info.pr_fname, lwpid);
+ }
(void) putp(t_eol);
(void) putchar('\n');
}
@@ -502,10 +528,15 @@ list_update(list_t *list, lwp_info_t *lwp)
if ((list->l_type == LT_ZONES) &&
(id->id_zoneid != lwp->li_info.pr_zoneid))
continue;
+ if ((list->l_type == LT_LGRPS) &&
+ (id->id_lgroup != lwp->li_info.pr_lwp.pr_lgrp))
+ continue;
id->id_nproc++;
id->id_taskid = lwp->li_info.pr_taskid;
id->id_projid = lwp->li_info.pr_projid;
id->id_zoneid = lwp->li_info.pr_zoneid;
+ id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp;
+
if (lwp->li_flags & LWP_REPRESENT) {
id->id_size += lwp->li_info.pr_size;
id->id_rssize += lwp->li_info.pr_rssize;
@@ -533,6 +564,7 @@ update:
id->id_projid = lwp->li_info.pr_projid;
id->id_taskid = lwp->li_info.pr_taskid;
id->id_zoneid = lwp->li_info.pr_zoneid;
+ id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp;
id->id_nproc++;
if (lwp->li_flags & LWP_REPRESENT) {
id->id_size = lwp->li_info.pr_size;
@@ -756,7 +788,9 @@ prstat_scandir(DIR *procdir)
if (!has_element(&cpu_tbl,
lwpsinfo->pr_onpro) ||
!has_element(&set_tbl,
- lwpsinfo->pr_bindpset))
+ lwpsinfo->pr_bindpset) ||
+ !has_element(&lgr_tbl,
+ lwpsinfo->pr_lgrp))
continue;
nlwps++;
if ((opts.o_outpmode & (OPT_PSETS | OPT_LWPS))
@@ -787,7 +821,8 @@ prstat_scandir(DIR *procdir)
}
} else {
if (!has_element(&cpu_tbl, psinfo.pr_lwp.pr_onpro) ||
- !has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset)) {
+ !has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset) ||
+ !has_element(&lgr_tbl, psinfo.pr_lwp.pr_lgrp)) {
fd_close(fds->fds_psinfo);
continue;
}
@@ -883,6 +918,8 @@ list_refresh(list_t *list)
list_update(&projects, lwp);
if (opts.o_outpmode & OPT_ZONES)
list_update(&zones, lwp);
+ if (opts.o_outpmode & OPT_LGRP)
+ list_update(&lgroups, lwp);
lwp->li_flags &= ~LWP_ALIVE;
lwp = lwp->li_next;
@@ -1155,7 +1192,7 @@ main(int argc, char **argv)
lwpid_init();
fd_init(Setrlimit());
- while ((opt = getopt(argc, argv, "vcmaRLtu:U:n:p:C:P:s:S:j:k:TJz:Z"))
+ while ((opt = getopt(argc, argv, "vcHmaRLtu:U:n:p:C:P:h:s:S:j:k:TJz:Z"))
!= (int)EOF) {
switch (opt) {
case 'R':
@@ -1165,6 +1202,12 @@ main(int argc, char **argv)
opts.o_outpmode &= ~OPT_TERMCAP;
opts.o_outpmode &= ~OPT_FULLSCREEN;
break;
+ case 'h':
+ fill_table(&lgr_tbl, optarg, 'h');
+ break;
+ case 'H':
+ opts.o_outpmode |= OPT_LGRP;
+ break;
case 'm':
case 'v':
opts.o_outpmode &= ~OPT_PSINFO;
@@ -1263,15 +1306,25 @@ main(int argc, char **argv)
"-a, -J, -T or -Z\n"));
if ((opts.o_outpmode & OPT_USERS) &&
- (opts.o_outpmode & (OPT_TASKS | OPT_PROJECTS | OPT_ZONES)))
+ (opts.o_outpmode &
+ (OPT_TASKS | OPT_PROJECTS | OPT_ZONES)))
Die(gettext("-a option cannot be used with "
"-t, -J, -T or -Z\n"));
if (((opts.o_outpmode & OPT_TASKS) &&
(opts.o_outpmode & (OPT_PROJECTS|OPT_ZONES))) ||
((opts.o_outpmode & OPT_PROJECTS) &&
- (opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) {
- Die(gettext("-J, -T and -Z options are mutually exclusive\n"));
+ (opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) {
+ Die(gettext(
+ "-J, -T and -Z options are mutually exclusive\n"));
+ }
+
+ /*
+ * There is not enough space to combine microstate information and
+ * lgroup information and still fit in 80-column output.
+ */
+ if ((opts.o_outpmode & OPT_LGRP) && (opts.o_outpmode & OPT_MSACCT)) {
+ Die(gettext("-H and -m options are mutually exclusive\n"));
}
if (argc > optind)
@@ -1299,11 +1352,13 @@ main(int argc, char **argv)
list_alloc(&tasks, opts.o_nbottom);
list_alloc(&projects, opts.o_nbottom);
list_alloc(&zones, opts.o_nbottom);
+ list_alloc(&lgroups, opts.o_nbottom);
list_setkeyfunc(sortk, &opts, &lwps, LT_LWPS);
list_setkeyfunc(NULL, &opts, &users, LT_USERS);
list_setkeyfunc(NULL, &opts, &tasks, LT_TASKS);
list_setkeyfunc(NULL, &opts, &projects, LT_PROJECTS);
list_setkeyfunc(NULL, &opts, &zones, LT_ZONES);
+ list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS);
if (opts.o_outpmode & OPT_TERMCAP)
curses_on();
if ((procdir = opendir("/proc")) == NULL)
diff --git a/usr/src/cmd/prstat/prstat.h b/usr/src/cmd/prstat/prstat.h
index 7bfe4798a6..1a13329845 100644
--- a/usr/src/cmd/prstat/prstat.h
+++ b/usr/src/cmd/prstat/prstat.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -69,6 +69,7 @@ extern "C" {
#define OPT_PROJECTS 0x1000 /* report about system projects */
#define OPT_ZONES 0x2000 /* report about zones */
#define OPT_PSETS 0x4000 /* report for specified psets */
+#define OPT_LGRP 0x8000 /* report home lgroups */
/*
* Flags to keep track of process or lwp status
@@ -84,6 +85,7 @@ extern "C" {
#define LT_TASKS 0x0004
#define LT_PROJECTS 0x0008
#define LT_ZONES 0x0010
+#define LT_LGRPS 0x0020
/*
* Linked list of per-process or per-lwp statistics
@@ -110,13 +112,15 @@ typedef struct lwp_info {
} lwp_info_t;
/*
- * Linked list of collective per-uid, per-taskid, or per-projid statistics
+ * Linked list of collective per-uid, per-taskid, per-projid or per-lgroup
+ * statistics
*/
typedef struct id_info {
uid_t id_uid; /* user id */
taskid_t id_taskid; /* task id */
projid_t id_projid; /* project id */
zoneid_t id_zoneid; /* zone id */
+ int id_lgroup; /* lgroup id */
uint_t id_nproc; /* number of processes */
size_t id_size; /* memory usage */
size_t id_rssize; /* resident set size */
diff --git a/usr/src/cmd/prstat/prutil.c b/usr/src/cmd/prstat/prutil.c
index 8785f6a107..638dc8086f 100644
--- a/usr/src/cmd/prstat/prutil.c
+++ b/usr/src/cmd/prstat/prutil.c
@@ -106,8 +106,8 @@ void
Usage()
{
(void) fprintf(stderr, gettext(
- "Usage:\tprstat [-acJLmRtTvZ] [-u euidlist] [-U uidlist]\n"
- "\t[-p pidlist] [-P cpulist] [-C psrsetlist]\n"
+ "Usage:\tprstat [-acHJLmRtTvZ] [-u euidlist] [-U uidlist]\n"
+ "\t[-p pidlist] [-P cpulist] [-C psrsetlist] [-h lgrouplist]\n"
"\t[-j projidlist] [-k taskidlist] [-z zoneidlist]\n"
"\t[-s key | -S key] [-n nprocs[,nusers]]\n"
"\t[interval [counter]]\n"));
diff --git a/usr/src/cmd/ps/ps.c b/usr/src/cmd/ps/ps.c
index 1f769a308d..0bbb763eef 100644
--- a/usr/src/cmd/ps/ps.c
+++ b/usr/src/cmd/ps/ps.c
@@ -65,7 +65,7 @@
#define max(a, b) ((a) < (b) ? (b) : (a))
#define NTTYS 20 /* initial size of table for -t option */
-#define SIZ 30 /* initial size of tables for -p, -s, -g, and -z */
+#define SIZ 30 /* initial size of tables for -p, -s, -g, -h and -z */
/*
* Size of buffer holding args for t, p, s, g, u, U, G, z options.
@@ -131,7 +131,8 @@ enum fname { /* enumeration of field names */
F_PSET, /* bound processor set */
F_ZONE, /* zone name */
F_ZONEID, /* zone id */
- F_CTID /* process contract id */
+ F_CTID, /* process contract id */
+ F_LGRP /* process home lgroup */
};
struct field {
@@ -204,6 +205,7 @@ static struct def_field fname[] = {
{ "zone", "ZONE", 8, 8 },
{ "zoneid", "ZONEID", 5, 5 },
{ "ctid", "CTID", 5, 5 },
+ { "lgrp", "LGRP", 4, 2 },
};
#define NFIELDS (sizeof (fname) / sizeof (fname[0]))
@@ -228,6 +230,8 @@ static int sflg;
static int tflg;
static int zflg;
static int Zflg;
+static int hflg;
+static int Hflg;
static uid_t tuid = -1;
static int errflg;
@@ -252,6 +256,13 @@ static pid_t *pid = NULL; /* for p option */
static size_t pidsz = 0;
static size_t npid = 0;
+static int *lgrps = NULL; /* list of lgroup IDs for for h option */
+static size_t lgrps_size = 0; /* size of the lgrps list */
+static size_t nlgrps = 0; /* number elements in the list */
+
+/* Maximum possible lgroup ID value */
+#define MAX_LGRP_ID 256
+
static pid_t *grpid = NULL; /* for g option */
static size_t grpidsz = 0;
static int ngrpid = 0;
@@ -354,9 +365,60 @@ main(int argc, char **argv)
fname[F_PID].width = fname[F_PPID].width = pidwidth;
fname[F_PGID].width = fname[F_SID].width = pidwidth;
- while ((c = getopt(argc, argv, "jlfceAadLPyZt:p:g:u:U:G:n:s:o:z:")) !=
- EOF)
+ while ((c = getopt(argc, argv, "jlfceAadLPyZHh:t:p:g:u:U:G:n:s:o:z:"))
+ != EOF)
switch (c) {
+ case 'H': /* Show home lgroups */
+ Hflg++;
+ break;
+ case 'h':
+ /*
+ * Show processes/threads with given home lgroups
+ */
+ hflg++;
+ p1 = optarg;
+ do {
+ int id;
+
+ /*
+ * Get all IDs in the list, verify for
+ * correctness and place in lgrps array.
+ */
+ parg = getarg(&p1);
+ /* Convert string to integer */
+ ret = str2id(parg, (pid_t *)&id, 0,
+ MAX_LGRP_ID);
+ /* Complain if ID didn't parse correctly */
+ if (ret != 0) {
+ pgerrflg++;
+ (void) fprintf(stderr,
+ gettext("ps: %s "), parg);
+ if (ret == EINVAL)
+ (void) fprintf(stderr,
+ gettext("is an invalid "
+ "non-numeric argument"));
+ else
+ (void) fprintf(stderr,
+ gettext("exceeds valid "
+ "range"));
+ (void) fprintf(stderr,
+ gettext(" for -h option\n"));
+ continue;
+ }
+
+ /* Extend lgrps array if needed */
+ if (nlgrps == lgrps_size) {
+ /* Double the size of the lgrps array */
+ if (lgrps_size == 0)
+ lgrps_size = SIZ;
+ lgrps_size *= 2;
+ lgrps = Realloc(lgrps,
+ lgrps_size * sizeof (int));
+ }
+ /* place the id in the lgrps table */
+ lgrps[nlgrps++] = id;
+ } while (*p1);
+ break;
case 'l': /* long listing */
lflg++;
break;
@@ -379,7 +441,7 @@ main(int argc, char **argv)
case 'e': /* (obsolete) list every process */
Aflg++;
tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0;
- zflg = 0;
+ zflg = hflg = 0;
break;
case 'a':
/*
@@ -604,7 +666,7 @@ main(int argc, char **argv)
* If an appropriate option has not been specified, use the
* current terminal and effective uid as the default.
*/
- if (!(aflg|Aflg|dflg|Gflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) {
+ if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) {
psinfo_t info;
int procfd;
char *name;
@@ -642,7 +704,7 @@ main(int argc, char **argv)
}
if (Aflg) {
Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0;
- zflg = 0;
+ zflg = hflg = 0;
}
if (Aflg | aflg | dflg)
tflg = 0;
@@ -741,6 +803,8 @@ main(int argc, char **argv)
}
if (fflg)
(void) printf(" STIME");
+ if (Hflg)
+ (void) printf(" LGRP");
if (Lflg)
(void) printf(" TTY LTIME CMD\n");
else
@@ -748,7 +812,7 @@ main(int argc, char **argv)
}
- if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|tflg|gflg|sflg|zflg) &&
+ if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) &&
npid <= PTHRESHOLD) {
/*
* If we are looking at specific processes go straight
@@ -858,6 +922,8 @@ retry:
found++; /* sessid in s option arg list */
else if (zflg && search(zoneid, nzoneid, info.pr_zoneid))
found++; /* zoneid in z option arg list */
+ else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp))
+ found++; /* home lgroup in h option arg list */
if (!found && !tflg && !aflg)
return (1);
if (!prfind(found, &info, &tp))
@@ -919,11 +985,12 @@ static void
usage(void) /* print usage message and quit */
{
static char usage1[] =
- "ps [ -aAdeflcjLPyZ ] [ -o format ] [ -t termlist ]";
+ "ps [ -aAdefHlcjLPyZ ] [ -o format ] [ -t termlist ]";
static char usage2[] =
"\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
static char usage3[] =
- "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ] [ -z zonelist ]";
+ "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ] [ -z zonelist ] "
+ "[-h lgrplist]";
static char usage4[] =
" 'format' is one or more of:";
static char usage5[] =
@@ -934,7 +1001,7 @@ usage(void) /* print usage message and quit */
"zoneid";
static char usage7[] =
"\tf s c lwp nlwp psr tty addr wchan fname comm args "
- "projid project pset";
+ "projid project pset lgrp";
(void) fprintf(stderr,
gettext("usage: %s\n%s\n%s\n%s\n%s\n%s\n%s\n"),
@@ -1339,6 +1406,12 @@ prcom(psinfo_t *psinfo, char *ttyp)
else
prtime(psinfo->pr_start, 9, 1);
}
+
+ if (Hflg) {
+ /* Display home lgroup */
+ (void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp);
+ }
+
(void) printf(" %-8.14s", ttyp); /* TTY */
if (Lflg) {
tm = psinfo->pr_lwp.pr_time.tv_sec;
@@ -1362,6 +1435,7 @@ prcom(psinfo_t *psinfo, char *ttyp)
return;
}
+
/*
* PRARGSZ == length of cmd arg string.
*/
@@ -1748,6 +1822,10 @@ print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
else
(void) printf("%*ld", width, (long)psinfo->pr_contract);
break;
+ case F_LGRP:
+ /* Display home lgroup */
+ (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp);
+ break;
}
}
@@ -1991,6 +2069,10 @@ przom(psinfo_t *psinfo)
(void) printf("%8.8s ", zonename);
}
}
+ if (Hflg) {
+ /* Display home lgroup */
+ (void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */
+ }
if (fflg) {
if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
(void) printf("%8.8s ", pwd->pw_name);
diff --git a/usr/src/cmd/ptools/Makefile b/usr/src/cmd/ptools/Makefile
index df73467065..081145ded2 100644
--- a/usr/src/cmd/ptools/Makefile
+++ b/usr/src/cmd/ptools/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,12 +18,14 @@
#
# CDDL HEADER END
#
+
#
-#ident "%Z%%M% %I% %E% SMI"
-#
-# Copyright 1994-2003 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
include ../Makefile.cmd
@@ -52,6 +53,8 @@ LEGACY_SUBDIRS =\
#
NEW_SUBDIRS = \
pargs \
+ plgrp \
+ pmadvise \
ppriv \
preap
@@ -62,6 +65,13 @@ install := TARGET = install
clean := TARGET = clean
clobber := TARGET = clobber
lint := TARGET = lint
+_msg := TARGET = _msg
+
+#
+# Commands with messages support
+#
+POFILES = plgrp/plgrp.po pmadvise/pmadvise.po
+POFILE = ptools.po
.KEEP_STATE:
@@ -74,4 +84,15 @@ $(NEW_SUBDIRS): FRC
$(LEGACY_SUBDIRS): FRC
@cd $@; pwd; $(MAKE) PTOOL_TYPE=LEGACY -f ../Makefile.ptool $(TARGET)
+
+#
+# Combine all messages files into a single file and copy it to
+# MSGDOMAIN directory
+#
+_msg: ${POFILES}
+ $(RM) $(POFILE)
+ $(CAT) $(POFILES) > $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
FRC:
diff --git a/usr/src/cmd/ptools/Makefile.bld b/usr/src/cmd/ptools/Makefile.bld
index 4ef5cb60d7..446bdac5ae 100644
--- a/usr/src/cmd/ptools/Makefile.bld
+++ b/usr/src/cmd/ptools/Makefile.bld
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,12 +18,12 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
-#
PROG:sh = basename `cd ..; pwd`
@@ -42,7 +41,9 @@ LDLIBS_pcred = -lproc
LDLIBS_pfiles = -lproc -lnsl
LDLIBS_pflags = -lproc
LDLIBS_pldd = -lproc
+LDLIBS_plgrp = -lproc -llgrp
LDLIBS_pmap = -lproc
+LDLIBS_pmadvise = -lproc
LDLIBS_ppriv = -lproc
LDLIBS_preap = -lproc
LDLIBS_prun = -lproc
diff --git a/usr/src/cmd/ptools/plgrp/plgrp.c b/usr/src/cmd/ptools/plgrp/plgrp.c
new file mode 100644
index 0000000000..0f8bb3f2bc
--- /dev/null
+++ b/usr/src/cmd/ptools/plgrp/plgrp.c
@@ -0,0 +1,1591 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The plgrp utility allows a user to display and modify the home lgroup and
+ * lgroup affinities of the specified threads
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <libproc.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/lgrp_user.h>
+
+
+/*
+ * Delimiters
+ */
+#define DELIMIT_AFF '/' /* lgroup affinity from lgroups */
+#define DELIMIT_LGRP "," /* lgroups from each other */
+#define DELIMIT_LWP "/" /* thread/LWP IDs from process ID */
+#define DELIMIT_RANGE '-' /* range of IDs (eg. lgroup) */
+#define DELIMIT_AFF_LST ',' /* list of affinities from another list */
+
+/*
+ * Exit values other than EXIT_{SUCCESS,FAILURE}
+ */
+#define EXIT_NONFATAL 2 /* non-fatal errors */
+
+/*
+ * Header and format strings
+ */
+#define HDR_PLGRP_AFF_GET " PID/LWPID HOME AFFINITY\n"
+#define HDR_PLGRP_AFF_SET " PID/LWPID HOME AFFINITY\n"
+#define HDR_PLGRP_HOME_GET " PID/LWPID HOME\n"
+#define HDR_PLGRP_HOME_SET " PID/LWPID HOME\n"
+
+/*
+ * Part of the HDR_PLGRP_AFF_SET header used to calculate space needed to
+ * represent changing home as old => new
+ */
+#define HDR_PLGRP_HOME_CHANGE "HOME "
+
+#define FMT_AFF "%d/%s"
+#define FMT_AFF_STR "%s"
+#define FMT_HOME "%-6d"
+#define FMT_NEWHOME "%d => %d"
+#define FMT_THREAD "%8d/%-8d"
+
+/*
+ * How much to allocate for lgroup bitmap array as it grows
+ */
+#define LGRP_BITMAP_CHUNK 8
+
+/*
+ * Strings that can be given for lgroups
+ */
+#define LGRP_ALL_STR "all"
+#define LGRP_LEAVES_STR "leaves"
+#define LGRP_ROOT_STR "root"
+
+/*
+ * Strings corresponding to lgroup affinities
+ */
+#define LGRP_AFF_NONE_STR "none"
+#define LGRP_AFF_STRONG_STR "strong"
+#define LGRP_AFF_WEAK_STR "weak"
+
+/*
+ * Invalid value for lgroup affinity
+ */
+#define LGRP_AFF_INVALID -1
+
+/*
+ * Number of args needed for lgroup system call
+ */
+#define LGRPSYS_NARGS 3
+
+#ifndef TEXT_DOMAIN /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */
+#endif
+
+/*
+ * plgrp(1) operations
+ */
+typedef enum plgrp_ops {
+ PLGRP_AFFINITY_GET,
+ PLGRP_AFFINITY_SET,
+ PLGRP_HOME_GET,
+ PLGRP_HOME_SET,
+ PLGRP_NO_OP
+} plgrp_ops_t;
+
+/*
+ * Arguments specified to plgrp(1) and any state needed to do everything
+ * that plgrp(1) does for one operation from inside Plwp_iter_all()
+ */
+typedef struct plgrp_args {
+ struct ps_prochandle *Ph; /* proc handle for process */
+ const char *lwps; /* LWPs */
+ lgrp_id_t *lgrps; /* lgroups */
+ lgrp_affinity_t *affs; /* lgroup affinities */
+ int nlgrps; /* number of lgroups */
+ int nelements; /* number of elements */
+ int index; /* index */
+ int nthreads; /* threads processed */
+ plgrp_ops_t op; /* operation */
+} plgrp_args_t;
+
+/*
+ * How many signals caught from terminal
+ * We bail out as soon as possible when interrupt is set
+ */
+static int interrupt = 0;
+
+/*
+ * How many non-fatal errors ocurred
+ */
+static int nerrors = 0;
+
+/*
+ * Name of this program
+ */
+static char *progname;
+
+/*
+ * Root of the lgroup hierarchy
+ */
+static lgrp_id_t root = LGRP_NONE;
+
+/*
+ * Bitmap of all lgroups in the system
+ */
+static char *lgrps_bitmap = NULL;
+
+/*
+ * Size of lgrps_bitmap array
+ */
+static int lgrps_bitmap_nelements = 0;
+
+/*
+ * Macro LGRP_VALID returns true when lgrp is present in the system.
+ */
+#define LGRP_VALID(lgrp) (lgrps_bitmap[lgrp] != 0)
+
+
+/*
+ * Maximum lgroup value.
+ */
+static int max_lgrpid = LGRP_NONE;
+
+/*
+ * Total possible number of lgroups
+ */
+#define NLGRPS (max_lgrpid + 1)
+
+
+static void
+usage(int rc)
+{
+ (void) fprintf(stderr,
+ gettext("Usage:\t%s [-h] <pid> | <core> [/lwps] ...\n"), progname);
+ (void) fprintf(stderr,
+ gettext("\t%s [-F] -a <lgroup list> <pid>[/lwps] ...\n"), progname);
+ (void) fprintf(stderr,
+ gettext("\t%s [-F] -A <lgroup list>/none|weak|strong[,...] "
+ " <pid>[/lwps] ...\n"), progname);
+ (void) fprintf(stderr,
+ gettext("\t%s [-F] -H <lgroup list> <pid>[/lwps] ...\n"), progname);
+ (void) fprintf(stderr,
+ gettext("\n\twhere <lgroup list> is a comma separated list of\n"
+ "\tone or more of the following:\n\n"
+ "\t - lgroup ID\n"
+ "\t - Range of lgroup IDs specified as\n"
+ "\t\t<start lgroup ID>-<end lgroup ID>\n"
+ "\t - \"all\"\n"
+ "\t - \"root\"\n"
+ "\t - \"leaves\"\n\n"));
+
+ exit(rc);
+}
+
+/*
+ * Handler for catching signals from terminal
+ */
+/* ARGSUSED */
+static void
+intr(int sig)
+{
+ interrupt++;
+}
+
+
+/*
+ * Return string name for given lgroup affinity
+ */
+static char *
+lgrp_affinity_string(lgrp_affinity_t aff)
+{
+ char *rc = "unknown";
+
+ switch (aff) {
+ case LGRP_AFF_STRONG:
+ rc = "strong";
+ break;
+ case LGRP_AFF_WEAK:
+ rc = "weak";
+ break;
+ case LGRP_AFF_NONE:
+ rc = "none";
+ break;
+ default:
+ break;
+ }
+
+ return (rc);
+}
+
+
+/*
+ * Add a new lgroup into lgroup array in "arg", growing lgroup and affinity
+ * arrays if necessary
+ */
+static void
+lgrps_add_lgrp(plgrp_args_t *arg, int id)
+{
+
+ if (arg->nlgrps == arg->nelements) {
+ arg->nelements += LGRP_BITMAP_CHUNK;
+
+ arg->lgrps = realloc(arg->lgrps,
+ arg->nelements * sizeof (lgrp_id_t));
+ if (arg->lgrps == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"),
+ progname);
+ exit(EXIT_FAILURE);
+ }
+
+ arg->affs = realloc(arg->affs,
+ arg->nelements * sizeof (lgrp_affinity_t));
+
+ if (arg->affs == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"),
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ arg->lgrps[arg->nlgrps] = id;
+ arg->affs[arg->nlgrps] = LGRP_AFF_INVALID;
+ arg->nlgrps++;
+}
+
+
+/*
+ * Return an array having '1' for each lgroup present in given subtree under
+ * specified lgroup in lgroup hierarchy
+ */
+static void
+lgrps_bitmap_init(lgrp_cookie_t cookie, lgrp_id_t lgrpid, char **bitmap_array,
+ int *bitmap_nelements)
+{
+ lgrp_id_t *children;
+ int i;
+ int nchildren;
+
+ if (lgrpid < 0) {
+ lgrpid = lgrp_root(cookie);
+ if (lgrpid < 0)
+ return;
+ }
+
+ /*
+ * If new lgroup cannot fit, grow the array and fill unused portion
+ * with zeroes.
+ */
+ while (lgrpid >= *bitmap_nelements) {
+ *bitmap_nelements += LGRP_BITMAP_CHUNK;
+ *bitmap_array = realloc(*bitmap_array,
+ *bitmap_nelements * sizeof (char));
+ if (*bitmap_array == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"),
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ bzero(*bitmap_array + NLGRPS,
+ (*bitmap_nelements - NLGRPS) * sizeof (char));
+ }
+
+ /*
+ * Insert lgroup into bitmap and update max lgroup ID seen so far
+ */
+ (*bitmap_array)[lgrpid] = 1;
+ if (lgrpid > max_lgrpid)
+ max_lgrpid = lgrpid;
+
+ /*
+ * Get children of specified lgroup and insert descendants of each
+ * of them
+ */
+ nchildren = lgrp_children(cookie, lgrpid, NULL, 0);
+ if (nchildren > 0) {
+ children = malloc(nchildren * sizeof (lgrp_id_t));
+ if (children == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"),
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ if (lgrp_children(cookie, lgrpid, children, nchildren) !=
+ nchildren) {
+ free(children);
+ return;
+ }
+
+ for (i = 0; i < nchildren; i++)
+ lgrps_bitmap_init(cookie, children[i], bitmap_array,
+ bitmap_nelements);
+
+ free(children);
+ }
+}
+
+
+/*
+ * Parse lgroup affinity from given string
+ *
+ * Return lgroup affinity or LGRP_AFF_INVALID if string doesn't match any
+ * existing lgroup affinity and return pointer to position just after affinity
+ * string.
+ */
+static lgrp_affinity_t
+parse_lgrp_affinity(char *string, char **next)
+{
+ int rc = LGRP_AFF_INVALID;
+
+ if (string == NULL)
+ return (LGRP_AFF_INVALID);
+
+ /*
+ * Skip delimiter
+ */
+ if (string[0] == DELIMIT_AFF)
+ string++;
+
+ /*
+ * Return lgroup affinity matching string
+ */
+ if (strncmp(string, LGRP_AFF_NONE_STR, strlen(LGRP_AFF_NONE_STR))
+ == 0) {
+ rc = LGRP_AFF_NONE;
+ *next = string + strlen(LGRP_AFF_NONE_STR);
+ } else if (strncmp(string,
+ LGRP_AFF_WEAK_STR, strlen(LGRP_AFF_WEAK_STR)) == 0) {
+ rc = LGRP_AFF_WEAK;
+ *next = string + strlen(LGRP_AFF_WEAK_STR);
+ } else if (strncmp(string, LGRP_AFF_STRONG_STR,
+ strlen(LGRP_AFF_STRONG_STR)) == 0) {
+ rc = LGRP_AFF_STRONG;
+ *next = string + strlen(LGRP_AFF_STRONG_STR);
+ }
+
+ return (rc);
+}
+
+
+/*
+ * Parse lgroups from given string
+ * Returns the set containing all lgroups parsed or NULL.
+ */
+static int
+parse_lgrps(lgrp_cookie_t cookie, plgrp_args_t *arg, char *s)
+{
+ lgrp_id_t i;
+ char *token;
+
+ if (cookie == LGRP_COOKIE_NONE || s == NULL || NLGRPS <= 0)
+ return (0);
+
+ /*
+ * Parse first lgroup (if any)
+ */
+ token = strtok(s, DELIMIT_LGRP);
+ if (token == NULL)
+ return (-1);
+
+ do {
+ /*
+ * Parse lgroups
+ */
+ if (isdigit(*token)) {
+ lgrp_id_t first;
+ lgrp_id_t last;
+ char *p;
+
+ /*
+ * lgroup ID(s)
+ *
+ * Can be <lgroup ID>[-<lgroup ID>]
+ */
+ p = strchr(token, DELIMIT_RANGE);
+ first = atoi(token);
+ if (p == NULL)
+ last = first;
+ else
+ last = atoi(++p);
+
+ for (i = first; i <= last; i++) {
+ /*
+ * Add valid lgroups to lgroup array
+ */
+ if ((i >= 0) && (i < NLGRPS) && LGRP_VALID(i))
+ lgrps_add_lgrp(arg, i);
+ else {
+ (void) fprintf(stderr,
+ gettext("%s: bad lgroup %d\n"),
+ progname, i);
+ nerrors++;
+ }
+ }
+ } else if (strncmp(token, LGRP_ALL_STR,
+ strlen(LGRP_ALL_STR)) == 0) {
+ /*
+ * Add "all" lgroups to lgroups array
+ */
+ for (i = 0; i < NLGRPS; i++) {
+ if (LGRP_VALID(i))
+ lgrps_add_lgrp(arg, i);
+ }
+ } else if (strncmp(token, LGRP_ROOT_STR,
+ strlen(LGRP_ROOT_STR)) == 0) {
+ if (root < 0)
+ root = lgrp_root(cookie);
+ lgrps_add_lgrp(arg, root);
+ } else if (strncmp(token, LGRP_LEAVES_STR,
+ strlen(LGRP_LEAVES_STR)) == 0) {
+ /*
+ * Add leaf lgroups to lgroups array
+ */
+ for (i = 0; i < NLGRPS; i++) {
+ if (LGRP_VALID(i) &&
+ lgrp_children(cookie, i, NULL, 0) == 0)
+ lgrps_add_lgrp(arg, i);
+ }
+ } else {
+ return (-1);
+ }
+ } while (token = strtok(NULL, DELIMIT_LGRP));
+
+ return (0);
+}
+
+/*
+ * Print array of lgroup IDs, collapsing any consecutive runs of IDs into a
+ * range (eg. 2,3,4 into 2-4)
+ */
+static void
+print_lgrps(lgrp_id_t *lgrps, int nlgrps)
+{
+ lgrp_id_t start;
+ lgrp_id_t end;
+ int i;
+
+ /*
+ * Initial range consists of the first element
+ */
+ start = end = lgrps[0];
+
+ for (i = 1; i < nlgrps; i++) {
+ lgrp_id_t lgrpid;
+
+ lgrpid = lgrps[i];
+ if (lgrpid == end + 1) {
+ /*
+ * Got consecutive lgroup ID, so extend end of range
+ * without printing anything since the range may extend
+ * further
+ */
+ end = lgrpid;
+ } else {
+ /*
+ * Next lgroup ID is not consecutive, so print lgroup
+ * IDs gotten so far.
+ */
+ if (end == start) { /* same value */
+ (void) printf("%d,", (int)start);
+ } else if (end > start + 1) { /* range */
+ (void) printf("%d-%d,", (int)start, (int)end);
+ } else { /* different values */
+ (void) printf("%d,%d,", (int)start, (int)end);
+ }
+
+ /*
+ * Try finding consecutive range starting from this
+ * lgroup ID
+ */
+ start = end = lgrpid;
+ }
+ }
+
+ /*
+ * Print last lgroup ID(s)
+ */
+ if (end == start) {
+ (void) printf("%d", (int)start);
+ } else if (end > start + 1) {
+ (void) printf("%d-%d", (int)start, (int)end);
+ } else {
+ (void) printf("%d,%d", (int)start, (int)end);
+ }
+}
+
+/*
+ * Print lgroup affinities given array of lgroups, corresponding array of
+ * affinities, and number of elements.
+ * Skip any lgroups set to LGRP_NONE or having invalid affinity.
+ */
+static void
+print_affinities(lgrp_id_t *lgrps, lgrp_affinity_t *affs, int nelements)
+{
+ int i;
+ lgrp_id_t *lgrps_none;
+ lgrp_id_t *lgrps_strong;
+ lgrp_id_t *lgrps_weak;
+ int nlgrps_none;
+ int nlgrps_strong;
+ int nlgrps_weak;
+
+ nlgrps_strong = nlgrps_weak = nlgrps_none = 0;
+
+ lgrps_strong = malloc(nelements * sizeof (lgrp_id_t));
+ lgrps_weak = malloc(nelements * sizeof (lgrp_id_t));
+ lgrps_none = malloc(nelements * sizeof (lgrp_id_t));
+
+ if (lgrps_strong == NULL || lgrps_weak == NULL || lgrps_none == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"),
+ progname);
+ interrupt = 1;
+ return;
+ }
+
+ /*
+ * Group lgroups by affinity
+ */
+ for (i = 0; i < nelements; i++) {
+ lgrp_id_t lgrpid = lgrps[i];
+
+ /*
+ * Skip any lgroups set to LGRP_NONE
+ */
+ if (lgrpid == LGRP_NONE)
+ continue;
+
+ switch (affs[i]) {
+ case LGRP_AFF_STRONG:
+ lgrps_strong[nlgrps_strong++] = lgrpid;
+ break;
+ case LGRP_AFF_WEAK:
+ lgrps_weak[nlgrps_weak++] = lgrpid;
+ break;
+ case LGRP_AFF_NONE:
+ lgrps_none[nlgrps_none++] = lgrpid;
+ break;
+ default:
+ /*
+ * Skip any lgroups with invalid affinity.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Print all lgroups with same affinity together
+ */
+ if (nlgrps_strong) {
+ print_lgrps(lgrps_strong, nlgrps_strong);
+ (void) printf("/%s", lgrp_affinity_string(LGRP_AFF_STRONG));
+ if (nlgrps_weak || nlgrps_none)
+ (void) printf("%c", DELIMIT_AFF_LST);
+ }
+
+ if (nlgrps_weak) {
+ print_lgrps(lgrps_weak, nlgrps_weak);
+ (void) printf("/%s", lgrp_affinity_string(LGRP_AFF_WEAK));
+ if (nlgrps_none)
+ (void) printf("%c", DELIMIT_AFF_LST);
+ }
+
+ if (nlgrps_none) {
+ print_lgrps(lgrps_none, nlgrps_none);
+ (void) printf("/%s", lgrp_affinity_string(LGRP_AFF_NONE));
+ }
+
+ free(lgrps_strong);
+ free(lgrps_weak);
+ free(lgrps_none);
+}
+
+
+/*
+ * Print heading for specified operation
+ */
+static void
+print_heading(plgrp_ops_t op)
+{
+
+ switch (op) {
+ case PLGRP_AFFINITY_GET:
+ (void) printf(HDR_PLGRP_AFF_GET);
+ break;
+
+ case PLGRP_AFFINITY_SET:
+ (void) printf(HDR_PLGRP_AFF_SET);
+ break;
+
+ case PLGRP_HOME_GET:
+ (void) printf(HDR_PLGRP_HOME_GET);
+ break;
+
+ case PLGRP_HOME_SET:
+ (void) printf(HDR_PLGRP_HOME_SET);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Use /proc to call lgrp_affinity_get() in another process
+ */
+static lgrp_affinity_t
+Plgrp_affinity_get(struct ps_prochandle *Ph, idtype_t idtype, id_t id,
+ lgrp_id_t lgrp)
+{
+ lgrp_affinity_args_t args;
+ argdes_t Pargd[3];
+ argdes_t *Pargdp;
+ int Pnargs;
+ int Pretval;
+ sysret_t retval;
+ int syscall;
+
+ /*
+ * Fill in arguments needed for syscall(SYS_lgrpsys,
+ * LGRP_SYS_AFFINITY_GET, 0, &args)
+ */
+ syscall = SYS_lgrpsys;
+
+ args.idtype = idtype;
+ args.id = id;
+ args.lgrp = lgrp;
+ args.aff = LGRP_AFF_INVALID;
+
+ /*
+ * Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
+ * LGRP_SYS_AFFINITY_GET, idtype, id)
+ */
+ Pnargs = LGRPSYS_NARGS;
+ Pargdp = &Pargd[0];
+ Pargdp->arg_value = LGRP_SYS_AFFINITY_GET;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ Pargdp->arg_value = 0;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ Pargdp->arg_value = 0;
+ Pargdp->arg_object = &args;
+ Pargdp->arg_type = AT_BYREF;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = sizeof (lgrp_affinity_args_t);
+ Pargdp++;
+
+ /*
+ * Have agent LWP call syscall with appropriate arguments in target
+ * process
+ */
+ Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]);
+ if (Pretval) {
+ errno = (Pretval < 0) ? ENOSYS : Pretval;
+ return (LGRP_AFF_INVALID);
+ }
+
+ return (retval.sys_rval1);
+}
+
+
+/*
+ * Use /proc to call lgrp_affinity_set() in another process
+ */
+static int
+Plgrp_affinity_set(struct ps_prochandle *Ph, idtype_t idtype, id_t id,
+ lgrp_id_t lgrp, lgrp_affinity_t aff)
+{
+ lgrp_affinity_args_t args;
+ argdes_t Pargd[3];
+ argdes_t *Pargdp;
+ int Pnargs;
+ int Pretval;
+ sysret_t retval;
+ int syscall;
+
+ /*
+ * Fill in arguments needed for syscall(SYS_lgrpsys,
+ * LGRP_SYS_AFFINITY_SET, 0, &args)
+ */
+ syscall = SYS_lgrpsys;
+
+ args.idtype = idtype;
+ args.id = id;
+ args.lgrp = lgrp;
+ args.aff = aff;
+
+ /*
+ * Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
+ * LGRP_SYS_AFFINITY_SET, idtype, id)
+ */
+ Pnargs = LGRPSYS_NARGS;
+ Pargdp = &Pargd[0];
+ Pargdp->arg_value = LGRP_SYS_AFFINITY_SET;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ Pargdp->arg_value = 0;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ Pargdp->arg_value = 0;
+ Pargdp->arg_object = &args;
+ Pargdp->arg_type = AT_BYREF;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = sizeof (lgrp_affinity_args_t);
+ Pargdp++;
+
+ /*
+ * Have agent LWP call syscall with appropriate arguments in
+ * target process
+ */
+ Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]);
+ if (Pretval) {
+ errno = (Pretval < 0) ? ENOSYS : Pretval;
+ return (-1);
+ }
+
+ return (retval.sys_rval1);
+}
+
+/*
+ * Use /proc to call lgrp_home() in another process
+ */
+static lgrp_id_t
+Plgrp_home(struct ps_prochandle *Ph, idtype_t idtype, id_t id)
+{
+ argdes_t Pargd[3];
+ argdes_t *Pargdp;
+ int Pnargs;
+ int Pretval;
+ sysret_t retval;
+ int syscall;
+
+ /*
+ * Fill in arguments needed for syscall(SYS_lgrpsys,
+ * LGRP_SYS_HOME, idtype, id)
+ */
+ syscall = SYS_lgrpsys;
+
+ /*
+ * Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
+ * LGRP_SYS_HOME, idtype, id)
+ */
+ Pnargs = LGRPSYS_NARGS;
+ Pargdp = &Pargd[0];
+ Pargdp->arg_value = LGRP_SYS_HOME;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ Pargdp->arg_value = idtype;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ Pargdp->arg_value = id;
+ Pargdp->arg_object = NULL;
+ Pargdp->arg_type = AT_BYVAL;
+ Pargdp->arg_inout = AI_INPUT;
+ Pargdp->arg_size = 0;
+ Pargdp++;
+
+ /*
+ * Have agent LWP call syscall with appropriate arguments in
+ * target process
+ */
+ Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]);
+ if (Pretval) {
+ errno = (Pretval < 0) ? ENOSYS : Pretval;
+ return (-1);
+ }
+
+ return (retval.sys_rval1);
+}
+
+/*
+ * Use /proc to call lgrp_affinity_set(3LGRP) to set home lgroup of given
+ * thread
+ */
+static int
+Plgrp_home_set(struct ps_prochandle *Ph, idtype_t idtype, id_t id,
+ lgrp_id_t lgrp)
+{
+ return (Plgrp_affinity_set(Ph, idtype, id, lgrp,
+ LGRP_AFF_STRONG));
+}
+
+
+/*
+ * Do plgrp(1) operation on specified thread
+ */
+static int
+do_op(plgrp_args_t *plgrp_args, id_t pid, id_t lwpid,
+ const lwpsinfo_t *lwpsinfo)
+{
+ lgrp_affinity_t *affs;
+ lgrp_affinity_t *cur_affs;
+ lgrp_id_t home;
+ int i;
+ lgrp_affinity_t *init_affs;
+ lgrp_id_t *lgrps;
+ lgrp_id_t *lgrps_changed;
+ int nlgrps;
+ lgrp_id_t old_home;
+ lgrp_id_t lgrpid;
+ struct ps_prochandle *Ph;
+ int nchanged;
+
+ /*
+ * No args, so nothing to do.
+ */
+ if (plgrp_args == NULL)
+ return (0);
+
+ /*
+ * Unpack plgrp(1) arguments and state needed to process this LWP
+ */
+ Ph = plgrp_args->Ph;
+ lgrps = plgrp_args->lgrps;
+ affs = plgrp_args->affs;
+ nlgrps = plgrp_args->nlgrps;
+
+ switch (plgrp_args->op) {
+
+ case PLGRP_HOME_GET:
+ /*
+ * Get and display home lgroup for given LWP
+ */
+ home = lwpsinfo->pr_lgrp;
+ (void) printf(FMT_HOME"\n", (int)home);
+ break;
+
+ case PLGRP_AFFINITY_GET:
+ /*
+ * Get and display this LWP's home lgroup and affinities
+ * for specified lgroups
+ */
+ home = lwpsinfo->pr_lgrp;
+ (void) printf(FMT_HOME, (int)home);
+
+ /*
+ * Collect affinity values
+ */
+ for (i = 0; i < nlgrps; i++) {
+ affs[i] = Plgrp_affinity_get(Ph, P_LWPID, lwpid,
+ lgrps[i]);
+
+ if (affs[i] == LGRP_AFF_INVALID) {
+ nerrors++;
+ (void) fprintf(stderr,
+ gettext("%s: cannot get affinity"
+ " for lgroup %d for %d/%d: %s\n"),
+ progname, lgrps[i], pid, lwpid,
+ strerror(errno));
+ }
+ }
+
+ /*
+ * Print affinities for each type.
+ */
+ print_affinities(lgrps, affs, nlgrps);
+ (void) printf("\n");
+
+ break;
+
+ case PLGRP_HOME_SET:
+ /*
+ * Get home lgroup before and after setting it and display
+ * change. If more than one lgroup and one LWP are specified,
+ * then home LWPs to lgroups in round robin fashion.
+ */
+ old_home = lwpsinfo->pr_lgrp;
+
+ i = plgrp_args->index;
+ if (Plgrp_home_set(Ph, P_LWPID, lwpid, lgrps[i]) != 0) {
+ nerrors++;
+ (void) fprintf(stderr,
+ gettext("%s: cannot set home lgroup of %d/%d"
+ " to lgroup %d: %s\n"),
+ progname, pid, lwpid, lgrps[i],
+ strerror(errno));
+ (void) printf("\n");
+ } else {
+ int len;
+ int width = strlen(HDR_PLGRP_HOME_CHANGE);
+
+ home = Plgrp_home(Ph, P_LWPID, lwpid);
+
+ if (home < 0) {
+ (void) fprintf(stderr,
+ gettext("%s cannot get home lgroup for"
+ " %d/%d: %s\n"),
+ progname, pid, lwpid, strerror(errno));
+ nerrors++;
+ }
+
+ len = printf(FMT_NEWHOME, (int)old_home, (int)home);
+ if (len < width)
+ (void) printf("%*c\n", (int)(width - len), ' ');
+ }
+
+ plgrp_args->index = (i + 1) % nlgrps;
+
+ break;
+
+ case PLGRP_AFFINITY_SET:
+ /*
+ * Set affinities for specified lgroups and print old and new
+ * affinities and any resulting change in home lgroups
+ */
+
+ /*
+ * Get initial home lgroup as it may change.
+ */
+ old_home = lwpsinfo->pr_lgrp;
+
+ /*
+ * Need to allocate arrays indexed by lgroup (ID) for
+ * affinities and lgroups because user may specify affinity
+ * for same lgroup multiple times....
+ *
+ * Keeping these arrays by lgroup (ID) eliminates any
+ * duplication and makes it easier to just print initial and
+ * final lgroup affinities (instead of trying to keep a list
+ * of lgroups specified which may include duplicates)
+ */
+ init_affs = malloc(NLGRPS * sizeof (lgrp_affinity_t));
+ cur_affs = malloc(NLGRPS * sizeof (lgrp_affinity_t));
+ lgrps_changed = malloc(NLGRPS * sizeof (lgrp_id_t));
+
+ if (init_affs == NULL || cur_affs == NULL ||
+ lgrps_changed == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"),
+ progname);
+ Prelease(Ph, PRELEASE_RETAIN);
+ exit(EXIT_NONFATAL);
+ }
+
+ /*
+ * Initialize current and initial lgroup affinities and
+ * lgroups changed
+ */
+ for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) {
+
+ if (!LGRP_VALID(lgrpid)) {
+ init_affs[lgrpid] = LGRP_AFF_INVALID;
+ } else {
+ init_affs[lgrpid] =
+ Plgrp_affinity_get(Ph, P_LWPID,
+ lwpid, lgrpid);
+
+ if (init_affs[lgrpid] == LGRP_AFF_INVALID) {
+ nerrors++;
+ (void) fprintf(stderr,
+ gettext("%s: cannot get"
+ " affinity for lgroup %d"
+ " for %d/%d: %s\n"),
+ progname, lgrpid, pid, lwpid,
+ strerror(errno));
+ }
+ }
+
+ cur_affs[lgrpid] = init_affs[lgrpid];
+ lgrps_changed[lgrpid] = LGRP_NONE;
+ }
+
+ /*
+ * Change affinities.
+ */
+ for (i = 0; i < nlgrps; i++) {
+ lgrp_affinity_t aff = affs[i];
+
+ lgrpid = lgrps[i];
+
+ /*
+ * If the suggested affinity is the same as the current
+ * one, skip this lgroup.
+ */
+ if (aff == cur_affs[lgrpid])
+ continue;
+
+ /*
+ * Set affinity to the new value
+ */
+ if (Plgrp_affinity_set(Ph, P_LWPID, lwpid, lgrpid,
+ aff) < 0) {
+ nerrors++;
+ (void) fprintf(stderr,
+ gettext("%s: cannot set"
+ " %s affinity for lgroup %d"
+ " for %d/%d: %s\n"),
+ progname, lgrp_affinity_string(aff),
+ lgrpid, pid, lwpid,
+ strerror(errno));
+ continue;
+ }
+
+ /*
+ * Get the new value and verify that it changed as
+ * expected.
+ */
+ cur_affs[lgrpid] =
+ Plgrp_affinity_get(Ph, P_LWPID, lwpid, lgrpid);
+
+ if (cur_affs[lgrpid] == LGRP_AFF_INVALID) {
+ nerrors++;
+ (void) fprintf(stderr,
+ gettext("%s: cannot get"
+ " affinity for lgroup %d"
+ " for %d/%d: %s\n"),
+ progname, lgrpid, pid, lwpid,
+ strerror(errno));
+ continue;
+ }
+
+ if (aff != cur_affs[lgrpid]) {
+ (void) fprintf(stderr,
+ gettext("%s: affinity for"
+ " lgroup %d is set to %d instead of %d"
+ " for %d/%d\n"),
+ progname, lgrpid, cur_affs[lgrpid], aff,
+ pid, lwpid);
+ nerrors++;
+ }
+ }
+
+ /*
+ * Compare current and initial affinities and mark lgroups with
+ * changed affinities.
+ */
+ nchanged = 0;
+ for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) {
+ if (init_affs[lgrpid] != cur_affs[lgrpid]) {
+ lgrps_changed[lgrpid] = lgrpid;
+ nchanged++;
+ }
+ }
+
+ if (nchanged == 0) {
+ /*
+ * Nothing changed, so just print current affinities for
+ * specified lgroups.
+ */
+ for (i = 0; i < nlgrps; i++) {
+ lgrps_changed[lgrps[i]] = lgrps[i];
+ }
+
+ (void) printf("%-*d",
+ (int)strlen(HDR_PLGRP_HOME_CHANGE),
+ (int)old_home);
+
+ print_affinities(lgrps_changed, cur_affs, NLGRPS);
+ (void) printf("\n");
+ } else {
+ int width = strlen(HDR_PLGRP_HOME_CHANGE);
+
+ /*
+ * Some lgroup affinities changed, so display old
+ * and new home lgroups for thread and its old and new
+ * affinities for affected lgroups
+ */
+ home = Plgrp_home(Ph, P_LWPID, lwpid);
+ if (home < 0) {
+ (void) fprintf(stderr,
+ gettext("%s: cannot get home"
+ " for %d/%d: %s\n"),
+ progname, pid, lwpid, strerror(errno));
+ nerrors++;
+ }
+ if (old_home != home) {
+ int len;
+
+ /*
+ * Fit string into fixed width
+ */
+ len = printf(FMT_NEWHOME,
+ (int)old_home, (int)home);
+ if (len < width)
+ (void) printf("%*c", width - len, ' ');
+ } else {
+ (void) printf("%-*d", width, (int)home);
+ }
+
+ /*
+ * Print change in affinities from old to new
+ */
+ print_affinities(lgrps_changed, init_affs, NLGRPS);
+ (void) printf(" => ");
+ print_affinities(lgrps_changed, cur_affs, NLGRPS);
+ (void) printf("\n");
+ }
+
+ free(lgrps_changed);
+ free(init_affs);
+ free(cur_affs);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Routine called by Plwp_iter_all() as it iterates through LWPs of another
+ * process
+ */
+/* ARGSUSED */
+static int
+Plwp_iter_handler(void *arg, const lwpstatus_t *lwpstatus,
+ const lwpsinfo_t *lwpsinfo)
+{
+ id_t lwpid;
+ struct ps_prochandle *Ph;
+ const pstatus_t *pstatus;
+ plgrp_args_t *plgrp_args;
+
+ /*
+ * Nothing to do if no arguments
+ */
+ if (arg == NULL || interrupt)
+ return (0);
+
+ /*
+ * Unpack plgrp(1) arguments and state needed to process this LWP
+ */
+ plgrp_args = arg;
+ Ph = plgrp_args->Ph;
+
+ /*
+ * Just return if no /proc handle for process
+ */
+ if (Ph == NULL)
+ return (0);
+
+ pstatus = Pstatus(Ph);
+
+ /*
+ * Skip agent LWP and any LWPs that weren't specified
+ */
+ lwpid = lwpsinfo->pr_lwpid;
+ if (lwpid == pstatus->pr_agentid ||
+ !proc_lwp_in_set(plgrp_args->lwps, lwpid))
+ return (0);
+
+ plgrp_args->nthreads++;
+
+ /*
+ * Do all plgrp(1) operations specified on given thread
+ */
+ (void) printf(FMT_THREAD" ", (int)pstatus->pr_pid, (int)lwpid);
+ return (do_op(plgrp_args, pstatus->pr_pid, lwpid, lwpsinfo));
+}
+
+/*
+ * Get target process specified in "pidstring" argument to do operation(s)
+ * specified in "plgrp_todo" using /proc and agent LWP
+ */
+static void
+do_process(char *pidstring, plgrp_args_t *plgrp_todo, int force)
+{
+ int error;
+ const char *lwps;
+ struct ps_prochandle *Ph;
+
+ /*
+ * Nothing to do, so return.
+ */
+ if (plgrp_todo == NULL || interrupt)
+ return;
+
+ /*
+ * Grab target process or core and return
+ * /proc handle for process and string of LWP
+ * IDs
+ */
+ Ph = proc_arg_xgrab(pidstring, NULL,
+ PR_ARG_ANY, force | PGRAB_RETAIN | PGRAB_NOSTOP, &error, &lwps);
+ if (Ph == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Unable to grab process %s: %s\n"),
+ progname, pidstring, Pgrab_error(error));
+ nerrors++;
+ return;
+ }
+
+ /*
+ * Fill in remaining plgrp(1) arguments and state needed to do
+ * plgrp(1) operation(s) on desired LWPs in our handler
+ * called by Plwp_iter_all() as it iterates over LWPs
+ * in given process
+ */
+ plgrp_todo->Ph = Ph;
+ plgrp_todo->lwps = lwps;
+
+ /*
+ * Iterate over LWPs in process and do specified
+ * operation(s) on those specified
+ */
+ if (Plwp_iter_all(Ph, Plwp_iter_handler, plgrp_todo) != 0) {
+ (void) fprintf(stderr,
+ gettext("%s: error iterating over threads\n"),
+ progname);
+ nerrors++;
+ }
+
+ Prelease(Ph, PRELEASE_RETAIN);
+}
+
+
+/*
+ * Parse command line and kick off any resulting actions
+ *
+ * plgrp(1) has the following command line syntax:
+ *
+ * plgrp [-h] <pid> | <core> [/lwps] ...
+ * plgrp [-F] -a <lgroup>,... <pid>[/lwps] ...
+ * plgrp [-F] -H <lgroup>,... <pid>[/lwps] ...
+ * plgrp [-F] -A <lgroup>,... [/none|weak|strong] ... <pid>[/lwps] ...
+ *
+ * where <lgroup> is an lgroup ID, "all", "root", "leaves".
+ */
+int
+main(int argc, char *argv[])
+{
+ lgrp_affinity_t aff;
+ char *affstring;
+ int c;
+ lgrp_cookie_t cookie;
+ int Fflag;
+ int i;
+ int opt_seen;
+ plgrp_args_t plgrp_todo;
+ char *s;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ opt_seen = 0;
+
+ /*
+ * Get name of program
+ */
+ progname = basename(argv[0]);
+
+ /*
+ * Not much to do when only name of program given
+ */
+ if (argc == 1)
+ usage(0);
+
+ /*
+ * Catch signals from terminal, so they can be handled asynchronously
+ * when we're ready instead of when we're not (;-)
+ */
+ if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGHUP, intr);
+ if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGINT, intr);
+ if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGQUIT, intr);
+ (void) sigset(SIGPIPE, intr);
+ (void) sigset(SIGTERM, intr);
+
+ /*
+ * Take snapshot of lgroup hierarchy
+ */
+ cookie = lgrp_init(LGRP_VIEW_OS);
+ if (cookie == LGRP_COOKIE_NONE) {
+ (void) fprintf(stderr,
+ gettext("%s: Fatal error: cannot get lgroup"
+ " information from the OS: %s\n"),
+ progname, strerror(errno));
+ return (EXIT_FAILURE);
+ }
+
+ root = lgrp_root(cookie);
+ lgrps_bitmap_init(cookie, root, &lgrps_bitmap, &lgrps_bitmap_nelements);
+
+ /*
+ * Remember arguments and state needed to do plgrp(1) operation
+ * on desired LWPs
+ */
+ bzero(&plgrp_todo, sizeof (plgrp_args_t));
+ plgrp_todo.op = PLGRP_HOME_GET;
+
+ /*
+ * Parse options
+ */
+ opterr = 0;
+ Fflag = 0;
+ while (!interrupt && (c = getopt(argc, argv, "a:A:FhH:")) != -1) {
+ /*
+ * Parse option and only allow one option besides -F to be
+ * specified
+ */
+ switch (c) {
+
+ case 'h': /* Get home lgroup */
+ /*
+ * Only allow one option (besides -F) to be specified
+ */
+ if (opt_seen)
+ usage(EXIT_FAILURE);
+ opt_seen = 1;
+
+ plgrp_todo.op = PLGRP_HOME_GET;
+ break;
+
+ case 'H': /* Set home lgroup */
+
+ /*
+ * Fail if already specified option (besides -F)
+ * or no more arguments
+ */
+ if (opt_seen || optind >= argc) {
+ usage(EXIT_FAILURE);
+ }
+ opt_seen = 1;
+
+ plgrp_todo.op = PLGRP_HOME_SET;
+
+ if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0)
+ usage(EXIT_FAILURE);
+
+ /* If there are no valid lgroups exit immediately */
+ if (plgrp_todo.nlgrps == 0) {
+ (void) fprintf(stderr,
+ gettext("%s: no valid lgroups"
+ " specified for -%c\n\n"),
+ progname, c);
+ usage(EXIT_FAILURE);
+ }
+
+ break;
+
+ case 'a': /* Get lgroup affinity */
+
+ /*
+ * Fail if already specified option (besides -F)
+ * or no more arguments
+ */
+ if (opt_seen || optind >= argc) {
+ usage(EXIT_FAILURE);
+ }
+ opt_seen = 1;
+
+ plgrp_todo.op = PLGRP_AFFINITY_GET;
+
+ if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0)
+ usage(EXIT_FAILURE);
+
+ /* If there are no valid lgroups exit immediately */
+ if (plgrp_todo.nlgrps == 0) {
+ (void) fprintf(stderr,
+ gettext("%s: no valid lgroups specified"
+ " for -%c\n\n"),
+ progname, c);
+ usage(EXIT_FAILURE);
+ }
+
+ break;
+
+ case 'A': /* Set lgroup affinity */
+
+ /*
+ * Fail if already specified option (besides -F)
+ * or no more arguments
+ */
+ if (opt_seen || optind >= argc) {
+ usage(EXIT_FAILURE);
+ }
+ opt_seen = 1;
+
+ plgrp_todo.op = PLGRP_AFFINITY_SET;
+
+ /*
+ * 'affstring' is the unparsed prtion of the affinity
+ * specification like 1,2/none,2/weak,0/strong
+ *
+ * 'next' is the next affinity specification to parse.
+ */
+ affstring = optarg;
+ while (affstring != NULL && strlen(affstring) > 0) {
+ char *next;
+
+ /*
+ * affstring points to the first affinity
+ * specification. Split the string by
+ * DELIMIT_AFF separator and parse lgroups and
+ * affinity value separately.
+ */
+ s = strchr(affstring, DELIMIT_AFF);
+ if (s == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid "
+ "syntax >%s<\n"),
+ progname, affstring);
+ usage(EXIT_FAILURE);
+ }
+
+ aff = parse_lgrp_affinity(s, &next);
+ if (aff == LGRP_AFF_INVALID) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid "
+ "affinity >%s<\n"),
+ progname, affstring);
+ usage(EXIT_FAILURE);
+ }
+
+ /*
+ * next should either point to the empty string
+ * or to the DELIMIT_AFF_LST separator.
+ */
+ if (*next != '\0') {
+ if (*next != DELIMIT_AFF_LST) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid "
+ "syntax >%s<\n"),
+ progname, next);
+ usage(EXIT_FAILURE);
+ }
+ *next = '\0';
+ next++;
+ }
+
+
+ /*
+ * Now parse the list of lgroups
+ */
+ if (parse_lgrps(cookie, &plgrp_todo,
+ affstring) < 0) {
+ usage(EXIT_FAILURE);
+ }
+
+ /*
+ * Set desired affinity for specified lgroup to
+ * the specified affinity.
+ */
+ for (i = 0; i < plgrp_todo.nlgrps; i++) {
+ if (plgrp_todo.affs[i] ==
+ LGRP_AFF_INVALID)
+ plgrp_todo.affs[i] = aff;
+ }
+
+ /*
+ * We processed the leftmost element of the
+ * list. Advance affstr to the remaining part of
+ * the list. and repeat.
+ */
+ affstring = next;
+ }
+
+ /*
+ * If there are no valid lgroups, exit immediately
+ */
+ if (plgrp_todo.nlgrps == 0) {
+ (void) fprintf(stderr,
+ gettext("%s: no valid lgroups specified "
+ "for -%c\n\n"), progname, c);
+ usage(EXIT_FAILURE);
+ }
+
+ break;
+
+ case 'F': /* Force */
+
+ /*
+ * Only allow one occurrence
+ */
+ if (Fflag != 0) {
+ usage(EXIT_FAILURE);
+ }
+
+ /*
+ * Set flag to force /proc to grab process even though
+ * it's been grabbed by another process already
+ */
+ Fflag = PGRAB_FORCE;
+ break;
+
+ case '?': /* Unrecognized option */
+ default:
+ usage(EXIT_FAILURE);
+ break;
+
+ }
+ }
+
+ /*
+ * Should have more arguments left at least for PID or core
+ */
+ if (optind >= argc)
+ usage(EXIT_FAILURE);
+
+ (void) lgrp_fini(cookie);
+
+ /*
+ * Print heading and process each [pid | core]/lwps argument
+ */
+ print_heading(plgrp_todo.op);
+ for (i = optind; i < argc && !interrupt; i++) {
+ do_process(argv[i], &plgrp_todo, Fflag);
+ }
+
+ if (plgrp_todo.nthreads == 0) {
+ (void) fprintf(stderr, gettext("%s: no matching LWPs found\n"),
+ progname);
+ }
+
+ return ((nerrors ||interrupt) ? EXIT_NONFATAL : EXIT_SUCCESS);
+}
diff --git a/usr/src/cmd/ptools/pmadvise/pmadvise.c b/usr/src/cmd/ptools/pmadvise/pmadvise.c
new file mode 100644
index 0000000000..b3caca2b0d
--- /dev/null
+++ b/usr/src/cmd/ptools/pmadvise/pmadvise.c
@@ -0,0 +1,1269 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * pmadvise
+ *
+ * ptool wrapper for madvise(3C) to apply memory advice to running processes
+ *
+ * usage: pmadvise -o option[,option] [-v] [-F] pid ...
+ * (Give "advice" about a process's memory)
+ * -o option[,option]: options are
+ * private=<advice>
+ * shared=<advice>
+ * heap=<advice>
+ * stack=<advice>
+ * <segaddr>[:<length>]=<advice>
+ * valid <advice> is one of:
+ * normal, random, sequential, willneed, dontneed,
+ * free, access_lwp, access_many, access_default
+ * -v: verbose output
+ * -F: force grabbing of the target process(es)
+ * pid: process id list
+ *
+ *
+ * Advice passed to this tool are organized into various lists described here:
+ * rawadv_list: includes all specific advice from command line (specific
+ * advice being those given to a particular address range rather
+ * than a type like "heap" or "stack". In contrast, these
+ * types are referred to as generic advice). Duplicates allowed.
+ * List ordered by addr, then by size (largest size first).
+ * Created once per run.
+ * merged_list: includes all specific advice from the rawadv_list as well as
+ * all generic advice. This must be recreated for each process
+ * as the generic advice will apply to different regions for
+ * different processes. Duplicates allowed. List ordered by addr,
+ * then by size (largest size first). Created once per pid.
+ * chopped_list: used for verbose output only. This list parses the merged
+ * list such that it eliminates any overlap and combines the
+ * advice. Easiest to think of this visually: if you take all
+ * the advice in the merged list and lay them down on a memory
+ * range of the entire process (laying on top of each other when
+ * necessary), then flatten them into one layer, combining advice
+ * in the case of overlap, you get the chopped_list of advice.
+ * Duplicate entries not allowed (since there is no overlap by
+ * definition in this list). List ordered by addr. Created once
+ * per pid.
+ *
+ * Example:
+ * merged_list: |-----adv1----|---------adv3---------|
+ * |--adv2--|--adv4--|-----adv5----|
+ * ||
+ * \/
+ * chopped_list: |adv1|-adv1,2-|-adv3,4-|----adv3,5---|
+ *
+ * maplist: list of memory mappings for a particular process. Used to create
+ * generic advice entries for merged_list and for pmap like verbose
+ * output. Created once per pid.
+ *
+ * Multiple lists are necessary because the actual advice applied given a set
+ * of generic and specific advice changes from process to process, so for each
+ * pid pmadvise is passed, it must create a new merged_list from which to apply
+ * advice (and a new chopped_list if verbose output is requested).
+ *
+ * Pseudo-code:
+ * I. Input advice from command line
+ * II. Create [raw advice list] of specific advice
+ * III. Iterate through PIDs:
+ * A. Create [map list]
+ * B. Merge generic advice and [raw advice list] into [merged list]
+ * C. Apply advice from [merged list]; upon error:
+ * i. output madvise error message
+ * ii. remove element from [merged list]
+ * D. If verbose output:
+ * i. Create [chopped list] from [merged list]
+ * ii. Iterate through [map list]:
+ * a. output advice as given by [merged list]
+ * iii. Delete [chopped list]
+ * E. Delete [merged list]
+ * F. Delete [map list]
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <string.h>
+#include <dirent.h>
+#include <limits.h>
+#include <link.h>
+#include <libelf.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <assert.h>
+#include <libproc.h>
+#include <libgen.h>
+#include <signal.h>
+
+#ifndef TEXT_DOMAIN /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */
+#endif
+
+#define KILOBYTE 1024
+
+/*
+ * Round up the value to the nearest kilobyte
+ */
+#define ROUNDUP_KB(x) (((x) + (KILOBYTE - 1)) / KILOBYTE)
+
+#define NO_ADVICE 0
+
+/*
+ * The following definitions are used as the third argument in insert_addr()
+ * NODUPS = no duplicates are not allowed, thus if the addr being inserted
+ * already exists in the list, return without inserting again.
+ *
+ * YESDUPS = yes duplicates are allowed, thus always insert the addr
+ * regardless of whether it already exists in the list or not.
+ */
+#define NODUPS 1
+#define YESDUPS 0
+
+/*
+ * Advice that can be passed to madvise fit into three groups that each
+ * contain 3 mutually exclusive options. These groups are defined below:
+ * Group 1: normal, random, sequential
+ * Group 2: willneed, dontneed, free
+ * Group 3: default, accesslwp, accessmany
+ * Thus, advice that includes (at most) one from each group is valid.
+ *
+ * The following #define's are used as masks to determine which group(s) a
+ * particular advice fall under.
+ */
+
+#define GRP1_ADV (1 << MADV_NORMAL | 1 << MADV_RANDOM | \
+ 1 << MADV_SEQUENTIAL)
+#define GRP2_ADV (1 << MADV_WILLNEED | 1 << MADV_DONTNEED | \
+ 1 << MADV_FREE)
+#define GRP3_ADV (1 << MADV_ACCESS_DEFAULT | 1 << MADV_ACCESS_LWP | \
+ 1 << MADV_ACCESS_MANY)
+
+static int create_maplist(void *, const prmap_t *, const char *);
+static int pr_madvise(struct ps_prochandle *, caddr_t, size_t, int);
+
+static char *mflags(uint_t);
+static char *advtostr(int);
+
+static int addr_width, size_width;
+static char *progname;
+static struct ps_prochandle *Pr;
+
+typedef struct lwpstack {
+ lwpid_t lwps_lwpid;
+ stack_t lwps_stack;
+} lwpstack_t;
+
+static lwpstack_t *stacks;
+static uint_t nstacks;
+
+/*
+ * Used to set the advice type var (at_map) when parsing the arguments to
+ * pmadvise. Later, when creating the map list, at_map is used as a mask
+ * to determine if any generic advice applies to each memory mapping.
+ */
+enum atype_enum {
+ AT_PRIVM,
+ AT_SHARED,
+ AT_HEAP,
+ AT_STACK,
+ AT_SEG,
+ AT_NTYPES
+};
+
+static char *suboptstr[] = {
+ "private",
+ "shared",
+ "heap",
+ "stack",
+ NULL
+};
+
+
+int generic_adv[] = {NO_ADVICE, NO_ADVICE, NO_ADVICE, NO_ADVICE};
+int at_map = 0;
+
+typedef struct saddr_struct {
+ uintptr_t addr;
+ size_t length;
+ int adv;
+ struct saddr_struct *next;
+} saddr_t;
+static int apply_advice(saddr_t **);
+static void set_advice(int *, int);
+static void create_choplist(saddr_t **, saddr_t *);
+
+/*
+ * The segment address advice from the command line
+ */
+saddr_t *rawadv_list = NULL;
+/*
+ * The rawadv_list + list entries for the generic advice (if any).
+ * This must be recreated for each PID as the memory maps might be different.
+ */
+saddr_t *merged_list = NULL;
+/*
+ * The merged_list cut up so as to remove all overlap
+ * e.g. if merged_list contained two entries:
+ *
+ * [0x38000:0x3e000) = adv1
+ * [0x3a000:0x3c000) = adv2
+ *
+ * the chopped list will contain three entries:
+ *
+ * [0x38000:0x3a000) = adv1
+ * [0x3a000:0x3c000) = adv1,adv2
+ * [0x3c000:0x3e000) = adv1
+ *
+ */
+saddr_t *chopped_list = NULL;
+
+typedef struct mapnode_struct {
+ prmap_t *pmp;
+ char label[PATH_MAX];
+ int mtypes;
+ struct mapnode_struct *next;
+} mapnode_t;
+
+mapnode_t *maplist_head = NULL;
+mapnode_t *maplist_tail = NULL;
+static void print_advice(saddr_t *, mapnode_t *);
+
+int opt_verbose;
+
+static char *advicestr[] = {
+ "normal",
+ "random",
+ "sequential",
+ "willneed",
+ "dontneed",
+ "free",
+ "access_default",
+ "access_lwp",
+ "access_many"
+};
+
+/*
+ * How many signals caught from terminal
+ * We bail out as soon as possible when interrupt is set
+ */
+static int interrupt = 0;
+
+/*
+ * Interrupt handler
+ */
+static void intr(int);
+
+/*
+ * Iterative function passed to Plwp_iter to
+ * get alt and main stacks for given lwp.
+ */
+static int
+getstack(void *data, const lwpstatus_t *lsp)
+{
+ int *np = (int *)data;
+
+ if (Plwp_alt_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
+ stacks[*np].lwps_stack.ss_flags |= SS_ONSTACK;
+ stacks[*np].lwps_lwpid = lsp->pr_lwpid;
+ (*np)++;
+ }
+
+ if (Plwp_main_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
+ stacks[*np].lwps_lwpid = lsp->pr_lwpid;
+ (*np)++;
+ }
+
+ return (0);
+}
+
+/*
+ * We compare the high memory addresses since stacks are faulted in from
+ * high memory addresses to low memory addresses, and our prmap_t
+ * structures identify only the range of addresses that have been faulted
+ * in so far.
+ */
+static int
+cmpstacks(const void *ap, const void *bp)
+{
+ const lwpstack_t *as = ap;
+ const lwpstack_t *bs = bp;
+ uintptr_t a = (uintptr_t)as->lwps_stack.ss_sp + as->lwps_stack.ss_size;
+ uintptr_t b = (uintptr_t)bs->lwps_stack.ss_sp + bs->lwps_stack.ss_size;
+
+ if (a < b)
+ return (1);
+ if (a > b)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Prints usage and exits
+ */
+static void
+usage()
+{
+ (void) fprintf(stderr,
+ gettext("usage:\t%s -o option[,option] [-v] [-F] pid ...\n"),
+ progname);
+ (void) fprintf(stderr,
+ gettext(" (Give \"advice\" about a process's memory)\n"
+ " -o option[,option]: options are\n"
+ " private=<advice>\n"
+ " shared=<advice>\n"
+ " heap=<advice>\n"
+ " stack=<advice>\n"
+ " <segaddr>[:<length>]=<advice>\n"
+ " valid <advice> is one of:\n"
+ " normal, random, sequential, willneed, dontneed,\n"
+ " free, access_lwp, access_many, access_default\n"
+ " -v: verbose output\n"
+ " -F: force grabbing of the target process(es)\n"
+ " pid: process id list\n"));
+ exit(2);
+}
+
+/*
+ * Function to parse advice from options string
+ */
+static int
+get_advice(char *optarg)
+{
+ /*
+ * Determine which advice is given, we use shifted values as
+ * multiple pieces of advice may apply for a particular region.
+ * (See comment above regarding GRP[1,2,3]_ADV definitions for
+ * breakdown of advice groups).
+ */
+ if (strcmp(optarg, "access_default") == 0)
+ return (1 << MADV_ACCESS_DEFAULT);
+ else if (strcmp(optarg, "access_many") == 0)
+ return (1 << MADV_ACCESS_MANY);
+ else if (strcmp(optarg, "access_lwp") == 0)
+ return (1 << MADV_ACCESS_LWP);
+ else if (strcmp(optarg, "sequential") == 0)
+ return (1 << MADV_SEQUENTIAL);
+ else if (strcmp(optarg, "willneed") == 0)
+ return (1 << MADV_WILLNEED);
+ else if (strcmp(optarg, "dontneed") == 0)
+ return (1 << MADV_DONTNEED);
+ else if (strcmp(optarg, "random") == 0)
+ return (1 << MADV_RANDOM);
+ else if (strcmp(optarg, "normal") == 0)
+ return (1 << MADV_NORMAL);
+ else if (strcmp(optarg, "free") == 0)
+ return (1 << MADV_FREE);
+ else {
+ (void) fprintf(stderr, gettext("%s: invalid advice: %s\n"),
+ progname, optarg);
+ usage();
+ return (-1);
+ }
+}
+
+/*
+ * Function to convert character size indicators into actual size
+ * (i.e., 123M => sz = 123 * 1024 * 1024)
+ */
+static size_t
+atosz(char *optarg, char **endptr)
+{
+ size_t sz = 0;
+
+ if (optarg == NULL || optarg[0] == '\0')
+ return (0);
+
+ sz = strtoll(optarg, endptr, 0);
+
+ switch (**endptr) {
+ case 'E':
+ case 'e':
+ sz *= KILOBYTE;
+ /* FALLTHRU */
+ case 'P':
+ case 'p':
+ sz *= KILOBYTE;
+ /* FALLTHRU */
+ case 'T':
+ case 't':
+ sz *= KILOBYTE;
+ /* FALLTHRU */
+ case 'G':
+ case 'g':
+ sz *= KILOBYTE;
+ /* FALLTHRU */
+ case 'M':
+ case 'm':
+ sz *= KILOBYTE;
+ /* FALLTHRU */
+ case 'K':
+ case 'k':
+ sz *= KILOBYTE;
+ /* FALLTHRU */
+ case 'B':
+ case 'b':
+ (*endptr)++;
+ /* FALLTHRU */
+ default:
+ break;
+ }
+ return (sz);
+}
+
+/*
+ * Inserts newaddr into list. dups indicates whether we allow duplicate
+ * addr entries in the list (valid values are NODUPS and YESDUPS).
+ */
+static void
+insert_addr(saddr_t **list, saddr_t *newaddr, int dups)
+{
+ saddr_t *prev = *list;
+ saddr_t *psaddr;
+
+ if (*list == NULL) {
+ newaddr->next = *list;
+ *list = newaddr;
+ return;
+ }
+
+ for (psaddr = (*list)->next; psaddr != NULL; psaddr = psaddr->next) {
+ if ((dups == NODUPS) && (psaddr->addr == newaddr->addr)) {
+ free(newaddr);
+ return;
+ }
+
+ /*
+ * primary level of comparison is by address; smaller addr 1st
+ * secondary level of comparison is by length; bigger length 1st
+ */
+ if ((psaddr->addr > newaddr->addr) ||
+ (psaddr->addr == newaddr->addr &&
+ psaddr->length < newaddr->length))
+ break;
+
+ prev = psaddr;
+ }
+
+ prev->next = newaddr;
+ newaddr->next = psaddr;
+}
+
+/*
+ * Deletes given element from list
+ */
+static void
+delete_addr(saddr_t **list, saddr_t *delme)
+{
+ saddr_t *prev = *list;
+
+ if (delme == *list) {
+ *list = delme->next;
+ free(delme);
+ return;
+ }
+
+ while (prev != NULL && prev->next != delme) {
+ prev = prev->next;
+ }
+
+ if (prev) {
+ prev->next = delme->next;
+ free(delme);
+ }
+}
+
+/*
+ * Delete entire list
+ */
+static void
+delete_list(saddr_t **list)
+{
+ saddr_t *psaddr = *list;
+
+ while (psaddr != NULL) {
+ saddr_t *temp = psaddr;
+
+ psaddr = psaddr->next;
+ free(temp);
+ }
+ *list = NULL;
+}
+
+static saddr_t *
+parse_suboptions(char *value)
+{
+ char *endptr;
+ saddr_t *psaddr = malloc(sizeof (saddr_t));
+
+ /*
+ * This must (better) be a segment addr
+ */
+ psaddr->addr =
+ strtoull(value, &endptr, 16);
+
+ /*
+ * Check to make sure strtoul worked correctly (a properly formatted
+ * string will terminate in a ':' (if size is given) or an '=' (if size
+ * is not specified). Also check to make sure a 0 addr wasn't returned
+ * indicating strtoll was unable to convert).
+ */
+ if ((psaddr->addr == 0) || (*endptr != ':' && *endptr != '=')) {
+ free(psaddr);
+ (void) fprintf(stderr,
+ gettext("%s: invalid option %s\n"),
+ progname, value);
+ usage();
+ } else {
+ /* init other fields */
+ psaddr->length = 0;
+ psaddr->adv = NO_ADVICE;
+ psaddr->next = NULL;
+
+ /* skip past address */
+ value = endptr;
+
+ /* check for length */
+ if (*value == ':') {
+ /* skip the ":" */
+ value++;
+ psaddr->length = atosz(value, &endptr);
+ }
+
+ if (*endptr != '=') {
+ (void) fprintf(stderr,
+ gettext("%s: invalid option %s\n"),
+ progname, value);
+ /*
+ * if improperly formatted, free mem, print usage, and
+ * exit Note: usage ends with a call to exit()
+ */
+ free(psaddr);
+ usage();
+ }
+ /* skip the "=" */
+ value = endptr + 1;
+ at_map |= (1 << AT_SEG);
+ psaddr->adv =
+ get_advice(value);
+ }
+
+ return (psaddr);
+}
+
+/*
+ * Create labels for non-anon, non-heap mappings
+ */
+static char *
+make_name(struct ps_prochandle *Pr, uintptr_t addr, const char *mapname,
+ char *buf, size_t bufsz)
+{
+ const pstatus_t *Psp = Pstatus(Pr);
+ char fname[100];
+ struct stat statb;
+ int len;
+
+ if (strcmp(mapname, "a.out") == 0 &&
+ Pexecname(Pr, buf, bufsz) != NULL)
+ return (buf);
+
+ if (Pobjname(Pr, addr, buf, bufsz) != NULL) {
+ if ((len = resolvepath(buf, buf, bufsz)) > 0) {
+ buf[len] = '\0';
+ return (buf);
+ }
+ }
+
+ if (*mapname != '\0') {
+ (void) snprintf(fname, sizeof (fname), "/proc/%d/object/%s",
+ (int)Psp->pr_pid, mapname);
+ if (stat(fname, &statb) == 0) {
+ dev_t dev = statb.st_dev;
+ ino_t ino = statb.st_ino;
+ (void) snprintf(buf, bufsz, "dev:%lu,%lu ino:%lu",
+ (ulong_t)major(dev), (ulong_t)minor(dev), ino);
+ return (buf);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Create label for anon mappings
+ */
+static char *
+anon_name(char *name, const pstatus_t *Psp,
+ uintptr_t vaddr, size_t size, int mflags, int shmid, int *mtypes)
+{
+ if (mflags & MA_ISM) {
+ if (shmid == -1)
+ (void) snprintf(name, PATH_MAX, " [ %s shmid=null ]",
+ (mflags & MA_NORESERVE) ? "ism" : "dism");
+ else
+ (void) snprintf(name, PATH_MAX, " [ %s shmid=0x%x ]",
+ (mflags & MA_NORESERVE) ? "ism" : "dism", shmid);
+ *mtypes |= (1 << AT_SHARED);
+ } else if (mflags & MA_SHM) {
+ if (shmid == -1)
+ (void) sprintf(name, " [ shmid=null ]");
+ else
+ (void) sprintf(name, " [ shmid=0x%x ]", shmid);
+ *mtypes |= (1 << AT_SHARED);
+
+ } else if (vaddr + size > Psp->pr_stkbase &&
+ vaddr < Psp->pr_stkbase + Psp->pr_stksize) {
+ (void) strcpy(name, " [ stack ]");
+ *mtypes |= (1 << AT_STACK);
+
+ } else if ((mflags & MA_ANON) &&
+ vaddr + size > Psp->pr_brkbase &&
+ vaddr < Psp->pr_brkbase + Psp->pr_brksize) {
+ (void) strcpy(name, " [ heap ]");
+ *mtypes |= (1 << AT_HEAP);
+
+ } else {
+ lwpstack_t key, *stk;
+
+ key.lwps_stack.ss_sp = (void *)vaddr;
+ key.lwps_stack.ss_size = size;
+ if (nstacks > 0 &&
+ (stk = bsearch(&key, stacks, nstacks, sizeof (stacks[0]),
+ cmpstacks)) != NULL) {
+ (void) snprintf(name, PATH_MAX, " [ %s tid=%d ]",
+ (stk->lwps_stack.ss_flags & SS_ONSTACK) ?
+ "altstack" : "stack",
+ stk->lwps_lwpid);
+ *mtypes |= (1 << AT_STACK);
+ } else {
+ (void) strcpy(name, " [ anon ]");
+ *mtypes |= (1 << AT_PRIVM);
+ }
+ }
+
+ return (name);
+}
+
+/*
+ * Create linked list of mappings for current process
+ * In addition, add generic advice and raw advice
+ * entries to merged_list.
+ */
+/* ARGSUSED */
+static int
+create_maplist(void *arg, const prmap_t *pmp, const char *object_name)
+{
+ const pstatus_t *Psp = Pstatus(Pr);
+ mapnode_t *newmap = malloc(sizeof (mapnode_t));
+ saddr_t *newaddr;
+ saddr_t *psaddr;
+ char *lname = NULL;
+ int i;
+
+ if (interrupt)
+ return (0);
+
+ newmap->pmp = malloc(sizeof (prmap_t));
+ newmap->label[0] = '\0';
+ newmap->mtypes = 0;
+ newmap->next = NULL;
+ (void) memcpy(newmap->pmp, pmp, sizeof (prmap_t));
+
+ /*
+ * If the mapping is not anon or not part of the heap, make a name
+ * for it. We don't want to report the heap as a.out's data.
+ */
+ if (!(pmp->pr_mflags & MA_ANON) ||
+ (pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
+ pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize)) {
+ lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
+ newmap->label, sizeof (newmap->label));
+ if (pmp->pr_mflags & MA_SHARED)
+ newmap->mtypes |= 1 << AT_SHARED;
+ else
+ newmap->mtypes |= 1 << AT_PRIVM;
+ }
+
+ if (lname == NULL && (pmp->pr_mflags & MA_ANON)) {
+ lname = anon_name(newmap->label, Psp, pmp->pr_vaddr,
+ pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid,
+ &newmap->mtypes);
+ }
+
+ /*
+ * Add raw advice that applies to this mapping to the merged_list
+ */
+ psaddr = rawadv_list;
+ /*
+ * Advance to point in rawadv_list that applies to this mapping
+ */
+ while (psaddr && psaddr->addr < pmp->pr_vaddr)
+ psaddr = psaddr->next;
+ /*
+ * Copy over to merged_list, check to see if size needs to be filled in
+ */
+ while (psaddr && psaddr->addr < (pmp->pr_vaddr + pmp->pr_size)) {
+ newaddr = malloc(sizeof (saddr_t));
+ (void) memcpy(newaddr, psaddr, sizeof (saddr_t));
+ insert_addr(&merged_list, newaddr, YESDUPS);
+ /*
+ * For raw advice that is given without size, try to default
+ * size to size of mapping (only allowed if raw adv addr is
+ * equal to beginning of mapping). Don't change the entry
+ * in rawadv_list, only in the merged_list as the mappings
+ * (and thus the default sizes) will be different for
+ * different processes.
+ */
+ if ((pmp->pr_vaddr == psaddr->addr) && (psaddr->length == 0))
+ newaddr->length = pmp->pr_size;
+ psaddr = psaddr->next;
+ }
+
+ /*
+ * Put mapping into merged list with no advice, then
+ * check to see if any generic advice applies.
+ */
+ newaddr = malloc(sizeof (saddr_t));
+ newaddr->addr = pmp->pr_vaddr;
+ newaddr->length = pmp->pr_size;
+ newaddr->adv = NO_ADVICE;
+ insert_addr(&merged_list, newaddr, YESDUPS);
+
+ newmap->mtypes &= at_map;
+ for (i = AT_STACK; i >= AT_PRIVM; i--) {
+ if (newmap->mtypes & (1 << i)) {
+ assert(generic_adv[i] != NO_ADVICE);
+ newaddr->adv = generic_adv[i];
+ break;
+ }
+ }
+
+ /*
+ * Add to linked list of mappings
+ */
+ if (maplist_tail == NULL) {
+ maplist_head = maplist_tail = newmap;
+ } else {
+ maplist_tail->next = newmap;
+ maplist_tail = newmap;
+ }
+
+
+ return (0);
+}
+
+/*
+ * Traverse advice list and apply all applicable advice to each region
+ */
+static int
+apply_advice(saddr_t **advicelist)
+{
+ saddr_t *psaddr = *advicelist;
+ saddr_t *next;
+ int i;
+
+
+ while (!interrupt && psaddr != NULL) {
+ /*
+ * Save next pointer since element may be removed before
+ * we get a chance to advance psaddr.
+ */
+ next = psaddr->next;
+
+ /*
+ * Since mappings have been added to the merged list
+ * even if no generic advice was given for the map,
+ * check to make sure advice exists before bothering
+ * with the for loop.
+ */
+ if (psaddr->adv != NO_ADVICE) {
+ for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) {
+ if ((psaddr->adv & (1 << i)) &&
+ (pr_madvise(Pr, (caddr_t)psaddr->addr,
+ psaddr->length, i) < 0)) {
+ /*
+ * madvise(3C) call failed trying to
+ * apply advice output error and remove
+ * from advice list
+ */
+ (void) fprintf(stderr,
+ gettext("Error applying "
+ "advice (%s) to memory range "
+ "[%lx, %lx):\n"),
+ advicestr[i], (ulong_t)psaddr->addr,
+ (ulong_t)psaddr->addr +
+ psaddr->length);
+ perror("madvise");
+ /*
+ * Clear this advice from the advice
+ * mask. If no more advice is given
+ * for this element, remove element
+ * from list.
+ */
+ psaddr->adv &= ~(1 << i);
+ if (psaddr->adv == 0) {
+ delete_addr(advicelist, psaddr);
+ break;
+ }
+ }
+ }
+ }
+ psaddr = next;
+ }
+ return (0);
+}
+
+/*
+ * Set advice but keep mutual exclusive property of advice groupings
+ */
+static void
+set_advice(int *combined_adv, int new_adv) {
+ /*
+ * Since advice falls in 3 groups of mutually exclusive options,
+ * clear previous value if new advice overwrites that group.
+ */
+
+ /*
+ * If this is the first advice to be applied, clear invalid value (-1)
+ */
+ if (*combined_adv == -1)
+ *combined_adv = 0;
+
+ if (new_adv & GRP1_ADV)
+ *combined_adv &= ~GRP1_ADV;
+ else if (new_adv & GRP2_ADV)
+ *combined_adv &= ~GRP2_ADV;
+ else
+ *combined_adv &= ~GRP3_ADV;
+
+ *combined_adv |= new_adv;
+}
+
+/*
+ * Create chopped list from merged list for use with verbose output
+ */
+static void
+create_choplist(saddr_t **choppedlist, saddr_t *mergedlist)
+{
+ saddr_t *mlptr, *clptr;
+
+ for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
+ clptr = malloc(sizeof (saddr_t));
+ clptr->addr = mlptr->addr;
+ clptr->length = 0;
+ /*
+ * Initialize the adv to -1 as an indicator for invalid
+ * elements in the chopped list (created from gaps between
+ * memory maps).
+ */
+ clptr->adv = -1;
+ clptr->next = NULL;
+ insert_addr(choppedlist, clptr, NODUPS);
+
+ clptr = malloc(sizeof (saddr_t));
+ clptr->addr = mlptr->addr + mlptr->length;
+ clptr->length = 0;
+ /*
+ * Again, initialize to -1 as an indicatorfor invalid elements
+ */
+ clptr->adv = -1;
+ clptr->next = NULL;
+ insert_addr(choppedlist, clptr, NODUPS);
+ }
+
+ for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
+ if (clptr->next)
+ clptr->length = clptr->next->addr - clptr->addr;
+ else {
+ /*
+ * must be last element, now that we've calculated
+ * all segment lengths, we can remove this node
+ */
+ delete_addr(choppedlist, clptr);
+ }
+ }
+
+ for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
+ for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
+ if (mlptr->addr <= clptr->addr &&
+ mlptr->addr + mlptr->length >=
+ clptr->addr + clptr->length)
+ /*
+ * set_advice() will take care of conflicting
+ * advice by taking only the last advice
+ * applied for each of the 3 groups of advice.
+ */
+ set_advice(&clptr->adv, mlptr->adv);
+ if (mlptr->addr + mlptr->length <
+ clptr->addr)
+ break;
+ }
+ }
+}
+
+/*
+ * Print advice in pmap style for verbose output
+ */
+static void
+print_advice(saddr_t *advlist, mapnode_t *maplist)
+{
+ saddr_t *psaddr = advlist;
+ mapnode_t *pmapnode;
+ char *advice;
+
+ pmapnode = maplist;
+
+ while (psaddr) {
+ /*
+ * Using indicator flag from create_choppedlist, we know
+ * which entries in the chopped_list are gaps and should
+ * not be printed.
+ */
+ if (psaddr->adv == -1) {
+ psaddr = psaddr->next;
+ continue;
+ }
+
+ while (pmapnode && (pmapnode->pmp->pr_vaddr +
+ pmapnode->pmp->pr_size <= psaddr->addr))
+ pmapnode = pmapnode->next;
+
+ advice = advtostr(psaddr->adv);
+
+ /*
+ * Print segment mapping and advice if there is any, or just a
+ * segment mapping.
+ */
+ if (strlen(advice) > 0) {
+ (void) printf("%.*lX %*uK %6s %s\t%s\n",
+ addr_width, (ulong_t)psaddr->addr, size_width - 1,
+ (int)ROUNDUP_KB(psaddr->length),
+ mflags(pmapnode->pmp->pr_mflags), pmapnode->label,
+ advice);
+ } else {
+ (void) printf("%.*lX %*uK %6s %s\n",
+ addr_width, (ulong_t)psaddr->addr, size_width - 1,
+ (int)ROUNDUP_KB(psaddr->length),
+ mflags(pmapnode->pmp->pr_mflags), pmapnode->label);
+ }
+ psaddr = psaddr->next;
+
+ }
+}
+
+/*
+ * Call madvise(3c) in the context of the target process
+ */
+static int
+pr_madvise(struct ps_prochandle *Pr, caddr_t addr, size_t len, int advice)
+{
+ return (pr_memcntl(Pr, addr, len, MC_ADVISE,
+ (caddr_t)(uintptr_t)advice, 0, 0));
+}
+
+static char *
+mflags(uint_t arg)
+{
+ static char code_buf[80];
+
+ /*
+ * rwxsR
+ *
+ * r - segment is readable
+ * w - segment is writable
+ * x - segment is executable
+ * s - segment is shared
+ * R - segment is mapped MAP_NORESERVE
+ *
+ */
+ (void) snprintf(code_buf, sizeof (code_buf), "%c%c%c%c%c ",
+ arg & MA_READ ? 'r' : '-',
+ arg & MA_WRITE ? 'w' : '-',
+ arg & MA_EXEC ? 'x' : '-',
+ arg & MA_SHARED ? 's' : '-',
+ arg & MA_NORESERVE ? 'R' : '-');
+
+ return (code_buf);
+}
+
+/*
+ * Convert advice to a string containing a commented list of applicable advice
+ */
+static char *
+advtostr(int adv)
+{
+ static char buf[50];
+ int i;
+
+ *buf = '\0';
+
+ if (adv != NO_ADVICE) {
+ for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) {
+ if (adv & (1 << i)) {
+ /*
+ * check if it's the first advice entry
+ */
+ if (*buf == '\0')
+ (void) snprintf(buf, sizeof (buf) - 1,
+ "<= %s", advicestr[i]);
+ else
+ (void) snprintf(buf, sizeof (buf) - 1,
+ "%s,%s", buf, advicestr[i]);
+ }
+ }
+ }
+
+ return (buf);
+}
+
+/*
+ * Handler for catching signals from terminal
+ */
+/* ARGSUSED */
+static void
+intr(int sig)
+{
+ interrupt++;
+}
+
+int
+main(int argc, char **argv)
+{
+ int Fflag = 0;
+ int rc = 0;
+ int opt, subopt;
+ int tmpadv;
+ char *options, *value;
+ saddr_t *psaddr;
+ mapnode_t *pmapnode, *tempmapnode;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Get name of program for error messages
+ */
+ progname = basename(argv[0]);
+
+ /*
+ * Not much to do when only name of program given
+ */
+ if (argc == 1)
+ usage();
+
+ /*
+ * Catch signals from terminal, so they can be handled asynchronously
+ * when we're ready instead of when we're not (;-)
+ */
+ if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGHUP, intr);
+ if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGINT, intr);
+ if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGQUIT, intr);
+ (void) sigset(SIGPIPE, intr);
+ (void) sigset(SIGTERM, intr);
+
+ /*
+ * Parse options, record generic advice if any and create
+ * rawadv_list from specific address advice.
+ */
+
+ while ((opt = getopt(argc, argv, "Fo:v")) != EOF) {
+ switch (opt) {
+ case 'o':
+ options = optarg;
+ while (*options != '\0') {
+ subopt = getsubopt(&options, suboptstr,
+ &value);
+ switch (subopt) {
+ case AT_PRIVM:
+ case AT_HEAP:
+ case AT_SHARED:
+ case AT_STACK:
+ at_map |= (1 << subopt);
+ tmpadv = get_advice(value);
+ set_advice(&generic_adv[subopt],
+ tmpadv);
+ break;
+ default:
+ at_map |= (1 << AT_SEG);
+ psaddr = parse_suboptions(value);
+ if (psaddr == NULL) {
+ usage();
+ } else {
+ insert_addr(&rawadv_list,
+ psaddr, YESDUPS);
+ }
+ break;
+ }
+ }
+ break;
+ case 'v':
+ opt_verbose = 1;
+ break;
+ case 'F': /* force grabbing (no O_EXCL) */
+ Fflag = PGRAB_FORCE;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0) {
+ usage();
+ }
+
+ /*
+ * Iterate through all pid arguments, create new merged_list, maplist,
+ * (and chopped_list if using verbose output) based on each process'
+ * memory map.
+ */
+
+ while (!interrupt && argc-- > 0) {
+ char *arg;
+ int gcode;
+ psinfo_t psinfo;
+
+ if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_PIDS,
+ PGRAB_RETAIN | Fflag, &gcode)) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: cannot examine %s: %s\n"),
+ progname, arg, Pgrab_error(gcode));
+ rc++;
+ continue;
+ }
+
+
+ addr_width =
+ (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8;
+ size_width =
+ (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8;
+ (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
+
+ if (opt_verbose) {
+ proc_unctrl_psinfo(&psinfo);
+ (void) printf("%d:\t%.70s\n",
+ (int)psinfo.pr_pid, psinfo.pr_psargs);
+ }
+
+ /*
+ * Get mappings for a process unless it is a system process.
+ */
+ if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
+ nstacks = psinfo.pr_nlwp * 2;
+ stacks = calloc(nstacks, sizeof (stacks[0]));
+ if (stacks != NULL) {
+ int n = 0;
+ (void) Plwp_iter(Pr, getstack, &n);
+ qsort(stacks, nstacks, sizeof (stacks[0]),
+ cmpstacks);
+ }
+
+ if (Pgetauxval(Pr, AT_BASE) != -1L &&
+ Prd_agent(Pr) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: warning: "
+ "librtld_db failed to initialize; "
+ "shared library information will not "
+ "be available\n"),
+ progname);
+ }
+
+ /*
+ * Create linked list of mappings for current process
+ * In addition, add generic advice and raw advice
+ * entries to merged_list.
+ * e.g. if rawadv_list contains:
+ * [0x38000,0x3a000) = adv1
+ * [0x3a000,0x3c000) = adv2
+ * and there is generic advice:
+ * heap = adv3
+ * where heap corresponds to 0x38000, then merged_list
+ * will contain:
+ * ... (include all other mappings from process)
+ * [0x38000,0x3c000) = adv3
+ * [0x38000,0x3a000) = adv1
+ * [0x3a000,0x3c000) = adv2
+ * ... (include all other mappings from process)
+ */
+ assert(merged_list == NULL);
+ maplist_head = maplist_tail = NULL;
+ rc += Pmapping_iter(Pr, (proc_map_f *)create_maplist,
+ NULL);
+
+ /*
+ * Apply advice by iterating through merged list
+ */
+ (void) apply_advice(&merged_list);
+
+ if (opt_verbose) {
+ assert(chopped_list == NULL);
+ /*
+ * Create chopped_list from merged_list
+ */
+ create_choplist(&chopped_list, merged_list);
+
+ /*
+ * Iterate through maplist and output as
+ * given by chopped_list
+ */
+ print_advice(chopped_list, maplist_head);
+ delete_list(&chopped_list);
+ }
+
+ delete_list(&merged_list);
+
+ /*
+ * Clear maplist
+ */
+ pmapnode = maplist_head;
+ while (pmapnode) {
+ tempmapnode = pmapnode;
+ pmapnode = pmapnode->next;
+ free(tempmapnode);
+ }
+
+ if (stacks != NULL) {
+ free(stacks);
+ stacks = NULL;
+ }
+ }
+
+ Prelease(Pr, 0);
+ }
+
+ return (rc);
+}
diff --git a/usr/src/cmd/ptools/pmap/pmap.c b/usr/src/cmd/ptools/pmap/pmap.c
index 7d2234aced..866964ef3a 100644
--- a/usr/src/cmd/ptools/pmap/pmap.c
+++ b/usr/src/cmd/ptools/pmap/pmap.c
@@ -18,6 +18,7 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -37,10 +38,29 @@
#include <link.h>
#include <libelf.h>
#include <sys/types.h>
+#include <signal.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
+#include <sys/mman.h>
+#include <sys/lgrp_user.h>
#include <libproc.h>
+#define KILOBYTE 1024
+#define MEGABYTE (KILOBYTE * KILOBYTE)
+#define GIGABYTE (KILOBYTE * KILOBYTE * KILOBYTE)
+
+/*
+ * Round up the value to the nearest kilobyte
+ */
+#define ROUNDUP_KB(x) (((x) + (KILOBYTE - 1)) / KILOBYTE)
+
+/*
+ * The alignment should be a power of 2.
+ */
+#define P2ALIGN(x, align) ((x) & -(align))
+
+#define INVALID_ADDRESS (uintptr_t)(-1)
+
struct totals {
ulong_t total_size;
ulong_t total_swap;
@@ -49,6 +69,35 @@ struct totals {
ulong_t total_locked;
};
+/*
+ * -L option requires per-page information. The information is presented in an
+ * array of page_descr structures.
+ */
+typedef struct page_descr {
+ uintptr_t pd_start; /* start address of a page */
+ size_t pd_pagesize; /* page size in bytes */
+ lgrp_id_t pd_lgrp; /* lgroup of memory backing the page */
+ int pd_valid; /* valid page description if non-zero */
+} page_descr_t;
+
+/*
+ * Per-page information for a memory chunk.
+ * The meminfo(2) system call accepts up to MAX_MEMINFO_CNT pages at once.
+ * When we need to scan larger ranges we divide them in MAX_MEMINFO_CNT sized
+ * chunks. The chunk information is stored in the memory_chunk structure.
+ */
+typedef struct memory_chunk {
+ page_descr_t page_info[MAX_MEMINFO_CNT];
+ uintptr_t end_addr;
+ uintptr_t chunk_start; /* Starting address */
+ uintptr_t chunk_end; /* chunk_end is always <= end_addr */
+ size_t page_size;
+ int page_index; /* Current page */
+ int page_count; /* Number of pages */
+} memory_chunk_t;
+
+static volatile int interrupt;
+
typedef int proc_xmap_f(void *, const prxmap_t *, const char *, int, int);
static int xmapping_iter(struct ps_prochandle *, proc_xmap_f *, void *,
@@ -65,18 +114,43 @@ static int gather_map(void *, const prmap_t *, const char *);
static int gather_xmap(void *, const prxmap_t *, const char *, int, int);
static int iter_map(proc_map_f *, void *);
static int iter_xmap(proc_xmap_f *, void *);
+static int parse_addr_range(char *, uintptr_t *, uintptr_t *);
+static void mem_chunk_init(memory_chunk_t *, uintptr_t, size_t);
static int perr(char *);
static void printK(long, int);
static char *mflags(uint_t);
+static size_t get_contiguous_region(memory_chunk_t *, uintptr_t,
+ uintptr_t, size_t, lgrp_id_t *);
+static void mem_chunk_get(memory_chunk_t *, uintptr_t);
+static lgrp_id_t addr_to_lgrp(memory_chunk_t *, uintptr_t, size_t *);
+static char *lgrp2str(lgrp_id_t);
+
+static int address_in_range(uintptr_t, uintptr_t, size_t);
+static size_t adjust_addr_range(uintptr_t, uintptr_t, size_t,
+ uintptr_t *, uintptr_t *);
+
static int lflag = 0;
+static int Lflag = 0;
static int aflag = 0;
+
+/*
+ * The -A address range is represented as a pair of addresses
+ * <start_addr, end_addr>. Either one of these may be unspecified (set to
+ * INVALID_ADDRESS). If both are unspecified, no address range restrictions are
+ * in place.
+ */
+static uintptr_t start_addr = INVALID_ADDRESS;
+static uintptr_t end_addr = INVALID_ADDRESS;
+
static int addr_width, size_width;
static char *command;
static char *procname;
static struct ps_prochandle *Pr;
+static void intr(int);
+
typedef struct lwpstack {
lwpid_t lwps_lwpid;
stack_t lwps_stack;
@@ -86,7 +160,7 @@ typedef struct {
prxmap_t md_xmap;
prmap_t md_map;
char *md_objname;
- int md_last;
+ boolean_t md_last;
int md_doswap;
} mapdata_t;
@@ -143,7 +217,7 @@ cmpstacks(const void *ap, const void *bp)
int
main(int argc, char **argv)
{
- int rflag = 0, sflag = 0, xflag = 0;
+ int rflag = 0, sflag = 0, xflag = 0, Fflag = 0;
int errflg = 0, Sflag = 0;
int rc = 0;
int opt;
@@ -160,7 +234,7 @@ main(int argc, char **argv)
else
command = argv[0];
- while ((opt = getopt(argc, argv, "arsxSlF")) != EOF) {
+ while ((opt = getopt(argc, argv, "arsxSlLFA:")) != EOF) {
switch (opt) {
case 'a': /* include shared mappings in -[xS] */
aflag = 1;
@@ -180,12 +254,16 @@ main(int argc, char **argv)
case 'l': /* show unresolved link map names */
lflag = 1;
break;
- case 'F':
- /*
- * Since we grab the process readonly now, the -F flag
- * is meaningless. Consume it anyway it for backwards
- * compatbility.
- */
+ case 'L': /* show lgroup information */
+ Lflag = 1;
+ break;
+ case 'F': /* force grabbing (no O_EXCL) */
+ Fflag = PGRAB_FORCE;
+ break;
+ case 'A':
+ if (parse_addr_range(optarg, &start_addr, &end_addr)
+ != 0)
+ errflg++;
break;
default:
errflg = 1;
@@ -197,21 +275,28 @@ main(int argc, char **argv)
argv += optind;
if ((Sflag && (xflag || rflag || sflag)) || (xflag && rflag) ||
- (aflag && (!xflag && !Sflag))) {
+ (aflag && (!xflag && !Sflag)) ||
+ (Lflag && (xflag || Sflag))) {
errflg = 1;
}
if (errflg || argc <= 0) {
(void) fprintf(stderr,
- "usage:\t%s [-rslF] { pid | core } ...\n", command);
+ "usage:\t%s [-rslF] [-A start[,end]] { pid | core } ...\n",
+ command);
(void) fprintf(stderr,
"\t\t(report process address maps)\n");
(void) fprintf(stderr,
- "\t%s -x [-aslF] pid ...\n", command);
+ "\t%s -L [-rslF] [-A start[,end]] pid ...\n", command);
+ (void) fprintf(stderr,
+ "\t\t(report process address maps lgroups mappings)\n");
+ (void) fprintf(stderr,
+ "\t%s -x [-aslF] [-A start[,end]] pid ...\n", command);
(void) fprintf(stderr,
"\t\t(show resident/anon/locked mapping details)\n");
(void) fprintf(stderr,
- "\t%s -S [-alF] { pid | core } ...\n", command);
+ "\t%s -S [-alF] [-A start[,end]] { pid | core } ...\n",
+ command);
(void) fprintf(stderr,
"\t\t(show swap reservations)\n\n");
(void) fprintf(stderr,
@@ -224,6 +309,10 @@ main(int argc, char **argv)
"\t-l: show unresolved dynamic linker map names\n");
(void) fprintf(stderr,
"\t-F: force grabbing of the target process\n");
+ (void) fprintf(stderr,
+ "\t-L: show lgroup mappings\n");
+ (void) fprintf(stderr,
+ "\t-A start,end: limit output to the specified range\n");
return (2);
}
@@ -242,9 +331,16 @@ main(int argc, char **argv)
int gcode;
psinfo_t psinfo;
int tries = 0;
+ int prg_gflags = PGRAB_RDONLY;
+ int prr_flags = 0;
+
+ if (Lflag) {
+ prg_gflags = PGRAB_RETAIN | Fflag;
+ prr_flags = PRELEASE_RETAIN;
+ }
if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY,
- PGRAB_RDONLY, &gcode)) == NULL) {
+ prg_gflags, &gcode)) == NULL) {
(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
command, arg, Pgrab_error(gcode));
rc++;
@@ -267,7 +363,7 @@ main(int argc, char **argv)
"examine %s: lost control of "
"process\n", command, arg);
rc++;
- Prelease(Pr, 0);
+ Prelease(Pr, prr_flags);
continue;
}
} else {
@@ -281,11 +377,12 @@ again:
(void) printf("core '%s' of %d:\t%.70s\n",
arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
- if (rflag || sflag || xflag || Sflag) {
+ if (rflag || sflag || xflag || Sflag || Lflag) {
(void) printf(" -%c option is not compatible "
"with core files\n", xflag ? 'x' :
- sflag ? 's' : rflag ? 'r' : 'S');
- Prelease(Pr, 0);
+ sflag ? 's' : rflag ? 'r' :
+ Lflag ? 'L' : 'S');
+ Prelease(Pr, prr_flags);
rc++;
continue;
}
@@ -295,6 +392,27 @@ again:
(int)psinfo.pr_pid, psinfo.pr_psargs);
}
+ if (Lflag) {
+ /*
+ * The implementation of -L option creates an agent LWP
+ * in the target process address space. The agent LWP
+ * issues meminfo(2) system calls on behalf of the
+ * target process. If we are interrupted prematurely,
+ * the target process remains in the stopped state with
+ * the agent still attached to it. To prevent such
+ * situation we catch signals from terminal and
+ * terminate gracefully.
+ */
+ if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGHUP, intr);
+ if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGINT, intr);
+ if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
+ (void) sigset(SIGQUIT, intr);
+ (void) sigset(SIGPIPE, intr);
+ (void) sigset(SIGTERM, intr);
+ }
+
if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
struct totals t;
@@ -305,7 +423,7 @@ again:
*/
if (Pstate(Pr) != PS_DEAD) {
if (tries++ == MAX_TRIES) {
- Prelease(Pr, 0);
+ Prelease(Pr, prr_flags);
(void) close(mapfd);
(void) fprintf(stderr, "%s: cannot "
"examine %s: address space is "
@@ -314,7 +432,7 @@ again:
}
if (fstat64(mapfd, &statbuf) != 0) {
- Prelease(Pr, 0);
+ Prelease(Pr, prr_flags);
(void) close(mapfd);
(void) fprintf(stderr, "%s: cannot "
"examine %s: lost control of "
@@ -409,7 +527,8 @@ again:
(void) printf("\n");
} else if (Sflag) {
- (void) printf("%*s%*s%*s Mode Mapped File\n",
+ (void) printf("%*s%*s%*s Mode"
+ " Mapped File\n",
addr_width, "Address",
size_width, "Kbytes",
size_width, "Swap");
@@ -433,11 +552,23 @@ again:
if (rflag) {
rc += iter_map(look_map, &t);
} else if (sflag) {
- (void) printf("%*s %*s %4s %-6s %s\n",
- addr_width, "Address", size_width,
- "Bytes", "Pgsz", "Mode ",
- "Mapped File");
- rc += iter_xmap(look_smap, &t);
+ if (Lflag) {
+ (void) printf("%*s %*s %4s"
+ " %-6s %s %s\n",
+ addr_width, "Address",
+ size_width,
+ "Bytes", "Pgsz", "Mode ",
+ "Lgrp", "Mapped File");
+ rc += iter_xmap(look_smap, &t);
+ } else {
+ (void) printf("%*s %*s %4s"
+ " %-6s %s\n",
+ addr_width, "Address",
+ size_width,
+ "Bytes", "Pgsz", "Mode ",
+ "Mapped File");
+ rc += iter_xmap(look_smap, &t);
+ }
} else {
rc += iter_map(look_map, &t);
}
@@ -455,7 +586,7 @@ again:
}
- Prelease(Pr, 0);
+ Prelease(Pr, prr_flags);
if (mapfd != -1)
(void) close(mapfd);
}
@@ -631,6 +762,12 @@ again:
}
}
+ /*
+ * Mark the last element.
+ */
+ if (map_count > 0)
+ maps[map_count - 1].md_last = B_TRUE;
+
free(prmapp);
return (0);
}
@@ -641,16 +778,21 @@ look_map(void *data, const prmap_t *pmp, const char *object_name)
{
struct totals *t = data;
const pstatus_t *Psp = Pstatus(Pr);
- size_t size = (pmp->pr_size + 1023) / 1024;
+ size_t size;
char mname[PATH_MAX];
char *lname = NULL;
+ size_t psz = pmp->pr_pagesize;
+ uintptr_t vaddr = pmp->pr_vaddr;
+ uintptr_t segment_end = vaddr + pmp->pr_size;
+ lgrp_id_t lgrp;
+ memory_chunk_t mchunk;
/*
* If the mapping is not anon or not part of the heap, make a name
* for it. We don't want to report the heap as a.out's data.
*/
if (!(pmp->pr_mflags & MA_ANON) ||
- pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
+ segment_end <= Psp->pr_brkbase ||
pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
mname, sizeof (mname));
@@ -662,11 +804,70 @@ look_map(void *data, const prmap_t *pmp, const char *object_name)
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
- (void) printf(lname ? "%.*lX %*luK %-6s %s\n" : "%.*lX %*luK %s\n",
- addr_width, (uintptr_t)pmp->pr_vaddr,
- size_width - 1, size, mflags(pmp->pr_mflags), lname);
+ /*
+ * Adjust the address range if -A is specified.
+ */
+ size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz,
+ &vaddr, &segment_end);
+
+ if (size == 0)
+ return (0);
+
+ if (!Lflag) {
+ /*
+ * Display the whole mapping
+ */
+ size = ROUNDUP_KB(size);
+
+ (void) printf(lname ?
+ "%.*lX %*luK %-6s %s\n" :
+ "%.*lX %*luK %s\n",
+ addr_width, vaddr,
+ size_width - 1, size, mflags(pmp->pr_mflags), lname);
+
+ t->total_size += size;
+ return (0);
+ }
+
+ /*
+ * We need to display lgroups backing physical memory, so we break the
+ * segment into individual pages and coalesce pages with the same lgroup
+ * into one "segment".
+ */
+
+ /*
+ * Initialize address descriptions for the mapping.
+ */
+ mem_chunk_init(&mchunk, segment_end, psz);
+ size = 0;
- t->total_size += size;
+ /*
+ * Walk mapping (page by page) and display contiguous ranges of memory
+ * allocated to same lgroup.
+ */
+ do {
+ size_t size_contig;
+
+ /*
+ * Get contiguous region of memory starting from vaddr allocated
+ * from the same lgroup.
+ */
+ size_contig = get_contiguous_region(&mchunk, vaddr,
+ segment_end, pmp->pr_pagesize, &lgrp);
+
+ (void) printf(lname ? "%.*lX %*luK %-6s%s %s\n" :
+ "%.*lX %*luK %s %s\n",
+ addr_width, vaddr,
+ size_width - 1, size_contig / KILOBYTE,
+ mflags(pmp->pr_mflags),
+ lgrp2str(lgrp), lname);
+
+ vaddr += size_contig;
+ size += size_contig;
+ } while (vaddr < segment_end && !interrupt);
+
+ /* Update the total size */
+ t->total_size += ROUNDUP_KB(size);
return (0);
}
@@ -689,16 +890,16 @@ pagesize(const prxmap_t *pmp)
return ("-"); /* no underlying HAT mapping */
}
- if (pagesize >= 1024 && (pagesize % 1024) == 0) {
- if ((pagesize % (1024 * 1024 * 1024)) == 0)
+ if (pagesize >= KILOBYTE && (pagesize % KILOBYTE) == 0) {
+ if ((pagesize % GIGABYTE) == 0)
(void) snprintf(buf, sizeof (buf), "%dG",
- pagesize / (1024 * 1024 * 1024));
- else if ((pagesize % (1024 * 1024)) == 0)
+ pagesize / GIGABYTE);
+ else if ((pagesize % MEGABYTE) == 0)
(void) snprintf(buf, sizeof (buf), "%dM",
- pagesize / (1024 * 1024));
+ pagesize / MEGABYTE);
else
(void) snprintf(buf, sizeof (buf), "%dK",
- pagesize / 1024);
+ pagesize / KILOBYTE);
} else
(void) snprintf(buf, sizeof (buf), "%db", pagesize);
@@ -714,10 +915,15 @@ look_smap(void *data,
{
struct totals *t = data;
const pstatus_t *Psp = Pstatus(Pr);
- size_t size = (pmp->pr_size + 1023) / 1024;
+ size_t size;
char mname[PATH_MAX];
char *lname = NULL;
const char *format;
+ size_t psz = pmp->pr_pagesize;
+ uintptr_t vaddr = pmp->pr_vaddr;
+ uintptr_t segment_end = vaddr + pmp->pr_size;
+ lgrp_id_t lgrp;
+ memory_chunk_t mchunk;
/*
* If the mapping is not anon or not part of the heap, make a name
@@ -736,16 +942,74 @@ look_smap(void *data,
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
+ /*
+ * Adjust the address range if -A is specified.
+ */
+ size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz,
+ &vaddr, &segment_end);
+
+ if (size == 0)
+ return (0);
+
+ if (!Lflag) {
+ /*
+ * Display the whole mapping
+ */
+ if (lname != NULL)
+ format = "%.*lX %*luK %4s %-6s %s\n";
+ else
+ format = "%.*lX %*luK %4s %s\n";
+
+ size = ROUNDUP_KB(size);
+
+ (void) printf(format, addr_width, vaddr, size_width - 1, size,
+ pagesize(pmp), mflags(pmp->pr_mflags), lname);
+
+ t->total_size += size;
+ return (0);
+ }
+
if (lname != NULL)
- format = "%.*lX %*luK %4s %-6s %s\n";
+ format = "%.*lX %*luK %4s %-6s%s %s\n";
else
- format = "%.*lX %*luK %4s %s\n";
+ format = "%.*lX %*luK %4s%s %s\n";
- (void) printf(format, addr_width, (uintptr_t)pmp->pr_vaddr,
- size_width - 1, size,
- pagesize(pmp), mflags(pmp->pr_mflags), lname);
+ /*
+ * We need to display lgroups backing physical memory, so we break the
+ * segment into individual pages and coalesce pages with the same lgroup
+ * into one "segment".
+ */
+
+ /*
+ * Initialize address descriptions for the mapping.
+ */
+ mem_chunk_init(&mchunk, segment_end, psz);
+ size = 0;
- t->total_size += size;
+ /*
+ * Walk mapping (page by page) and display contiguous ranges of memory
+ * allocated to same lgroup.
+ */
+ do {
+ size_t size_contig;
+
+ /*
+ * Get contiguous region of memory starting from vaddr allocated
+ * from the same lgroup.
+ */
+ size_contig = get_contiguous_region(&mchunk, vaddr,
+ segment_end, pmp->pr_pagesize, &lgrp);
+
+ (void) printf(format, addr_width, vaddr,
+ size_width - 1, size_contig / KILOBYTE,
+ pagesize(pmp), mflags(pmp->pr_mflags),
+ lgrp2str(lgrp), lname);
+
+ vaddr += size_contig;
+ size += size_contig;
+ } while (vaddr < segment_end && !interrupt);
+
+ t->total_size += ROUNDUP_KB(size);
return (0);
}
@@ -786,17 +1050,17 @@ look_xmap(void *data,
(void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr);
- printK((pmp->pr_size + 1023) / 1024, size_width);
- printK(pmp->pr_rss * (pmp->pr_pagesize / 1024), size_width);
- printK(ANON(pmp) * (pmp->pr_pagesize / 1024), size_width);
- printK(pmp->pr_locked * (pmp->pr_pagesize / 1024), size_width);
+ printK(ROUNDUP_KB(pmp->pr_size), size_width);
+ printK(pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE), size_width);
+ printK(ANON(pmp) * (pmp->pr_pagesize / KILOBYTE), size_width);
+ printK(pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE), size_width);
(void) printf(lname ? " %4s %-6s %s\n" : " %4s %s\n",
pagesize(pmp), mflags(pmp->pr_mflags), lname);
- t->total_size += (pmp->pr_size + 1023) / 1024;
- t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / 1024);
- t->total_anon += ANON(pmp) * (pmp->pr_pagesize / 1024);
- t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / 1024));
+ t->total_size += ROUNDUP_KB(pmp->pr_size);
+ t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE);
+ t->total_anon += ANON(pmp) * (pmp->pr_pagesize / KILOBYTE);
+ t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE));
return (0);
}
@@ -863,9 +1127,9 @@ look_xmap_nopgsz(void *data,
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
- kperpage = pmp->pr_pagesize / 1024;
+ kperpage = pmp->pr_pagesize / KILOBYTE;
- t->total_size += (pmp->pr_size + 1023) / 1024;
+ t->total_size += ROUNDUP_KB(pmp->pr_size);
t->total_rss += pmp->pr_rss * kperpage;
t->total_anon += ANON(pmp) * kperpage;
t->total_locked += pmp->pr_locked * kperpage;
@@ -910,7 +1174,7 @@ look_xmap_nopgsz(void *data,
}
(void) printf("%.*lX", addr_width, (ulong_t)prev_vaddr);
- printK((prev_size + 1023) / 1024, size_width);
+ printK(ROUNDUP_KB(prev_size), size_width);
if (doswap)
printK(prev_swap, size_width);
@@ -919,7 +1183,7 @@ look_xmap_nopgsz(void *data,
printK(prev_anon, size_width);
printK(prev_locked, size_width);
}
- (void) printf(prev_lname ? " %-6s %s\n" : " %s\n",
+ (void) printf(prev_lname ? " %-6s %s\n" : "%s\n",
mflags(prev_mflags), prev_lname);
if (last == 0) {
@@ -939,7 +1203,7 @@ look_xmap_nopgsz(void *data,
prev_swap = swap * kperpage;
} else if (merged == 0) {
(void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr);
- printK((pmp->pr_size + 1023) / 1024, size_width);
+ printK(ROUNDUP_KB(pmp->pr_size), size_width);
if (doswap)
printK(swap * kperpage, size_width);
else {
@@ -1026,8 +1290,14 @@ nextmap(void)
static int
gather_map(void *ignored, const prmap_t *map, const char *objname)
{
- mapdata_t *data = nextmap();
+ mapdata_t *data;
+ /* Skip mappings which are outside the range specified by -A */
+ if (!address_in_range(map->pr_vaddr,
+ map->pr_vaddr + map->pr_size, map->pr_pagesize))
+ return (0);
+
+ data = nextmap();
data->md_map = *map;
if (data->md_objname != NULL)
free(data->md_objname);
@@ -1041,8 +1311,14 @@ static int
gather_xmap(void *ignored, const prxmap_t *xmap, const char *objname,
int last, int doswap)
{
- mapdata_t *data = nextmap();
+ mapdata_t *data;
+
+ /* Skip mappings which are outside the range specified by -A */
+ if (!address_in_range(xmap->pr_vaddr,
+ xmap->pr_vaddr + xmap->pr_size, xmap->pr_pagesize))
+ return (0);
+ data = nextmap();
data->md_xmap = *xmap;
if (data->md_objname != NULL)
free(data->md_objname);
@@ -1060,6 +1336,8 @@ iter_map(proc_map_f *func, void *data)
int ret;
for (i = 0; i < map_count; i++) {
+ if (interrupt)
+ break;
if ((ret = func(data, &maps[i].md_map,
maps[i].md_objname)) != 0)
return (ret);
@@ -1075,6 +1353,8 @@ iter_xmap(proc_xmap_f *func, void *data)
int ret;
for (i = 0; i < map_count; i++) {
+ if (interrupt)
+ break;
if ((ret = func(data, &maps[i].md_xmap, maps[i].md_objname,
maps[i].md_last, maps[i].md_doswap)) != 0)
return (ret);
@@ -1082,3 +1362,401 @@ iter_xmap(proc_xmap_f *func, void *data)
return (0);
}
+
+/*
+ * Convert lgroup ID to string.
+ * returns dash when lgroup ID is invalid.
+ */
+static char *
+lgrp2str(lgrp_id_t lgrp)
+{
+ static char lgrp_buf[20];
+ char *str = lgrp_buf;
+
+ (void) sprintf(str, lgrp == LGRP_NONE ? " -" : "%4d", lgrp);
+ return (str);
+}
+
+/*
+ * Parse address range specification for -A option.
+ * The address range may have the following forms:
+ *
+ * address
+ * start and end is set to address
+ * address,
+ * start is set to address, end is set to INVALID_ADDRESS
+ * ,address
+ * start is set to 0, end is set to address
+ * address1,address2
+ * start is set to address1, end is set to address2
+ *
+ */
+static int
+parse_addr_range(char *input_str, uintptr_t *start, uintptr_t *end)
+{
+ char *startp = input_str;
+ char *endp = strchr(input_str, ',');
+ ulong_t s = (ulong_t)INVALID_ADDRESS;
+ ulong_t e = (ulong_t)INVALID_ADDRESS;
+
+ if (endp != NULL) {
+ /*
+ * Comma is present. If there is nothing after comma, the end
+ * remains set at INVALID_ADDRESS. Otherwise it is set to the
+ * value after comma.
+ */
+ *endp = '\0';
+ endp++;
+
+ if ((*endp != '\0') && sscanf(endp, "%lx", &e) != 1)
+ return (1);
+ }
+
+ if (startp != NULL) {
+ /*
+ * Read the start address, if it is specified. If the address is
+ * missing, start will be set to INVALID_ADDRESS.
+ */
+ if ((*startp != '\0') && sscanf(startp, "%lx", &s) != 1)
+ return (1);
+ }
+
+ /* If there is no comma, end becomes equal to start */
+ if (endp == NULL)
+ e = s;
+
+ /*
+ * ,end implies 0..end range
+ */
+ if (e != INVALID_ADDRESS && s == INVALID_ADDRESS)
+ s = 0;
+
+ *start = (uintptr_t)s;
+ *end = (uintptr_t)e;
+
+ /* Return error if neither start nor end address were specified */
+ return (! (s != INVALID_ADDRESS || e != INVALID_ADDRESS));
+}
+
+/*
+ * Check whether any portion of [start, end] segment is within the
+ * [start_addr, end_addr] range.
+ *
+ * Return values:
+ * 0 - address is outside the range
+ * 1 - address is within the range
+ */
+static int
+address_in_range(uintptr_t start, uintptr_t end, size_t psz)
+{
+ int rc = 1;
+
+ /*
+ * Nothing to do if there is no address range specified with -A
+ */
+ if (start_addr != INVALID_ADDRESS || end_addr != INVALID_ADDRESS) {
+ /* The segment end is below the range start */
+ if ((start_addr != INVALID_ADDRESS) &&
+ (end < P2ALIGN(start_addr, psz)))
+ rc = 0;
+
+ /* The segment start is above the range end */
+ if ((end_addr != INVALID_ADDRESS) &&
+ (start > P2ALIGN(end_addr + psz, psz)))
+ rc = 0;
+ }
+ return (rc);
+}
+
+/*
+ * Returns an intersection of the [start, end] interval and the range specified
+ * by -A flag [start_addr, end_addr]. Unspecified parts of the address range
+ * have value INVALID_ADDRESS.
+ *
+ * The start_addr address is rounded down to the beginning of page and end_addr
+ * is rounded up to the end of page.
+ *
+ * Returns the size of the resulting interval or zero if the interval is empty
+ * or invalid.
+ */
+static size_t
+adjust_addr_range(uintptr_t start, uintptr_t end, size_t psz,
+ uintptr_t *new_start, uintptr_t *new_end)
+{
+ uintptr_t from; /* start_addr rounded down */
+ uintptr_t to; /* end_addr rounded up */
+
+ /*
+ * Round down the lower address of the range to the beginning of page.
+ */
+ if (start_addr == INVALID_ADDRESS) {
+ /*
+ * No start_addr specified by -A, the lower part of the interval
+ * does not change.
+ */
+ *new_start = start;
+ } else {
+ from = P2ALIGN(start_addr, psz);
+ /*
+ * If end address is outside the range, return an empty
+ * interval
+ */
+ if (end < from) {
+ *new_start = *new_end = 0;
+ return (0);
+ }
+ /*
+ * The adjusted start address is the maximum of requested start
+ * and the aligned start_addr of the -A range.
+ */
+ *new_start = start < from ? from : start;
+ }
+
+ /*
+ * Round up the higher address of the range to the end of page.
+ */
+ if (end_addr == INVALID_ADDRESS) {
+ /*
+ * No end_addr specified by -A, the upper part of the interval
+ * does not change.
+ */
+ *new_end = end;
+ } else {
+ /*
+ * If only one address is specified and it is the beginning of a
+ * segment, get information about the whole segment. This
+ * function is called once per segment and the 'end' argument is
+ * always the end of a segment, so just use the 'end' value.
+ */
+ to = (end_addr == start_addr && start == start_addr) ?
+ end :
+ P2ALIGN(end_addr + psz, psz);
+ /*
+ * If start address is outside the range, return an empty
+ * interval
+ */
+ if (start > to) {
+ *new_start = *new_end = 0;
+ return (0);
+ }
+ /*
+ * The adjusted end address is the minimum of requested end
+ * and the aligned end_addr of the -A range.
+ */
+ *new_end = end > to ? to : end;
+ }
+
+ /*
+ * Make sure that the resulting interval is legal.
+ */
+ if (*new_end < *new_start)
+ *new_start = *new_end = 0;
+
+ /* Return the size of the interval */
+ return (*new_end - *new_start);
+}
+
+/*
+ * Initialize memory_info data structure with information about a new segment.
+ */
+static void
+mem_chunk_init(memory_chunk_t *chunk, uintptr_t end, size_t psz)
+{
+ chunk->end_addr = end;
+ chunk->page_size = psz;
+ chunk->page_index = 0;
+ chunk->chunk_start = chunk->chunk_end = 0;
+}
+
+/*
+ * Create a new chunk of addresses starting from vaddr.
+ * Pass the whole chunk to pr_meminfo to collect lgroup and page size
+ * information for each page in the chunk.
+ */
+static void
+mem_chunk_get(memory_chunk_t *chunk, uintptr_t vaddr)
+{
+ page_descr_t *pdp = chunk->page_info;
+ size_t psz = chunk->page_size;
+ uintptr_t addr = vaddr;
+ uint64_t inaddr[MAX_MEMINFO_CNT];
+ uint64_t outdata[2 * MAX_MEMINFO_CNT];
+ uint_t info[2] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
+ uint_t validity[MAX_MEMINFO_CNT];
+ uint64_t *dataptr = inaddr;
+ uint64_t *outptr = outdata;
+ uint_t *valptr = validity;
+ int i, j, rc;
+
+ chunk->chunk_start = vaddr;
+ chunk->page_index = 0; /* reset index for the new chunk */
+
+ /*
+ * Fill in MAX_MEMINFO_CNT wotrh of pages starting from vaddr. Also,
+ * copy starting address of each page to inaddr array for pr_meminfo.
+ */
+ for (i = 0, pdp = chunk->page_info;
+ (i < MAX_MEMINFO_CNT) && (addr <= chunk->end_addr);
+ i++, pdp++, dataptr++, addr += psz) {
+ *dataptr = (uint64_t)addr;
+ pdp->pd_start = addr;
+ pdp->pd_lgrp = LGRP_NONE;
+ pdp->pd_valid = 0;
+ pdp->pd_pagesize = 0;
+ }
+
+ /* Mark the number of entries in the chunk and the last address */
+ chunk->page_count = i;
+ chunk->chunk_end = addr - psz;
+
+ if (interrupt)
+ return;
+
+ /* Call meminfo for all collected addresses */
+ rc = pr_meminfo(Pr, inaddr, i, info, 2, outdata, validity);
+ if (rc < 0) {
+ (void) perr("can not get memory information");
+ return;
+ }
+
+ /* Verify validity of each result and fill in the addrs array */
+ pdp = chunk->page_info;
+ for (j = 0; j < i; j++, pdp++, valptr++, outptr += 2) {
+ /* Skip invalid address pointers */
+ if ((*valptr & 1) == 0) {
+ continue;
+ }
+
+ /* Is lgroup information available? */
+ if ((*valptr & 2) != 0) {
+ pdp->pd_lgrp = (lgrp_id_t)*outptr;
+ pdp->pd_valid = 1;
+ }
+
+ /* Is page size informaion available? */
+ if ((*valptr & 4) != 0) {
+ pdp->pd_pagesize = *(outptr + 1);
+ }
+ }
+}
+
+/*
+ * Starting from address 'vaddr' find the region with pages allocated from the
+ * same lgroup.
+ *
+ * Arguments:
+ * mchunk Initialized memory chunk structure
+ * vaddr Starting address of the region
+ * maxaddr Upper bound of the region
+ * pagesize Default page size to use
+ * ret_lgrp On exit contains the lgroup ID of all pages in the
+ * region.
+ *
+ * Returns:
+ * Size of the contiguous region in bytes
+ * The lgroup ID of all pages in the region in ret_lgrp argument.
+ */
+static size_t
+get_contiguous_region(memory_chunk_t *mchunk, uintptr_t vaddr,
+ uintptr_t maxaddr, size_t pagesize, lgrp_id_t *ret_lgrp)
+{
+ size_t size_contig = 0;
+ lgrp_id_t lgrp; /* Lgroup of the region start */
+ lgrp_id_t curr_lgrp; /* Lgroup of the current page */
+ size_t psz = pagesize; /* Pagesize to use */
+
+ /* Set both lgroup IDs to the lgroup of the first page */
+ curr_lgrp = lgrp = addr_to_lgrp(mchunk, vaddr, &psz);
+
+ /*
+ * Starting from vaddr, walk page by page until either the end
+ * of the segment is reached or a page is allocated from a different
+ * lgroup. Also stop if interrupted from keyboard.
+ */
+ while ((vaddr < maxaddr) && (curr_lgrp == lgrp) && !interrupt) {
+ /*
+ * Get lgroup ID and the page size of the current page.
+ */
+ curr_lgrp = addr_to_lgrp(mchunk, vaddr, &psz);
+ /* If there is no page size information, use the default */
+ if (psz == 0)
+ psz = pagesize;
+
+ if (curr_lgrp == lgrp) {
+ /*
+ * This page belongs to the contiguous region.
+ * Increase the region size and advance to the new page.
+ */
+ size_contig += psz;
+ vaddr += psz;
+ }
+ }
+
+ /* Return the region lgroup ID and the size */
+ *ret_lgrp = lgrp;
+ return (size_contig);
+}
+
+/*
+ * Given a virtual address, return its lgroup and page size. If there is meminfo
+ * information for an address, use it, otherwise shift the chunk window to the
+ * vaddr and create a new chunk with known meminfo information.
+ */
+static lgrp_id_t
+addr_to_lgrp(memory_chunk_t *chunk, uintptr_t vaddr, size_t *psz)
+{
+ page_descr_t *pdp;
+ lgrp_id_t lgrp = LGRP_NONE;
+ int i;
+
+ *psz = chunk->page_size;
+
+ if (interrupt)
+ return (0);
+
+ /*
+ * Is there information about this address? If not, create a new chunk
+ * starting from vaddr and apply pr_meminfo() to the whole chunk.
+ */
+ if (vaddr < chunk->chunk_start || vaddr > chunk->chunk_end) {
+ /*
+ * This address is outside the chunk, get the new chunk and
+ * collect meminfo information for it.
+ */
+ mem_chunk_get(chunk, vaddr);
+ }
+
+ /*
+ * Find information about the address.
+ */
+ pdp = &chunk->page_info[chunk->page_index];
+ for (i = chunk->page_index; i < chunk->page_count; i++, pdp++) {
+ if (pdp->pd_start == vaddr) {
+ if (pdp->pd_valid) {
+ lgrp = pdp->pd_lgrp;
+ /*
+ * Override page size information if it is
+ * present.
+ */
+ if (pdp->pd_pagesize > 0)
+ *psz = pdp->pd_pagesize;
+ }
+ break;
+ }
+ }
+ /*
+ * Remember where we ended - the next search will start here.
+ * We can query for the lgrp for the same address again, so do not
+ * advance index past the current value.
+ */
+ chunk->page_index = i;
+
+ return (lgrp);
+}
+
+/* ARGSUSED */
+static void
+intr(int sig)
+{
+ interrupt = 1;
+}
diff --git a/usr/src/lib/libdtrace/common/procfs.d.in b/usr/src/lib/libdtrace/common/procfs.d.in
index 58dbeca9c4..5d97197c31 100644
--- a/usr/src/lib/libdtrace/common/procfs.d.in
+++ b/usr/src/lib/libdtrace/common/procfs.d.in
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -330,6 +330,7 @@ translator lwpsinfo_t < kthread_t *T > {
pr_onpro = T->t_cpu->cpu_id;
pr_bindpro = T->t_bind_cpu;
pr_bindpset = T->t_bind_pset;
+ pr_lgrp = T->t_lpl->lpl_lgrpid;
};
inline psinfo_t *curpsinfo = xlate <psinfo_t *> (curthread->t_procp);
diff --git a/usr/src/lib/libproc/common/P32ton.c b/usr/src/lib/libproc/common/P32ton.c
index c6b4b8fbcc..78219550c8 100644
--- a/usr/src/lib/libproc/common/P32ton.c
+++ b/usr/src/lib/libproc/common/P32ton.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -401,6 +400,7 @@ lwpsinfo_32_to_n(const lwpsinfo32_t *src, lwpsinfo_t *dst)
dst->pr_onpro = src->pr_onpro;
dst->pr_bindpro = src->pr_bindpro;
dst->pr_bindpset = src->pr_bindpset;
+ dst->pr_lgrp = src->pr_lgrp;
}
void
@@ -773,6 +773,7 @@ lwpsinfo_n_to_32(const lwpsinfo_t *src, lwpsinfo32_t *dst)
dst->pr_onpro = src->pr_onpro;
dst->pr_bindpro = src->pr_bindpro;
dst->pr_bindpset = src->pr_bindpset;
+ dst->pr_lgrp = src->pr_lgrp;
}
void
diff --git a/usr/src/pkgdefs/SUNWesu/prototype_com b/usr/src/pkgdefs/SUNWesu/prototype_com
index e53cf18680..170e58bc6e 100644
--- a/usr/src/pkgdefs/SUNWesu/prototype_com
+++ b/usr/src/pkgdefs/SUNWesu/prototype_com
@@ -72,6 +72,7 @@ f none usr/bin/last 555 root bin
f none usr/bin/lastcomm 555 root bin
f none usr/bin/logname 555 root bin
f none usr/bin/look 755 root bin
+f none usr/bin/lgrpinfo 555 root bin
f none usr/bin/mkfifo 555 root bin
f none usr/bin/nawk 555 root bin
f none usr/bin/newform 555 root bin
@@ -86,7 +87,9 @@ l none usr/bin/pcred=../../usr/lib/isaexec
l none usr/bin/pfiles=../../usr/lib/isaexec
l none usr/bin/pflags=../../usr/lib/isaexec
l none usr/bin/pldd=../../usr/lib/isaexec
+l none usr/bin/plgrp=../../usr/lib/isaexec
l none usr/bin/pmap=../../usr/lib/isaexec
+l none usr/bin/pmadvise=../../usr/lib/isaexec
l none usr/bin/ppgsz=../../usr/lib/isaexec
l none usr/bin/ppriv=../../usr/lib/isaexec
l none usr/bin/preap=../../usr/lib/isaexec
diff --git a/usr/src/pkgdefs/SUNWesu/prototype_i386 b/usr/src/pkgdefs/SUNWesu/prototype_i386
index 008d9a9834..fca1c84679 100644
--- a/usr/src/pkgdefs/SUNWesu/prototype_i386
+++ b/usr/src/pkgdefs/SUNWesu/prototype_i386
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -55,7 +55,9 @@ f none usr/bin/i86/pcred 555 root bin
f none usr/bin/i86/pfiles 555 root bin
f none usr/bin/i86/pflags 555 root bin
f none usr/bin/i86/pldd 555 root bin
+f none usr/bin/i86/plgrp 555 root bin
f none usr/bin/i86/pmap 555 root bin
+f none usr/bin/i86/pmadvise 555 root bin
f none usr/bin/i86/ppgsz 555 root bin
f none usr/bin/i86/ppriv 555 root bin
f none usr/bin/i86/preap 555 root bin
@@ -74,7 +76,9 @@ f none usr/bin/amd64/pcred 555 root bin
f none usr/bin/amd64/pfiles 555 root bin
f none usr/bin/amd64/pflags 555 root bin
f none usr/bin/amd64/pldd 555 root bin
+f none usr/bin/amd64/plgrp 555 root bin
f none usr/bin/amd64/pmap 555 root bin
+f none usr/bin/amd64/pmadvise 555 root bin
f none usr/bin/amd64/ppgsz 555 root bin
f none usr/bin/amd64/ppriv 555 root bin
f none usr/bin/amd64/preap 555 root bin
diff --git a/usr/src/pkgdefs/SUNWesu/prototype_sparc b/usr/src/pkgdefs/SUNWesu/prototype_sparc
index 0f150a9c61..ea686e295e 100644
--- a/usr/src/pkgdefs/SUNWesu/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWesu/prototype_sparc
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,7 +18,9 @@
#
# CDDL HEADER END
#
-# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -54,7 +55,9 @@ f none usr/bin/sparcv9/pcred 555 root bin
f none usr/bin/sparcv9/pfiles 555 root bin
f none usr/bin/sparcv9/pflags 555 root bin
f none usr/bin/sparcv9/pldd 555 root bin
+f none usr/bin/sparcv9/plgrp 555 root bin
f none usr/bin/sparcv9/pmap 555 root bin
+f none usr/bin/sparcv9/pmadvise 555 root bin
f none usr/bin/sparcv9/ppgsz 555 root bin
f none usr/bin/sparcv9/ppriv 555 root bin
f none usr/bin/sparcv9/preap 555 root bin
diff --git a/usr/src/pkgdefs/SUNWperl584core/prototype_com b/usr/src/pkgdefs/SUNWperl584core/prototype_com
index c2ddcda34f..491ac02f9f 100644
--- a/usr/src/pkgdefs/SUNWperl584core/prototype_com
+++ b/usr/src/pkgdefs/SUNWperl584core/prototype_com
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -210,6 +210,7 @@ f none usr/perl5/5.8.4/lib/Sun/Solaris/Project.pm 0444 root bin
f none usr/perl5/5.8.4/lib/Sun/Solaris/Task.pm 0444 root bin
f none usr/perl5/5.8.4/lib/Sun/Solaris/Ucred.pm 0444 root bin
f none usr/perl5/5.8.4/lib/Sun/Solaris/Utils.pm 0444 root bin
+f none usr/perl5/5.8.4/lib/Sun/Solaris/Lgrp.pm 0444 root bin
f none usr/perl5/5.8.4/lib/Switch.pm 0444 root bin
f none usr/perl5/5.8.4/lib/Symbol.pm 0444 root bin
d none usr/perl5/5.8.4/lib/Term 0755 root bin
diff --git a/usr/src/pkgdefs/SUNWperl584core/prototype_i386 b/usr/src/pkgdefs/SUNWperl584core/prototype_i386
index fe9f20046a..587739c185 100644
--- a/usr/src/pkgdefs/SUNWperl584core/prototype_i386
+++ b/usr/src/pkgdefs/SUNWperl584core/prototype_i386
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,11 +18,12 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
+# ident "%Z%%M% %I% %E% SMI"
#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
@@ -330,6 +330,9 @@ f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Privilege/Privil
d none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project 0755 root bin
f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.bs 0444 root bin
f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.so 0555 root bin
+d none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp 0755 root bin
+f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 0444 root bin
+f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 0555 root bin
d none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task 0755 root bin
f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.bs 0444 root bin
f none usr/perl5/5.8.4/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.so 0555 root bin
diff --git a/usr/src/pkgdefs/SUNWperl584core/prototype_sparc b/usr/src/pkgdefs/SUNWperl584core/prototype_sparc
index 5e66587b08..ac78b1c8ff 100644
--- a/usr/src/pkgdefs/SUNWperl584core/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWperl584core/prototype_sparc
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,11 +18,12 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
+# ident "%Z%%M% %I% %E% SMI"
#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
@@ -324,6 +324,9 @@ f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Intrs/Intrs.so 05
d none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Kstat 0755 root bin
f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Kstat/Kstat.bs 0444 root bin
f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Kstat/Kstat.so 0555 root bin
+d none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp 0755 root bin
+f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 0444 root bin
+f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 0555 root bin
d none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege 0755 root bin
f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege/Privilege.bs 0444 root bin
f none usr/perl5/5.8.4/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege/Privilege.so 0555 root bin
diff --git a/usr/src/pkgdefs/SUNWpl5u/prototype_com b/usr/src/pkgdefs/SUNWpl5u/prototype_com
index 38066b45d2..1544efe0e4 100644
--- a/usr/src/pkgdefs/SUNWpl5u/prototype_com
+++ b/usr/src/pkgdefs/SUNWpl5u/prototype_com
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,11 +18,12 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
+# ident "%Z%%M% %I% %E% SMI"
#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
@@ -132,6 +132,7 @@ f none usr/perl5/5.6.1/lib/Sun/Solaris/Intrs.pm 444 root bin
f none usr/perl5/5.6.1/lib/Sun/Solaris/Kstat.pm 444 root bin
f none usr/perl5/5.6.1/lib/Sun/Solaris/Privilege.pm 444 root bin
f none usr/perl5/5.6.1/lib/Sun/Solaris/Project.pm 444 root bin
+f none usr/perl5/5.6.1/lib/Sun/Solaris/Lgrp.pm 444 root bin
f none usr/perl5/5.6.1/lib/Sun/Solaris/Task.pm 444 root bin
f none usr/perl5/5.6.1/lib/Sun/Solaris/Ucred.pm 444 root bin
f none usr/perl5/5.6.1/lib/Sun/Solaris/Utils.pm 444 root bin
diff --git a/usr/src/pkgdefs/SUNWpl5u/prototype_i386 b/usr/src/pkgdefs/SUNWpl5u/prototype_i386
index 5356eb4591..a0ffecf76e 100644
--- a/usr/src/pkgdefs/SUNWpl5u/prototype_i386
+++ b/usr/src/pkgdefs/SUNWpl5u/prototype_i386
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,11 +18,12 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
+# ident "%Z%%M% %I% %E% SMI"
#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
@@ -275,6 +275,9 @@ f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Privilege/Privil
d none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project 755 root bin
f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.bs 444 root bin
f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.so 555 root bin
+d none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp 755 root bin
+f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 444 root bin
+f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 555 root bin
d none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task 755 root bin
f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.bs 444 root bin
f none usr/perl5/5.6.1/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.so 555 root bin
diff --git a/usr/src/pkgdefs/SUNWpl5u/prototype_sparc b/usr/src/pkgdefs/SUNWpl5u/prototype_sparc
index 1492575773..623711e70f 100644
--- a/usr/src/pkgdefs/SUNWpl5u/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWpl5u/prototype_sparc
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -275,6 +275,9 @@ f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Privilege/Privile
d none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Project 755 root bin
f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Project/Project.bs 444 root bin
f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Project/Project.so 555 root bin
+d none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp 755 root bin
+f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.bs 444 root bin
+f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 555 root bin
d none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Task 755 root bin
f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Task/Task.bs 444 root bin
f none usr/perl5/5.6.1/lib/sun4-solaris-64int/auto/Sun/Solaris/Task/Task.so 555 root bin
diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c
index 2c26ee77f3..e1931414ec 100644
--- a/usr/src/uts/common/fs/proc/prsubr.c
+++ b/usr/src/uts/common/fs/proc/prsubr.c
@@ -2449,6 +2449,7 @@ prgetlwpsinfo(kthread_t *t, lwpsinfo_t *psp)
psp->pr_onpro = t->t_cpu->cpu_id;
psp->pr_bindpro = t->t_bind_cpu;
psp->pr_bindpset = t->t_bind_pset;
+ psp->pr_lgrp = t->t_lpl->lpl_lgrpid;
}
#ifdef _SYSCALL32_IMPL
@@ -2517,6 +2518,7 @@ prgetlwpsinfo32(kthread_t *t, lwpsinfo32_t *psp)
psp->pr_onpro = t->t_cpu->cpu_id;
psp->pr_bindpro = t->t_bind_cpu;
psp->pr_bindpset = t->t_bind_pset;
+ psp->pr_lgrp = t->t_lpl->lpl_lgrpid;
}
#endif /* _SYSCALL32_IMPL */
diff --git a/usr/src/uts/common/os/lgrp.c b/usr/src/uts/common/os/lgrp.c
index 3a2a6f0a17..315c0ac873 100644
--- a/usr/src/uts/common/os/lgrp.c
+++ b/usr/src/uts/common/os/lgrp.c
@@ -168,7 +168,6 @@ lpl_t *lpl_bootstrap;
static lgrp_t lroot;
-
/*
* Size, in bytes, beyond which random memory allocation policy is applied
* to non-shared memory. Default is the maximum size, so random memory
@@ -176,6 +175,12 @@ static lgrp_t lroot;
*/
size_t lgrp_privm_random_thresh = (size_t)(-1);
+/* the maximum effect that a single thread can have on it's lgroup's load */
+#define LGRP_LOADAVG_MAX_EFFECT(ncpu) \
+ ((lgrp_loadavg_max_effect) / (ncpu))
+uint32_t lgrp_loadavg_max_effect = LGRP_LOADAVG_THREAD_MAX;
+
+
/*
* Size, in bytes, beyond which random memory allocation policy is applied to
* shared memory. Default is 8MB (2 ISM pages).
@@ -1765,6 +1770,8 @@ lgrp_kstat_extract(kstat_t *ksp, int rw)
ksd[stat + LGRP_NUM_PG_FREE].value.i64 =
lgrp_mem_size(lgrpid, LGRP_MEM_SIZE_FREE);
ksd[stat + LGRP_LOADAVG].value.i64 = lgrp_sum_loadavgs(lgrp);
+ ksd[stat + LGRP_LOADAVG_SCALE].value.i64 =
+ lgrp_loadavg_max_effect;
} else {
lgrp_kstat_reset(lgrpid);
}
@@ -2945,11 +2952,6 @@ lpl_topo_bootstrap(lpl_t *target, int size)
bzero(lpl_bootstrap_list, sizeof (lpl_bootstrap_list));
}
-/* the maximum effect that a single thread can have on it's lgroup's load */
-#define LGRP_LOADAVG_MAX_EFFECT(ncpu) \
- ((lgrp_loadavg_max_effect) / (ncpu))
-uint32_t lgrp_loadavg_max_effect = LGRP_LOADAVG_THREAD_MAX;
-
/*
* If the lowest load among the lgroups a process' threads are currently
* spread across is greater than lgrp_expand_proc_thresh, we'll consider
diff --git a/usr/src/uts/common/sys/lgrp.h b/usr/src/uts/common/sys/lgrp.h
index d8f26f787c..840a2cb564 100644
--- a/usr/src/uts/common/sys/lgrp.h
+++ b/usr/src/uts/common/sys/lgrp.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -170,7 +170,8 @@ typedef enum lgrp_snap_stat_types {
LGRP_NUM_PG_FREE, /* # of free pages */
LGRP_NUM_PG_AVAIL, /* # of allocatable physical pages */
LGRP_NUM_PG_INSTALL, /* # of installed physical pages */
- LGRP_LOADAVG, /* "load average" of this lgrp */
+ LGRP_LOADAVG, /* unscaled load average of this lgrp */
+ LGRP_LOADAVG_SCALE, /* load unit of one CPU bound thread */
LGRP_NUM_SNAPSHOT_STATS /* always last */
} lgrp_snap_stat_t;
@@ -198,7 +199,8 @@ static char *lgrp_kstat_names[] = { \
"pages free", \
"pages avail", \
"pages installed", \
- "load average" \
+ "load average", \
+ "loadscale" \
}
#define LGRP_NUM_STATS ((int)LGRP_NUM_COUNTER_STATS + \
diff --git a/usr/src/uts/common/sys/procfs.h b/usr/src/uts/common/sys/procfs.h
index 995870f7fc..6f8e1a663d 100644
--- a/usr/src/uts/common/sys/procfs.h
+++ b/usr/src/uts/common/sys/procfs.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -61,6 +61,7 @@ extern "C" {
#include <sys/pset.h>
#include <sys/procfs_isa.h>
#include <sys/priv.h>
+#include <sys/lgrp.h>
#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
#error "Cannot use procfs in the large file compilation environment"
@@ -262,7 +263,8 @@ typedef struct lwpsinfo {
processorid_t pr_onpro; /* processor which last ran this lwp */
processorid_t pr_bindpro; /* processor to which lwp is bound */
psetid_t pr_bindpset; /* processor set to which lwp is bound */
- int pr_filler[5]; /* reserved for future use */
+ lgrp_id_t pr_lgrp; /* lwp home lgroup */
+ int pr_filler[4]; /* reserved for future use */
} lwpsinfo_t;
/*
@@ -633,7 +635,8 @@ typedef struct lwpsinfo32 {
processorid_t pr_onpro; /* processor which last ran this lwp */
processorid_t pr_bindpro; /* processor to which lwp is bound */
psetid_t pr_bindpset; /* processor set to which lwp is bound */
- int pr_filler[5]; /* reserved for future use */
+ lgrp_id_t pr_lgrp; /* lwp home lgroup */
+ int pr_filler[4]; /* reserved for future use */
} lwpsinfo32_t;
/*
diff --git a/usr/src/uts/common/syscall/lgrpsys.c b/usr/src/uts/common/syscall/lgrpsys.c
index 09b9818ad6..17fa2e1ff9 100644
--- a/usr/src/uts/common/syscall/lgrpsys.c
+++ b/usr/src/uts/common/syscall/lgrpsys.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -502,14 +502,13 @@ lgrp_affinity_best(kthread_t *t, struct cpupart *cpupart, lgrp_id_t start)
lgrpid = start;
/*
- * Begin with home as best lgroup if it's root or in this pset
- * Otherwise, use starting lgroup given above as best first.
+ * Use starting lgroup given above as best first
*/
home = t->t_lpl->lpl_lgrpid;
- if (LGRP_CPUS_IN_PART(home, cpupart))
- best_lpl = &cpupart->cp_lgrploads[home];
- else
+ if (LGRP_CPUS_IN_PART(lgrpid, cpupart))
best_lpl = &cpupart->cp_lgrploads[lgrpid];
+ else
+ best_lpl = &cpupart->cp_lgrploads[home];
best_aff = affs[best_lpl->lpl_lgrpid];
@@ -529,7 +528,7 @@ lgrp_affinity_best(kthread_t *t, struct cpupart *cpupart, lgrp_id_t start)
*/
lpl = &cpupart->cp_lgrploads[lgrpid];
if (affs[lgrpid] > best_aff) {
- best_aff = affs[lgrpid];
+ best_aff = affs[lgrpid];
best_lpl = lpl;
}
@@ -558,10 +557,11 @@ int
lgrp_affinity_set_thread(kthread_t *t, lgrp_id_t lgrp, lgrp_affinity_t aff,
lgrp_affinity_t **aff_buf)
{
+ lgrp_affinity_t *affs;
+ lgrp_id_t best;
lpl_t *best_lpl;
lgrp_id_t home;
int retval;
- lgrp_id_t start;
ASSERT(t != NULL);
ASSERT(MUTEX_HELD(&ttoproc(t)->p_lock));
@@ -589,33 +589,26 @@ lgrp_affinity_set_thread(kthread_t *t, lgrp_id_t lgrp, lgrp_affinity_t aff,
*aff_buf = NULL;
}
- t->t_lgrp_affinity[lgrp] = aff;
-
- /*
- * Select a new home if the thread's affinity is being cleared
- */
- if (aff == LGRP_AFF_NONE) {
- lgrp_move_thread(t, lgrp_choose(t, t->t_cpupart), 1);
- thread_unlock(t);
- return (retval);
- }
+ affs = t->t_lgrp_affinity;
+ affs[lgrp] = aff;
/*
* Find lgroup for which thread has most affinity,
- * starting after home
+ * starting with lgroup for which affinity being set
*/
- home = t->t_lpl->lpl_lgrpid;
- start = home + 1;
- if (start > lgrp_alloc_max)
- start = 0;
-
- best_lpl = lgrp_affinity_best(t, t->t_cpupart, start);
+ best_lpl = lgrp_affinity_best(t, t->t_cpupart, lgrp);
/*
- * Rehome if found lgroup with more affinity than home
+ * Rehome if found lgroup with more affinity than home or lgroup for
+ * which affinity is being set has same affinity as home
*/
- if (best_lpl != NULL && best_lpl != t->t_lpl)
- lgrp_move_thread(t, best_lpl, 1);
+ home = t->t_lpl->lpl_lgrpid;
+ if (best_lpl != NULL && best_lpl != t->t_lpl) {
+ best = best_lpl->lpl_lgrpid;
+ if (affs[best] > affs[home] || (affs[best] == affs[home] &&
+ best == lgrp))
+ lgrp_move_thread(t, best_lpl, 1);
+ }
thread_unlock(t);