xref: /onnv-gate/usr/src/cmd/perl/contrib/Sun/Solaris/Pg/Pg.pm (revision 13124:8f28cf08bb11)
1*13124SAlexander.Kolbasov@Sun.COM#! /usr/perl5/bin/perl
2*13124SAlexander.Kolbasov@Sun.COM#
3*13124SAlexander.Kolbasov@Sun.COM# CDDL HEADER START
4*13124SAlexander.Kolbasov@Sun.COM#
5*13124SAlexander.Kolbasov@Sun.COM# The contents of this file are subject to the terms of the
6*13124SAlexander.Kolbasov@Sun.COM# Common Development and Distribution License (the "License").
7*13124SAlexander.Kolbasov@Sun.COM# You may not use this file except in compliance with the License.
8*13124SAlexander.Kolbasov@Sun.COM#
9*13124SAlexander.Kolbasov@Sun.COM# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*13124SAlexander.Kolbasov@Sun.COM# or http://www.opensolaris.org/os/licensing.
11*13124SAlexander.Kolbasov@Sun.COM# See the License for the specific language governing permissions
12*13124SAlexander.Kolbasov@Sun.COM# and limitations under the License.
13*13124SAlexander.Kolbasov@Sun.COM#
14*13124SAlexander.Kolbasov@Sun.COM# When distributing Covered Code, include this CDDL HEADER in each
15*13124SAlexander.Kolbasov@Sun.COM# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*13124SAlexander.Kolbasov@Sun.COM# If applicable, add the following below this CDDL HEADER, with the
17*13124SAlexander.Kolbasov@Sun.COM# fields enclosed by brackets "[]" replaced with your own identifying
18*13124SAlexander.Kolbasov@Sun.COM# information: Portions Copyright [yyyy] [name of copyright owner]
19*13124SAlexander.Kolbasov@Sun.COM#
20*13124SAlexander.Kolbasov@Sun.COM# CDDL HEADER END
21*13124SAlexander.Kolbasov@Sun.COM#
22*13124SAlexander.Kolbasov@Sun.COM
23*13124SAlexander.Kolbasov@Sun.COM#
24*13124SAlexander.Kolbasov@Sun.COM# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25*13124SAlexander.Kolbasov@Sun.COM#
26*13124SAlexander.Kolbasov@Sun.COM
27*13124SAlexander.Kolbasov@Sun.COM#
28*13124SAlexander.Kolbasov@Sun.COM# Pg.pm provides object-oriented interface to the Solaris
29*13124SAlexander.Kolbasov@Sun.COM# Processor Group kstats
30*13124SAlexander.Kolbasov@Sun.COM#
31*13124SAlexander.Kolbasov@Sun.COM# See comments in the end
32*13124SAlexander.Kolbasov@Sun.COM#
33*13124SAlexander.Kolbasov@Sun.COM
34*13124SAlexander.Kolbasov@Sun.COMpackage Sun::Solaris::Pg;
35*13124SAlexander.Kolbasov@Sun.COM
36*13124SAlexander.Kolbasov@Sun.COMuse strict;
37*13124SAlexander.Kolbasov@Sun.COMuse warnings;
38*13124SAlexander.Kolbasov@Sun.COMuse Sun::Solaris::Kstat;
39*13124SAlexander.Kolbasov@Sun.COMuse Carp;
40*13124SAlexander.Kolbasov@Sun.COMuse Errno;
41*13124SAlexander.Kolbasov@Sun.COMuse List::Util qw(max sum);
42*13124SAlexander.Kolbasov@Sun.COM
43*13124SAlexander.Kolbasov@Sun.COMour $VERSION = '1.1';
44*13124SAlexander.Kolbasov@Sun.COM
45*13124SAlexander.Kolbasov@Sun.COM#
46*13124SAlexander.Kolbasov@Sun.COM# Currently the OS does not have the root PG and PGs constitute a forest of
47*13124SAlexander.Kolbasov@Sun.COM# small trees. This module gathers all such trees under one root with ID zero.
48*13124SAlexander.Kolbasov@Sun.COM# If the root is present already, we do not use faked root.
49*13124SAlexander.Kolbasov@Sun.COM#
50*13124SAlexander.Kolbasov@Sun.COM
51*13124SAlexander.Kolbasov@Sun.COMmy $ROOT_ID = 0;
52*13124SAlexander.Kolbasov@Sun.COM
53*13124SAlexander.Kolbasov@Sun.COM#
54*13124SAlexander.Kolbasov@Sun.COM# PG_NO_PARENT means that kstats have PG parent ID and it is set to -1
55*13124SAlexander.Kolbasov@Sun.COM# PG_PARENT_UNDEF means that kstats have no PG parent ID
56*13124SAlexander.Kolbasov@Sun.COM#
57*13124SAlexander.Kolbasov@Sun.COMuse constant {
58*13124SAlexander.Kolbasov@Sun.COM	PG_NO_PARENT	=> -1,
59*13124SAlexander.Kolbasov@Sun.COM	PG_PARENT_UNDEF => -2,
60*13124SAlexander.Kolbasov@Sun.COM};
61*13124SAlexander.Kolbasov@Sun.COM
62*13124SAlexander.Kolbasov@Sun.COM#
63*13124SAlexander.Kolbasov@Sun.COM# Sorting order between different sharing relationships. This order is used to
64*13124SAlexander.Kolbasov@Sun.COM# break ties between PGs with the same number of CPUs. If there are two PGs with
65*13124SAlexander.Kolbasov@Sun.COM# the same set of CPUs, the one with the higher weight will be the parent of the
66*13124SAlexander.Kolbasov@Sun.COM# one with the lower weight.
67*13124SAlexander.Kolbasov@Sun.COM#
68*13124SAlexander.Kolbasov@Sun.COMmy %relationships_order = (
69*13124SAlexander.Kolbasov@Sun.COM			   'CPU_PM_Idle_Power_Domain' => 1,
70*13124SAlexander.Kolbasov@Sun.COM			   'Integer_Pipeline' => 2,
71*13124SAlexander.Kolbasov@Sun.COM			   'Cache' => 3,
72*13124SAlexander.Kolbasov@Sun.COM			   'CPU_PM_Active_Power_Domain' => 4,
73*13124SAlexander.Kolbasov@Sun.COM			   'Floating_Point_Unit' => 5,
74*13124SAlexander.Kolbasov@Sun.COM			   'Data_Pipe_to_memory' => 6,
75*13124SAlexander.Kolbasov@Sun.COM			   'Memory' => 7,
76*13124SAlexander.Kolbasov@Sun.COM			   'Socket' => 8,
77*13124SAlexander.Kolbasov@Sun.COM			   'System' => 9,
78*13124SAlexander.Kolbasov@Sun.COM			  );
79*13124SAlexander.Kolbasov@Sun.COM
80*13124SAlexander.Kolbasov@Sun.COM#
81*13124SAlexander.Kolbasov@Sun.COM# Object interface to the library. These are methods that can be used by the
82*13124SAlexander.Kolbasov@Sun.COM# module user.
83*13124SAlexander.Kolbasov@Sun.COM#
84*13124SAlexander.Kolbasov@Sun.COM
85*13124SAlexander.Kolbasov@Sun.COM#
86*13124SAlexander.Kolbasov@Sun.COM# Create a new object representing PG
87*13124SAlexander.Kolbasov@Sun.COM# All the heavy lifting is performed by _init function.
88*13124SAlexander.Kolbasov@Sun.COM# This function performs all the Perl blessing magic.
89*13124SAlexander.Kolbasov@Sun.COM#
90*13124SAlexander.Kolbasov@Sun.COM# The new() method accepts arguments in the form of a hash. The following
91*13124SAlexander.Kolbasov@Sun.COM# subarguments are supported:
92*13124SAlexander.Kolbasov@Sun.COM#
93*13124SAlexander.Kolbasov@Sun.COM#   -cpudata	# Collect per-CPU data from kstats if this is T
94*13124SAlexander.Kolbasov@Sun.COM#   -tags	# Match PGs to physical relationships if this is T
95*13124SAlexander.Kolbasov@Sun.COM#   -swload	# Collect software CPU load if this is T
96*13124SAlexander.Kolbasov@Sun.COM#   -retry	# how many times to retry PG initialization when it fails
97*13124SAlexander.Kolbasov@Sun.COM#   -delay # Delay in seconds between retries
98*13124SAlexander.Kolbasov@Sun.COM#
99*13124SAlexander.Kolbasov@Sun.COM# The arguments are passed to _init().
100*13124SAlexander.Kolbasov@Sun.COM#
101*13124SAlexander.Kolbasov@Sun.COMsub new
102*13124SAlexander.Kolbasov@Sun.COM{
103*13124SAlexander.Kolbasov@Sun.COM	my $class = shift;
104*13124SAlexander.Kolbasov@Sun.COM	my %args = @_;
105*13124SAlexander.Kolbasov@Sun.COM	my $retry_count = $args{-retry} || 0;
106*13124SAlexander.Kolbasov@Sun.COM	my $retry_delay = $args{-delay} || 1;
107*13124SAlexander.Kolbasov@Sun.COM
108*13124SAlexander.Kolbasov@Sun.COM	my $self =  _init(@_);
109*13124SAlexander.Kolbasov@Sun.COM
110*13124SAlexander.Kolbasov@Sun.COM	#
111*13124SAlexander.Kolbasov@Sun.COM	# If PG initialization fails with EAGAIN error and the caller requested
112*13124SAlexander.Kolbasov@Sun.COM	# retries, retry initialization.
113*13124SAlexander.Kolbasov@Sun.COM	#
114*13124SAlexander.Kolbasov@Sun.COM	for (; !$self && ($! == &Errno::EAGAIN) && $retry_count;
115*13124SAlexander.Kolbasov@Sun.COM	     $retry_count--) {
116*13124SAlexander.Kolbasov@Sun.COM		select(undef,undef,undef, $retry_delay);
117*13124SAlexander.Kolbasov@Sun.COM		$self = _init(@_);
118*13124SAlexander.Kolbasov@Sun.COM	}
119*13124SAlexander.Kolbasov@Sun.COM
120*13124SAlexander.Kolbasov@Sun.COM	if ($self) {
121*13124SAlexander.Kolbasov@Sun.COM		bless($self, $class) if defined($class);
122*13124SAlexander.Kolbasov@Sun.COM		bless($self) unless defined($class);
123*13124SAlexander.Kolbasov@Sun.COM	}
124*13124SAlexander.Kolbasov@Sun.COM
125*13124SAlexander.Kolbasov@Sun.COM	return ($self);
126*13124SAlexander.Kolbasov@Sun.COM}
127*13124SAlexander.Kolbasov@Sun.COM
128*13124SAlexander.Kolbasov@Sun.COM#
129*13124SAlexander.Kolbasov@Sun.COM# Functions below use internal function _pg_get which returns PG hash reference
130*13124SAlexander.Kolbasov@Sun.COM# corresponding to PG ID specified or 'undef' if the PG can't be found.
131*13124SAlexander.Kolbasov@Sun.COM#
132*13124SAlexander.Kolbasov@Sun.COM
133*13124SAlexander.Kolbasov@Sun.COM#
134*13124SAlexander.Kolbasov@Sun.COM# All methods return 'undef' in scalar context and an empty list in list
135*13124SAlexander.Kolbasov@Sun.COM# context when unrecoverable errors are detected.
136*13124SAlexander.Kolbasov@Sun.COM#
137*13124SAlexander.Kolbasov@Sun.COM
138*13124SAlexander.Kolbasov@Sun.COM#
139*13124SAlexander.Kolbasov@Sun.COM# Return the root ID of PG hierarchy
140*13124SAlexander.Kolbasov@Sun.COM#
141*13124SAlexander.Kolbasov@Sun.COMsub root
142*13124SAlexander.Kolbasov@Sun.COM{
143*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("root(cookie)");
144*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
145*13124SAlexander.Kolbasov@Sun.COM
146*13124SAlexander.Kolbasov@Sun.COM	return unless $self->{PGTREE};
147*13124SAlexander.Kolbasov@Sun.COM
148*13124SAlexander.Kolbasov@Sun.COM	return ($ROOT_ID);
149*13124SAlexander.Kolbasov@Sun.COM}
150*13124SAlexander.Kolbasov@Sun.COM
151*13124SAlexander.Kolbasov@Sun.COM#
152*13124SAlexander.Kolbasov@Sun.COM# Return list of all pgs numerically sorted In scalar context return number of
153*13124SAlexander.Kolbasov@Sun.COM# PGs
154*13124SAlexander.Kolbasov@Sun.COM#
155*13124SAlexander.Kolbasov@Sun.COMsub all
156*13124SAlexander.Kolbasov@Sun.COM{
157*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("all(cookie)");
158*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
159*13124SAlexander.Kolbasov@Sun.COM	my $pgtree =  $self->{PGTREE} or return;
160*13124SAlexander.Kolbasov@Sun.COM	my @ids = keys(%{$pgtree});
161*13124SAlexander.Kolbasov@Sun.COM
162*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ? _nsort(@ids) : scalar @ids);
163*13124SAlexander.Kolbasov@Sun.COM}
164*13124SAlexander.Kolbasov@Sun.COM
165*13124SAlexander.Kolbasov@Sun.COM#
166*13124SAlexander.Kolbasov@Sun.COM# Return list of all pgs by walking the tree depth first.
167*13124SAlexander.Kolbasov@Sun.COM#
168*13124SAlexander.Kolbasov@Sun.COMsub all_depth_first
169*13124SAlexander.Kolbasov@Sun.COM{
170*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("all_depth_first(cookie)");
171*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
172*13124SAlexander.Kolbasov@Sun.COM
173*13124SAlexander.Kolbasov@Sun.COM	_walk_depth_first($self, $self->root());
174*13124SAlexander.Kolbasov@Sun.COM}
175*13124SAlexander.Kolbasov@Sun.COM
176*13124SAlexander.Kolbasov@Sun.COM#
177*13124SAlexander.Kolbasov@Sun.COM# Return list of all pgs by walking the tree breadth first.
178*13124SAlexander.Kolbasov@Sun.COM#
179*13124SAlexander.Kolbasov@Sun.COMsub all_breadth_first
180*13124SAlexander.Kolbasov@Sun.COM{
181*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("all_breadth_first(cookie)");
182*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
183*13124SAlexander.Kolbasov@Sun.COM
184*13124SAlexander.Kolbasov@Sun.COM	_walk_breadth_first($self, $self->root());
185*13124SAlexander.Kolbasov@Sun.COM}
186*13124SAlexander.Kolbasov@Sun.COM
187*13124SAlexander.Kolbasov@Sun.COM#
188*13124SAlexander.Kolbasov@Sun.COM# Return list of CPUs in the PG specified
189*13124SAlexander.Kolbasov@Sun.COM# CPUs returned are numerically sorted
190*13124SAlexander.Kolbasov@Sun.COM# In scalar context return number of CPUs
191*13124SAlexander.Kolbasov@Sun.COM#
192*13124SAlexander.Kolbasov@Sun.COMsub cpus
193*13124SAlexander.Kolbasov@Sun.COM{
194*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("cpus(cookie, pg)");
195*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
196*13124SAlexander.Kolbasov@Sun.COM	my @cpus =  @{$pg->{cpus}};
197*13124SAlexander.Kolbasov@Sun.COM
198*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ? _nsort(@cpus) : _collapse(@cpus));
199*13124SAlexander.Kolbasov@Sun.COM}
200*13124SAlexander.Kolbasov@Sun.COM
201*13124SAlexander.Kolbasov@Sun.COM#
202*13124SAlexander.Kolbasov@Sun.COM# Return a parent for a given PG
203*13124SAlexander.Kolbasov@Sun.COM# Returns undef if there is no parent
204*13124SAlexander.Kolbasov@Sun.COM#
205*13124SAlexander.Kolbasov@Sun.COMsub parent
206*13124SAlexander.Kolbasov@Sun.COM{
207*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("parent(cookie, pg)");
208*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
209*13124SAlexander.Kolbasov@Sun.COM	my $parent = $pg->{parent};
210*13124SAlexander.Kolbasov@Sun.COM
211*13124SAlexander.Kolbasov@Sun.COM	return (defined($parent) && $parent >= 0 ? $parent : undef);
212*13124SAlexander.Kolbasov@Sun.COM}
213*13124SAlexander.Kolbasov@Sun.COM
214*13124SAlexander.Kolbasov@Sun.COM#
215*13124SAlexander.Kolbasov@Sun.COM# Return list of children for a given PG
216*13124SAlexander.Kolbasov@Sun.COM# In scalar context return list of children
217*13124SAlexander.Kolbasov@Sun.COM#
218*13124SAlexander.Kolbasov@Sun.COMsub children
219*13124SAlexander.Kolbasov@Sun.COM{
220*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("children(cookie, pg)");
221*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
222*13124SAlexander.Kolbasov@Sun.COM
223*13124SAlexander.Kolbasov@Sun.COM	my $children = $pg->{children} or return;
224*13124SAlexander.Kolbasov@Sun.COM	my @children = @{$children};
225*13124SAlexander.Kolbasov@Sun.COM
226*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ? _nsort(@children) : scalar @children);
227*13124SAlexander.Kolbasov@Sun.COM}
228*13124SAlexander.Kolbasov@Sun.COM
229*13124SAlexander.Kolbasov@Sun.COM#
230*13124SAlexander.Kolbasov@Sun.COM# Return sharing name for the PG
231*13124SAlexander.Kolbasov@Sun.COM#
232*13124SAlexander.Kolbasov@Sun.COMsub sh_name
233*13124SAlexander.Kolbasov@Sun.COM{
234*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("sh_name(cookie, pg)");
235*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
236*13124SAlexander.Kolbasov@Sun.COM	return ($pg->{sh_name});
237*13124SAlexander.Kolbasov@Sun.COM}
238*13124SAlexander.Kolbasov@Sun.COM
239*13124SAlexander.Kolbasov@Sun.COM#
240*13124SAlexander.Kolbasov@Sun.COM# Return T if specified PG ID is a leaf PG
241*13124SAlexander.Kolbasov@Sun.COM#
242*13124SAlexander.Kolbasov@Sun.COMsub is_leaf
243*13124SAlexander.Kolbasov@Sun.COM{
244*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("is_leaf(cookie, pg)");
245*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
246*13124SAlexander.Kolbasov@Sun.COM	return ($pg->{is_leaf});
247*13124SAlexander.Kolbasov@Sun.COM}
248*13124SAlexander.Kolbasov@Sun.COM
249*13124SAlexander.Kolbasov@Sun.COM#
250*13124SAlexander.Kolbasov@Sun.COM# Return leaf PGs
251*13124SAlexander.Kolbasov@Sun.COM#
252*13124SAlexander.Kolbasov@Sun.COMsub leaves
253*13124SAlexander.Kolbasov@Sun.COM{
254*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("leaves(cookie, pg)");
255*13124SAlexander.Kolbasov@Sun.COM
256*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
257*13124SAlexander.Kolbasov@Sun.COM
258*13124SAlexander.Kolbasov@Sun.COM	return (grep { is_leaf($self, $_) } $self->all());
259*13124SAlexander.Kolbasov@Sun.COM}
260*13124SAlexander.Kolbasov@Sun.COM
261*13124SAlexander.Kolbasov@Sun.COM#
262*13124SAlexander.Kolbasov@Sun.COM# Update varying data in the snapshot
263*13124SAlexander.Kolbasov@Sun.COM#
264*13124SAlexander.Kolbasov@Sun.COMsub update
265*13124SAlexander.Kolbasov@Sun.COM{
266*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("update(cookie)");
267*13124SAlexander.Kolbasov@Sun.COM
268*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
269*13124SAlexander.Kolbasov@Sun.COM	my $ks = $self->{KSTAT};
270*13124SAlexander.Kolbasov@Sun.COM
271*13124SAlexander.Kolbasov@Sun.COM	$ks->update();
272*13124SAlexander.Kolbasov@Sun.COM
273*13124SAlexander.Kolbasov@Sun.COM	my $pgtree = $self->{PGTREE};
274*13124SAlexander.Kolbasov@Sun.COM	my $pg_info = $ks->{$self->{PG_MODULE}};
275*13124SAlexander.Kolbasov@Sun.COM
276*13124SAlexander.Kolbasov@Sun.COM	#
277*13124SAlexander.Kolbasov@Sun.COM	# Walk PG kstats and copy updated data from kstats to the snapshot
278*13124SAlexander.Kolbasov@Sun.COM	#
279*13124SAlexander.Kolbasov@Sun.COM	foreach my $id (keys %$pg_info) {
280*13124SAlexander.Kolbasov@Sun.COM		my $pg = $pgtree->{$id} or next;
281*13124SAlexander.Kolbasov@Sun.COM
282*13124SAlexander.Kolbasov@Sun.COM		my $pg_ks = _kstat_get_pg($pg_info, $id,
283*13124SAlexander.Kolbasov@Sun.COM					  $self->{USE_OLD_KSTATS});
284*13124SAlexander.Kolbasov@Sun.COM		return unless $pg_ks;
285*13124SAlexander.Kolbasov@Sun.COM
286*13124SAlexander.Kolbasov@Sun.COM		#
287*13124SAlexander.Kolbasov@Sun.COM		# Update PG from kstats
288*13124SAlexander.Kolbasov@Sun.COM		#
289*13124SAlexander.Kolbasov@Sun.COM		$pg->{util} = $pg_ks->{hw_util};
290*13124SAlexander.Kolbasov@Sun.COM		$pg->{current_rate} = $pg_ks->{hw_util_rate};
291*13124SAlexander.Kolbasov@Sun.COM		$pg->{util_rate_max} = $pg_ks->{hw_util_rate_max};
292*13124SAlexander.Kolbasov@Sun.COM		$pg->{util_time_running} = $pg_ks->{hw_util_time_running};
293*13124SAlexander.Kolbasov@Sun.COM		$pg->{util_time_stopped} = $pg_ks->{hw_util_time_stopped};
294*13124SAlexander.Kolbasov@Sun.COM		$pg->{snaptime} = $pg_ks->{snaptime};
295*13124SAlexander.Kolbasov@Sun.COM		$pg->{generation} = $pg_ks->{generation};
296*13124SAlexander.Kolbasov@Sun.COM	}
297*13124SAlexander.Kolbasov@Sun.COM
298*13124SAlexander.Kolbasov@Sun.COM	#
299*13124SAlexander.Kolbasov@Sun.COM	# Update software load for each CPU
300*13124SAlexander.Kolbasov@Sun.COM	#
301*13124SAlexander.Kolbasov@Sun.COM	$self->{CPU_LOAD} = _get_sw_cpu_load($ks);
302*13124SAlexander.Kolbasov@Sun.COM
303*13124SAlexander.Kolbasov@Sun.COM	#
304*13124SAlexander.Kolbasov@Sun.COM	# Get hardware load per CPU
305*13124SAlexander.Kolbasov@Sun.COM	#
306*13124SAlexander.Kolbasov@Sun.COM	if ($self->{GET_CPU_DATA}) {
307*13124SAlexander.Kolbasov@Sun.COM		_get_hw_cpu_load($self);
308*13124SAlexander.Kolbasov@Sun.COM	}
309*13124SAlexander.Kolbasov@Sun.COM
310*13124SAlexander.Kolbasov@Sun.COM	return (1);
311*13124SAlexander.Kolbasov@Sun.COM}
312*13124SAlexander.Kolbasov@Sun.COM
313*13124SAlexander.Kolbasov@Sun.COM#
314*13124SAlexander.Kolbasov@Sun.COM# Return list of physical tags for the given PG
315*13124SAlexander.Kolbasov@Sun.COM#
316*13124SAlexander.Kolbasov@Sun.COMsub tags
317*13124SAlexander.Kolbasov@Sun.COM{
318*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("tags(cookie, pg)");
319*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
320*13124SAlexander.Kolbasov@Sun.COM
321*13124SAlexander.Kolbasov@Sun.COM	my $tags = $pg->{tags} or return;
322*13124SAlexander.Kolbasov@Sun.COM
323*13124SAlexander.Kolbasov@Sun.COM	my @tags = _uniq(@{$tags});
324*13124SAlexander.Kolbasov@Sun.COM
325*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ? @tags : join (',', @tags));
326*13124SAlexander.Kolbasov@Sun.COM}
327*13124SAlexander.Kolbasov@Sun.COM
328*13124SAlexander.Kolbasov@Sun.COM#
329*13124SAlexander.Kolbasov@Sun.COM# Return list of sharing relationships in the snapshot Relationships are sorted
330*13124SAlexander.Kolbasov@Sun.COM# by the level in the hierarchy If any PGs are given on the command line, only
331*13124SAlexander.Kolbasov@Sun.COM# return sharing relationships for given PGs, but still keep them sorted.
332*13124SAlexander.Kolbasov@Sun.COM#
333*13124SAlexander.Kolbasov@Sun.COMsub sharing_relationships
334*13124SAlexander.Kolbasov@Sun.COM{
335*13124SAlexander.Kolbasov@Sun.COM	scalar @_ or _usage("sharing_relationships(cookie, [pg, ...])");
336*13124SAlexander.Kolbasov@Sun.COM
337*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
338*13124SAlexander.Kolbasov@Sun.COM	my @pgs = $self->all_breadth_first();
339*13124SAlexander.Kolbasov@Sun.COM
340*13124SAlexander.Kolbasov@Sun.COM	if (scalar @_ > 0) {
341*13124SAlexander.Kolbasov@Sun.COM		#
342*13124SAlexander.Kolbasov@Sun.COM		# Caller specified PGs, remove any PGs not in caller's list
343*13124SAlexander.Kolbasov@Sun.COM		#
344*13124SAlexander.Kolbasov@Sun.COM		my %seen;
345*13124SAlexander.Kolbasov@Sun.COM		map { $seen{$_} = 1 } @_;
346*13124SAlexander.Kolbasov@Sun.COM
347*13124SAlexander.Kolbasov@Sun.COM		# Remove any PGs not provided by user
348*13124SAlexander.Kolbasov@Sun.COM		@pgs = grep { $seen{$_} } @pgs;
349*13124SAlexander.Kolbasov@Sun.COM	}
350*13124SAlexander.Kolbasov@Sun.COM
351*13124SAlexander.Kolbasov@Sun.COM	return (_uniq(map { $self->sh_name($_) } @pgs));
352*13124SAlexander.Kolbasov@Sun.COM}
353*13124SAlexander.Kolbasov@Sun.COM
354*13124SAlexander.Kolbasov@Sun.COM#
355*13124SAlexander.Kolbasov@Sun.COM# Return PG generation number. If PG is specified in the argument, return its
356*13124SAlexander.Kolbasov@Sun.COM# generation, otherwise return snapshot generation.
357*13124SAlexander.Kolbasov@Sun.COM# Snapshot generation is calculated as the total of PG generations
358*13124SAlexander.Kolbasov@Sun.COM#
359*13124SAlexander.Kolbasov@Sun.COMsub generation
360*13124SAlexander.Kolbasov@Sun.COM{
361*13124SAlexander.Kolbasov@Sun.COM	(scalar @_ == 1 || scalar @_ == 2) or _usage("generation(cookie, [pg])");
362*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
363*13124SAlexander.Kolbasov@Sun.COM
364*13124SAlexander.Kolbasov@Sun.COM	if (scalar @_ == 0) {
365*13124SAlexander.Kolbasov@Sun.COM		my @generations = map { $_->{generation} }
366*13124SAlexander.Kolbasov@Sun.COM				  values %{$self->{PGTREE}};
367*13124SAlexander.Kolbasov@Sun.COM		return (sum(@generations));
368*13124SAlexander.Kolbasov@Sun.COM
369*13124SAlexander.Kolbasov@Sun.COM	} else {
370*13124SAlexander.Kolbasov@Sun.COM		my $id = shift;
371*13124SAlexander.Kolbasov@Sun.COM		my $pg = _pg_get($self, $id) or return;
372*13124SAlexander.Kolbasov@Sun.COM		return ($pg->{generation});
373*13124SAlexander.Kolbasov@Sun.COM	}
374*13124SAlexander.Kolbasov@Sun.COM}
375*13124SAlexander.Kolbasov@Sun.COM
376*13124SAlexander.Kolbasov@Sun.COM#
377*13124SAlexander.Kolbasov@Sun.COM# Return level of PG in the tree, starting from root.
378*13124SAlexander.Kolbasov@Sun.COM# PG level is cached in the $pg->{level} field.
379*13124SAlexander.Kolbasov@Sun.COM#
380*13124SAlexander.Kolbasov@Sun.COMsub level
381*13124SAlexander.Kolbasov@Sun.COM{
382*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("level(cookie, pg)");
383*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
384*13124SAlexander.Kolbasov@Sun.COM	my $pgid = shift;
385*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get($self, $pgid) or return;
386*13124SAlexander.Kolbasov@Sun.COM
387*13124SAlexander.Kolbasov@Sun.COM	return $pg->{level} if defined($pg->{level});
388*13124SAlexander.Kolbasov@Sun.COM
389*13124SAlexander.Kolbasov@Sun.COM	$pg->{level} = 0;
390*13124SAlexander.Kolbasov@Sun.COM
391*13124SAlexander.Kolbasov@Sun.COM	my $parent = _pg_get($self, $pg->{parent});
392*13124SAlexander.Kolbasov@Sun.COM	while ($parent) {
393*13124SAlexander.Kolbasov@Sun.COM		$pg->{level}++;
394*13124SAlexander.Kolbasov@Sun.COM		$parent = _pg_get($self, $parent->{parent});
395*13124SAlexander.Kolbasov@Sun.COM	}
396*13124SAlexander.Kolbasov@Sun.COM
397*13124SAlexander.Kolbasov@Sun.COM	return ($pg->{level});
398*13124SAlexander.Kolbasov@Sun.COM}
399*13124SAlexander.Kolbasov@Sun.COM
400*13124SAlexander.Kolbasov@Sun.COM#
401*13124SAlexander.Kolbasov@Sun.COM# Return T if PG supports utilization We assume that utilization is supported by
402*13124SAlexander.Kolbasov@Sun.COM# PG if it shows any non-zero time in util_time_running. It is possible that the
403*13124SAlexander.Kolbasov@Sun.COM# same condition may be caused by cpustat(1) running ever since PG was created,
404*13124SAlexander.Kolbasov@Sun.COM# but there is not much we can do about it.
405*13124SAlexander.Kolbasov@Sun.COM#
406*13124SAlexander.Kolbasov@Sun.COMsub has_utilization
407*13124SAlexander.Kolbasov@Sun.COM{
408*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("has_utilization(cookie, pg)");
409*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get(shift, shift) or return;
410*13124SAlexander.Kolbasov@Sun.COM
411*13124SAlexander.Kolbasov@Sun.COM	return ($pg->{util_time_running} != 0);
412*13124SAlexander.Kolbasov@Sun.COM}
413*13124SAlexander.Kolbasov@Sun.COM
414*13124SAlexander.Kolbasov@Sun.COM
415*13124SAlexander.Kolbasov@Sun.COM#
416*13124SAlexander.Kolbasov@Sun.COM# Return utilization for the PG
417*13124SAlexander.Kolbasov@Sun.COM# Utilization is a difference in utilization value between two snapshots.
418*13124SAlexander.Kolbasov@Sun.COM# We can only compare utilization between PGs having the same generation ID.
419*13124SAlexander.Kolbasov@Sun.COM#
420*13124SAlexander.Kolbasov@Sun.COMsub utilization
421*13124SAlexander.Kolbasov@Sun.COM{
422*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 3 or _usage("utilization(cookie, cookie1, pg");
423*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
424*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
425*13124SAlexander.Kolbasov@Sun.COM	my $id = shift;
426*13124SAlexander.Kolbasov@Sun.COM
427*13124SAlexander.Kolbasov@Sun.COM	#
428*13124SAlexander.Kolbasov@Sun.COM	# Since we have two cookies, update capacity in both
429*13124SAlexander.Kolbasov@Sun.COM	#
430*13124SAlexander.Kolbasov@Sun.COM	_capacity_update($c1, $c2, $id);
431*13124SAlexander.Kolbasov@Sun.COM
432*13124SAlexander.Kolbasov@Sun.COM	my $pg1 = _pg_get($c1, $id) or return;
433*13124SAlexander.Kolbasov@Sun.COM	my $pg2 = _pg_get($c2, $id) or return;
434*13124SAlexander.Kolbasov@Sun.COM
435*13124SAlexander.Kolbasov@Sun.COM	#
436*13124SAlexander.Kolbasov@Sun.COM	# Nothing to return if one of the utilizations wasn't measured
437*13124SAlexander.Kolbasov@Sun.COM	#
438*13124SAlexander.Kolbasov@Sun.COM	return unless ($pg1->{util_time_running} && $pg2->{util_time_running});
439*13124SAlexander.Kolbasov@Sun.COM
440*13124SAlexander.Kolbasov@Sun.COM	#
441*13124SAlexander.Kolbasov@Sun.COM	# Verify generation IDs
442*13124SAlexander.Kolbasov@Sun.COM	#
443*13124SAlexander.Kolbasov@Sun.COM	return unless $pg1->{generation} eq $pg2->{generation};
444*13124SAlexander.Kolbasov@Sun.COM	my $u1 = $pg1->{util};
445*13124SAlexander.Kolbasov@Sun.COM	my $u2 = $pg2->{util};
446*13124SAlexander.Kolbasov@Sun.COM	return unless defined ($u1) && defined ($u2);
447*13124SAlexander.Kolbasov@Sun.COM
448*13124SAlexander.Kolbasov@Sun.COM	return (abs($u2 - $u1));
449*13124SAlexander.Kolbasov@Sun.COM}
450*13124SAlexander.Kolbasov@Sun.COM
451*13124SAlexander.Kolbasov@Sun.COM#
452*13124SAlexander.Kolbasov@Sun.COM# Return an estimate of PG capacity Capacity is calculated as the maximum of
453*13124SAlexander.Kolbasov@Sun.COM# observed utilization expressed in units per second or maximum CPU frequency
454*13124SAlexander.Kolbasov@Sun.COM# for all CPUs.
455*13124SAlexander.Kolbasov@Sun.COM#
456*13124SAlexander.Kolbasov@Sun.COM# We store capacity per sharing relationship, assuming that the same sharing has
457*13124SAlexander.Kolbasov@Sun.COM# the same capacity. This may not be true for heterogeneous systems.
458*13124SAlexander.Kolbasov@Sun.COM#
459*13124SAlexander.Kolbasov@Sun.COMsub capacity
460*13124SAlexander.Kolbasov@Sun.COM{
461*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 2 or _usage("capacity(cookie, pg");
462*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
463*13124SAlexander.Kolbasov@Sun.COM	my $pgid = shift;
464*13124SAlexander.Kolbasov@Sun.COM	my $pg = _pg_get($self, $pgid) or return;
465*13124SAlexander.Kolbasov@Sun.COM	my $shname = $pg->{sh_name} or return;
466*13124SAlexander.Kolbasov@Sun.COM
467*13124SAlexander.Kolbasov@Sun.COM	return (max($self->{MAX_FREQUENCY}, $self->{CAPACITY}->{$shname}));
468*13124SAlexander.Kolbasov@Sun.COM}
469*13124SAlexander.Kolbasov@Sun.COM
470*13124SAlexander.Kolbasov@Sun.COM#
471*13124SAlexander.Kolbasov@Sun.COM# Return accuracy of utilization calculation between two snapshots The accuracy
472*13124SAlexander.Kolbasov@Sun.COM# is determined based on the total time spent running and not running the
473*13124SAlexander.Kolbasov@Sun.COM# counters. If T1 is the time counters were running during the period and T2 is
474*13124SAlexander.Kolbasov@Sun.COM# the time they were turned off, the accuracy is T1 / (T1 + T2), expressed in
475*13124SAlexander.Kolbasov@Sun.COM# percentages.
476*13124SAlexander.Kolbasov@Sun.COM#
477*13124SAlexander.Kolbasov@Sun.COMsub accuracy
478*13124SAlexander.Kolbasov@Sun.COM{
479*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 3 or _usage("accuracy(cookie, cookie1, pg)");
480*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
481*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
482*13124SAlexander.Kolbasov@Sun.COM	my $id = shift;
483*13124SAlexander.Kolbasov@Sun.COM	my $trun;
484*13124SAlexander.Kolbasov@Sun.COM	my $tstop;
485*13124SAlexander.Kolbasov@Sun.COM
486*13124SAlexander.Kolbasov@Sun.COM	my $pg1 = _pg_get($c1, $id) or return;
487*13124SAlexander.Kolbasov@Sun.COM	my $pg2 = _pg_get($c2, $id) or return;
488*13124SAlexander.Kolbasov@Sun.COM
489*13124SAlexander.Kolbasov@Sun.COM	# Both PGs should have the same generation
490*13124SAlexander.Kolbasov@Sun.COM	return unless $pg1->{generation} eq $pg2->{generation};
491*13124SAlexander.Kolbasov@Sun.COM
492*13124SAlexander.Kolbasov@Sun.COM	#
493*13124SAlexander.Kolbasov@Sun.COM	# Get time spent with running and stopped counters
494*13124SAlexander.Kolbasov@Sun.COM	#
495*13124SAlexander.Kolbasov@Sun.COM	$trun = abs($pg2->{util_time_running} -
496*13124SAlexander.Kolbasov@Sun.COM		    $pg1->{util_time_running});
497*13124SAlexander.Kolbasov@Sun.COM	$tstop = abs($pg2->{util_time_stopped} -
498*13124SAlexander.Kolbasov@Sun.COM		     $pg1->{util_time_stopped});
499*13124SAlexander.Kolbasov@Sun.COM
500*13124SAlexander.Kolbasov@Sun.COM	my $total = $trun + $tstop;
501*13124SAlexander.Kolbasov@Sun.COM
502*13124SAlexander.Kolbasov@Sun.COM	#
503*13124SAlexander.Kolbasov@Sun.COM	# Calculate accuracy as percentage
504*13124SAlexander.Kolbasov@Sun.COM	#
505*13124SAlexander.Kolbasov@Sun.COM	my $accuracy = $total ? ($trun * 100) / $total : 0;
506*13124SAlexander.Kolbasov@Sun.COM	$accuracy = int($accuracy + 0.5);
507*13124SAlexander.Kolbasov@Sun.COM	$accuracy = 100 if $accuracy > 100;
508*13124SAlexander.Kolbasov@Sun.COM	return ($accuracy);
509*13124SAlexander.Kolbasov@Sun.COM}
510*13124SAlexander.Kolbasov@Sun.COM
511*13124SAlexander.Kolbasov@Sun.COM#
512*13124SAlexander.Kolbasov@Sun.COM# Return time difference in seconds between two snapshots
513*13124SAlexander.Kolbasov@Sun.COM#
514*13124SAlexander.Kolbasov@Sun.COMsub tdelta
515*13124SAlexander.Kolbasov@Sun.COM{
516*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 3 or _usage("tdelta(cookie, cookie1, pg)");
517*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
518*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
519*13124SAlexander.Kolbasov@Sun.COM	my $id = shift;
520*13124SAlexander.Kolbasov@Sun.COM
521*13124SAlexander.Kolbasov@Sun.COM	my $pg1 = _pg_get($c1, $id) or return;
522*13124SAlexander.Kolbasov@Sun.COM	my $pg2 = _pg_get($c2, $id) or return;
523*13124SAlexander.Kolbasov@Sun.COM
524*13124SAlexander.Kolbasov@Sun.COM	return unless $pg1->{generation} eq $pg2->{generation};
525*13124SAlexander.Kolbasov@Sun.COM
526*13124SAlexander.Kolbasov@Sun.COM	my $t1 = $pg1->{snaptime};
527*13124SAlexander.Kolbasov@Sun.COM	my $t2 = $pg2->{snaptime};
528*13124SAlexander.Kolbasov@Sun.COM	my $delta = abs($t1 - $t2);
529*13124SAlexander.Kolbasov@Sun.COM	return ($delta);
530*13124SAlexander.Kolbasov@Sun.COM}
531*13124SAlexander.Kolbasov@Sun.COM
532*13124SAlexander.Kolbasov@Sun.COM#
533*13124SAlexander.Kolbasov@Sun.COM# Return software utilization between two snapshots
534*13124SAlexander.Kolbasov@Sun.COM# In scalar context return software load as percentage.
535*13124SAlexander.Kolbasov@Sun.COM# In list context return a list (USER, SYSTEM, IDLE, SWLOAD)
536*13124SAlexander.Kolbasov@Sun.COM# All loads are returned as percentages
537*13124SAlexander.Kolbasov@Sun.COM#
538*13124SAlexander.Kolbasov@Sun.COMsub sw_utilization
539*13124SAlexander.Kolbasov@Sun.COM{
540*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 3 or _usage("tdelta(cookie, cookie1, pg)");
541*13124SAlexander.Kolbasov@Sun.COM
542*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
543*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
544*13124SAlexander.Kolbasov@Sun.COM	my $id = shift;
545*13124SAlexander.Kolbasov@Sun.COM
546*13124SAlexander.Kolbasov@Sun.COM	my $pg1 = _pg_get($c1, $id) or return;
547*13124SAlexander.Kolbasov@Sun.COM	my $pg2 = _pg_get($c2, $id) or return;
548*13124SAlexander.Kolbasov@Sun.COM
549*13124SAlexander.Kolbasov@Sun.COM	return unless $pg1->{generation} eq $pg2->{generation};
550*13124SAlexander.Kolbasov@Sun.COM
551*13124SAlexander.Kolbasov@Sun.COM	my @cpus = $c1->cpus($id);
552*13124SAlexander.Kolbasov@Sun.COM
553*13124SAlexander.Kolbasov@Sun.COM	my $load1 = $c1->{CPU_LOAD};
554*13124SAlexander.Kolbasov@Sun.COM	my $load2 = $c2->{CPU_LOAD};
555*13124SAlexander.Kolbasov@Sun.COM
556*13124SAlexander.Kolbasov@Sun.COM	my $idle = 0;
557*13124SAlexander.Kolbasov@Sun.COM	my $user = 0;
558*13124SAlexander.Kolbasov@Sun.COM	my $sys = 0;
559*13124SAlexander.Kolbasov@Sun.COM	my $total = 0;
560*13124SAlexander.Kolbasov@Sun.COM	my $swload = 0;
561*13124SAlexander.Kolbasov@Sun.COM
562*13124SAlexander.Kolbasov@Sun.COM	foreach my $cpu (@cpus) {
563*13124SAlexander.Kolbasov@Sun.COM		my $ld1 = $load1->{$cpu};
564*13124SAlexander.Kolbasov@Sun.COM		my $ld2 = $load2->{$cpu};
565*13124SAlexander.Kolbasov@Sun.COM		next unless $ld1 && $ld2;
566*13124SAlexander.Kolbasov@Sun.COM
567*13124SAlexander.Kolbasov@Sun.COM		$idle += $ld2->{cpu_idle} - $ld1->{cpu_idle};
568*13124SAlexander.Kolbasov@Sun.COM		$user += $ld2->{cpu_user} - $ld1->{cpu_user};
569*13124SAlexander.Kolbasov@Sun.COM		$sys  += $ld2->{cpu_sys}  - $ld1->{cpu_sys};
570*13124SAlexander.Kolbasov@Sun.COM	}
571*13124SAlexander.Kolbasov@Sun.COM
572*13124SAlexander.Kolbasov@Sun.COM	$total = $idle + $user + $sys;
573*13124SAlexander.Kolbasov@Sun.COM
574*13124SAlexander.Kolbasov@Sun.COM	# Prevent division by zero
575*13124SAlexander.Kolbasov@Sun.COM	$total = 1 unless $total;
576*13124SAlexander.Kolbasov@Sun.COM
577*13124SAlexander.Kolbasov@Sun.COM	$swload = ($user + $sys) * 100 / $total;
578*13124SAlexander.Kolbasov@Sun.COM	$idle   = $idle * 100 / $total;
579*13124SAlexander.Kolbasov@Sun.COM	$user   = $user * 100 / $total;
580*13124SAlexander.Kolbasov@Sun.COM	$sys    = $sys  * 100 / $total;
581*13124SAlexander.Kolbasov@Sun.COM
582*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ? ($user, $sys, $idle, $swload) : $swload);
583*13124SAlexander.Kolbasov@Sun.COM}
584*13124SAlexander.Kolbasov@Sun.COM
585*13124SAlexander.Kolbasov@Sun.COM#
586*13124SAlexander.Kolbasov@Sun.COM# Return utilization for the PG for a given CPU
587*13124SAlexander.Kolbasov@Sun.COM# Utilization is a difference in utilization value between two snapshots.
588*13124SAlexander.Kolbasov@Sun.COM# We can only compare utilization between PGs having the same generation ID.
589*13124SAlexander.Kolbasov@Sun.COM#
590*13124SAlexander.Kolbasov@Sun.COMsub cpu_utilization
591*13124SAlexander.Kolbasov@Sun.COM{
592*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 4 or _usage("utilization(cookie, cookie1, pg, cpu");
593*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
594*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
595*13124SAlexander.Kolbasov@Sun.COM	my $id = shift;
596*13124SAlexander.Kolbasov@Sun.COM	my $cpu = shift;
597*13124SAlexander.Kolbasov@Sun.COM
598*13124SAlexander.Kolbasov@Sun.COM	my $idle = 0;
599*13124SAlexander.Kolbasov@Sun.COM	my $user = 0;
600*13124SAlexander.Kolbasov@Sun.COM	my $sys = 0;
601*13124SAlexander.Kolbasov@Sun.COM	my $swtotal = 0;
602*13124SAlexander.Kolbasov@Sun.COM	my $swload = 0;
603*13124SAlexander.Kolbasov@Sun.COM
604*13124SAlexander.Kolbasov@Sun.COM	#
605*13124SAlexander.Kolbasov@Sun.COM	# Since we have two cookies, update capacity in both
606*13124SAlexander.Kolbasov@Sun.COM	#
607*13124SAlexander.Kolbasov@Sun.COM	_capacity_update($c1, $c2, $id);
608*13124SAlexander.Kolbasov@Sun.COM
609*13124SAlexander.Kolbasov@Sun.COM	my $pg1 = _pg_get($c1, $id) or return;
610*13124SAlexander.Kolbasov@Sun.COM	my $pg2 = _pg_get($c2, $id) or return;
611*13124SAlexander.Kolbasov@Sun.COM
612*13124SAlexander.Kolbasov@Sun.COM	#
613*13124SAlexander.Kolbasov@Sun.COM	# Nothing to return if one of the utilizations wasn't measured
614*13124SAlexander.Kolbasov@Sun.COM	#
615*13124SAlexander.Kolbasov@Sun.COM	return unless ($pg1->{util_time_running} && $pg2->{util_time_running});
616*13124SAlexander.Kolbasov@Sun.COM
617*13124SAlexander.Kolbasov@Sun.COM	#
618*13124SAlexander.Kolbasov@Sun.COM	# Nothing to return if CPU data is missing
619*13124SAlexander.Kolbasov@Sun.COM	#
620*13124SAlexander.Kolbasov@Sun.COM	return unless $pg1->{cpudata} && $pg2->{cpudata};
621*13124SAlexander.Kolbasov@Sun.COM
622*13124SAlexander.Kolbasov@Sun.COM	#
623*13124SAlexander.Kolbasov@Sun.COM	# Verify generation IDs
624*13124SAlexander.Kolbasov@Sun.COM	#
625*13124SAlexander.Kolbasov@Sun.COM	return unless $pg1->{generation} eq $pg2->{generation};
626*13124SAlexander.Kolbasov@Sun.COM
627*13124SAlexander.Kolbasov@Sun.COM	#
628*13124SAlexander.Kolbasov@Sun.COM	# Get data for the given CPU
629*13124SAlexander.Kolbasov@Sun.COM	#
630*13124SAlexander.Kolbasov@Sun.COM	my $cpudata1 = $pg1->{cpudata}->{$cpu};
631*13124SAlexander.Kolbasov@Sun.COM	my $cpudata2 = $pg2->{cpudata}->{$cpu};
632*13124SAlexander.Kolbasov@Sun.COM
633*13124SAlexander.Kolbasov@Sun.COM	return unless $cpudata1 && $cpudata2;
634*13124SAlexander.Kolbasov@Sun.COM
635*13124SAlexander.Kolbasov@Sun.COM	return unless $cpudata1->{generation} == $cpudata2->{generation};
636*13124SAlexander.Kolbasov@Sun.COM
637*13124SAlexander.Kolbasov@Sun.COM	my $u1 = $cpudata1->{util};
638*13124SAlexander.Kolbasov@Sun.COM	my $u2 = $cpudata2->{util};
639*13124SAlexander.Kolbasov@Sun.COM	return unless defined ($u1) && defined ($u2);
640*13124SAlexander.Kolbasov@Sun.COM	my $hw_utilization = abs ($u1 - $u2);
641*13124SAlexander.Kolbasov@Sun.COM
642*13124SAlexander.Kolbasov@Sun.COM	#
643*13124SAlexander.Kolbasov@Sun.COM	# Get time spent with running and stopped counters
644*13124SAlexander.Kolbasov@Sun.COM	#
645*13124SAlexander.Kolbasov@Sun.COM	my $trun = abs($cpudata1->{util_time_running} -
646*13124SAlexander.Kolbasov@Sun.COM		       $cpudata2->{util_time_running});
647*13124SAlexander.Kolbasov@Sun.COM	my $tstop = abs($cpudata1->{util_time_stopped} -
648*13124SAlexander.Kolbasov@Sun.COM			$cpudata2->{util_time_stopped});
649*13124SAlexander.Kolbasov@Sun.COM
650*13124SAlexander.Kolbasov@Sun.COM	my $total = $trun + $tstop;
651*13124SAlexander.Kolbasov@Sun.COM
652*13124SAlexander.Kolbasov@Sun.COM	#
653*13124SAlexander.Kolbasov@Sun.COM	# Calculate accuracy as percentage
654*13124SAlexander.Kolbasov@Sun.COM	#
655*13124SAlexander.Kolbasov@Sun.COM	my $accuracy = $total ? ($trun * 100) / $total : 0;
656*13124SAlexander.Kolbasov@Sun.COM	$accuracy = int($accuracy + 0.5);
657*13124SAlexander.Kolbasov@Sun.COM	$accuracy = 100 if $accuracy > 100;
658*13124SAlexander.Kolbasov@Sun.COM
659*13124SAlexander.Kolbasov@Sun.COM	my $t1 = $cpudata1->{snaptime};
660*13124SAlexander.Kolbasov@Sun.COM	my $t2 = $cpudata2->{snaptime};
661*13124SAlexander.Kolbasov@Sun.COM	my $tdelta = abs ($t1 - $t2);
662*13124SAlexander.Kolbasov@Sun.COM
663*13124SAlexander.Kolbasov@Sun.COM	my $shname = $pg2->{sh_name} or return;
664*13124SAlexander.Kolbasov@Sun.COM	my $capacity = max($c2->{MAX_FREQUENCY}, $c2->{CAPACITY}->{$shname});
665*13124SAlexander.Kolbasov@Sun.COM	my $utilization = $hw_utilization / $tdelta;
666*13124SAlexander.Kolbasov@Sun.COM	$capacity = $utilization unless $capacity;
667*13124SAlexander.Kolbasov@Sun.COM	$utilization /= $capacity;
668*13124SAlexander.Kolbasov@Sun.COM	$utilization *= 100;
669*13124SAlexander.Kolbasov@Sun.COM
670*13124SAlexander.Kolbasov@Sun.COM	my $ld1 = $c1->{CPU_LOAD}->{$cpu};
671*13124SAlexander.Kolbasov@Sun.COM	my $ld2 = $c2->{CPU_LOAD}->{$cpu};
672*13124SAlexander.Kolbasov@Sun.COM
673*13124SAlexander.Kolbasov@Sun.COM	if ($ld1 && $ld2) {
674*13124SAlexander.Kolbasov@Sun.COM		$idle = $ld2->{cpu_idle} - $ld1->{cpu_idle};
675*13124SAlexander.Kolbasov@Sun.COM		$user = $ld2->{cpu_user} - $ld1->{cpu_user};
676*13124SAlexander.Kolbasov@Sun.COM		$sys  = $ld2->{cpu_sys}  - $ld1->{cpu_sys};
677*13124SAlexander.Kolbasov@Sun.COM
678*13124SAlexander.Kolbasov@Sun.COM		$swtotal = $idle + $user + $sys;
679*13124SAlexander.Kolbasov@Sun.COM
680*13124SAlexander.Kolbasov@Sun.COM		# Prevent division by zero
681*13124SAlexander.Kolbasov@Sun.COM		$swtotal = 1 unless $swtotal;
682*13124SAlexander.Kolbasov@Sun.COM
683*13124SAlexander.Kolbasov@Sun.COM		$swload = ($user + $sys) * 100 / $swtotal;
684*13124SAlexander.Kolbasov@Sun.COM		$idle   = $idle * 100 / $swtotal;
685*13124SAlexander.Kolbasov@Sun.COM		$user   = $user * 100 / $swtotal;
686*13124SAlexander.Kolbasov@Sun.COM		$sys    = $sys  * 100 / $swtotal;
687*13124SAlexander.Kolbasov@Sun.COM	}
688*13124SAlexander.Kolbasov@Sun.COM
689*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ?
690*13124SAlexander.Kolbasov@Sun.COM		($utilization, $accuracy, $hw_utilization,
691*13124SAlexander.Kolbasov@Sun.COM		 $swload, $user, $sys, $idle) :
692*13124SAlexander.Kolbasov@Sun.COM		$utilization);
693*13124SAlexander.Kolbasov@Sun.COM}
694*13124SAlexander.Kolbasov@Sun.COM
695*13124SAlexander.Kolbasov@Sun.COM#
696*13124SAlexander.Kolbasov@Sun.COM# online_cpus(kstat)
697*13124SAlexander.Kolbasov@Sun.COM# Return list of on-line CPUs
698*13124SAlexander.Kolbasov@Sun.COM#
699*13124SAlexander.Kolbasov@Sun.COMsub online_cpus
700*13124SAlexander.Kolbasov@Sun.COM{
701*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 1 or _usage("online_cpus(cookie)");
702*13124SAlexander.Kolbasov@Sun.COM
703*13124SAlexander.Kolbasov@Sun.COM	my $self = shift or return;
704*13124SAlexander.Kolbasov@Sun.COM	my $ks = $self->{KSTAT} or return;
705*13124SAlexander.Kolbasov@Sun.COM
706*13124SAlexander.Kolbasov@Sun.COM	my $cpu_info = $ks->{cpu_info} or return;
707*13124SAlexander.Kolbasov@Sun.COM
708*13124SAlexander.Kolbasov@Sun.COM	my @cpus = grep {
709*13124SAlexander.Kolbasov@Sun.COM		my $cp = $cpu_info->{$_}->{"cpu_info$_"};
710*13124SAlexander.Kolbasov@Sun.COM		my $state = $cp->{state};
711*13124SAlexander.Kolbasov@Sun.COM		$state eq 'on-line' || $state eq 'no-intr';
712*13124SAlexander.Kolbasov@Sun.COM	} keys %{$cpu_info};
713*13124SAlexander.Kolbasov@Sun.COM
714*13124SAlexander.Kolbasov@Sun.COM	return (wantarray() ? @cpus : _nsort(@cpus));
715*13124SAlexander.Kolbasov@Sun.COM}
716*13124SAlexander.Kolbasov@Sun.COM
717*13124SAlexander.Kolbasov@Sun.COM#
718*13124SAlexander.Kolbasov@Sun.COM# Support methods
719*13124SAlexander.Kolbasov@Sun.COM#
720*13124SAlexander.Kolbasov@Sun.COM# The following methods are not PG specific but are generally useful for PG
721*13124SAlexander.Kolbasov@Sun.COM# interface consumers
722*13124SAlexander.Kolbasov@Sun.COM#
723*13124SAlexander.Kolbasov@Sun.COM
724*13124SAlexander.Kolbasov@Sun.COM#
725*13124SAlexander.Kolbasov@Sun.COM# Sort the list numerically
726*13124SAlexander.Kolbasov@Sun.COM#
727*13124SAlexander.Kolbasov@Sun.COMsub nsort
728*13124SAlexander.Kolbasov@Sun.COM{
729*13124SAlexander.Kolbasov@Sun.COM	scalar @_ > 0 or _usage("nsort(cookie, val, ...)");
730*13124SAlexander.Kolbasov@Sun.COM	shift;
731*13124SAlexander.Kolbasov@Sun.COM
732*13124SAlexander.Kolbasov@Sun.COM	return (_nsort(@_));
733*13124SAlexander.Kolbasov@Sun.COM}
734*13124SAlexander.Kolbasov@Sun.COM
735*13124SAlexander.Kolbasov@Sun.COM#
736*13124SAlexander.Kolbasov@Sun.COM# Return the input list with duplicates removed.
737*13124SAlexander.Kolbasov@Sun.COM# Should be used in list context
738*13124SAlexander.Kolbasov@Sun.COM#
739*13124SAlexander.Kolbasov@Sun.COMsub uniq
740*13124SAlexander.Kolbasov@Sun.COM{
741*13124SAlexander.Kolbasov@Sun.COM	scalar @_ > 0 or _usage("uniq(cookie, val, ...)");
742*13124SAlexander.Kolbasov@Sun.COM	shift;
743*13124SAlexander.Kolbasov@Sun.COM
744*13124SAlexander.Kolbasov@Sun.COM	return (_uniq(@_));
745*13124SAlexander.Kolbasov@Sun.COM}
746*13124SAlexander.Kolbasov@Sun.COM
747*13124SAlexander.Kolbasov@Sun.COM#
748*13124SAlexander.Kolbasov@Sun.COM# Sort list numerically and remove duplicates
749*13124SAlexander.Kolbasov@Sun.COM# Should be called in list context
750*13124SAlexander.Kolbasov@Sun.COM#
751*13124SAlexander.Kolbasov@Sun.COMsub uniqsort
752*13124SAlexander.Kolbasov@Sun.COM{
753*13124SAlexander.Kolbasov@Sun.COM	scalar @_ > 0 or _usage("uniqsort(cookie, val, ...)");
754*13124SAlexander.Kolbasov@Sun.COM	shift;
755*13124SAlexander.Kolbasov@Sun.COM
756*13124SAlexander.Kolbasov@Sun.COM	return (_uniqsort(@_));
757*13124SAlexander.Kolbasov@Sun.COM}
758*13124SAlexander.Kolbasov@Sun.COM
759*13124SAlexander.Kolbasov@Sun.COM
760*13124SAlexander.Kolbasov@Sun.COM#
761*13124SAlexander.Kolbasov@Sun.COM# Expand all arguments and present them as a numerically sorted list
762*13124SAlexander.Kolbasov@Sun.COM# x,y is expanded as (x y)
763*13124SAlexander.Kolbasov@Sun.COM# 1-3 ranges are expandes as (1 2 3)
764*13124SAlexander.Kolbasov@Sun.COM#
765*13124SAlexander.Kolbasov@Sun.COMsub expand
766*13124SAlexander.Kolbasov@Sun.COM{
767*13124SAlexander.Kolbasov@Sun.COM	scalar @_ > 0 or _usage("expand(cookie, val, ...)");
768*13124SAlexander.Kolbasov@Sun.COM	shift;
769*13124SAlexander.Kolbasov@Sun.COM
770*13124SAlexander.Kolbasov@Sun.COM	return (_uniqsort(map { _expand($_) } @_));
771*13124SAlexander.Kolbasov@Sun.COM}
772*13124SAlexander.Kolbasov@Sun.COM
773*13124SAlexander.Kolbasov@Sun.COM#
774*13124SAlexander.Kolbasov@Sun.COM# Consolidate consecutive ids as start-end
775*13124SAlexander.Kolbasov@Sun.COM# Input: list of ids
776*13124SAlexander.Kolbasov@Sun.COM# Output: string with space-sepated cpu values with ranges
777*13124SAlexander.Kolbasov@Sun.COM#   collapsed as x-y
778*13124SAlexander.Kolbasov@Sun.COM#
779*13124SAlexander.Kolbasov@Sun.COMsub id_collapse
780*13124SAlexander.Kolbasov@Sun.COM{
781*13124SAlexander.Kolbasov@Sun.COM	scalar @_ > 0 or _usage("collapse(cookie, val, ...)");
782*13124SAlexander.Kolbasov@Sun.COM	shift;
783*13124SAlexander.Kolbasov@Sun.COM
784*13124SAlexander.Kolbasov@Sun.COM	return _collapse(@_);
785*13124SAlexander.Kolbasov@Sun.COM}
786*13124SAlexander.Kolbasov@Sun.COM
787*13124SAlexander.Kolbasov@Sun.COM#
788*13124SAlexander.Kolbasov@Sun.COM# Return elements of the second list not present in the first list. Both lists
789*13124SAlexander.Kolbasov@Sun.COM# are passed by reference.
790*13124SAlexander.Kolbasov@Sun.COM#
791*13124SAlexander.Kolbasov@Sun.COMsub set_subtract
792*13124SAlexander.Kolbasov@Sun.COM{
793*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 3 or _usage("set_subtract(cookie, left, right)");
794*13124SAlexander.Kolbasov@Sun.COM	shift;
795*13124SAlexander.Kolbasov@Sun.COM
796*13124SAlexander.Kolbasov@Sun.COM	return (_set_subtract(@_));
797*13124SAlexander.Kolbasov@Sun.COM}
798*13124SAlexander.Kolbasov@Sun.COM
799*13124SAlexander.Kolbasov@Sun.COM#
800*13124SAlexander.Kolbasov@Sun.COM# Return the intersection of two lists passed by reference
801*13124SAlexander.Kolbasov@Sun.COM# Convert the first list to a hash with seen entries marked as 1-values
802*13124SAlexander.Kolbasov@Sun.COM# Then grep only elements present in the first list from the second list.
803*13124SAlexander.Kolbasov@Sun.COM# As a little optimization, use the shorter list to build a hash.
804*13124SAlexander.Kolbasov@Sun.COM#
805*13124SAlexander.Kolbasov@Sun.COMsub intersect
806*13124SAlexander.Kolbasov@Sun.COM{
807*13124SAlexander.Kolbasov@Sun.COM	scalar @_ == 3 or _usage("intersect(cookie, left, right)");
808*13124SAlexander.Kolbasov@Sun.COM	shift;
809*13124SAlexander.Kolbasov@Sun.COM
810*13124SAlexander.Kolbasov@Sun.COM	return (_set_intersect(@_));
811*13124SAlexander.Kolbasov@Sun.COM}
812*13124SAlexander.Kolbasov@Sun.COM
813*13124SAlexander.Kolbasov@Sun.COM#
814*13124SAlexander.Kolbasov@Sun.COM# Return elements of the second list not present in the first list. Both lists
815*13124SAlexander.Kolbasov@Sun.COM# are passed by reference.
816*13124SAlexander.Kolbasov@Sun.COM#
817*13124SAlexander.Kolbasov@Sun.COMsub _set_subtract
818*13124SAlexander.Kolbasov@Sun.COM{
819*13124SAlexander.Kolbasov@Sun.COM	my ($left, $right) = @_;
820*13124SAlexander.Kolbasov@Sun.COM	my %seen;	# Set to 1 for everything in the first list
821*13124SAlexander.Kolbasov@Sun.COM	# Create a hash indexed by elements in @left with ones as a value.
822*13124SAlexander.Kolbasov@Sun.COM	map { $seen{$_} = 1 } @$left;
823*13124SAlexander.Kolbasov@Sun.COM	# Find members of @right present in @left
824*13124SAlexander.Kolbasov@Sun.COM	return (grep { ! $seen{$_} } @$right);
825*13124SAlexander.Kolbasov@Sun.COM}
826*13124SAlexander.Kolbasov@Sun.COM
827*13124SAlexander.Kolbasov@Sun.COM#
828*13124SAlexander.Kolbasov@Sun.COM# END OF PUBLIC INTERFACE
829*13124SAlexander.Kolbasov@Sun.COM#
830*13124SAlexander.Kolbasov@Sun.COM
831*13124SAlexander.Kolbasov@Sun.COM#
832*13124SAlexander.Kolbasov@Sun.COM# INTERNAL FUNCTIONS
833*13124SAlexander.Kolbasov@Sun.COM#
834*13124SAlexander.Kolbasov@Sun.COM
835*13124SAlexander.Kolbasov@Sun.COM#
836*13124SAlexander.Kolbasov@Sun.COM# _usage(): print error message and terminate the program.
837*13124SAlexander.Kolbasov@Sun.COM#
838*13124SAlexander.Kolbasov@Sun.COMsub _usage
839*13124SAlexander.Kolbasov@Sun.COM{
840*13124SAlexander.Kolbasov@Sun.COM	my $msg = shift;
841*13124SAlexander.Kolbasov@Sun.COM	Carp::croak "Usage: Sun::Solaris::Pg::$msg";
842*13124SAlexander.Kolbasov@Sun.COM}
843*13124SAlexander.Kolbasov@Sun.COM
844*13124SAlexander.Kolbasov@Sun.COM#
845*13124SAlexander.Kolbasov@Sun.COM# Sort the list numerically
846*13124SAlexander.Kolbasov@Sun.COM# Should be called in list context
847*13124SAlexander.Kolbasov@Sun.COM#
848*13124SAlexander.Kolbasov@Sun.COMsub _nsort
849*13124SAlexander.Kolbasov@Sun.COM{
850*13124SAlexander.Kolbasov@Sun.COM	return (sort { $a <=> $b } @_);
851*13124SAlexander.Kolbasov@Sun.COM}
852*13124SAlexander.Kolbasov@Sun.COM
853*13124SAlexander.Kolbasov@Sun.COM#
854*13124SAlexander.Kolbasov@Sun.COM# Return the input list with duplicates removed.
855*13124SAlexander.Kolbasov@Sun.COM# Should be used in list context
856*13124SAlexander.Kolbasov@Sun.COM#
857*13124SAlexander.Kolbasov@Sun.COMsub _uniq
858*13124SAlexander.Kolbasov@Sun.COM{
859*13124SAlexander.Kolbasov@Sun.COM	my %seen;
860*13124SAlexander.Kolbasov@Sun.COM	return (grep { ++$seen{$_} == 1 } @_);
861*13124SAlexander.Kolbasov@Sun.COM}
862*13124SAlexander.Kolbasov@Sun.COM
863*13124SAlexander.Kolbasov@Sun.COM#
864*13124SAlexander.Kolbasov@Sun.COM# Sort list numerically and remove duplicates
865*13124SAlexander.Kolbasov@Sun.COM# Should be called in list context
866*13124SAlexander.Kolbasov@Sun.COM#
867*13124SAlexander.Kolbasov@Sun.COMsub _uniqsort
868*13124SAlexander.Kolbasov@Sun.COM{
869*13124SAlexander.Kolbasov@Sun.COM	return (sort { $a <=> $b } _uniq(@_));
870*13124SAlexander.Kolbasov@Sun.COM}
871*13124SAlexander.Kolbasov@Sun.COM
872*13124SAlexander.Kolbasov@Sun.COM# Get PG from the snapshot by id
873*13124SAlexander.Kolbasov@Sun.COMsub _pg_get
874*13124SAlexander.Kolbasov@Sun.COM{
875*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
876*13124SAlexander.Kolbasov@Sun.COM	my $pgid = shift;
877*13124SAlexander.Kolbasov@Sun.COM
878*13124SAlexander.Kolbasov@Sun.COM	return unless defined $pgid;
879*13124SAlexander.Kolbasov@Sun.COM	my $pgtree = $self->{PGTREE} or return;
880*13124SAlexander.Kolbasov@Sun.COM
881*13124SAlexander.Kolbasov@Sun.COM	return ($pgtree->{$pgid});
882*13124SAlexander.Kolbasov@Sun.COM}
883*13124SAlexander.Kolbasov@Sun.COM
884*13124SAlexander.Kolbasov@Sun.COM#
885*13124SAlexander.Kolbasov@Sun.COM# Copy data from kstat representation to our representation
886*13124SAlexander.Kolbasov@Sun.COM# Arguments:
887*13124SAlexander.Kolbasov@Sun.COM#   PG kstat
888*13124SAlexander.Kolbasov@Sun.COM#   Reference to the list of CPUs.
889*13124SAlexander.Kolbasov@Sun.COM# Any CPUs in the PG kstat not present in the CPU list are ignored.
890*13124SAlexander.Kolbasov@Sun.COM#
891*13124SAlexander.Kolbasov@Sun.COMsub _pg_create_from_kstat
892*13124SAlexander.Kolbasov@Sun.COM{
893*13124SAlexander.Kolbasov@Sun.COM	my $pg_ks = shift;
894*13124SAlexander.Kolbasov@Sun.COM	my $all_cpus = shift;
895*13124SAlexander.Kolbasov@Sun.COM	my %all_cpus;
896*13124SAlexander.Kolbasov@Sun.COM	my $pg = ();
897*13124SAlexander.Kolbasov@Sun.COM
898*13124SAlexander.Kolbasov@Sun.COM	#
899*13124SAlexander.Kolbasov@Sun.COM	# Mark CPUs available
900*13124SAlexander.Kolbasov@Sun.COM	#
901*13124SAlexander.Kolbasov@Sun.COM	map { $all_cpus{$_}++ } @$all_cpus;
902*13124SAlexander.Kolbasov@Sun.COM
903*13124SAlexander.Kolbasov@Sun.COM	return unless $pg_ks;
904*13124SAlexander.Kolbasov@Sun.COM
905*13124SAlexander.Kolbasov@Sun.COM	#
906*13124SAlexander.Kolbasov@Sun.COM	# Convert CPU list in the kstat from x-y,z form to the proper list
907*13124SAlexander.Kolbasov@Sun.COM	#
908*13124SAlexander.Kolbasov@Sun.COM	my @cpus = _expand($pg_ks->{cpus});
909*13124SAlexander.Kolbasov@Sun.COM
910*13124SAlexander.Kolbasov@Sun.COM	#
911*13124SAlexander.Kolbasov@Sun.COM	# Remove any CPUs not present in the arguments
912*13124SAlexander.Kolbasov@Sun.COM	#
913*13124SAlexander.Kolbasov@Sun.COM	@cpus = grep { $all_cpus{$_} } @cpus;
914*13124SAlexander.Kolbasov@Sun.COM
915*13124SAlexander.Kolbasov@Sun.COM	#
916*13124SAlexander.Kolbasov@Sun.COM	# Do not create PG unless it has any CPUs
917*13124SAlexander.Kolbasov@Sun.COM	#
918*13124SAlexander.Kolbasov@Sun.COM	return unless scalar @cpus;
919*13124SAlexander.Kolbasov@Sun.COM
920*13124SAlexander.Kolbasov@Sun.COM	#
921*13124SAlexander.Kolbasov@Sun.COM	# Copy data to the $pg structure
922*13124SAlexander.Kolbasov@Sun.COM	#
923*13124SAlexander.Kolbasov@Sun.COM	$pg->{ncpus} = scalar @cpus;
924*13124SAlexander.Kolbasov@Sun.COM	$pg->{cpus} = \@cpus;
925*13124SAlexander.Kolbasov@Sun.COM	$pg->{id} = defined($pg_ks->{pg_id}) ? $pg_ks->{pg_id} : $pg_ks->{id};
926*13124SAlexander.Kolbasov@Sun.COM	$pg->{util} = $pg_ks->{hw_util};
927*13124SAlexander.Kolbasov@Sun.COM	$pg->{current_rate} = $pg_ks->{hw_util_rate};
928*13124SAlexander.Kolbasov@Sun.COM	$pg->{util_rate_max} = $pg_ks->{hw_util_rate_max};
929*13124SAlexander.Kolbasov@Sun.COM	$pg->{util_time_running} = $pg_ks->{hw_util_time_running};
930*13124SAlexander.Kolbasov@Sun.COM	$pg->{util_time_stopped} = $pg_ks->{hw_util_time_stopped};
931*13124SAlexander.Kolbasov@Sun.COM	$pg->{snaptime} = $pg_ks->{snaptime};
932*13124SAlexander.Kolbasov@Sun.COM	$pg->{generation} = $pg_ks->{generation};
933*13124SAlexander.Kolbasov@Sun.COM	$pg->{sh_name} = $pg_ks->{relationship} || $pg_ks->{sharing_relation};
934*13124SAlexander.Kolbasov@Sun.COM	$pg->{parent} = $pg_ks->{parent_pg_id};
935*13124SAlexander.Kolbasov@Sun.COM	$pg->{parent} = PG_PARENT_UNDEF unless defined $pg->{parent};
936*13124SAlexander.Kolbasov@Sun.COM	#
937*13124SAlexander.Kolbasov@Sun.COM	# Replace spaces with underscores in sharing names
938*13124SAlexander.Kolbasov@Sun.COM	#
939*13124SAlexander.Kolbasov@Sun.COM	$pg->{sh_name} =~ s/ /_/g;
940*13124SAlexander.Kolbasov@Sun.COM	$pg->{is_leaf} = 1;
941*13124SAlexander.Kolbasov@Sun.COM
942*13124SAlexander.Kolbasov@Sun.COM	return $pg;
943*13124SAlexander.Kolbasov@Sun.COM}
944*13124SAlexander.Kolbasov@Sun.COM
945*13124SAlexander.Kolbasov@Sun.COM#
946*13124SAlexander.Kolbasov@Sun.COM# Create fake root PG with all CPUs
947*13124SAlexander.Kolbasov@Sun.COM# Arguments: list of CPUs
948*13124SAlexander.Kolbasov@Sun.COM#
949*13124SAlexander.Kolbasov@Sun.COMsub _pg_create_root
950*13124SAlexander.Kolbasov@Sun.COM{
951*13124SAlexander.Kolbasov@Sun.COM	my $pg = ();
952*13124SAlexander.Kolbasov@Sun.COM	my @cpus = @_;
953*13124SAlexander.Kolbasov@Sun.COM
954*13124SAlexander.Kolbasov@Sun.COM	$pg->{id} = $ROOT_ID;
955*13124SAlexander.Kolbasov@Sun.COM	$pg->{ncpus} = scalar @cpus;
956*13124SAlexander.Kolbasov@Sun.COM	$pg->{util} = 0;
957*13124SAlexander.Kolbasov@Sun.COM	$pg->{current_rate} = 0;
958*13124SAlexander.Kolbasov@Sun.COM	$pg->{util_rate_max} = 0;
959*13124SAlexander.Kolbasov@Sun.COM	$pg->{util_time_running} = 0;
960*13124SAlexander.Kolbasov@Sun.COM	$pg->{util_time_stopped} = 0;
961*13124SAlexander.Kolbasov@Sun.COM	$pg->{snaptime} = 0;
962*13124SAlexander.Kolbasov@Sun.COM	$pg->{generation} = 0;
963*13124SAlexander.Kolbasov@Sun.COM	$pg->{sh_name} = 'System';
964*13124SAlexander.Kolbasov@Sun.COM	$pg->{is_leaf} = 0;
965*13124SAlexander.Kolbasov@Sun.COM	$pg->{cpus} = \@cpus;
966*13124SAlexander.Kolbasov@Sun.COM	$pg->{parent} = PG_NO_PARENT;
967*13124SAlexander.Kolbasov@Sun.COM
968*13124SAlexander.Kolbasov@Sun.COM	return ($pg);
969*13124SAlexander.Kolbasov@Sun.COM}
970*13124SAlexander.Kolbasov@Sun.COM
971*13124SAlexander.Kolbasov@Sun.COM#
972*13124SAlexander.Kolbasov@Sun.COM# _pg_all_from_kstats(SNAPSHOT)
973*13124SAlexander.Kolbasov@Sun.COM# Extract all PG information from kstats
974*13124SAlexander.Kolbasov@Sun.COM#
975*13124SAlexander.Kolbasov@Sun.COMsub _pg_all_from_kstats
976*13124SAlexander.Kolbasov@Sun.COM{
977*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
978*13124SAlexander.Kolbasov@Sun.COM	my $ks = $self->{KSTAT};
979*13124SAlexander.Kolbasov@Sun.COM	my @all_cpus = @{$self->{CPUS}};
980*13124SAlexander.Kolbasov@Sun.COM
981*13124SAlexander.Kolbasov@Sun.COM	return unless $ks;
982*13124SAlexander.Kolbasov@Sun.COM
983*13124SAlexander.Kolbasov@Sun.COM	my $pgtree = ();
984*13124SAlexander.Kolbasov@Sun.COM	my $pg_info = $ks->{$self->{PG_MODULE}};
985*13124SAlexander.Kolbasov@Sun.COM
986*13124SAlexander.Kolbasov@Sun.COM	#
987*13124SAlexander.Kolbasov@Sun.COM	# Walk all PG kstats and copy them to $pgtree->{$id}
988*13124SAlexander.Kolbasov@Sun.COM	#
989*13124SAlexander.Kolbasov@Sun.COM	foreach my $id (keys %$pg_info) {
990*13124SAlexander.Kolbasov@Sun.COM		my $pg_ks = _kstat_get_pg($pg_info, $id,
991*13124SAlexander.Kolbasov@Sun.COM					  $self->{USE_OLD_KSTATS});
992*13124SAlexander.Kolbasov@Sun.COM		next unless $pg_ks;
993*13124SAlexander.Kolbasov@Sun.COM
994*13124SAlexander.Kolbasov@Sun.COM		my $pg = _pg_create_from_kstat($pg_ks, \@all_cpus);
995*13124SAlexander.Kolbasov@Sun.COM
996*13124SAlexander.Kolbasov@Sun.COM		$pgtree->{$id} = $pg if $pg;
997*13124SAlexander.Kolbasov@Sun.COM	}
998*13124SAlexander.Kolbasov@Sun.COM
999*13124SAlexander.Kolbasov@Sun.COM	#
1000*13124SAlexander.Kolbasov@Sun.COM	# OS does not have root PG, so create one.
1001*13124SAlexander.Kolbasov@Sun.COM	#
1002*13124SAlexander.Kolbasov@Sun.COM	if (!$pgtree->{$ROOT_ID}) {
1003*13124SAlexander.Kolbasov@Sun.COM		$pgtree->{$ROOT_ID} = _pg_create_root (@all_cpus);
1004*13124SAlexander.Kolbasov@Sun.COM	}
1005*13124SAlexander.Kolbasov@Sun.COM
1006*13124SAlexander.Kolbasov@Sun.COM	#
1007*13124SAlexander.Kolbasov@Sun.COM	# Construct parent-child relationships between PGs
1008*13124SAlexander.Kolbasov@Sun.COM	#
1009*13124SAlexander.Kolbasov@Sun.COM
1010*13124SAlexander.Kolbasov@Sun.COM	#
1011*13124SAlexander.Kolbasov@Sun.COM	# Get list of PGs sorted by number of CPUs
1012*13124SAlexander.Kolbasov@Sun.COM	# If two PGs have the same number of CPUs, sort by relationship order.
1013*13124SAlexander.Kolbasov@Sun.COM	#
1014*13124SAlexander.Kolbasov@Sun.COM	my @lineage = sort {
1015*13124SAlexander.Kolbasov@Sun.COM		$a->{ncpus} <=> $b->{ncpus} ||
1016*13124SAlexander.Kolbasov@Sun.COM		_relationship_order($a->{sh_name}) <=>
1017*13124SAlexander.Kolbasov@Sun.COM		_relationship_order($b->{sh_name})
1018*13124SAlexander.Kolbasov@Sun.COM	    } values %$pgtree;
1019*13124SAlexander.Kolbasov@Sun.COM
1020*13124SAlexander.Kolbasov@Sun.COM	#
1021*13124SAlexander.Kolbasov@Sun.COM	# For each PG in the lineage discover its parent if it doesn't have one.
1022*13124SAlexander.Kolbasov@Sun.COM	#
1023*13124SAlexander.Kolbasov@Sun.COM	for (my $i = 0; $i < scalar @lineage; $i++) {
1024*13124SAlexander.Kolbasov@Sun.COM		my $pg = $lineage[$i];
1025*13124SAlexander.Kolbasov@Sun.COM
1026*13124SAlexander.Kolbasov@Sun.COM		#
1027*13124SAlexander.Kolbasov@Sun.COM		# Ignore PGs which already have parent in kstats
1028*13124SAlexander.Kolbasov@Sun.COM		#
1029*13124SAlexander.Kolbasov@Sun.COM		my $parent = $pg->{parent};
1030*13124SAlexander.Kolbasov@Sun.COM		next if ($parent >= PG_NO_PARENT);
1031*13124SAlexander.Kolbasov@Sun.COM
1032*13124SAlexander.Kolbasov@Sun.COM		my $ncpus = $pg->{ncpus};
1033*13124SAlexander.Kolbasov@Sun.COM		my @cpus = @{$pg->{cpus}};
1034*13124SAlexander.Kolbasov@Sun.COM
1035*13124SAlexander.Kolbasov@Sun.COM		#
1036*13124SAlexander.Kolbasov@Sun.COM		# Walk the lineage, ignoring any CPUs with the same number of
1037*13124SAlexander.Kolbasov@Sun.COM		# CPUs
1038*13124SAlexander.Kolbasov@Sun.COM		for (my $j = $i + 1; $j < scalar @lineage; $j++) {
1039*13124SAlexander.Kolbasov@Sun.COM			my $pg1 = $lineage[$j];
1040*13124SAlexander.Kolbasov@Sun.COM			my @parent_cpus = @{$pg1->{cpus}};
1041*13124SAlexander.Kolbasov@Sun.COM			if (_is_subset(\@cpus, \@parent_cpus)) {
1042*13124SAlexander.Kolbasov@Sun.COM				$pg->{parent} = $pg1->{id};
1043*13124SAlexander.Kolbasov@Sun.COM				last;
1044*13124SAlexander.Kolbasov@Sun.COM			}
1045*13124SAlexander.Kolbasov@Sun.COM		}
1046*13124SAlexander.Kolbasov@Sun.COM	}
1047*13124SAlexander.Kolbasov@Sun.COM
1048*13124SAlexander.Kolbasov@Sun.COM	#
1049*13124SAlexander.Kolbasov@Sun.COM	# Find all top-level PGs and put them under $root
1050*13124SAlexander.Kolbasov@Sun.COM	#
1051*13124SAlexander.Kolbasov@Sun.COM	foreach my $pgid (keys %$pgtree) {
1052*13124SAlexander.Kolbasov@Sun.COM		next if $pgid == $ROOT_ID;
1053*13124SAlexander.Kolbasov@Sun.COM		my $pg = $pgtree->{$pgid};
1054*13124SAlexander.Kolbasov@Sun.COM		$pg->{parent} = $ROOT_ID unless $pg->{parent} >= 0;
1055*13124SAlexander.Kolbasov@Sun.COM	}
1056*13124SAlexander.Kolbasov@Sun.COM
1057*13124SAlexander.Kolbasov@Sun.COM	#
1058*13124SAlexander.Kolbasov@Sun.COM	# Now that we know parents, for each parent add all direct children to
1059*13124SAlexander.Kolbasov@Sun.COM	# their parent sets
1060*13124SAlexander.Kolbasov@Sun.COM	#
1061*13124SAlexander.Kolbasov@Sun.COM	foreach my $pg (@lineage) {
1062*13124SAlexander.Kolbasov@Sun.COM		my $parentid = $pg->{parent};
1063*13124SAlexander.Kolbasov@Sun.COM		next unless defined $parentid;
1064*13124SAlexander.Kolbasov@Sun.COM
1065*13124SAlexander.Kolbasov@Sun.COM		my $parent = $pgtree->{$parentid};
1066*13124SAlexander.Kolbasov@Sun.COM		push (@{$parent->{children}}, $pg->{id});
1067*13124SAlexander.Kolbasov@Sun.COM	}
1068*13124SAlexander.Kolbasov@Sun.COM
1069*13124SAlexander.Kolbasov@Sun.COM	return ($pgtree);
1070*13124SAlexander.Kolbasov@Sun.COM}
1071*13124SAlexander.Kolbasov@Sun.COM
1072*13124SAlexander.Kolbasov@Sun.COM#
1073*13124SAlexander.Kolbasov@Sun.COM# Read kstats and initialize PG object
1074*13124SAlexander.Kolbasov@Sun.COM# Collect basic information about cmt_pg
1075*13124SAlexander.Kolbasov@Sun.COM# Add list of children and list of CPUs
1076*13124SAlexander.Kolbasov@Sun.COM# Returns the hash reference indexed by pg id
1077*13124SAlexander.Kolbasov@Sun.COM#
1078*13124SAlexander.Kolbasov@Sun.COM# The _init() function accepts arguments in the form of a hash. The following
1079*13124SAlexander.Kolbasov@Sun.COM# subarguments are supported:
1080*13124SAlexander.Kolbasov@Sun.COM#
1081*13124SAlexander.Kolbasov@Sun.COM#   -cpudata	# Collect per-CPU data from kstats if this is T
1082*13124SAlexander.Kolbasov@Sun.COM#   -tags	# Match PGs to physical relationships if this is T
1083*13124SAlexander.Kolbasov@Sun.COM#   -swload	# Collect software CPU load if this is T
1084*13124SAlexander.Kolbasov@Sun.COM
1085*13124SAlexander.Kolbasov@Sun.COMsub _init
1086*13124SAlexander.Kolbasov@Sun.COM{
1087*13124SAlexander.Kolbasov@Sun.COM	my $ks = Sun::Solaris::Kstat->new(strip_strings => 1);
1088*13124SAlexander.Kolbasov@Sun.COM	return unless $ks;
1089*13124SAlexander.Kolbasov@Sun.COM
1090*13124SAlexander.Kolbasov@Sun.COM	my %args = @_;
1091*13124SAlexander.Kolbasov@Sun.COM	my $get_cpu_data = $args{-cpudata};
1092*13124SAlexander.Kolbasov@Sun.COM	my $get_tags = $args{-tags};
1093*13124SAlexander.Kolbasov@Sun.COM	my $get_swload = $args{-swload};
1094*13124SAlexander.Kolbasov@Sun.COM
1095*13124SAlexander.Kolbasov@Sun.COM	my $self;
1096*13124SAlexander.Kolbasov@Sun.COM
1097*13124SAlexander.Kolbasov@Sun.COM	my $use_old_kstat_names = scalar(grep {/^pg_hw_perf/ } keys (%$ks)) == 0;
1098*13124SAlexander.Kolbasov@Sun.COM
1099*13124SAlexander.Kolbasov@Sun.COM	my @frequencies;
1100*13124SAlexander.Kolbasov@Sun.COM	$self->{MAX_FREQUENCY} = 0;
1101*13124SAlexander.Kolbasov@Sun.COM
1102*13124SAlexander.Kolbasov@Sun.COM	$self->{PG_MODULE} = $use_old_kstat_names ? 'pg' : 'pg_hw_perf';
1103*13124SAlexander.Kolbasov@Sun.COM	$self->{PG_CPU_MODULE} =  $use_old_kstat_names ?
1104*13124SAlexander.Kolbasov@Sun.COM	  'pg_cpu' : 'pg_hw_perf_cpu';
1105*13124SAlexander.Kolbasov@Sun.COM	$self->{USE_OLD_KSTATS} = $use_old_kstat_names;
1106*13124SAlexander.Kolbasov@Sun.COM
1107*13124SAlexander.Kolbasov@Sun.COM	$get_cpu_data = 0 unless  scalar(grep {/^$self->{PG_CPU_MODULE}/ }
1108*13124SAlexander.Kolbasov@Sun.COM					 keys (%$ks));
1109*13124SAlexander.Kolbasov@Sun.COM
1110*13124SAlexander.Kolbasov@Sun.COM	# Get list of PG-related kstats
1111*13124SAlexander.Kolbasov@Sun.COM	my $pg_keys = $use_old_kstat_names ? 'pg' : 'pg_hw';
1112*13124SAlexander.Kolbasov@Sun.COM
1113*13124SAlexander.Kolbasov@Sun.COM	if (scalar(grep { /^$pg_keys/ } keys (%$ks)) == 0) {
1114*13124SAlexander.Kolbasov@Sun.COM		if (exists(&Errno::ENOTSUPP)) {
1115*13124SAlexander.Kolbasov@Sun.COM			$! = &Errno::ENOTSUPP;
1116*13124SAlexander.Kolbasov@Sun.COM		} else {
1117*13124SAlexander.Kolbasov@Sun.COM			$! = 48;
1118*13124SAlexander.Kolbasov@Sun.COM		}
1119*13124SAlexander.Kolbasov@Sun.COM		return;
1120*13124SAlexander.Kolbasov@Sun.COM	}
1121*13124SAlexander.Kolbasov@Sun.COM
1122*13124SAlexander.Kolbasov@Sun.COM
1123*13124SAlexander.Kolbasov@Sun.COM	#
1124*13124SAlexander.Kolbasov@Sun.COM	# Mapping of cores and chips to CPUs
1125*13124SAlexander.Kolbasov@Sun.COM	#
1126*13124SAlexander.Kolbasov@Sun.COM	my $hw_mapping;
1127*13124SAlexander.Kolbasov@Sun.COM
1128*13124SAlexander.Kolbasov@Sun.COM	#
1129*13124SAlexander.Kolbasov@Sun.COM	# Get list of all CPUs
1130*13124SAlexander.Kolbasov@Sun.COM	#
1131*13124SAlexander.Kolbasov@Sun.COM	my $cpu_info = $ks->{cpu_info};
1132*13124SAlexander.Kolbasov@Sun.COM
1133*13124SAlexander.Kolbasov@Sun.COM	#
1134*13124SAlexander.Kolbasov@Sun.COM	# @all-cpus is a list of all cpus
1135*13124SAlexander.Kolbasov@Sun.COM	#
1136*13124SAlexander.Kolbasov@Sun.COM	my @all_cpus = keys %$cpu_info;
1137*13124SAlexander.Kolbasov@Sun.COM
1138*13124SAlexander.Kolbasov@Sun.COM	#
1139*13124SAlexander.Kolbasov@Sun.COM	# Save list of all CPUs in the snapshot
1140*13124SAlexander.Kolbasov@Sun.COM	#
1141*13124SAlexander.Kolbasov@Sun.COM	$self->{CPUS} = \@all_cpus;
1142*13124SAlexander.Kolbasov@Sun.COM
1143*13124SAlexander.Kolbasov@Sun.COM	#
1144*13124SAlexander.Kolbasov@Sun.COM	# Find CPUs for each socket and chip
1145*13124SAlexander.Kolbasov@Sun.COM	# Also while we scan CPU kstats, get maximum frequency of each CPU.
1146*13124SAlexander.Kolbasov@Sun.COM	#
1147*13124SAlexander.Kolbasov@Sun.COM	foreach my $id (@all_cpus) {
1148*13124SAlexander.Kolbasov@Sun.COM		my $ci = $cpu_info->{$id}->{"cpu_info$id"};
1149*13124SAlexander.Kolbasov@Sun.COM		next unless $ci;
1150*13124SAlexander.Kolbasov@Sun.COM		my $core_id = $ci->{core_id};
1151*13124SAlexander.Kolbasov@Sun.COM		my $chip_id = $ci->{chip_id};
1152*13124SAlexander.Kolbasov@Sun.COM
1153*13124SAlexander.Kolbasov@Sun.COM		push(@{$hw_mapping->{core}->{$core_id}}, $id)
1154*13124SAlexander.Kolbasov@Sun.COM		  if defined $core_id;
1155*13124SAlexander.Kolbasov@Sun.COM		push(@{$hw_mapping->{chip}->{$chip_id}}, $id)
1156*13124SAlexander.Kolbasov@Sun.COM		  if defined $chip_id;
1157*13124SAlexander.Kolbasov@Sun.COM
1158*13124SAlexander.Kolbasov@Sun.COM		# Read CPU frequencies separated by commas
1159*13124SAlexander.Kolbasov@Sun.COM		my $freqs = $ci->{supported_frequencies_Hz};
1160*13124SAlexander.Kolbasov@Sun.COM		my $max_freq = max(split(/:/, $freqs));
1161*13124SAlexander.Kolbasov@Sun.COM
1162*13124SAlexander.Kolbasov@Sun.COM		# Calculate maximum frequency for the snapshot.
1163*13124SAlexander.Kolbasov@Sun.COM		$self->{MAX_FREQUENCY} = $max_freq if
1164*13124SAlexander.Kolbasov@Sun.COM		  $self->{MAX_FREQUENCY} < $max_freq;
1165*13124SAlexander.Kolbasov@Sun.COM	}
1166*13124SAlexander.Kolbasov@Sun.COM
1167*13124SAlexander.Kolbasov@Sun.COM	$self->{KSTAT} = $ks;
1168*13124SAlexander.Kolbasov@Sun.COM
1169*13124SAlexander.Kolbasov@Sun.COM	#
1170*13124SAlexander.Kolbasov@Sun.COM	# Convert kstats to PG tree
1171*13124SAlexander.Kolbasov@Sun.COM	#
1172*13124SAlexander.Kolbasov@Sun.COM	my $pgtree = _pg_all_from_kstats($self);
1173*13124SAlexander.Kolbasov@Sun.COM	$self->{PGTREE} = $pgtree;
1174*13124SAlexander.Kolbasov@Sun.COM
1175*13124SAlexander.Kolbasov@Sun.COM	#
1176*13124SAlexander.Kolbasov@Sun.COM	# Find capacity estimate per sharing relationship
1177*13124SAlexander.Kolbasov@Sun.COM	#
1178*13124SAlexander.Kolbasov@Sun.COM	foreach my $pgid (keys %$pgtree) {
1179*13124SAlexander.Kolbasov@Sun.COM		my $pg = $pgtree->{$pgid};
1180*13124SAlexander.Kolbasov@Sun.COM		my $shname = $pg->{sh_name};
1181*13124SAlexander.Kolbasov@Sun.COM		my $max_rate = $pg->{util_rate_max};
1182*13124SAlexander.Kolbasov@Sun.COM		$self->{CAPACITY}->{$shname} = $max_rate if
1183*13124SAlexander.Kolbasov@Sun.COM		  !$self->{CAPACITY}->{$shname} ||
1184*13124SAlexander.Kolbasov@Sun.COM		    $self->{CAPACITY}->{$shname} < $max_rate;
1185*13124SAlexander.Kolbasov@Sun.COM	}
1186*13124SAlexander.Kolbasov@Sun.COM
1187*13124SAlexander.Kolbasov@Sun.COM	if ($get_tags) {
1188*13124SAlexander.Kolbasov@Sun.COM		#
1189*13124SAlexander.Kolbasov@Sun.COM		# Walk all PGs and mark all PGs that have corresponding hardware
1190*13124SAlexander.Kolbasov@Sun.COM		# entities (system, chips, cores).
1191*13124SAlexander.Kolbasov@Sun.COM		#
1192*13124SAlexander.Kolbasov@Sun.COM		foreach my $pgid (keys %$pgtree) {
1193*13124SAlexander.Kolbasov@Sun.COM			my $pg = $pgtree->{$pgid};
1194*13124SAlexander.Kolbasov@Sun.COM			my @cpus = @{$pg->{cpus}};
1195*13124SAlexander.Kolbasov@Sun.COM			next unless scalar @cpus > 1;
1196*13124SAlexander.Kolbasov@Sun.COM
1197*13124SAlexander.Kolbasov@Sun.COM			if (_set_equal (\@cpus, \@all_cpus)) {
1198*13124SAlexander.Kolbasov@Sun.COM				#
1199*13124SAlexander.Kolbasov@Sun.COM				# PG has all CPUs in the system.
1200*13124SAlexander.Kolbasov@Sun.COM				#
1201*13124SAlexander.Kolbasov@Sun.COM				push (@{$pg->{tags}}, 'system');
1202*13124SAlexander.Kolbasov@Sun.COM			}
1203*13124SAlexander.Kolbasov@Sun.COM
1204*13124SAlexander.Kolbasov@Sun.COM			foreach my $name ('core', 'chip') {
1205*13124SAlexander.Kolbasov@Sun.COM				my $hwdata = $hw_mapping->{$name};
1206*13124SAlexander.Kolbasov@Sun.COM				foreach my $id (keys %$hwdata) {
1207*13124SAlexander.Kolbasov@Sun.COM					# CPUs for this entity
1208*13124SAlexander.Kolbasov@Sun.COM					my @hw_cpus = @{$hwdata->{$id}};
1209*13124SAlexander.Kolbasov@Sun.COM					if (_set_equal (\@cpus, \@hw_cpus)) {
1210*13124SAlexander.Kolbasov@Sun.COM						#
1211*13124SAlexander.Kolbasov@Sun.COM						# PG has exactly the same CPUs
1212*13124SAlexander.Kolbasov@Sun.COM						#
1213*13124SAlexander.Kolbasov@Sun.COM						push (@{$pg->{tags}}, $name);
1214*13124SAlexander.Kolbasov@Sun.COM					}
1215*13124SAlexander.Kolbasov@Sun.COM				}
1216*13124SAlexander.Kolbasov@Sun.COM			}
1217*13124SAlexander.Kolbasov@Sun.COM		}
1218*13124SAlexander.Kolbasov@Sun.COM	}
1219*13124SAlexander.Kolbasov@Sun.COM
1220*13124SAlexander.Kolbasov@Sun.COM	#
1221*13124SAlexander.Kolbasov@Sun.COM	# Save software load for each CPU
1222*13124SAlexander.Kolbasov@Sun.COM	#
1223*13124SAlexander.Kolbasov@Sun.COM	if ($get_swload) {
1224*13124SAlexander.Kolbasov@Sun.COM		$self->{CPU_LOAD} = _get_sw_cpu_load($ks);
1225*13124SAlexander.Kolbasov@Sun.COM	}
1226*13124SAlexander.Kolbasov@Sun.COM
1227*13124SAlexander.Kolbasov@Sun.COM	#
1228*13124SAlexander.Kolbasov@Sun.COM	# Collect per-CPU utilization data if requested
1229*13124SAlexander.Kolbasov@Sun.COM	#
1230*13124SAlexander.Kolbasov@Sun.COM	if ($get_cpu_data) {
1231*13124SAlexander.Kolbasov@Sun.COM		_get_hw_cpu_load($self);
1232*13124SAlexander.Kolbasov@Sun.COM	}
1233*13124SAlexander.Kolbasov@Sun.COM
1234*13124SAlexander.Kolbasov@Sun.COM	$self->{GET_CPU_DATA} = $get_cpu_data;
1235*13124SAlexander.Kolbasov@Sun.COM
1236*13124SAlexander.Kolbasov@Sun.COM	#
1237*13124SAlexander.Kolbasov@Sun.COM	# Verify that in the end we have the same PG generation for each PG
1238*13124SAlexander.Kolbasov@Sun.COM	#
1239*13124SAlexander.Kolbasov@Sun.COM	if (! _same_generation($self)) {
1240*13124SAlexander.Kolbasov@Sun.COM		$! = &Errno::EAGAIN;
1241*13124SAlexander.Kolbasov@Sun.COM		return;
1242*13124SAlexander.Kolbasov@Sun.COM	}
1243*13124SAlexander.Kolbasov@Sun.COM
1244*13124SAlexander.Kolbasov@Sun.COM	return ($self);
1245*13124SAlexander.Kolbasov@Sun.COM}
1246*13124SAlexander.Kolbasov@Sun.COM
1247*13124SAlexander.Kolbasov@Sun.COM#
1248*13124SAlexander.Kolbasov@Sun.COM# Verify that topology is the same as at the time snapshot was created
1249*13124SAlexander.Kolbasov@Sun.COM#
1250*13124SAlexander.Kolbasov@Sun.COMsub _same_generation
1251*13124SAlexander.Kolbasov@Sun.COM{
1252*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
1253*13124SAlexander.Kolbasov@Sun.COM	my $pgtree =  $self->{PGTREE} or return;
1254*13124SAlexander.Kolbasov@Sun.COM
1255*13124SAlexander.Kolbasov@Sun.COM	return (0) unless $self;
1256*13124SAlexander.Kolbasov@Sun.COM
1257*13124SAlexander.Kolbasov@Sun.COM	my $ks = $self->{KSTAT};
1258*13124SAlexander.Kolbasov@Sun.COM	$ks->update();
1259*13124SAlexander.Kolbasov@Sun.COM	my $pg_info = $ks->{$self->{PG_MODULE}};
1260*13124SAlexander.Kolbasov@Sun.COM	foreach my $id (keys %$pg_info) {
1261*13124SAlexander.Kolbasov@Sun.COM		my $pg = $pgtree->{$id} or next;
1262*13124SAlexander.Kolbasov@Sun.COM
1263*13124SAlexander.Kolbasov@Sun.COM		my $pg_ks = _kstat_get_pg($pg_info, $id,
1264*13124SAlexander.Kolbasov@Sun.COM					  $self->{USE_OLD_KSTATS});
1265*13124SAlexander.Kolbasov@Sun.COM		return unless $pg_ks;
1266*13124SAlexander.Kolbasov@Sun.COM		return (0) unless $pg->{generation} == $pg_ks->{generation};
1267*13124SAlexander.Kolbasov@Sun.COM	}
1268*13124SAlexander.Kolbasov@Sun.COM	return (1);
1269*13124SAlexander.Kolbasov@Sun.COM}
1270*13124SAlexander.Kolbasov@Sun.COM
1271*13124SAlexander.Kolbasov@Sun.COM#
1272*13124SAlexander.Kolbasov@Sun.COM# Update capacity for both PGs
1273*13124SAlexander.Kolbasov@Sun.COM#
1274*13124SAlexander.Kolbasov@Sun.COMsub _capacity_update
1275*13124SAlexander.Kolbasov@Sun.COM{
1276*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
1277*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
1278*13124SAlexander.Kolbasov@Sun.COM
1279*13124SAlexander.Kolbasov@Sun.COM	my $pgtree1 = $c1->{PGTREE};
1280*13124SAlexander.Kolbasov@Sun.COM	my $pgtree2 = $c2->{PGTREE};
1281*13124SAlexander.Kolbasov@Sun.COM
1282*13124SAlexander.Kolbasov@Sun.COM	foreach my $pgid (keys %$pgtree1) {
1283*13124SAlexander.Kolbasov@Sun.COM		my $pg1 = $pgtree1->{$pgid};
1284*13124SAlexander.Kolbasov@Sun.COM		my $pg2 = $pgtree2->{$pgid};
1285*13124SAlexander.Kolbasov@Sun.COM		next unless $pg1 && $pg2;
1286*13124SAlexander.Kolbasov@Sun.COM		next unless $pg1->{generation} != $pg2->{generation};
1287*13124SAlexander.Kolbasov@Sun.COM		my $shname1 = $pg1->{sh_name};
1288*13124SAlexander.Kolbasov@Sun.COM		my $shname2 = $pg2->{sh_name};
1289*13124SAlexander.Kolbasov@Sun.COM		next unless $shname1 eq $shname2;
1290*13124SAlexander.Kolbasov@Sun.COM		my $max_rate = max($pg1->{util_rate_max}, $pg2->{util_rate_max});
1291*13124SAlexander.Kolbasov@Sun.COM
1292*13124SAlexander.Kolbasov@Sun.COM		my $utilization = abs($pg1->{util} - $pg2->{util});
1293*13124SAlexander.Kolbasov@Sun.COM		my $tdelta = abs($pg1->{snaptime} - $pg2->{snaptime});
1294*13124SAlexander.Kolbasov@Sun.COM		$utilization /= $tdelta if $utilization && $tdelta;
1295*13124SAlexander.Kolbasov@Sun.COM		$max_rate = $utilization if
1296*13124SAlexander.Kolbasov@Sun.COM		  $utilization && $max_rate < $utilization;
1297*13124SAlexander.Kolbasov@Sun.COM
1298*13124SAlexander.Kolbasov@Sun.COM		$c1->{CAPACITY}->{$shname1} = $max_rate if
1299*13124SAlexander.Kolbasov@Sun.COM		  !$c1->{CAPACITY}->{$shname1} ||
1300*13124SAlexander.Kolbasov@Sun.COM		    !$c1->{CAPACITY}->{$shname1} < $max_rate;
1301*13124SAlexander.Kolbasov@Sun.COM		$c2->{CAPACITY}->{$shname2} = $max_rate if
1302*13124SAlexander.Kolbasov@Sun.COM		  !$c2->{CAPACITY}->{$shname2} ||
1303*13124SAlexander.Kolbasov@Sun.COM		    !$c2->{CAPACITY}->{$shname2} < $max_rate;
1304*13124SAlexander.Kolbasov@Sun.COM	}
1305*13124SAlexander.Kolbasov@Sun.COM}
1306*13124SAlexander.Kolbasov@Sun.COM
1307*13124SAlexander.Kolbasov@Sun.COM#
1308*13124SAlexander.Kolbasov@Sun.COM# Return list of PGs breadth first
1309*13124SAlexander.Kolbasov@Sun.COM#
1310*13124SAlexander.Kolbasov@Sun.COMsub _walk_depth_first
1311*13124SAlexander.Kolbasov@Sun.COM{
1312*13124SAlexander.Kolbasov@Sun.COM	my $p = shift;
1313*13124SAlexander.Kolbasov@Sun.COM	# Nothing to do if list is empty
1314*13124SAlexander.Kolbasov@Sun.COM	return unless scalar (@_);
1315*13124SAlexander.Kolbasov@Sun.COM
1316*13124SAlexander.Kolbasov@Sun.COM	return (map { ($_, _walk_depth_first ($p, $p->children($_))) } @_);
1317*13124SAlexander.Kolbasov@Sun.COM}
1318*13124SAlexander.Kolbasov@Sun.COM
1319*13124SAlexander.Kolbasov@Sun.COM#
1320*13124SAlexander.Kolbasov@Sun.COM# Return list of PGs breadth first
1321*13124SAlexander.Kolbasov@Sun.COM#
1322*13124SAlexander.Kolbasov@Sun.COMsub _walk_breadth_first
1323*13124SAlexander.Kolbasov@Sun.COM{
1324*13124SAlexander.Kolbasov@Sun.COM	my $p = shift;
1325*13124SAlexander.Kolbasov@Sun.COM	# Nothing to do if list is empty
1326*13124SAlexander.Kolbasov@Sun.COM	return unless scalar (@_);
1327*13124SAlexander.Kolbasov@Sun.COM
1328*13124SAlexander.Kolbasov@Sun.COM	return (@_, _walk_breadth_first($p, map { $p->children($_) } @_));
1329*13124SAlexander.Kolbasov@Sun.COM}
1330*13124SAlexander.Kolbasov@Sun.COM
1331*13124SAlexander.Kolbasov@Sun.COM#
1332*13124SAlexander.Kolbasov@Sun.COM# Given the kstat reference (already hashed by module name) and PG ID return the
1333*13124SAlexander.Kolbasov@Sun.COM# corresponding kstat.
1334*13124SAlexander.Kolbasov@Sun.COM#
1335*13124SAlexander.Kolbasov@Sun.COMsub _kstat_get_pg
1336*13124SAlexander.Kolbasov@Sun.COM{
1337*13124SAlexander.Kolbasov@Sun.COM	my $mod = shift;
1338*13124SAlexander.Kolbasov@Sun.COM	my $pgid = shift;
1339*13124SAlexander.Kolbasov@Sun.COM	my $use_old_kstats = shift;
1340*13124SAlexander.Kolbasov@Sun.COM
1341*13124SAlexander.Kolbasov@Sun.COM	my $id_field = $use_old_kstats ? 'id' : 'pg_id';
1342*13124SAlexander.Kolbasov@Sun.COM
1343*13124SAlexander.Kolbasov@Sun.COM	return ($mod->{$pgid}->{hardware}) if $use_old_kstats;
1344*13124SAlexander.Kolbasov@Sun.COM
1345*13124SAlexander.Kolbasov@Sun.COM	my @instances = grep { $_->{$id_field} == $pgid }
1346*13124SAlexander.Kolbasov@Sun.COM	  values(%{$mod->{$pgid}});
1347*13124SAlexander.Kolbasov@Sun.COM	return ($instances[0]);
1348*13124SAlexander.Kolbasov@Sun.COM}
1349*13124SAlexander.Kolbasov@Sun.COM
1350*13124SAlexander.Kolbasov@Sun.COM######################################################################
1351*13124SAlexander.Kolbasov@Sun.COM# Set routines
1352*13124SAlexander.Kolbasov@Sun.COM#######################################################################
1353*13124SAlexander.Kolbasov@Sun.COM#
1354*13124SAlexander.Kolbasov@Sun.COM# Return T if one list contains all the elements of another list.
1355*13124SAlexander.Kolbasov@Sun.COM# All lists are passed by reference
1356*13124SAlexander.Kolbasov@Sun.COM#
1357*13124SAlexander.Kolbasov@Sun.COMsub _is_subset
1358*13124SAlexander.Kolbasov@Sun.COM{
1359*13124SAlexander.Kolbasov@Sun.COM	my ($left, $right) = @_;
1360*13124SAlexander.Kolbasov@Sun.COM	my %seen;	# Set to 1 for everything in the first list
1361*13124SAlexander.Kolbasov@Sun.COM	# Put the shortest list in $left
1362*13124SAlexander.Kolbasov@Sun.COM
1363*13124SAlexander.Kolbasov@Sun.COM	Carp::croak "invalid left argument" unless ref ($left) eq 'ARRAY';
1364*13124SAlexander.Kolbasov@Sun.COM	Carp::croak "invalid right argument" unless ref ($right) eq 'ARRAY';
1365*13124SAlexander.Kolbasov@Sun.COM
1366*13124SAlexander.Kolbasov@Sun.COM	# Create a hash indexed by elements in @right with ones as a value.
1367*13124SAlexander.Kolbasov@Sun.COM	map { $seen{$_} = 1 } @$right;
1368*13124SAlexander.Kolbasov@Sun.COM
1369*13124SAlexander.Kolbasov@Sun.COM	# Find members of @left not present in @right
1370*13124SAlexander.Kolbasov@Sun.COM	my @extra = grep { !$seen{$_} } @$left;
1371*13124SAlexander.Kolbasov@Sun.COM	return (!scalar(@extra));
1372*13124SAlexander.Kolbasov@Sun.COM}
1373*13124SAlexander.Kolbasov@Sun.COM
1374*13124SAlexander.Kolbasov@Sun.COMsub _is_member
1375*13124SAlexander.Kolbasov@Sun.COM{
1376*13124SAlexander.Kolbasov@Sun.COM	my $set = shift;
1377*13124SAlexander.Kolbasov@Sun.COM	my $element = shift;
1378*13124SAlexander.Kolbasov@Sun.COM	my %seen;
1379*13124SAlexander.Kolbasov@Sun.COM
1380*13124SAlexander.Kolbasov@Sun.COM	map { $seen{$_} = 1 } @$set;
1381*13124SAlexander.Kolbasov@Sun.COM
1382*13124SAlexander.Kolbasov@Sun.COM	return ($seen{$element});
1383*13124SAlexander.Kolbasov@Sun.COM}
1384*13124SAlexander.Kolbasov@Sun.COM
1385*13124SAlexander.Kolbasov@Sun.COM#
1386*13124SAlexander.Kolbasov@Sun.COM# Return T if C1 and C2 contain the same elements
1387*13124SAlexander.Kolbasov@Sun.COM#
1388*13124SAlexander.Kolbasov@Sun.COMsub _set_equal
1389*13124SAlexander.Kolbasov@Sun.COM{
1390*13124SAlexander.Kolbasov@Sun.COM	my $c1 = shift;
1391*13124SAlexander.Kolbasov@Sun.COM	my $c2 = shift;
1392*13124SAlexander.Kolbasov@Sun.COM
1393*13124SAlexander.Kolbasov@Sun.COM	return 0 unless scalar @$c1 == scalar @$c2;
1394*13124SAlexander.Kolbasov@Sun.COM
1395*13124SAlexander.Kolbasov@Sun.COM	return (_is_subset($c1, $c2) && _is_subset($c2, $c1));
1396*13124SAlexander.Kolbasov@Sun.COM}
1397*13124SAlexander.Kolbasov@Sun.COM
1398*13124SAlexander.Kolbasov@Sun.COM#
1399*13124SAlexander.Kolbasov@Sun.COM# Return the intersection of two lists passed by reference
1400*13124SAlexander.Kolbasov@Sun.COM# Convert the first list to a hash with seen entries marked as 1-values
1401*13124SAlexander.Kolbasov@Sun.COM# Then grep only elements present in the first list from the second list.
1402*13124SAlexander.Kolbasov@Sun.COM# As a little optimization, use the shorter list to build a hash.
1403*13124SAlexander.Kolbasov@Sun.COM#
1404*13124SAlexander.Kolbasov@Sun.COMsub _set_intersect
1405*13124SAlexander.Kolbasov@Sun.COM{
1406*13124SAlexander.Kolbasov@Sun.COM	my ($left, $right) = @_;
1407*13124SAlexander.Kolbasov@Sun.COM	my %seen;	# Set to 1 for everything in the first list
1408*13124SAlexander.Kolbasov@Sun.COM	# Put the shortest list in $left
1409*13124SAlexander.Kolbasov@Sun.COM	scalar @$left <= scalar @$right or ($right, $left) = ($left, $right);
1410*13124SAlexander.Kolbasov@Sun.COM
1411*13124SAlexander.Kolbasov@Sun.COM	# Create a hash indexed by elements in @left with ones as a value.
1412*13124SAlexander.Kolbasov@Sun.COM	map { $seen{$_} = 1 } @$left;
1413*13124SAlexander.Kolbasov@Sun.COM	# Find members of @right present in @left
1414*13124SAlexander.Kolbasov@Sun.COM	return (grep { $seen{$_} } @$right);
1415*13124SAlexander.Kolbasov@Sun.COM}
1416*13124SAlexander.Kolbasov@Sun.COM
1417*13124SAlexander.Kolbasov@Sun.COM#
1418*13124SAlexander.Kolbasov@Sun.COM# Expand start-end into the list of values
1419*13124SAlexander.Kolbasov@Sun.COM# Input: string containing a single numeric ID or x-y range
1420*13124SAlexander.Kolbasov@Sun.COM# Output: single value or a list of values
1421*13124SAlexander.Kolbasov@Sun.COM# Ranges with start being more than end are inverted
1422*13124SAlexander.Kolbasov@Sun.COM#
1423*13124SAlexander.Kolbasov@Sun.COMsub _expand
1424*13124SAlexander.Kolbasov@Sun.COM{
1425*13124SAlexander.Kolbasov@Sun.COM	# Skip the first argument if it is the object reference
1426*13124SAlexander.Kolbasov@Sun.COM	shift if ref $@[0] eq 'HASH';
1427*13124SAlexander.Kolbasov@Sun.COM
1428*13124SAlexander.Kolbasov@Sun.COM	my $arg = shift;
1429*13124SAlexander.Kolbasov@Sun.COM
1430*13124SAlexander.Kolbasov@Sun.COM	return unless defined $arg;
1431*13124SAlexander.Kolbasov@Sun.COM
1432*13124SAlexander.Kolbasov@Sun.COM	my @args = split /,/, $arg;
1433*13124SAlexander.Kolbasov@Sun.COM
1434*13124SAlexander.Kolbasov@Sun.COM	return map { _expand($_) } @args if scalar @args > 1;
1435*13124SAlexander.Kolbasov@Sun.COM
1436*13124SAlexander.Kolbasov@Sun.COM	$arg = shift @args;
1437*13124SAlexander.Kolbasov@Sun.COM	return unless defined $arg;
1438*13124SAlexander.Kolbasov@Sun.COM
1439*13124SAlexander.Kolbasov@Sun.COM	if ($arg =~ m/^\d+$/) {
1440*13124SAlexander.Kolbasov@Sun.COM		# single number
1441*13124SAlexander.Kolbasov@Sun.COM		return ($arg);
1442*13124SAlexander.Kolbasov@Sun.COM	} elsif ($arg =~ m/^(\d+)\-(\d+)$/) {
1443*13124SAlexander.Kolbasov@Sun.COM		my ($start, $end) = ($1, $2);	# $start-$end
1444*13124SAlexander.Kolbasov@Sun.COM		# Reverse the interval if start > end
1445*13124SAlexander.Kolbasov@Sun.COM		($start, $end) = ($end, $start) if $start > $end;
1446*13124SAlexander.Kolbasov@Sun.COM		return ($start .. $end);
1447*13124SAlexander.Kolbasov@Sun.COM	} else {
1448*13124SAlexander.Kolbasov@Sun.COM		return $arg;
1449*13124SAlexander.Kolbasov@Sun.COM	}
1450*13124SAlexander.Kolbasov@Sun.COM	return;
1451*13124SAlexander.Kolbasov@Sun.COM}
1452*13124SAlexander.Kolbasov@Sun.COM
1453*13124SAlexander.Kolbasov@Sun.COM#
1454*13124SAlexander.Kolbasov@Sun.COM# Consolidate consecutive ids as start-end
1455*13124SAlexander.Kolbasov@Sun.COM# Input: list of ids
1456*13124SAlexander.Kolbasov@Sun.COM# Output: string with space-sepated cpu values with ranges
1457*13124SAlexander.Kolbasov@Sun.COM#   collapsed as x-y
1458*13124SAlexander.Kolbasov@Sun.COM#
1459*13124SAlexander.Kolbasov@Sun.COMsub _collapse
1460*13124SAlexander.Kolbasov@Sun.COM{
1461*13124SAlexander.Kolbasov@Sun.COM	return ('') unless @_;
1462*13124SAlexander.Kolbasov@Sun.COM	my @args = _uniqsort(@_);
1463*13124SAlexander.Kolbasov@Sun.COM	my $start = shift(@args);
1464*13124SAlexander.Kolbasov@Sun.COM	my $result = '';
1465*13124SAlexander.Kolbasov@Sun.COM	my $end = $start;	# Initial range consists of the first element
1466*13124SAlexander.Kolbasov@Sun.COM	foreach my $el (@args) {
1467*13124SAlexander.Kolbasov@Sun.COM		if (!$el =~ /^\d+$/) {
1468*13124SAlexander.Kolbasov@Sun.COM			$result = "$result $el";
1469*13124SAlexander.Kolbasov@Sun.COM			$end = $el;
1470*13124SAlexander.Kolbasov@Sun.COM		} elsif ($el == ($end + 1)) {
1471*13124SAlexander.Kolbasov@Sun.COM			#
1472*13124SAlexander.Kolbasov@Sun.COM			# Got consecutive ID, so extend end of range without
1473*13124SAlexander.Kolbasov@Sun.COM			# printing anything since the range may extend further
1474*13124SAlexander.Kolbasov@Sun.COM			#
1475*13124SAlexander.Kolbasov@Sun.COM			$end = $el;
1476*13124SAlexander.Kolbasov@Sun.COM		} else {
1477*13124SAlexander.Kolbasov@Sun.COM			#
1478*13124SAlexander.Kolbasov@Sun.COM			# Next ID is not consecutive, so print IDs gotten so
1479*13124SAlexander.Kolbasov@Sun.COM			# far.
1480*13124SAlexander.Kolbasov@Sun.COM			#
1481*13124SAlexander.Kolbasov@Sun.COM			if ($end > $start + 1) {	# range
1482*13124SAlexander.Kolbasov@Sun.COM				$result = "$result $start-$end";
1483*13124SAlexander.Kolbasov@Sun.COM			} elsif ($end > $start) {	# different values
1484*13124SAlexander.Kolbasov@Sun.COM				$result = "$result $start $end";
1485*13124SAlexander.Kolbasov@Sun.COM			} else {	# same value
1486*13124SAlexander.Kolbasov@Sun.COM				$result = "$result $start";
1487*13124SAlexander.Kolbasov@Sun.COM			}
1488*13124SAlexander.Kolbasov@Sun.COM
1489*13124SAlexander.Kolbasov@Sun.COM			# Try finding consecutive range starting from this ID
1490*13124SAlexander.Kolbasov@Sun.COM			$start = $end = $el;
1491*13124SAlexander.Kolbasov@Sun.COM		}
1492*13124SAlexander.Kolbasov@Sun.COM	}
1493*13124SAlexander.Kolbasov@Sun.COM
1494*13124SAlexander.Kolbasov@Sun.COM	# Print last ID(s)
1495*13124SAlexander.Kolbasov@Sun.COM	if (! ($end =~ /^\d+$/)) {
1496*13124SAlexander.Kolbasov@Sun.COM		$result = "$result $end";
1497*13124SAlexander.Kolbasov@Sun.COM	} elsif ($end > $start + 1) {
1498*13124SAlexander.Kolbasov@Sun.COM		$result = "$result $start-$end";
1499*13124SAlexander.Kolbasov@Sun.COM	} elsif ($end > $start) {
1500*13124SAlexander.Kolbasov@Sun.COM		$result = "$result $start $end";
1501*13124SAlexander.Kolbasov@Sun.COM	} else {
1502*13124SAlexander.Kolbasov@Sun.COM		$result = "$result $start";
1503*13124SAlexander.Kolbasov@Sun.COM	}
1504*13124SAlexander.Kolbasov@Sun.COM	# Remove any spaces in the beginning
1505*13124SAlexander.Kolbasov@Sun.COM	$result =~ s/^\s+//;
1506*13124SAlexander.Kolbasov@Sun.COM	return ($result);
1507*13124SAlexander.Kolbasov@Sun.COM}
1508*13124SAlexander.Kolbasov@Sun.COM
1509*13124SAlexander.Kolbasov@Sun.COM#
1510*13124SAlexander.Kolbasov@Sun.COM# get relationship order from relationship name.
1511*13124SAlexander.Kolbasov@Sun.COM# return 0 for all unknown names.
1512*13124SAlexander.Kolbasov@Sun.COM#
1513*13124SAlexander.Kolbasov@Sun.COMsub _relationship_order
1514*13124SAlexander.Kolbasov@Sun.COM{
1515*13124SAlexander.Kolbasov@Sun.COM	my $name = shift;
1516*13124SAlexander.Kolbasov@Sun.COM	return ($relationships_order{$name} || 0);
1517*13124SAlexander.Kolbasov@Sun.COM}
1518*13124SAlexander.Kolbasov@Sun.COM
1519*13124SAlexander.Kolbasov@Sun.COM#
1520*13124SAlexander.Kolbasov@Sun.COM# Get software load for each CPU from kstats
1521*13124SAlexander.Kolbasov@Sun.COM# Argument: kstat reference
1522*13124SAlexander.Kolbasov@Sun.COM# Returns: reference to the hash with
1523*13124SAlexander.Kolbasov@Sun.COM# cpu_idle, cpu_user, cpu_sys keys.
1524*13124SAlexander.Kolbasov@Sun.COM#
1525*13124SAlexander.Kolbasov@Sun.COMsub _get_sw_cpu_load
1526*13124SAlexander.Kolbasov@Sun.COM{
1527*13124SAlexander.Kolbasov@Sun.COM	my $ks = shift or return;
1528*13124SAlexander.Kolbasov@Sun.COM
1529*13124SAlexander.Kolbasov@Sun.COM	my $loads;
1530*13124SAlexander.Kolbasov@Sun.COM	my $sys_ks = $ks->{cpu};
1531*13124SAlexander.Kolbasov@Sun.COM	foreach my $cpu (keys %$sys_ks) {
1532*13124SAlexander.Kolbasov@Sun.COM		my $sys = $sys_ks->{$cpu}->{sys};
1533*13124SAlexander.Kolbasov@Sun.COM		$loads->{$cpu}->{cpu_idle} = $sys->{cpu_ticks_idle};
1534*13124SAlexander.Kolbasov@Sun.COM		$loads->{$cpu}->{cpu_user} = $sys->{cpu_ticks_user};
1535*13124SAlexander.Kolbasov@Sun.COM		$loads->{$cpu}->{cpu_sys} = $sys->{cpu_ticks_kernel};
1536*13124SAlexander.Kolbasov@Sun.COM	}
1537*13124SAlexander.Kolbasov@Sun.COM
1538*13124SAlexander.Kolbasov@Sun.COM	return ($loads);
1539*13124SAlexander.Kolbasov@Sun.COM}
1540*13124SAlexander.Kolbasov@Sun.COM
1541*13124SAlexander.Kolbasov@Sun.COM#
1542*13124SAlexander.Kolbasov@Sun.COM# Get software load for each CPU from kstats
1543*13124SAlexander.Kolbasov@Sun.COM# Arguments:
1544*13124SAlexander.Kolbasov@Sun.COM#  pgtree reference
1545*13124SAlexander.Kolbasov@Sun.COM#  kstat reference
1546*13124SAlexander.Kolbasov@Sun.COM#
1547*13124SAlexander.Kolbasov@Sun.COM# Returns: nothing
1548*13124SAlexander.Kolbasov@Sun.COM# Stores CPU load in the $pg->{cpudata} hash for each PG
1549*13124SAlexander.Kolbasov@Sun.COM#
1550*13124SAlexander.Kolbasov@Sun.COMsub _get_hw_cpu_load
1551*13124SAlexander.Kolbasov@Sun.COM{
1552*13124SAlexander.Kolbasov@Sun.COM	my $self = shift;
1553*13124SAlexander.Kolbasov@Sun.COM	my $pgtree = $self->{PGTREE};
1554*13124SAlexander.Kolbasov@Sun.COM	my $ks = $self->{KSTAT};
1555*13124SAlexander.Kolbasov@Sun.COM
1556*13124SAlexander.Kolbasov@Sun.COM	my $pg_cpu_ks = $ks->{$self->{PG_CPU_MODULE}};
1557*13124SAlexander.Kolbasov@Sun.COM
1558*13124SAlexander.Kolbasov@Sun.COM	foreach my $pgid (keys %$pgtree) {
1559*13124SAlexander.Kolbasov@Sun.COM		my $pg = $pgtree->{$pgid};
1560*13124SAlexander.Kolbasov@Sun.COM		my @cpus = @{$pg->{cpus}};
1561*13124SAlexander.Kolbasov@Sun.COM		my $cpu;
1562*13124SAlexander.Kolbasov@Sun.COM		my $pg_id;
1563*13124SAlexander.Kolbasov@Sun.COM		foreach my $cpu (keys %$pg_cpu_ks) {
1564*13124SAlexander.Kolbasov@Sun.COM			next unless _is_member(\@cpus, $cpu);
1565*13124SAlexander.Kolbasov@Sun.COM			my $cpu_hw_data = $pg_cpu_ks->{$cpu};
1566*13124SAlexander.Kolbasov@Sun.COM			foreach my $hw (keys %$cpu_hw_data) {
1567*13124SAlexander.Kolbasov@Sun.COM				my $cpudata = $cpu_hw_data->{$hw};
1568*13124SAlexander.Kolbasov@Sun.COM
1569*13124SAlexander.Kolbasov@Sun.COM				#
1570*13124SAlexander.Kolbasov@Sun.COM				# Only consider information for this PG
1571*13124SAlexander.Kolbasov@Sun.COM				#
1572*13124SAlexander.Kolbasov@Sun.COM				next unless $cpudata->{pg_id} == $pgid;
1573*13124SAlexander.Kolbasov@Sun.COM
1574*13124SAlexander.Kolbasov@Sun.COM				$pg->{cpudata}->{$cpu}->{generation} =
1575*13124SAlexander.Kolbasov@Sun.COM				  $cpudata->{generation};
1576*13124SAlexander.Kolbasov@Sun.COM				$pg->{cpudata}->{$cpu}->{util} =
1577*13124SAlexander.Kolbasov@Sun.COM				  $cpudata->{hw_util};
1578*13124SAlexander.Kolbasov@Sun.COM				$pg->{cpudata}->{$cpu}->{util_time_running} =
1579*13124SAlexander.Kolbasov@Sun.COM				  $cpudata->{hw_util_time_running};
1580*13124SAlexander.Kolbasov@Sun.COM				$pg->{cpudata}->{$cpu}->{util_time_stopped} =
1581*13124SAlexander.Kolbasov@Sun.COM				  $cpudata->{hw_util_time_stopped};
1582*13124SAlexander.Kolbasov@Sun.COM				$pg->{cpudata}->{$cpu}->{snaptime} =
1583*13124SAlexander.Kolbasov@Sun.COM				  $cpudata->{snaptime};
1584*13124SAlexander.Kolbasov@Sun.COM			}
1585*13124SAlexander.Kolbasov@Sun.COM		}
1586*13124SAlexander.Kolbasov@Sun.COM	}
1587*13124SAlexander.Kolbasov@Sun.COM}
1588*13124SAlexander.Kolbasov@Sun.COM
1589*13124SAlexander.Kolbasov@Sun.COM1;
1590*13124SAlexander.Kolbasov@Sun.COM
1591*13124SAlexander.Kolbasov@Sun.COM__END__
1592*13124SAlexander.Kolbasov@Sun.COM
1593*13124SAlexander.Kolbasov@Sun.COM#
1594*13124SAlexander.Kolbasov@Sun.COM# The information about PG hierarchy is contained in a object return by the
1595*13124SAlexander.Kolbasov@Sun.COM# new() method.
1596*13124SAlexander.Kolbasov@Sun.COM#
1597*13124SAlexander.Kolbasov@Sun.COM# This module can deal with old PG kstats that have 'pg' and 'pg_cpu' as module
1598*13124SAlexander.Kolbasov@Sun.COM# names as well as new PG kstats which use 'pg_hw_perf' and ''pg_hw_perf_cpu' as
1599*13124SAlexander.Kolbasov@Sun.COM# the module name.
1600*13124SAlexander.Kolbasov@Sun.COM#
1601*13124SAlexander.Kolbasov@Sun.COM# The object contains the following fields:
1602*13124SAlexander.Kolbasov@Sun.COM#
1603*13124SAlexander.Kolbasov@Sun.COM#   CPUS		List of all CPUs present.
1604*13124SAlexander.Kolbasov@Sun.COM#   CAPACITY		Estimate of capacity for each sharing
1605*13124SAlexander.Kolbasov@Sun.COM#   PGTREE		The PG tree. See below for the tree representation.
1606*13124SAlexander.Kolbasov@Sun.COM#
1607*13124SAlexander.Kolbasov@Sun.COM#   PG_MODULE 		Module name for the PG kstats. It is either 'pg' for
1608*13124SAlexander.Kolbasov@Sun.COM#			 old style kstats, or 'pg_hw_perf' for new style kstats.
1609*13124SAlexander.Kolbasov@Sun.COM#
1610*13124SAlexander.Kolbasov@Sun.COM#   MAX_FREQUENCY	Maximum CPU frequency
1611*13124SAlexander.Kolbasov@Sun.COM#   USE_OLD_KSTATS	True if we are dealing with old style kstats
1612*13124SAlexander.Kolbasov@Sun.COM#   KSTAT		The kstat object used to generate this hierarchy.
1613*13124SAlexander.Kolbasov@Sun.COM#
1614*13124SAlexander.Kolbasov@Sun.COM# The PG tree is represented as a hash table indexed by PG ID. Each element of
1615*13124SAlexander.Kolbasov@Sun.COM# the table is the hash reference with the following fields:
1616*13124SAlexander.Kolbasov@Sun.COM#
1617*13124SAlexander.Kolbasov@Sun.COM#   children		Reference to the list of children PG IDs
1618*13124SAlexander.Kolbasov@Sun.COM#   cpus		Reference to the list of cpu IDs in the PG
1619*13124SAlexander.Kolbasov@Sun.COM#   current_rate	Current utilization rate
1620*13124SAlexander.Kolbasov@Sun.COM#   generation		PG generation
1621*13124SAlexander.Kolbasov@Sun.COM#   id			PG id
1622*13124SAlexander.Kolbasov@Sun.COM#   ncpus		number of CPUs in the PG
1623*13124SAlexander.Kolbasov@Sun.COM#   parent		PG parent id, or -1 if there is none.
1624*13124SAlexander.Kolbasov@Sun.COM#   sh_name		Sharing name
1625*13124SAlexander.Kolbasov@Sun.COM#   snaptime		Snapshot time
1626*13124SAlexander.Kolbasov@Sun.COM#   util		Hardware utilization
1627*13124SAlexander.Kolbasov@Sun.COM#   util_rate_max	Maximum utilization rate
1628*13124SAlexander.Kolbasov@Sun.COM#   util_time_running	Time (in nanoseconds) when utilization data is collected
1629*13124SAlexander.Kolbasov@Sun.COM#   util_time_stopped	Time when utilization data is not collected
1630*13124SAlexander.Kolbasov@Sun.COM#
1631*13124SAlexander.Kolbasov@Sun.COM# The fields (with the exception of 'children') are a copy of the data from
1632*13124SAlexander.Kolbasov@Sun.COM# kstats.
1633*13124SAlexander.Kolbasov@Sun.COM#
1634*13124SAlexander.Kolbasov@Sun.COM# The PG hierarchy in the kernel does not have the root PG. We simulate the root
1635*13124SAlexander.Kolbasov@Sun.COM# (System) PG which is the parent of top level PGs in the system. This PG always
1636*13124SAlexander.Kolbasov@Sun.COM# has ID 0.
1637*13124SAlexander.Kolbasov@Sun.COM#
1638