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