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 */
48extern lgrp_id_t	_lgrp_home_fast(void);
49
50/*
51 * lgroup system call
52 */
53extern int		_lgrpsys(int subcode, long arg, void *ap);
54
55static 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 */
64static lgrp_gen_t
65lgrp_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 */
74int
75lgrp_version(int version)
76{
77	return (_lgrpsys(LGRP_SYS_VERSION, version, NULL));
78}
79
80
81/*
82 * Get affinity for given lgroup
83 */
84lgrp_affinity_t
85lgrp_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 */
99int
100lgrp_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 */
116lgrp_id_t
117lgrp_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 */
133static int
134lgrp_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 */
144static int
145parent_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 */
212static void
213prune_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 */
242static void
243prune_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 */
310static int
311prune_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 */
392lgrp_cookie_t
393lgrp_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 */
515int
516lgrp_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 */
554lgrp_view_t
555lgrp_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 */
572int
573lgrp_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 */
591lgrp_id_t
592lgrp_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 */
610int
611lgrp_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 */
687int
688lgrp_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 */
761static int
762lgrp_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 */
833int
834lgrp_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 */
910lgrp_mem_size_t
911lgrp_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 */
1020int
1021lgrp_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 */
1078int
1079lgrp_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 */
1107int
1108lgrp_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 */
1130int
1131lgrp_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