summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
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);