xref: /illumos-gate/usr/src/uts/common/os/pghw.c (revision b1e2e3fb17324e9ddf43db264a0c64da7756d9e6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2016 by Delphix. All rights reserved.
24  */
25 
26 #include <sys/systm.h>
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/thread.h>
30 #include <sys/cpuvar.h>
31 #include <sys/kmem.h>
32 #include <sys/cmn_err.h>
33 #include <sys/policy.h>
34 #include <sys/group.h>
35 #include <sys/pg.h>
36 #include <sys/pghw.h>
37 #include <sys/cpu_pm.h>
38 #include <sys/cap_util.h>
39 
40 /*
41  * Processor Groups: Hardware sharing relationship layer
42  *
43  * This file implements an extension to Processor Groups to capture
44  * hardware sharing relationships existing between logical CPUs. Examples of
45  * hardware sharing relationships include shared caches on some CMT
46  * procesoor architectures, or shared local memory controllers on NUMA
47  * based system architectures.
48  *
49  * The pghw_t structure represents the extended PG. The first member
50  * of the structure is the generic pg_t with the pghw specific members
51  * following. The generic pg_t *must* remain the first member of the
52  * structure as the code uses casting of structure references to access
53  * the generic pg_t structure elements.
54  *
55  * In addition to the generic CPU grouping, physical PGs have a hardware
56  * sharing relationship enumerated "type", and an instance id. The enumerated
57  * type is defined by the pghw_type_t enumeration, while the instance id
58  * uniquely identifies the sharing instance from among others of the same
59  * hardware sharing type.
60  *
61  * The physical PGs are organized into an overall hierarchy, and are tracked
62  * in a number of different per CPU, and per pghw_type_t type groups.
63  * As an example:
64  *
65  * -------------
66  * | pg_hw     |
67  * | (group_t) |
68  * -------------
69  *  ||                          ============================
70  *  ||\\-----------------------//       \\                 \\
71  *  ||  | hwset (PGC_HW_CHIP) |        -------------      -------------
72  *  ||  | (group_t)           |        | pghw_t    |      | pghw_t    |
73  *  ||  -----------------------        | chip 0    |      | chip 1    |
74  *  ||                                 -------------      -------------
75  *  ||                                 \\  \\  \\  \\     \\  \\  \\  \\
76  *  ||                                  cpu cpu cpu cpu    cpu cpu cpu cpu
77  *  ||
78  *  ||                          ============================
79  *  ||\\-----------------------//       \\                 \\
80  *  ||  | hwset (PGC_HW_IPIPE)|        -------------      -------------
81  *  ||  | (group_t)           |        | pghw_t    |      | pghw_t    |
82  *  ||  -----------------------        | ipipe 0   |      | ipipe 1   |
83  *  ||                                 -------------      -------------
84  *  ||                                 \\  \\             \\  \\
85  *  ||                                  cpu cpu            cpu cpu
86  *  ...
87  *
88  *
89  * The top level pg_hw is a group of "hwset" groups. Each hwset holds of group
90  * of physical PGs of the same hardware sharing type. Within each hwset, the
91  * PG's instance id uniquely identifies the grouping relationshsip among other
92  * groupings of the same sharing type. The instance id for a grouping is
93  * platform defined, and in some cases may be used by platform code as a handle
94  * to search for a particular relationship instance.
95  *
96  * Each physical PG (by virtue of the embedded pg_t) contains a group of CPUs
97  * that participate in the sharing relationship. Each CPU also has associated
98  * with it a grouping tracking the PGs in which the CPU belongs. This can be
99  * used to iterate over the various relationships in which the CPU participates
100  * (the CPU's chip, cache, lgroup, etc.).
101  *
102  * The hwsets are created dynamically as new hardware sharing relationship types
103  * are instantiated. They are never destroyed, as once a given relationship
104  * type appears in the system, it is quite likely that at least one instance of
105  * that relationship will always persist as long as the system is running.
106  */
107 
108 static group_t		*pg_hw;		/* top level pg hw group */
109 
110 /*
111  * Physical PG kstats
112  */
113 struct pghw_kstat {
114 	kstat_named_t	pg_id;
115 	kstat_named_t	pg_class;
116 	kstat_named_t	pg_ncpus;
117 	kstat_named_t	pg_instance_id;
118 	kstat_named_t	pg_hw;
119 	kstat_named_t	pg_policy;
120 } pghw_kstat = {
121 	{ "id",			KSTAT_DATA_INT32 },
122 	{ "pg_class",		KSTAT_DATA_STRING },
123 	{ "ncpus",		KSTAT_DATA_UINT32 },
124 	{ "instance_id",	KSTAT_DATA_UINT32 },
125 	{ "hardware",		KSTAT_DATA_STRING },
126 	{ "policy",		KSTAT_DATA_STRING },
127 };
128 
129 kmutex_t		pghw_kstat_lock;
130 
131 /*
132  * Capacity and Utilization PG kstats
133  *
134  * These kstats are updated one at a time, so we can have a single scratch space
135  * to fill the data.
136  *
137  * kstat fields:
138  *
139  *   pg_id		PG ID for PG described by this kstat
140  *
141  *   pg_parent		Parent PG ID. The value -1 means "no parent".
142  *
143  *   pg_ncpus		Number of CPUs within this PG
144  *
145  *   pg_cpus		String describing CPUs within this PG
146  *
147  *   pg_relationship	Name of sharing relationship for this PG
148  *
149  *   pg_generation	Generation value that increases whenever any CPU leaves
150  *			  or joins PG. Two kstat snapshots for the same
151  *			  CPU may only be compared if they have the same
152  *			  generation
153  *
154  *   pg_hw_util		Running value of PG utilization for the sharing
155  *			  relationship
156  *
157  *   pg_hw_util_time_running
158  *			Total time spent collecting CU data. The time may be
159  *			less than wall time if CU counters were stopped for
160  *			some time.
161  *
162  *   pg_hw_util_time_stopped Total time the CU counters were stopped.
163  *
164  *   pg_hw_util_rate	Utilization rate, expressed in operations per second.
165  *
166  *   pg_hw_util_rate_max Maximum observed value of utilization rate.
167  */
168 struct pghw_cu_kstat {
169 	kstat_named_t	pg_id;
170 	kstat_named_t	pg_parent_id;
171 	kstat_named_t	pg_ncpus;
172 	kstat_named_t	pg_generation;
173 	kstat_named_t	pg_hw_util;
174 	kstat_named_t	pg_hw_util_time_running;
175 	kstat_named_t	pg_hw_util_time_stopped;
176 	kstat_named_t	pg_hw_util_rate;
177 	kstat_named_t	pg_hw_util_rate_max;
178 	kstat_named_t	pg_cpus;
179 	kstat_named_t	pg_relationship;
180 } pghw_cu_kstat = {
181 	{ "pg_id",		KSTAT_DATA_INT32 },
182 	{ "parent_pg_id",	KSTAT_DATA_INT32 },
183 	{ "ncpus",		KSTAT_DATA_UINT32 },
184 	{ "generation",		KSTAT_DATA_UINT32   },
185 	{ "hw_util",		KSTAT_DATA_UINT64   },
186 	{ "hw_util_time_running",	KSTAT_DATA_UINT64   },
187 	{ "hw_util_time_stopped",	KSTAT_DATA_UINT64   },
188 	{ "hw_util_rate",	KSTAT_DATA_UINT64   },
189 	{ "hw_util_rate_max",	KSTAT_DATA_UINT64   },
190 	{ "cpus",		KSTAT_DATA_STRING   },
191 	{ "relationship",	KSTAT_DATA_STRING   },
192 };
193 
194 /*
195  * Calculate the string size to represent NCPUS. Allow 5 digits for each CPU ID
196  * plus one space per CPU plus NUL byte in the end. This is only an estimate,
197  * since we try to compress CPU ranges as x-y. In the worst case the string
198  * representation of CPUs may be truncated.
199  */
200 #define	CPUSTR_LEN(ncpus) ((ncpus) * 6)
201 
202 /*
203  * Maximum length of the string that represents list of CPUs
204  */
205 static int pg_cpulist_maxlen = 0;
206 
207 static void		pghw_kstat_create(pghw_t *);
208 static int		pghw_kstat_update(kstat_t *, int);
209 static int		pghw_cu_kstat_update(kstat_t *, int);
210 static int		cpu2id(void *);
211 
212 /*
213  * hwset operations
214  */
215 static group_t		*pghw_set_create(pghw_type_t);
216 static void		pghw_set_add(group_t *, pghw_t *);
217 static void		pghw_set_remove(group_t *, pghw_t *);
218 
219 static void		pghw_cpulist_alloc(pghw_t *);
220 static int		cpu2id(void *);
221 static pgid_t		pghw_parent_id(pghw_t *);
222 
223 /*
224  * Initialize the physical portion of a hardware PG
225  */
226 void
227 pghw_init(pghw_t *pg, cpu_t *cp, pghw_type_t hw)
228 {
229 	group_t		*hwset;
230 
231 	if ((hwset = pghw_set_lookup(hw)) == NULL) {
232 		/*
233 		 * Haven't seen this hardware type yet
234 		 */
235 		hwset = pghw_set_create(hw);
236 	}
237 
238 	pghw_set_add(hwset, pg);
239 	pg->pghw_hw = hw;
240 	pg->pghw_generation = 0;
241 	pg->pghw_instance =
242 	    pg_plat_hw_instance_id(cp, hw);
243 	pghw_kstat_create(pg);
244 
245 	/*
246 	 * Hardware sharing relationship specific initialization
247 	 */
248 	switch (pg->pghw_hw) {
249 	case PGHW_POW_ACTIVE:
250 		pg->pghw_handle =
251 		    (pghw_handle_t)cpupm_domain_init(cp, CPUPM_DTYPE_ACTIVE);
252 		break;
253 	case PGHW_POW_IDLE:
254 		pg->pghw_handle =
255 		    (pghw_handle_t)cpupm_domain_init(cp, CPUPM_DTYPE_IDLE);
256 		break;
257 	default:
258 		pg->pghw_handle = (pghw_handle_t)NULL;
259 	}
260 }
261 
262 /*
263  * Teardown the physical portion of a physical PG
264  */
265 void
266 pghw_fini(pghw_t *pg)
267 {
268 	group_t		*hwset;
269 
270 	pghw_cmt_fini(pg);
271 
272 	hwset = pghw_set_lookup(pg->pghw_hw);
273 	ASSERT(hwset != NULL);
274 
275 	pghw_set_remove(hwset, pg);
276 	pg->pghw_instance = (id_t)PGHW_INSTANCE_ANON;
277 	pg->pghw_hw = (pghw_type_t)-1;
278 
279 	if (pg->pghw_kstat != NULL)
280 		kstat_delete(pg->pghw_kstat);
281 
282 }
283 
284 /*
285  * PG is removed from CMT hierarchy
286  */
287 void
288 pghw_cmt_fini(pghw_t *pg)
289 {
290 	/*
291 	 * Destroy string representation of CPUs
292 	 */
293 	if (pg->pghw_cpulist != NULL) {
294 		kmem_free(pg->pghw_cpulist,
295 		    pg->pghw_cpulist_len);
296 		pg->pghw_cpulist = NULL;
297 	}
298 
299 	/*
300 	 * Destroy CU kstats
301 	 */
302 	if (pg->pghw_cu_kstat != NULL) {
303 		kstat_delete(pg->pghw_cu_kstat);
304 		pg->pghw_cu_kstat = NULL;
305 	}
306 }
307 
308 /*
309  * Find an existing physical PG in which to place
310  * the given CPU for the specified hardware sharing
311  * relationship
312  */
313 pghw_t *
314 pghw_place_cpu(cpu_t *cp, pghw_type_t hw)
315 {
316 	group_t		*hwset;
317 
318 	if ((hwset = pghw_set_lookup(hw)) == NULL) {
319 		return (NULL);
320 	}
321 
322 	return ((pghw_t *)pg_cpu_find_pg(cp, hwset));
323 }
324 
325 /*
326  * Find the pg representing the hw sharing relationship in which
327  * cp belongs
328  */
329 pghw_t *
330 pghw_find_pg(cpu_t *cp, pghw_type_t hw)
331 {
332 	group_iter_t	i;
333 	pghw_t	*pg;
334 
335 	group_iter_init(&i);
336 	while ((pg = group_iterate(&cp->cpu_pg->pgs, &i)) != NULL) {
337 		if (pg->pghw_hw == hw)
338 			return (pg);
339 	}
340 	return (NULL);
341 }
342 
343 /*
344  * Find the PG of the given hardware sharing relationship
345  * type with the given instance id
346  */
347 pghw_t *
348 pghw_find_by_instance(id_t id, pghw_type_t hw)
349 {
350 	group_iter_t	i;
351 	group_t		*set;
352 	pghw_t		*pg;
353 
354 	set = pghw_set_lookup(hw);
355 	if (!set)
356 		return (NULL);
357 
358 	group_iter_init(&i);
359 	while ((pg = group_iterate(set, &i)) != NULL) {
360 		if (pg->pghw_instance == id)
361 			return (pg);
362 	}
363 	return (NULL);
364 }
365 
366 /*
367  * CPUs physical ID cache creation / destruction
368  * The cache's elements are initialized to the CPU's id
369  */
370 void
371 pghw_physid_create(cpu_t *cp)
372 {
373 	int	i;
374 
375 	cp->cpu_physid = kmem_alloc(sizeof (cpu_physid_t), KM_SLEEP);
376 
377 	for (i = 0; i < (sizeof (cpu_physid_t) / sizeof (id_t)); i++) {
378 		((id_t *)cp->cpu_physid)[i] = cp->cpu_id;
379 	}
380 }
381 
382 void
383 pghw_physid_destroy(cpu_t *cp)
384 {
385 	if (cp->cpu_physid) {
386 		kmem_free(cp->cpu_physid, sizeof (cpu_physid_t));
387 		cp->cpu_physid = NULL;
388 	}
389 }
390 
391 /*
392  * Create a new, empty hwset.
393  * This routine may block, and must not be called from any
394  * paused CPU context.
395  */
396 static group_t	*
397 pghw_set_create(pghw_type_t hw)
398 {
399 	group_t	*g;
400 	int	ret;
401 
402 	/*
403 	 * Create the top level PG hw group if it doesn't already exist
404 	 * This is a "set" of hardware sets, that is ordered (and indexed)
405 	 * by the pghw_type_t enum.
406 	 */
407 	if (pg_hw == NULL) {
408 		pg_hw = kmem_alloc(sizeof (group_t), KM_SLEEP);
409 		group_create(pg_hw);
410 		group_expand(pg_hw, (uint_t)PGHW_NUM_COMPONENTS);
411 	}
412 
413 	/*
414 	 * Create the new hwset
415 	 * Add it to the top level pg_hw group.
416 	 */
417 	g = kmem_alloc(sizeof (group_t), KM_SLEEP);
418 	group_create(g);
419 
420 	ret = group_add_at(pg_hw, g, (uint_t)hw);
421 	ASSERT(ret == 0);
422 
423 	return (g);
424 }
425 
426 /*
427  * Find the hwset associated with the given hardware sharing type
428  */
429 group_t *
430 pghw_set_lookup(pghw_type_t hw)
431 {
432 	group_t	*hwset;
433 
434 	if (pg_hw == NULL)
435 		return (NULL);
436 
437 	hwset = GROUP_ACCESS(pg_hw, (uint_t)hw);
438 	return (hwset);
439 }
440 
441 /*
442  * Add a PG to a hwset
443  */
444 static void
445 pghw_set_add(group_t *hwset, pghw_t *pg)
446 {
447 	(void) group_add(hwset, pg, GRP_RESIZE);
448 }
449 
450 /*
451  * Remove a PG from a hwset
452  */
453 static void
454 pghw_set_remove(group_t *hwset, pghw_t *pg)
455 {
456 	int result;
457 
458 	result = group_remove(hwset, pg, GRP_RESIZE);
459 	ASSERT(result == 0);
460 }
461 
462 /*
463  * Return a string name given a pg_hw sharing type
464  */
465 char *
466 pghw_type_string(pghw_type_t hw)
467 {
468 	switch (hw) {
469 	case PGHW_IPIPE:
470 		return ("Integer Pipeline");
471 	case PGHW_CACHE:
472 		return ("Cache");
473 	case PGHW_FPU:
474 		return ("Floating Point Unit");
475 	case PGHW_MPIPE:
476 		return ("Data Pipe to memory");
477 	case PGHW_CHIP:
478 		return ("Socket");
479 	case PGHW_MEMORY:
480 		return ("Memory");
481 	case PGHW_POW_ACTIVE:
482 		return ("CPU PM Active Power Domain");
483 	case PGHW_POW_IDLE:
484 		return ("CPU PM Idle Power Domain");
485 	default:
486 		return ("unknown");
487 	}
488 }
489 
490 /*
491  * Create / Update routines for PG hw kstats
492  *
493  * It is the intention of these kstats to provide some level
494  * of informational / debugging observability into the types
495  * and nature of the system's detected hardware sharing relationships
496  */
497 void
498 pghw_kstat_create(pghw_t *pg)
499 {
500 	char *sharing = pghw_type_string(pg->pghw_hw);
501 	char name[KSTAT_STRLEN + 1];
502 
503 	/*
504 	 * Canonify PG name to conform to kstat name rules
505 	 */
506 	(void) strncpy(name, pghw_type_string(pg->pghw_hw), KSTAT_STRLEN + 1);
507 	strident_canon(name, KSTAT_STRLEN + 1);
508 
509 	/*
510 	 * Create a hardware performance kstat
511 	 */
512 	if ((pg->pghw_kstat = kstat_create("pg", ((pg_t *)pg)->pg_id,
513 	    "pg", "pg",
514 	    KSTAT_TYPE_NAMED,
515 	    sizeof (pghw_kstat) / sizeof (kstat_named_t),
516 	    KSTAT_FLAG_VIRTUAL)) != NULL) {
517 		/* Class string, hw string, and policy string */
518 		pg->pghw_kstat->ks_data_size += PG_CLASS_NAME_MAX;
519 		pg->pghw_kstat->ks_data_size += PGHW_KSTAT_STR_LEN_MAX;
520 		pg->pghw_kstat->ks_data_size += PGHW_KSTAT_STR_LEN_MAX;
521 		pg->pghw_kstat->ks_lock = &pghw_kstat_lock;
522 		pg->pghw_kstat->ks_data = &pghw_kstat;
523 		pg->pghw_kstat->ks_update = pghw_kstat_update;
524 		pg->pghw_kstat->ks_private = pg;
525 		kstat_install(pg->pghw_kstat);
526 	}
527 
528 	if (pg_cpulist_maxlen == 0)
529 		pg_cpulist_maxlen = CPUSTR_LEN(max_ncpus);
530 
531 	/*
532 	 * Create a physical pg kstat
533 	 */
534 	if ((pg->pghw_cu_kstat = kstat_create("pg_hw_perf", ((pg_t *)pg)->pg_id,
535 	    name, "processor_group",
536 	    KSTAT_TYPE_NAMED,
537 	    sizeof (pghw_cu_kstat) / sizeof (kstat_named_t),
538 	    KSTAT_FLAG_VIRTUAL)) != NULL) {
539 		pg->pghw_cu_kstat->ks_lock = &pghw_kstat_lock;
540 		pg->pghw_cu_kstat->ks_data = &pghw_cu_kstat;
541 		pg->pghw_cu_kstat->ks_update = pghw_cu_kstat_update;
542 		pg->pghw_cu_kstat->ks_private = pg;
543 		pg->pghw_cu_kstat->ks_data_size += strlen(sharing) + 1;
544 		/* Allow space for CPU strings */
545 		pg->pghw_cu_kstat->ks_data_size += PGHW_KSTAT_STR_LEN_MAX;
546 		pg->pghw_cu_kstat->ks_data_size += pg_cpulist_maxlen;
547 		kstat_install(pg->pghw_cu_kstat);
548 	}
549 }
550 
551 int
552 pghw_kstat_update(kstat_t *ksp, int rw)
553 {
554 	struct pghw_kstat	*pgsp = &pghw_kstat;
555 	pghw_t			*pg = ksp->ks_private;
556 
557 	if (rw == KSTAT_WRITE)
558 		return (EACCES);
559 
560 	pgsp->pg_id.value.ui32 = ((pg_t *)pg)->pg_id;
561 	pgsp->pg_ncpus.value.ui32 = GROUP_SIZE(&((pg_t *)pg)->pg_cpus);
562 	pgsp->pg_instance_id.value.ui32 = pg->pghw_instance;
563 	kstat_named_setstr(&pgsp->pg_class, ((pg_t *)pg)->pg_class->pgc_name);
564 	kstat_named_setstr(&pgsp->pg_hw, pghw_type_string(pg->pghw_hw));
565 	kstat_named_setstr(&pgsp->pg_policy, pg_policy_name((pg_t *)pg));
566 	return (0);
567 }
568 
569 int
570 pghw_cu_kstat_update(kstat_t *ksp, int rw)
571 {
572 	struct pghw_cu_kstat	*pgsp = &pghw_cu_kstat;
573 	pghw_t			*pg = ksp->ks_private;
574 	pghw_util_t		*hw_util = &pg->pghw_stats;
575 	boolean_t		has_cpc_privilege;
576 
577 	if (rw == KSTAT_WRITE)
578 		return (EACCES);
579 
580 	/*
581 	 * Check whether the caller has priv_cpc_cpu privilege. If it doesn't,
582 	 * it will not get hardware utilization data.
583 	 */
584 
585 	has_cpc_privilege = (secpolicy_cpc_cpu(crgetcred()) == 0);
586 
587 	pgsp->pg_id.value.i32 = ((pg_t *)pg)->pg_id;
588 	pgsp->pg_parent_id.value.i32 = (int)pghw_parent_id(pg);
589 
590 	pgsp->pg_ncpus.value.ui32 = GROUP_SIZE(&((pg_t *)pg)->pg_cpus);
591 
592 	/*
593 	 * Allocate memory for the string representing the list of CPUs in PG.
594 	 * This memory should persist past the call to pghw_cu_kstat_update()
595 	 * since the kstat snapshot routine will reference this memory.
596 	 */
597 	pghw_cpulist_alloc(pg);
598 
599 	if (pg->pghw_kstat_gen != pg->pghw_generation) {
600 		/*
601 		 * PG kstat generation number is out of sync with PG's
602 		 * generation mumber. It means that some CPUs could have joined
603 		 * or left PG and it is not possible to compare the numbers
604 		 * obtained before and after the generation change.
605 		 *
606 		 * Reset the maximum utilization rate and start computing it
607 		 * from scratch.
608 		 */
609 		hw_util->pghw_util = 0;
610 		hw_util->pghw_rate_max = 0;
611 		pg->pghw_kstat_gen = pg->pghw_generation;
612 	}
613 
614 	/*
615 	 * We can't block on CPU lock because when PG is destroyed (under
616 	 * cpu_lock) it tries to delete this kstat and it will wait for us to
617 	 * complete which will never happen since we are waiting for cpu_lock to
618 	 * drop. Deadlocks are fun!
619 	 */
620 	if (mutex_tryenter(&cpu_lock)) {
621 		if (pg->pghw_cpulist != NULL &&
622 		    *(pg->pghw_cpulist) == '\0') {
623 			(void) group2intlist(&(((pg_t *)pg)->pg_cpus),
624 			    pg->pghw_cpulist, pg->pghw_cpulist_len, cpu2id);
625 		}
626 
627 		if (has_cpc_privilege)
628 			cu_pg_update(pg);
629 
630 		mutex_exit(&cpu_lock);
631 	}
632 
633 	pgsp->pg_generation.value.ui32 = pg->pghw_kstat_gen;
634 	if (pg->pghw_cpulist != NULL)
635 		kstat_named_setstr(&pgsp->pg_cpus, pg->pghw_cpulist);
636 	else
637 		kstat_named_setstr(&pgsp->pg_cpus, "");
638 
639 	kstat_named_setstr(&pgsp->pg_relationship,
640 	    pghw_type_string(pg->pghw_hw));
641 
642 	if (has_cpc_privilege) {
643 		pgsp->pg_hw_util.value.ui64 = hw_util->pghw_util;
644 		pgsp->pg_hw_util_time_running.value.ui64 =
645 		    hw_util->pghw_time_running;
646 		pgsp->pg_hw_util_time_stopped.value.ui64 =
647 		    hw_util->pghw_time_stopped;
648 		pgsp->pg_hw_util_rate.value.ui64 = hw_util->pghw_rate;
649 		pgsp->pg_hw_util_rate_max.value.ui64 = hw_util->pghw_rate_max;
650 	} else {
651 		pgsp->pg_hw_util.value.ui64 = 0;
652 		pgsp->pg_hw_util_time_running.value.ui64 = 0;
653 		pgsp->pg_hw_util_time_stopped.value.ui64 = 0;
654 		pgsp->pg_hw_util_rate.value.ui64 = 0;
655 		pgsp->pg_hw_util_rate_max.value.ui64 = 0;
656 	}
657 
658 	return (0);
659 }
660 
661 /*
662  * Update the string representation of CPUs in PG (pg->pghw_cpulist).
663  * The string representation is used for kstats.
664  *
665  * The string is allocated if it has not already been or if it is already
666  * allocated and PG has more CPUs now. If PG has smaller or equal number of
667  * CPUs, but the actual CPUs may have changed, the string is reset to the empty
668  * string causes the string representation to be recreated. The pghw_generation
669  * field is used to detect whether CPUs within the pg may have changed.
670  */
671 static void
672 pghw_cpulist_alloc(pghw_t *pg)
673 {
674 	uint_t	ncpus = GROUP_SIZE(&((pg_t *)pg)->pg_cpus);
675 	size_t	len = CPUSTR_LEN(ncpus);
676 
677 	/*
678 	 * If the pghw_cpulist string is already allocated we need to make sure
679 	 * that it has sufficient length. Also if the set of CPUs may have
680 	 * changed, we need to re-generate the string.
681 	 */
682 	if (pg->pghw_cpulist != NULL &&
683 	    pg->pghw_kstat_gen != pg->pghw_generation) {
684 		if (len <= pg->pghw_cpulist_len) {
685 			/*
686 			 * There is sufficient space in the pghw_cpulist for
687 			 * the new set of CPUs. Just clear the string to trigger
688 			 * re-generation of list of CPUs
689 			 */
690 			*(pg->pghw_cpulist) = '\0';
691 		} else {
692 			/*
693 			 * There is, potentially, insufficient space in
694 			 * pghw_cpulist, so reallocate the string.
695 			 */
696 			ASSERT(strlen(pg->pghw_cpulist) < pg->pghw_cpulist_len);
697 			kmem_free(pg->pghw_cpulist, pg->pghw_cpulist_len);
698 			pg->pghw_cpulist = NULL;
699 			pg->pghw_cpulist_len = 0;
700 		}
701 	}
702 
703 	if (pg->pghw_cpulist == NULL) {
704 		/*
705 		 * Allocate space to hold cpulist.
706 		 *
707 		 * Length can not be bigger that the maximum space we have
708 		 * allowed for the kstat buffer
709 		 */
710 		if (len > pg_cpulist_maxlen)
711 			len = pg_cpulist_maxlen;
712 		if (len > 0) {
713 			pg->pghw_cpulist = kmem_zalloc(len, KM_NOSLEEP);
714 			if (pg->pghw_cpulist != NULL)
715 				pg->pghw_cpulist_len = len;
716 		}
717 	}
718 }
719 
720 static int
721 cpu2id(void *v)
722 {
723 	cpu_t *cp = (cpu_t *)v;
724 
725 	ASSERT(v != NULL);
726 
727 	return (cp->cpu_id);
728 }
729 
730 /*
731  * Return parent ID or -1 if there is no parent.
732  * All hardware PGs are currently also CMT PGs, but for safety we check the
733  * class matches cmt before we upcast the pghw pointer to pg_cmt_t.
734  */
735 static pgid_t
736 pghw_parent_id(pghw_t *pghw)
737 {
738 	pg_t *pg = (pg_t *)pghw;
739 	pgid_t parent_id = -1;
740 
741 	if (pg != NULL && strcmp(pg->pg_class->pgc_name, "cmt") == 0) {
742 		pg_cmt_t *cmt = (pg_cmt_t *)pg;
743 		pg_t *parent = (pg_t *)cmt->cmt_parent;
744 		if (parent != NULL)
745 			parent_id = parent->pg_id;
746 	}
747 
748 	return (parent_id);
749 }
750