xref: /illumos-gate/usr/src/lib/liblgrp/common/lgrp.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 /*
31  * lgroup interface
32  */
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <sys/bitmap.h>
39 #include <sys/pset.h>
40 #include <sys/types.h>
41 
42 #include <sys/lgrp_user.h>
43 
44 
45 /*
46  * Fast trap for getting home lgroup of current thread
47  */
48 extern lgrp_id_t	_lgrp_home_fast(void);
49 
50 /*
51  * lgroup system call
52  */
53 extern int		_lgrpsys(int subcode, long arg, void *ap);
54 
55 static int lgrp_cpus_hier(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp,
56     processorid_t **cpuids, uint_t *count);
57 
58 
59 /*
60  * Get generation ID of lgroup hierarchy given view
61  * which changes whenever the hierarchy changes (eg. DR or pset contents
62  * change for caller's view)
63  */
64 static lgrp_gen_t
65 lgrp_generation(lgrp_view_t view)
66 {
67 	return (_lgrpsys(LGRP_SYS_GENERATION, view, NULL));
68 }
69 
70 
71 /*
72  * Get supported revision number of lgroup interface
73  */
74 int
75 lgrp_version(int version)
76 {
77 	return (_lgrpsys(LGRP_SYS_VERSION, version, NULL));
78 }
79 
80 
81 /*
82  * Get affinity for given lgroup
83  */
84 lgrp_affinity_t
85 lgrp_affinity_get(idtype_t idtype, id_t id, lgrp_id_t lgrp)
86 {
87 	lgrp_affinity_args_t	args;
88 
89 	args.idtype = idtype;
90 	args.id = id;
91 	args.lgrp = lgrp;
92 	return (_lgrpsys(LGRP_SYS_AFFINITY_GET, 0, (void *)&args));
93 }
94 
95 
96 /*
97  * Set affinity for given lgroup
98  */
99 int
100 lgrp_affinity_set(idtype_t idtype, id_t id, lgrp_id_t lgrp,
101     lgrp_affinity_t aff)
102 {
103 	lgrp_affinity_args_t	args;
104 
105 	args.idtype = idtype;
106 	args.id = id;
107 	args.lgrp = lgrp;
108 	args.aff = aff;
109 	return (_lgrpsys(LGRP_SYS_AFFINITY_SET, 0, (void *)&args));
110 }
111 
112 
113 /*
114  * Get home lgroup for given process or thread
115  */
116 lgrp_id_t
117 lgrp_home(idtype_t idtype, id_t id)
118 {
119 	/*
120 	 * Use fast trap to get home lgroup of current thread or process
121 	 * Otherwise, use system call for other process or thread
122 	 */
123 	if (id == P_MYID && (idtype == P_LWPID || idtype == P_PID))
124 		return (_lgrp_home_fast());
125 	else
126 		return (_lgrpsys(LGRP_SYS_HOME, idtype, (void *)(intptr_t)id));
127 }
128 
129 
130 /*
131  * Get a snapshot of the lgroup hierarchy
132  */
133 static int
134 lgrp_snapshot(void *buf, size_t bufsize)
135 {
136 	return (_lgrpsys(LGRP_SYS_SNAPSHOT, bufsize, buf));
137 }
138 
139 
140 /*
141  * Find any orphan lgroups without parents and make them be children of
142  * root lgroup
143  */
144 static int
145 parent_orphans(lgrp_snapshot_header_t *snap)
146 {
147 	int		i;
148 	lgrp_info_t	*lgrp_info;
149 	int		nlgrpsmax;
150 	int		orphan;
151 	lgrp_info_t	*root;
152 	ulong_t		*parents;
153 
154 	if (snap == NULL || snap->ss_info == NULL ||
155 	    snap->ss_parents == NULL || snap->ss_root < 0 ||
156 	    snap->ss_root >= snap->ss_nlgrps_max)
157 		return (-1);
158 
159 	nlgrpsmax = snap->ss_nlgrps_max;
160 	root = &snap->ss_info[snap->ss_root];
161 
162 	for (i = 0; i < nlgrpsmax; i++) {
163 		int	j;
164 
165 		/*
166 		 * Skip root lgroup
167 		 */
168 		if (i == snap->ss_root)
169 			continue;
170 
171 		lgrp_info = &snap->ss_info[i];
172 		if (lgrp_info == NULL || lgrp_info->info_lgrpid == LGRP_NONE)
173 			continue;
174 
175 		/*
176 		 * Make sure parents bitmap is setup
177 		 */
178 		if (lgrp_info->info_parents == NULL)
179 			lgrp_info->info_parents =
180 			    (ulong_t *)((uintptr_t)snap->ss_parents +
181 			    (i * BT_SIZEOFMAP(nlgrpsmax)));
182 
183 		/*
184 		 * Look for orphans (lgroups with no parents)
185 		 */
186 		orphan = 1;
187 		parents = lgrp_info->info_parents;
188 		for (j = 0; j < BT_BITOUL(nlgrpsmax); j++)
189 			if (parents[j] != 0) {
190 				orphan = 0;
191 				break;
192 			}
193 
194 		/*
195 		 * Make root be parent of any orphans
196 		 */
197 		if (orphan) {
198 			BT_SET(parents, root->info_lgrpid);
199 			if (root->info_children) {
200 				BT_SET(root->info_children, i);
201 			}
202 		}
203 	}
204 
205 	return (0);
206 }
207 
208 
209 /*
210  * Remove given lgroup from parent lgroup(s)
211  */
212 static void
213 prune_child(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp)
214 {
215 	int		i;
216 	lgrp_info_t	*lgrp_info;
217 	ulong_t		*parents;
218 
219 	if (snap == NULL || lgrp < 0 || lgrp > snap->ss_nlgrps_max)
220 		return;
221 
222 	lgrp_info = &snap->ss_info[lgrp];
223 
224 	parents = lgrp_info->info_parents;
225 	if (parents == NULL)
226 		return;
227 
228 	/*
229 	 * Update children of parents not to include given lgroup
230 	 */
231 	for (i = 0; i < snap->ss_nlgrps_max; i++) {
232 		if (BT_TEST(parents, i)) {
233 			lgrp_info = &snap->ss_info[i];
234 			BT_CLEAR(lgrp_info->info_children, lgrp);
235 		}
236 	}
237 }
238 
239 /*
240  * Prune any CPUs not in given array from specified lgroup
241  */
242 static void
243 prune_cpus(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp, processorid_t *cpus,
244     int ncpus)
245 {
246 	int		count;
247 	int		i;
248 	int		j;
249 	int		k;
250 	lgrp_info_t	*lgrp_info;
251 	uint_t		lgrp_ncpus;
252 	processorid_t	*lgrp_cpus;
253 
254 	if (snap == NULL || lgrp < 0 || lgrp > snap->ss_nlgrps_max)
255 		return;
256 
257 	lgrp_info = &snap->ss_info[lgrp];
258 
259 	/*
260 	 * No CPUs to remove
261 	 */
262 	if (ncpus == 0 || lgrp_info->info_ncpus == 0)
263 		return;
264 
265 	/*
266 	 * Remove all CPUs from lgroup
267 	 */
268 	if (cpus == NULL && ncpus == -1) {
269 		lgrp_info->info_ncpus = 0;
270 		return;
271 	}
272 
273 	/*
274 	 * Remove any CPUs from lgroup not in given list of CPUs
275 	 */
276 	lgrp_cpus = lgrp_info->info_cpuids;
277 	lgrp_ncpus = lgrp_info->info_ncpus;
278 	i = 0;
279 	for (count = 0; count < lgrp_ncpus; count++) {
280 		/*
281 		 * Look for CPU in list
282 		 */
283 		for (j = 0; j < ncpus; j++)
284 			if (lgrp_cpus[i] == cpus[j])
285 				break;
286 
287 		/*
288 		 * Go to next CPU if found this one in list
289 		 */
290 		if (j < ncpus) {
291 			i++;
292 			continue;
293 		}
294 
295 		/*
296 		 * Remove this CPU and shift others into its place
297 		 * and decrement number of CPUs
298 		 */
299 		for (k = i + 1; k < lgrp_info->info_ncpus; k++)
300 			lgrp_cpus[k - 1] = lgrp_cpus[k];
301 		lgrp_cpus[k - 1] = -1;
302 		lgrp_info->info_ncpus--;
303 	}
304 }
305 
306 
307 /*
308  * Prune lgroup hierarchy for caller's view
309  */
310 static int
311 prune_tree(lgrp_snapshot_header_t *snap)
312 {
313 	processorid_t	*cpus;
314 	int		i;
315 	lgrp_info_t	*lgrp_info;
316 	lgrp_mem_size_t	nbytes;
317 	uint_t		ncpus;
318 	int		nlgrps_max;
319 
320 	if (snap == NULL || snap->ss_info == NULL)
321 		return (-1);
322 
323 	/*
324 	 * Get CPUs in caller's pset
325 	 */
326 	if (pset_info(PS_MYID, NULL, &ncpus, NULL) == -1)
327 		return (-1);
328 
329 	cpus = NULL;
330 	if (ncpus > 0) {
331 		cpus = malloc(ncpus * sizeof (processorid_t));
332 		if (pset_info(PS_MYID, NULL, &ncpus, cpus) == -1) {
333 			free(cpus);
334 			return (-1);
335 		}
336 	}
337 
338 	/*
339 	 * Remove any CPUs not in caller's pset from lgroup hierarchy
340 	 */
341 	nlgrps_max = snap->ss_nlgrps_max;
342 	for (i = 0; i < nlgrps_max; i++) {
343 		lgrp_info = &snap->ss_info[i];
344 		if (BT_TEST(snap->ss_lgrpset, i))
345 			prune_cpus(snap, i, cpus, ncpus);
346 		else if (lgrp_info->info_lgrpid != LGRP_NONE)
347 			prune_cpus(snap, i, NULL, -1);
348 	}
349 
350 	if (ncpus > 0)
351 		free(cpus);
352 
353 	/*
354 	 * Change lgroup bitmask from just reflecting lgroups overlapping
355 	 * caller's pset to all lgroups available to caller, starting by
356 	 * filling in all lgroups and then removing any empty ones below
357 	 */
358 	for (i = 0; i < nlgrps_max; i++) {
359 		lgrp_info = &snap->ss_info[i];
360 		if (lgrp_info->info_lgrpid == LGRP_NONE)
361 			continue;
362 
363 		BT_SET(snap->ss_lgrpset, i);
364 	}
365 
366 	/*
367 	 * Remove empty lgroups from lgroup hierarchy, removing it from its
368 	 * parents and decrementing nlgrps
369 	 */
370 	for (i = 0; i < nlgrps_max; i++) {
371 		lgrp_info = &snap->ss_info[i];
372 		if (lgrp_info->info_lgrpid == LGRP_NONE)
373 			continue;
374 
375 		ncpus = lgrp_cpus_hier(snap, i, NULL, NULL);
376 		nbytes = lgrp_mem_size((lgrp_cookie_t)snap, i,
377 		    LGRP_MEM_SZ_INSTALLED, LGRP_CONTENT_HIERARCHY);
378 		if (ncpus == 0 && nbytes == 0) {
379 			BT_CLEAR(snap->ss_lgrpset, i);
380 			prune_child(snap, i);
381 			snap->ss_nlgrps--;
382 		}
383 	}
384 
385 	return (0);
386 }
387 
388 
389 /*
390  * Initialize lgroup interface
391  */
392 lgrp_cookie_t
393 lgrp_init(lgrp_view_t view)
394 {
395 	ssize_t			bufsize;
396 	uint_t			gen;
397 	int			i;
398 	lgrp_snapshot_header_t	*snap;
399 
400 	/*
401 	 * Check for legal view
402 	 */
403 	if (view != LGRP_VIEW_OS && view != LGRP_VIEW_CALLER) {
404 		errno = EINVAL;
405 		return (LGRP_COOKIE_NONE);
406 	}
407 
408 	/*
409 	 * Try to take a consistent snapshot of lgroup hierarchy
410 	 */
411 	snap = NULL;
412 	while (snap == NULL) {
413 		/*
414 		 * Get lgroup generation number before taking snapshot
415 		 */
416 		gen = lgrp_generation(view);
417 
418 		/*
419 		 * Get size of buffer needed for snapshot
420 		 */
421 		bufsize = lgrp_snapshot(NULL, 0);
422 		if (bufsize <= 0) {
423 			if (errno == ENOMEM)
424 				return (LGRP_COOKIE_NONE);
425 
426 			snap = NULL;
427 			continue;
428 		}
429 
430 		/*
431 		 * Allocate buffer
432 		 */
433 		snap = malloc(bufsize);
434 		if (snap == NULL)
435 			return (LGRP_COOKIE_NONE);
436 		bzero(snap, bufsize);
437 
438 		/*
439 		 * Take snapshot of lgroup hierarchy
440 		 */
441 		bufsize = lgrp_snapshot(snap, bufsize);
442 		if (bufsize <= 0) {
443 			free(snap);
444 			if (errno == ENOMEM)
445 				return (LGRP_COOKIE_NONE);
446 
447 			snap = NULL;
448 			continue;
449 		}
450 
451 		/*
452 		 * See whether lgroup generation number changed
453 		 */
454 		if (gen == lgrp_generation(view))
455 			break;
456 
457 		free(snap);
458 		snap = NULL;
459 	}
460 
461 	/*
462 	 * Remember generation number and view of this snapshot
463 	 */
464 	snap->ss_gen = gen;
465 	snap->ss_view = view;
466 
467 	/*
468 	 * Keep caller's pset ID for caller's view
469 	 */
470 	snap->ss_pset = 0;
471 	if (view == LGRP_VIEW_CALLER) {
472 		psetid_t	pset;
473 
474 		if (pset_bind(PS_QUERY, P_LWPID, P_MYID, &pset) == -1)
475 			return ((uintptr_t)-1);
476 
477 		snap->ss_pset = pset;
478 	}
479 
480 	/*
481 	 * Find any orphan lgroups without parents and make them be children
482 	 * of the root lgroup
483 	 */
484 	if (snap->ss_levels > 1)
485 		(void) parent_orphans(snap);
486 
487 	/*
488 	 * Prune snapshot of lgroup hierarchy for caller's view
489 	 */
490 	if (view == LGRP_VIEW_CALLER)
491 		(void) prune_tree(snap);
492 	else {
493 		/*
494 		 * Change lgroup bitmask from just reflecting lgroups
495 		 * overlapping caller's pset to all lgroups available
496 		 */
497 		for (i = 0; i < snap->ss_nlgrps_max; i++) {
498 			lgrp_info_t	*lgrp_info;
499 
500 			lgrp_info = &snap->ss_info[i];
501 			if (lgrp_info->info_lgrpid == LGRP_NONE)
502 				continue;
503 
504 			BT_SET(snap->ss_lgrpset, i);
505 		}
506 	}
507 
508 	return ((uintptr_t)snap);
509 }
510 
511 
512 /*
513  * Return whether given cookie is out-of-date (stale) or not
514  */
515 int
516 lgrp_cookie_stale(lgrp_cookie_t cookie)
517 {
518 	psetid_t		pset;
519 	lgrp_snapshot_header_t	*snap;
520 
521 	/*
522 	 * Check for bad cookie
523 	 */
524 	snap = (lgrp_snapshot_header_t *)cookie;
525 	if (snap == NULL || snap->ss_magic != cookie) {
526 		errno = EINVAL;
527 		return (-1);
528 	}
529 
530 	/*
531 	 * Check generation number which changes when lgroup hierarchy changes
532 	 * or pset contents change for caller's view
533 	 */
534 	if (snap->ss_gen != lgrp_generation(snap->ss_view))
535 		return (1);
536 
537 	/*
538 	 * See whether pset binding has changed for caller's view
539 	 */
540 	if (snap->ss_view == LGRP_VIEW_CALLER) {
541 		if (pset_bind(PS_QUERY, P_LWPID, P_MYID, &pset) == -1)
542 			return (-1);
543 		if (snap->ss_pset != pset)
544 			return (1);
545 	}
546 
547 	return (0);	/* cookie isn't stale */
548 }
549 
550 
551 /*
552  * Get view of lgroup hierarchy from snapshot represented by given cookie
553  */
554 lgrp_view_t
555 lgrp_view(lgrp_cookie_t cookie)
556 {
557 	lgrp_snapshot_header_t	*snap;
558 
559 	snap = (lgrp_snapshot_header_t *)cookie;
560 	if (snap == NULL || snap->ss_magic != cookie) {
561 		errno = EINVAL;
562 		return (-1);
563 	}
564 
565 	return (snap->ss_view);
566 }
567 
568 
569 /*
570  * Get number of lgroups
571  */
572 int
573 lgrp_nlgrps(lgrp_cookie_t cookie)
574 {
575 	lgrp_snapshot_header_t	*snap;
576 
577 	snap = (lgrp_snapshot_header_t *)cookie;
578 
579 	if (snap == NULL || snap->ss_magic != cookie) {
580 		errno = EINVAL;
581 		return (-1);
582 	}
583 
584 	return (snap->ss_nlgrps);
585 }
586 
587 
588 /*
589  * Return root lgroup ID
590  */
591 lgrp_id_t
592 lgrp_root(lgrp_cookie_t cookie)
593 {
594 	lgrp_snapshot_header_t	*snap;
595 
596 	snap = (lgrp_snapshot_header_t *)cookie;
597 
598 	if (snap == NULL || snap->ss_magic != cookie) {
599 		errno = EINVAL;
600 		return (-1);
601 	}
602 
603 	return (snap->ss_root);
604 }
605 
606 
607 /*
608  * Get parent lgroups of given lgroup
609  */
610 int
611 lgrp_parents(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *parents,
612     uint_t count)
613 {
614 	int			i;
615 	ulong_t			*lgrp_parents;
616 	lgrp_snapshot_header_t	*snap;
617 	int			nlgrps_max;
618 	int			nparents;
619 
620 	snap = (lgrp_snapshot_header_t *)cookie;
621 
622 	/*
623 	 * Check for valid arguments
624 	 */
625 	if (snap == NULL || snap->ss_magic != cookie ||
626 	    lgrp < 0 || lgrp == LGRP_NONE) {
627 		errno = EINVAL;
628 		return (-1);
629 	}
630 
631 	/*
632 	 * See whether given lgroup exists
633 	 */
634 	nlgrps_max = snap->ss_nlgrps_max;
635 	if (lgrp >= nlgrps_max || !BT_TEST(snap->ss_lgrpset, lgrp)) {
636 		errno = ESRCH;
637 		return (-1);
638 	}
639 
640 	/*
641 	 * No parents, since given lgroup is root lgroup or
642 	 * only one level in lgroup hierarchy (ie. SMP)
643 	 */
644 	if (lgrp == snap->ss_root || snap->ss_levels == 1) {
645 		if (parents == NULL || count < 1)
646 			return (0);
647 		return (0);
648 	}
649 
650 	/*
651 	 * Make sure that parents exist
652 	 */
653 	if (snap->ss_parents == NULL) {
654 		errno = ESRCH;
655 		return (-1);
656 	}
657 
658 	/*
659 	 * Given lgroup should have a parent
660 	 */
661 	lgrp_parents = &snap->ss_parents[lgrp * BT_BITOUL(nlgrps_max)];
662 	if (lgrp_parents == NULL) {
663 		errno = ESRCH;
664 		return (-1);
665 	}
666 
667 	/*
668 	 * Check lgroup parents bitmask, fill in parents array, and return
669 	 * number of parents
670 	 */
671 	nparents = 0;
672 	for (i = 0; i < nlgrps_max; i++) {
673 		if (BT_TEST(lgrp_parents, i)) {
674 			if (parents != NULL && nparents < count) {
675 				parents[nparents] = i;
676 			}
677 			nparents++;
678 		}
679 	}
680 	return (nparents);
681 }
682 
683 
684 /*
685  * Get children lgroups of given lgroup
686  */
687 int
688 lgrp_children(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *children,
689     uint_t count)
690 {
691 	int			i;
692 	ulong_t			*lgrp_children;
693 	int			nlgrps_max;
694 	int			nchildren;
695 	lgrp_snapshot_header_t	*snap;
696 
697 	snap = (lgrp_snapshot_header_t *)cookie;
698 
699 	/*
700 	 * Check for valid arguments
701 	 */
702 	if (snap == NULL || snap->ss_magic != cookie ||
703 	    lgrp < 0 || lgrp == LGRP_NONE) {
704 		errno = EINVAL;
705 		return (-1);
706 	}
707 
708 	/*
709 	 * See whether given lgroup exists
710 	 */
711 	nlgrps_max = snap->ss_nlgrps_max;
712 	if (lgrp >= nlgrps_max || !BT_TEST(snap->ss_lgrpset, lgrp)) {
713 		errno = ESRCH;
714 		return (-1);
715 	}
716 
717 	/*
718 	 * No children, since only one level in lgroup hierarchy (ie. SMP)
719 	 */
720 	if (snap->ss_levels == 1) {
721 		if (children == NULL || count < 1)
722 			return (0);
723 		return (0);
724 	}
725 
726 	/*
727 	 * Make sure that children exist
728 	 */
729 	if (snap->ss_children == NULL) {
730 		errno = ESRCH;
731 		return (-1);
732 	}
733 
734 	/*
735 	 * Given lgroup may not have any children
736 	 */
737 	lgrp_children = &snap->ss_children[lgrp * BT_BITOUL(nlgrps_max)];
738 
739 	if (lgrp_children == NULL)
740 		return (0);
741 
742 	/*
743 	 * Check lgroup children bitmask, fill in children array, and return
744 	 * number of children
745 	 */
746 	nchildren = 0;
747 	for (i = 0; i < nlgrps_max; i++) {
748 		if (BT_TEST(lgrp_children, i)) {
749 			if (children != NULL && nchildren < count)
750 				children[nchildren] = i;
751 			nchildren++;
752 		}
753 	}
754 	return (nchildren);
755 }
756 
757 
758 /*
759  * Get all CPUs within given lgroup (hierarchy)
760  */
761 static int
762 lgrp_cpus_hier(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp,
763     processorid_t **cpuids, uint_t *count)
764 {
765 	processorid_t	*cpus;
766 	int		i;
767 	int		j;
768 	lgrp_info_t	*lgrp_info;
769 	int		ncpus;
770 	int		nlgrps_max;
771 	ulong_t		*rset;
772 	int		total;
773 
774 	/*
775 	 * Get lgroup info
776 	 */
777 	lgrp_info = &snap->ss_info[lgrp];
778 
779 	if (lgrp_info == NULL) {
780 		errno = ESRCH;
781 		return (-1);
782 	}
783 
784 	/*
785 	 * Check whether given lgroup contains any lgroups with CPU resources
786 	 */
787 	if (lgrp_info->info_rset == NULL)
788 		return (0);
789 
790 	nlgrps_max = snap->ss_nlgrps_max;
791 	rset = &lgrp_info->info_rset[LGRP_RSRC_CPU * BT_BITOUL(nlgrps_max)];
792 
793 	/*
794 	 * Get all CPUs within this lgroup
795 	 */
796 	total = 0;
797 	for (i = 0; i < nlgrps_max; i++) {
798 		if (!BT_TEST(rset, i))
799 			continue;
800 
801 		lgrp_info = &snap->ss_info[i];
802 
803 		/*
804 		 * Get all CPUs within lgroup
805 		 */
806 		cpus = lgrp_info->info_cpuids;
807 		ncpus = lgrp_info->info_ncpus;
808 		total += ncpus;
809 
810 		/*
811 		 * Copy as many CPU IDs into array that will fit
812 		 * and decrement count and increment array pointer
813 		 * as we go
814 		 */
815 		if (cpuids && *cpuids && count) {
816 			for (j = 0; j < ncpus; j++) {
817 				if (*count) {
818 					**cpuids = cpus[j];
819 					(*cpuids)++;
820 					(*count)--;
821 				}
822 			}
823 		}
824 	}
825 
826 	return (total);
827 }
828 
829 
830 /*
831  * Get CPUs in given lgroup
832  */
833 int
834 lgrp_cpus(lgrp_cookie_t cookie, lgrp_id_t lgrp, processorid_t *cpuids,
835     uint_t count, lgrp_content_t content)
836 {
837 	int			i;
838 	processorid_t		*cpus;
839 	lgrp_info_t		*lgrp_info;
840 	int			ncpus;
841 	lgrp_snapshot_header_t	*snap;
842 
843 	snap = (lgrp_snapshot_header_t *)cookie;
844 
845 	/*
846 	 * Check for valid arguments
847 	 */
848 	if (snap == NULL || snap->ss_magic != cookie ||
849 	    lgrp < 0 || lgrp == LGRP_NONE ||
850 	    (content != LGRP_CONTENT_DIRECT &&
851 	    content != LGRP_CONTENT_HIERARCHY)) {
852 		errno = EINVAL;
853 		return (-1);
854 	}
855 
856 	/*
857 	 * See whether given lgroup exists
858 	 */
859 	if (lgrp >= snap->ss_nlgrps_max || snap->ss_info == NULL ||
860 	    !BT_TEST(snap->ss_lgrpset, lgrp)) {
861 		errno = ESRCH;
862 		return (-1);
863 	}
864 
865 	/*
866 	 * Get lgroup info
867 	 */
868 	lgrp_info = &snap->ss_info[lgrp];
869 
870 	/*
871 	 * Get contents of lgroup
872 	 */
873 	switch (content) {
874 	case LGRP_CONTENT_DIRECT:
875 		/*
876 		 * Get CPUs contained directly within given lgroup
877 		 */
878 		cpus = lgrp_info->info_cpuids;
879 		ncpus = lgrp_info->info_ncpus;
880 
881 		/*
882 		 * No array to copy CPU IDs into,
883 		 * so just return number of CPUs.
884 		 */
885 		if (cpuids == NULL)
886 			return (ncpus);
887 
888 		/*
889 		 * Copy as many CPU IDs into array that will fit
890 		 */
891 		for (i = 0; i < ncpus; i++)
892 			if (i < count)
893 				cpuids[i] = cpus[i];
894 
895 		return (ncpus);
896 
897 	case LGRP_CONTENT_ALL:
898 		return (lgrp_cpus_hier(snap, lgrp, &cpuids, &count));
899 
900 	default:
901 		errno = EINVAL;
902 		return (-1);
903 	}
904 }
905 
906 
907 /*
908  * Return physical memory size in pages for given lgroup
909  */
910 lgrp_mem_size_t
911 lgrp_mem_size(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_mem_size_flag_t type,
912     lgrp_content_t content)
913 {
914 	int			i;
915 	lgrp_info_t		*lgrp_info;
916 	int			nlgrps_max;
917 	int			pgsz;
918 	ulong_t			*rset;
919 	lgrp_mem_size_t		size;
920 	lgrp_snapshot_header_t	*snap;
921 
922 	snap = (lgrp_snapshot_header_t *)cookie;
923 
924 	/*
925 	 * Check for valid arguments
926 	 */
927 	if (snap == NULL || snap->ss_magic != cookie ||
928 	    lgrp < 0 || lgrp == LGRP_NONE) {
929 		errno = EINVAL;
930 		return (-1);
931 	}
932 
933 	/*
934 	 * See whether given lgroup exists
935 	 */
936 	nlgrps_max = snap->ss_nlgrps_max;
937 	if (lgrp >= nlgrps_max || snap->ss_info == NULL ||
938 	    !BT_TEST(snap->ss_lgrpset, lgrp)) {
939 		errno = ESRCH;
940 		return (-1);
941 	}
942 
943 	pgsz = getpagesize();
944 
945 	/*
946 	 * Get lgroup info
947 	 */
948 	lgrp_info = &snap->ss_info[lgrp];
949 
950 	switch (content) {
951 	case LGRP_CONTENT_DIRECT:
952 		/*
953 		 * Get memory contained directly in this lgroup
954 		 */
955 		switch (type) {
956 		case LGRP_MEM_SZ_FREE:
957 			size = (lgrp_mem_size_t)pgsz *
958 			    lgrp_info->info_mem_free;
959 			return (size);
960 		case LGRP_MEM_SZ_INSTALLED:
961 			size = (lgrp_mem_size_t)pgsz *
962 			    lgrp_info->info_mem_install;
963 			return (size);
964 		default:
965 			errno = EINVAL;
966 			return (-1);
967 		}
968 
969 	case LGRP_CONTENT_ALL:
970 		/*
971 		 * Get memory contained within this lgroup (and its children)
972 		 */
973 		/*
974 		 * Check whether given lgroup contains any lgroups with CPU
975 		 * resources
976 		 */
977 		if (lgrp_info->info_rset == NULL)
978 			return (0);
979 
980 		rset = &lgrp_info->info_rset[LGRP_RSRC_MEM *
981 		    BT_BITOUL(nlgrps_max)];
982 
983 		/*
984 		 * Add up memory in lgroup resources
985 		 */
986 		size = 0;
987 		for (i = 0; i < nlgrps_max; i++) {
988 			if (!BT_TEST(rset, i))
989 				continue;
990 
991 			lgrp_info = &snap->ss_info[i];
992 			switch (type) {
993 			case LGRP_MEM_SZ_FREE:
994 				size += (lgrp_mem_size_t)pgsz *
995 				    lgrp_info->info_mem_free;
996 				break;
997 			case LGRP_MEM_SZ_INSTALLED:
998 				size += (lgrp_mem_size_t)pgsz *
999 				    lgrp_info->info_mem_install;
1000 				break;
1001 			default:
1002 				errno = EINVAL;
1003 				return (-1);
1004 			}
1005 
1006 		}
1007 
1008 		return (size);
1009 
1010 	default:
1011 		errno = EINVAL;
1012 		return (-1);
1013 	}
1014 }
1015 
1016 
1017 /*
1018  * Get resources for a particuliar lgroup
1019  */
1020 int
1021 lgrp_resources(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *lgrps,
1022     uint_t count, lgrp_rsrc_t type)
1023 {
1024 	int			i;
1025 	lgrp_info_t		*lgrp_info;
1026 	int			nlgrps;
1027 	int			nlgrps_max;
1028 	ulong_t			*rset;
1029 	lgrp_snapshot_header_t	*snap;
1030 
1031 	snap = (lgrp_snapshot_header_t *)cookie;
1032 
1033 	/*
1034 	 * Check for valid arguments
1035 	 */
1036 	if (snap == NULL || snap->ss_magic != cookie ||
1037 	    lgrp < 0 || lgrp == LGRP_NONE ||
1038 	    (type != LGRP_RSRC_CPU && type != LGRP_RSRC_MEM)) {
1039 		errno = EINVAL;
1040 		return (-1);
1041 	}
1042 
1043 	/*
1044 	 * See whether given lgroup exists
1045 	 */
1046 	nlgrps_max = snap->ss_nlgrps_max;
1047 	if (lgrp >= nlgrps_max || snap->ss_info == NULL ||
1048 	    !BT_TEST(snap->ss_lgrpset, lgrp)) {
1049 		errno = ESRCH;
1050 		return (-1);
1051 	}
1052 
1053 	/*
1054 	 * Get lgroup info
1055 	 */
1056 	lgrp_info = &snap->ss_info[lgrp];
1057 
1058 	/*
1059 	 * Count number lgroups contained within this lgroup and
1060 	 * copy as many lgroup IDs into array that will fit
1061 	 */
1062 	rset = &lgrp_info->info_rset[type * BT_BITOUL(nlgrps_max)];
1063 	nlgrps = 0;
1064 	for (i = 0; i < snap->ss_nlgrps_max; i++)
1065 		if (BT_TEST(rset, i)) {
1066 			if (lgrps != NULL && nlgrps < count)
1067 				lgrps[nlgrps] = i;
1068 			nlgrps++;
1069 		}
1070 
1071 	return (nlgrps);
1072 }
1073 
1074 
1075 /*
1076  * Finish using lgroup interface
1077  */
1078 int
1079 lgrp_fini(lgrp_cookie_t cookie)
1080 {
1081 	lgrp_snapshot_header_t	*snap;
1082 
1083 	snap = (lgrp_snapshot_header_t *)cookie;
1084 
1085 	if (snap == NULL || snap->ss_magic != cookie) {
1086 		errno = EINVAL;
1087 		return (-1);
1088 	}
1089 
1090 	bzero(snap, snap->ss_size);
1091 	free(snap);
1092 	snap = NULL;
1093 
1094 	return (0);
1095 }
1096 
1097 
1098 /*
1099  * Return latency between "from" and "to" lgroups
1100  *
1101  * This latency number can only be used for relative comparison
1102  * between lgroups on the running system, cannot be used across platforms,
1103  * and may not reflect the actual latency.  It is platform and implementation
1104  * specific, so platform gets to decide its value.  It would be nice if the
1105  * number was at least proportional to make comparisons more meaningful though.
1106  */
1107 int
1108 lgrp_latency(lgrp_id_t from, lgrp_id_t to)
1109 {
1110 	lgrp_cookie_t		cookie;
1111 	int			latency;
1112 
1113 	cookie = lgrp_init(LGRP_VIEW_OS);
1114 	latency = lgrp_latency_cookie(cookie, from, to, LGRP_LAT_CPU_TO_MEM);
1115 	(void) lgrp_fini(cookie);
1116 
1117 	return (latency);
1118 }
1119 
1120 
1121 /*
1122  * Return latency between "from" and "to" lgroups
1123  *
1124  * This latency number can only be used for relative comparison
1125  * between lgroups on the running system, cannot be used across platforms,
1126  * and may not reflect the actual latency.  It is platform and implementation
1127  * specific, so platform gets to decide its value.  It would be nice if the
1128  * number was at least proportional to make comparisons more meaningful though.
1129  */
1130 int
1131 lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to,
1132     lgrp_lat_between_t between)
1133 {
1134 	lgrp_info_t		*lgrp_info;
1135 	lgrp_mem_size_t		nbytes;
1136 	int			ncpus;
1137 	int			nlgrps_max;
1138 	lgrp_snapshot_header_t	*snap;
1139 
1140 	snap = (lgrp_snapshot_header_t *)cookie;
1141 
1142 	/*
1143 	 * Check for valid snapshot, lgroup, and between flag
1144 	 */
1145 	if (snap == NULL || snap->ss_magic != cookie || from < 0 || to < 0 ||
1146 	    between != LGRP_LAT_CPU_TO_MEM) {
1147 		errno = EINVAL;
1148 		return (-1);
1149 	}
1150 
1151 	/*
1152 	 * Check whether lgroups exist
1153 	 */
1154 	nlgrps_max = snap->ss_nlgrps_max;
1155 	if (from >= nlgrps_max || to >= nlgrps_max) {
1156 		errno = ESRCH;
1157 		return (-1);
1158 	}
1159 
1160 	/*
1161 	 * Check whether "from" lgroup has any CPUs
1162 	 */
1163 	ncpus = lgrp_cpus(cookie, from, NULL, 0, LGRP_CONTENT_HIERARCHY);
1164 	if (ncpus <= 0) {
1165 		if (ncpus == 0)
1166 			errno = ESRCH;
1167 		return (-1);
1168 	}
1169 
1170 	/*
1171 	 * Check whether "to" lgroup has any memory
1172 	 */
1173 	nbytes = lgrp_mem_size(cookie, to, LGRP_MEM_SZ_INSTALLED,
1174 	    LGRP_CONTENT_HIERARCHY);
1175 	if (nbytes <= 0) {
1176 		if (nbytes == 0)
1177 			errno = ESRCH;
1178 		return (-1);
1179 	}
1180 
1181 	if (from == to) {
1182 		lgrp_info = &snap->ss_info[from];
1183 		return (lgrp_info->info_latency);
1184 	}
1185 
1186 	return (snap->ss_latencies[from][to]);
1187 }
1188