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