root/usr/src/lib/liblgrp/common/lgrp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * lgroup interface
 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/bitmap.h>
#include <sys/pset.h>
#include <sys/types.h>

#include <sys/lgrp_user.h>


/*
 * Fast trap for getting home lgroup of current thread
 */
extern lgrp_id_t        _lgrp_home_fast(void);

/*
 * lgroup system call
 */
extern int              _lgrpsys(int subcode, long arg, void *ap);

static int lgrp_cpus_hier(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp,
    processorid_t **cpuids, uint_t *count);


/*
 * Get generation ID of lgroup hierarchy given view
 * which changes whenever the hierarchy changes (eg. DR or pset contents
 * change for caller's view)
 */
static lgrp_gen_t
lgrp_generation(lgrp_view_t view)
{
        return (_lgrpsys(LGRP_SYS_GENERATION, view, NULL));
}


/*
 * Get supported revision number of lgroup interface
 */
int
lgrp_version(int version)
{
        return (_lgrpsys(LGRP_SYS_VERSION, version, NULL));
}


/*
 * Get affinity for given lgroup
 */
lgrp_affinity_t
lgrp_affinity_get(idtype_t idtype, id_t id, lgrp_id_t lgrp)
{
        lgrp_affinity_args_t    args;

        args.idtype = idtype;
        args.id = id;
        args.lgrp = lgrp;
        return (_lgrpsys(LGRP_SYS_AFFINITY_GET, 0, (void *)&args));
}


/*
 * Set affinity for given lgroup
 */
int
lgrp_affinity_set(idtype_t idtype, id_t id, lgrp_id_t lgrp,
    lgrp_affinity_t aff)
{
        lgrp_affinity_args_t    args;

        args.idtype = idtype;
        args.id = id;
        args.lgrp = lgrp;
        args.aff = aff;
        return (_lgrpsys(LGRP_SYS_AFFINITY_SET, 0, (void *)&args));
}


/*
 * Get home lgroup for given process or thread
 */
lgrp_id_t
lgrp_home(idtype_t idtype, id_t id)
{
        /*
         * Use fast trap to get home lgroup of current thread or process
         * Otherwise, use system call for other process or thread
         */
        if (id == P_MYID && (idtype == P_LWPID || idtype == P_PID))
                return (_lgrp_home_fast());
        else
                return (_lgrpsys(LGRP_SYS_HOME, idtype, (void *)(intptr_t)id));
}


/*
 * Get a snapshot of the lgroup hierarchy
 */
static int
lgrp_snapshot(void *buf, size_t bufsize)
{
        return (_lgrpsys(LGRP_SYS_SNAPSHOT, bufsize, buf));
}


/*
 * Find any orphan lgroups without parents and make them be children of
 * root lgroup
 */
static int
parent_orphans(lgrp_snapshot_header_t *snap)
{
        int             i;
        lgrp_info_t     *lgrp_info;
        int             nlgrpsmax;
        int             orphan;
        lgrp_info_t     *root;
        ulong_t         *parents;

        if (snap == NULL || snap->ss_info == NULL ||
            snap->ss_parents == NULL || snap->ss_root < 0 ||
            snap->ss_root >= snap->ss_nlgrps_max)
                return (-1);

        nlgrpsmax = snap->ss_nlgrps_max;
        root = &snap->ss_info[snap->ss_root];

        for (i = 0; i < nlgrpsmax; i++) {
                int     j;

                /*
                 * Skip root lgroup
                 */
                if (i == snap->ss_root)
                        continue;

                lgrp_info = &snap->ss_info[i];
                if (lgrp_info == NULL || lgrp_info->info_lgrpid == LGRP_NONE)
                        continue;

                /*
                 * Make sure parents bitmap is setup
                 */
                if (lgrp_info->info_parents == NULL)
                        lgrp_info->info_parents =
                            (ulong_t *)((uintptr_t)snap->ss_parents +
                            (i * BT_SIZEOFMAP(nlgrpsmax)));

                /*
                 * Look for orphans (lgroups with no parents)
                 */
                orphan = 1;
                parents = lgrp_info->info_parents;
                for (j = 0; j < BT_BITOUL(nlgrpsmax); j++)
                        if (parents[j] != 0) {
                                orphan = 0;
                                break;
                        }

                /*
                 * Make root be parent of any orphans
                 */
                if (orphan) {
                        BT_SET(parents, root->info_lgrpid);
                        if (root->info_children) {
                                BT_SET(root->info_children, i);
                        }
                }
        }

        return (0);
}


/*
 * Remove given lgroup from parent lgroup(s)
 */
static void
prune_child(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp)
{
        int             i;
        lgrp_info_t     *lgrp_info;
        ulong_t         *parents;

        if (snap == NULL || lgrp < 0 || lgrp > snap->ss_nlgrps_max)
                return;

        lgrp_info = &snap->ss_info[lgrp];

        parents = lgrp_info->info_parents;
        if (parents == NULL)
                return;

        /*
         * Update children of parents not to include given lgroup
         */
        for (i = 0; i < snap->ss_nlgrps_max; i++) {
                if (BT_TEST(parents, i)) {
                        lgrp_info = &snap->ss_info[i];
                        BT_CLEAR(lgrp_info->info_children, lgrp);
                }
        }
}

/*
 * Prune any CPUs not in given array from specified lgroup
 */
static void
prune_cpus(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp, processorid_t *cpus,
    int ncpus)
{
        int             count;
        int             i;
        int             j;
        int             k;
        lgrp_info_t     *lgrp_info;
        uint_t          lgrp_ncpus;
        processorid_t   *lgrp_cpus;

        if (snap == NULL || lgrp < 0 || lgrp > snap->ss_nlgrps_max)
                return;

        lgrp_info = &snap->ss_info[lgrp];

        /*
         * No CPUs to remove
         */
        if (ncpus == 0 || lgrp_info->info_ncpus == 0)
                return;

        /*
         * Remove all CPUs from lgroup
         */
        if (cpus == NULL && ncpus == -1) {
                lgrp_info->info_ncpus = 0;
                return;
        }

        /*
         * Remove any CPUs from lgroup not in given list of CPUs
         */
        lgrp_cpus = lgrp_info->info_cpuids;
        lgrp_ncpus = lgrp_info->info_ncpus;
        i = 0;
        for (count = 0; count < lgrp_ncpus; count++) {
                /*
                 * Look for CPU in list
                 */
                for (j = 0; j < ncpus; j++)
                        if (lgrp_cpus[i] == cpus[j])
                                break;

                /*
                 * Go to next CPU if found this one in list
                 */
                if (j < ncpus) {
                        i++;
                        continue;
                }

                /*
                 * Remove this CPU and shift others into its place
                 * and decrement number of CPUs
                 */
                for (k = i + 1; k < lgrp_info->info_ncpus; k++)
                        lgrp_cpus[k - 1] = lgrp_cpus[k];
                lgrp_cpus[k - 1] = -1;
                lgrp_info->info_ncpus--;
        }
}


/*
 * Prune lgroup hierarchy for caller's view
 */
static int
prune_tree(lgrp_snapshot_header_t *snap)
{
        processorid_t   *cpus;
        int             i;
        lgrp_info_t     *lgrp_info;
        lgrp_mem_size_t nbytes;
        uint_t          ncpus;
        int             nlgrps_max;

        if (snap == NULL || snap->ss_info == NULL)
                return (-1);

        /*
         * Get CPUs in caller's pset
         */
        if (pset_info(PS_MYID, NULL, &ncpus, NULL) == -1)
                return (-1);

        cpus = NULL;
        if (ncpus > 0) {
                cpus = malloc(ncpus * sizeof (processorid_t));
                if (pset_info(PS_MYID, NULL, &ncpus, cpus) == -1) {
                        free(cpus);
                        return (-1);
                }
        }

        /*
         * Remove any CPUs not in caller's pset from lgroup hierarchy
         */
        nlgrps_max = snap->ss_nlgrps_max;
        for (i = 0; i < nlgrps_max; i++) {
                lgrp_info = &snap->ss_info[i];
                if (BT_TEST(snap->ss_lgrpset, i))
                        prune_cpus(snap, i, cpus, ncpus);
                else if (lgrp_info->info_lgrpid != LGRP_NONE)
                        prune_cpus(snap, i, NULL, -1);
        }

        if (ncpus > 0)
                free(cpus);

        /*
         * Change lgroup bitmask from just reflecting lgroups overlapping
         * caller's pset to all lgroups available to caller, starting by
         * filling in all lgroups and then removing any empty ones below
         */
        for (i = 0; i < nlgrps_max; i++) {
                lgrp_info = &snap->ss_info[i];
                if (lgrp_info->info_lgrpid == LGRP_NONE)
                        continue;

                BT_SET(snap->ss_lgrpset, i);
        }

        /*
         * Remove empty lgroups from lgroup hierarchy, removing it from its
         * parents and decrementing nlgrps
         */
        for (i = 0; i < nlgrps_max; i++) {
                lgrp_info = &snap->ss_info[i];
                if (lgrp_info->info_lgrpid == LGRP_NONE)
                        continue;

                ncpus = lgrp_cpus_hier(snap, i, NULL, NULL);
                nbytes = lgrp_mem_size((lgrp_cookie_t)snap, i,
                    LGRP_MEM_SZ_INSTALLED, LGRP_CONTENT_HIERARCHY);
                if (ncpus == 0 && nbytes == 0) {
                        BT_CLEAR(snap->ss_lgrpset, i);
                        prune_child(snap, i);
                        snap->ss_nlgrps--;
                }
        }

        return (0);
}


/*
 * Initialize lgroup interface
 */
lgrp_cookie_t
lgrp_init(lgrp_view_t view)
{
        ssize_t                 bufsize;
        uint_t                  gen;
        int                     i;
        lgrp_snapshot_header_t  *snap;

        /*
         * Check for legal view
         */
        if (view != LGRP_VIEW_OS && view != LGRP_VIEW_CALLER) {
                errno = EINVAL;
                return (LGRP_COOKIE_NONE);
        }

        /*
         * Try to take a consistent snapshot of lgroup hierarchy
         */
        snap = NULL;
        while (snap == NULL) {
                /*
                 * Get lgroup generation number before taking snapshot
                 */
                gen = lgrp_generation(view);

                /*
                 * Get size of buffer needed for snapshot
                 */
                bufsize = lgrp_snapshot(NULL, 0);
                if (bufsize <= 0) {
                        if (errno == ENOMEM)
                                return (LGRP_COOKIE_NONE);

                        snap = NULL;
                        continue;
                }

                /*
                 * Allocate buffer
                 */
                snap = malloc(bufsize);
                if (snap == NULL)
                        return (LGRP_COOKIE_NONE);
                bzero(snap, bufsize);

                /*
                 * Take snapshot of lgroup hierarchy
                 */
                bufsize = lgrp_snapshot(snap, bufsize);
                if (bufsize <= 0) {
                        free(snap);
                        if (errno == ENOMEM)
                                return (LGRP_COOKIE_NONE);

                        snap = NULL;
                        continue;
                }

                /*
                 * See whether lgroup generation number changed
                 */
                if (gen == lgrp_generation(view))
                        break;

                free(snap);
                snap = NULL;
        }

        /*
         * Remember generation number and view of this snapshot
         */
        snap->ss_gen = gen;
        snap->ss_view = view;

        /*
         * Keep caller's pset ID for caller's view
         */
        snap->ss_pset = 0;
        if (view == LGRP_VIEW_CALLER) {
                psetid_t        pset;

                if (pset_bind(PS_QUERY, P_LWPID, P_MYID, &pset) == -1)
                        return ((uintptr_t)-1);

                snap->ss_pset = pset;
        }

        /*
         * Find any orphan lgroups without parents and make them be children
         * of the root lgroup
         */
        if (snap->ss_levels > 1)
                (void) parent_orphans(snap);

        /*
         * Prune snapshot of lgroup hierarchy for caller's view
         */
        if (view == LGRP_VIEW_CALLER)
                (void) prune_tree(snap);
        else {
                /*
                 * Change lgroup bitmask from just reflecting lgroups
                 * overlapping caller's pset to all lgroups available
                 */
                for (i = 0; i < snap->ss_nlgrps_max; i++) {
                        lgrp_info_t     *lgrp_info;

                        lgrp_info = &snap->ss_info[i];
                        if (lgrp_info->info_lgrpid == LGRP_NONE)
                                continue;

                        BT_SET(snap->ss_lgrpset, i);
                }
        }

        return ((uintptr_t)snap);
}


/*
 * Return whether given cookie is out-of-date (stale) or not
 */
int
lgrp_cookie_stale(lgrp_cookie_t cookie)
{
        psetid_t                pset;
        lgrp_snapshot_header_t  *snap;

        /*
         * Check for bad cookie
         */
        snap = (lgrp_snapshot_header_t *)cookie;
        if (snap == NULL || snap->ss_magic != cookie) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * Check generation number which changes when lgroup hierarchy changes
         * or pset contents change for caller's view
         */
        if (snap->ss_gen != lgrp_generation(snap->ss_view))
                return (1);

        /*
         * See whether pset binding has changed for caller's view
         */
        if (snap->ss_view == LGRP_VIEW_CALLER) {
                if (pset_bind(PS_QUERY, P_LWPID, P_MYID, &pset) == -1)
                        return (-1);
                if (snap->ss_pset != pset)
                        return (1);
        }

        return (0);     /* cookie isn't stale */
}


/*
 * Get view of lgroup hierarchy from snapshot represented by given cookie
 */
lgrp_view_t
lgrp_view(lgrp_cookie_t cookie)
{
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;
        if (snap == NULL || snap->ss_magic != cookie) {
                errno = EINVAL;
                return (-1);
        }

        return (snap->ss_view);
}


/*
 * Get number of lgroups
 */
int
lgrp_nlgrps(lgrp_cookie_t cookie)
{
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        if (snap == NULL || snap->ss_magic != cookie) {
                errno = EINVAL;
                return (-1);
        }

        return (snap->ss_nlgrps);
}


/*
 * Return root lgroup ID
 */
lgrp_id_t
lgrp_root(lgrp_cookie_t cookie)
{
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        if (snap == NULL || snap->ss_magic != cookie) {
                errno = EINVAL;
                return (-1);
        }

        return (snap->ss_root);
}


/*
 * Get parent lgroups of given lgroup
 */
int
lgrp_parents(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *parents,
    uint_t count)
{
        int                     i;
        ulong_t                 *lgrp_parents;
        lgrp_snapshot_header_t  *snap;
        int                     nlgrps_max;
        int                     nparents;

        snap = (lgrp_snapshot_header_t *)cookie;

        /*
         * Check for valid arguments
         */
        if (snap == NULL || snap->ss_magic != cookie ||
            lgrp < 0 || lgrp == LGRP_NONE) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * See whether given lgroup exists
         */
        nlgrps_max = snap->ss_nlgrps_max;
        if (lgrp >= nlgrps_max || !BT_TEST(snap->ss_lgrpset, lgrp)) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * No parents, since given lgroup is root lgroup or
         * only one level in lgroup hierarchy (ie. SMP)
         */
        if (lgrp == snap->ss_root || snap->ss_levels == 1) {
                if (parents == NULL || count < 1)
                        return (0);
                return (0);
        }

        /*
         * Make sure that parents exist
         */
        if (snap->ss_parents == NULL) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Given lgroup should have a parent
         */
        lgrp_parents = &snap->ss_parents[lgrp * BT_BITOUL(nlgrps_max)];
        if (lgrp_parents == NULL) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Check lgroup parents bitmask, fill in parents array, and return
         * number of parents
         */
        nparents = 0;
        for (i = 0; i < nlgrps_max; i++) {
                if (BT_TEST(lgrp_parents, i)) {
                        if (parents != NULL && nparents < count) {
                                parents[nparents] = i;
                        }
                        nparents++;
                }
        }
        return (nparents);
}


/*
 * Get children lgroups of given lgroup
 */
int
lgrp_children(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *children,
    uint_t count)
{
        int                     i;
        ulong_t                 *lgrp_children;
        int                     nlgrps_max;
        int                     nchildren;
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        /*
         * Check for valid arguments
         */
        if (snap == NULL || snap->ss_magic != cookie ||
            lgrp < 0 || lgrp == LGRP_NONE) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * See whether given lgroup exists
         */
        nlgrps_max = snap->ss_nlgrps_max;
        if (lgrp >= nlgrps_max || !BT_TEST(snap->ss_lgrpset, lgrp)) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * No children, since only one level in lgroup hierarchy (ie. SMP)
         */
        if (snap->ss_levels == 1) {
                if (children == NULL || count < 1)
                        return (0);
                return (0);
        }

        /*
         * Make sure that children exist
         */
        if (snap->ss_children == NULL) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Given lgroup may not have any children
         */
        lgrp_children = &snap->ss_children[lgrp * BT_BITOUL(nlgrps_max)];

        if (lgrp_children == NULL)
                return (0);

        /*
         * Check lgroup children bitmask, fill in children array, and return
         * number of children
         */
        nchildren = 0;
        for (i = 0; i < nlgrps_max; i++) {
                if (BT_TEST(lgrp_children, i)) {
                        if (children != NULL && nchildren < count)
                                children[nchildren] = i;
                        nchildren++;
                }
        }
        return (nchildren);
}


/*
 * Get all CPUs within given lgroup (hierarchy)
 */
static int
lgrp_cpus_hier(lgrp_snapshot_header_t *snap, lgrp_id_t lgrp,
    processorid_t **cpuids, uint_t *count)
{
        processorid_t   *cpus;
        int             i;
        int             j;
        lgrp_info_t     *lgrp_info;
        int             ncpus;
        int             nlgrps_max;
        ulong_t         *rset;
        int             total;

        /*
         * Get lgroup info
         */
        lgrp_info = &snap->ss_info[lgrp];

        if (lgrp_info == NULL) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Check whether given lgroup contains any lgroups with CPU resources
         */
        if (lgrp_info->info_rset == NULL)
                return (0);

        nlgrps_max = snap->ss_nlgrps_max;
        rset = &lgrp_info->info_rset[LGRP_RSRC_CPU * BT_BITOUL(nlgrps_max)];

        /*
         * Get all CPUs within this lgroup
         */
        total = 0;
        for (i = 0; i < nlgrps_max; i++) {
                if (!BT_TEST(rset, i))
                        continue;

                lgrp_info = &snap->ss_info[i];

                /*
                 * Get all CPUs within lgroup
                 */
                cpus = lgrp_info->info_cpuids;
                ncpus = lgrp_info->info_ncpus;
                total += ncpus;

                /*
                 * Copy as many CPU IDs into array that will fit
                 * and decrement count and increment array pointer
                 * as we go
                 */
                if (cpuids && *cpuids && count) {
                        for (j = 0; j < ncpus; j++) {
                                if (*count) {
                                        **cpuids = cpus[j];
                                        (*cpuids)++;
                                        (*count)--;
                                }
                        }
                }
        }

        return (total);
}


/*
 * Get CPUs in given lgroup
 */
int
lgrp_cpus(lgrp_cookie_t cookie, lgrp_id_t lgrp, processorid_t *cpuids,
    uint_t count, lgrp_content_t content)
{
        int                     i;
        processorid_t           *cpus;
        lgrp_info_t             *lgrp_info;
        int                     ncpus;
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        /*
         * Check for valid arguments
         */
        if (snap == NULL || snap->ss_magic != cookie ||
            lgrp < 0 || lgrp == LGRP_NONE ||
            (content != LGRP_CONTENT_DIRECT &&
            content != LGRP_CONTENT_HIERARCHY)) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * See whether given lgroup exists
         */
        if (lgrp >= snap->ss_nlgrps_max || snap->ss_info == NULL ||
            !BT_TEST(snap->ss_lgrpset, lgrp)) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Get lgroup info
         */
        lgrp_info = &snap->ss_info[lgrp];

        /*
         * Get contents of lgroup
         */
        switch (content) {
        case LGRP_CONTENT_DIRECT:
                /*
                 * Get CPUs contained directly within given lgroup
                 */
                cpus = lgrp_info->info_cpuids;
                ncpus = lgrp_info->info_ncpus;

                /*
                 * No array to copy CPU IDs into,
                 * so just return number of CPUs.
                 */
                if (cpuids == NULL)
                        return (ncpus);

                /*
                 * Copy as many CPU IDs into array that will fit
                 */
                for (i = 0; i < ncpus; i++)
                        if (i < count)
                                cpuids[i] = cpus[i];

                return (ncpus);

        case LGRP_CONTENT_ALL:
                return (lgrp_cpus_hier(snap, lgrp, &cpuids, &count));

        default:
                errno = EINVAL;
                return (-1);
        }
}


/*
 * Return physical memory size in pages for given lgroup
 */
lgrp_mem_size_t
lgrp_mem_size(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_mem_size_flag_t type,
    lgrp_content_t content)
{
        int                     i;
        lgrp_info_t             *lgrp_info;
        int                     nlgrps_max;
        int                     pgsz;
        ulong_t                 *rset;
        lgrp_mem_size_t         size;
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        /*
         * Check for valid arguments
         */
        if (snap == NULL || snap->ss_magic != cookie ||
            lgrp < 0 || lgrp == LGRP_NONE) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * See whether given lgroup exists
         */
        nlgrps_max = snap->ss_nlgrps_max;
        if (lgrp >= nlgrps_max || snap->ss_info == NULL ||
            !BT_TEST(snap->ss_lgrpset, lgrp)) {
                errno = ESRCH;
                return (-1);
        }

        pgsz = getpagesize();

        /*
         * Get lgroup info
         */
        lgrp_info = &snap->ss_info[lgrp];

        switch (content) {
        case LGRP_CONTENT_DIRECT:
                /*
                 * Get memory contained directly in this lgroup
                 */
                switch (type) {
                case LGRP_MEM_SZ_FREE:
                        size = (lgrp_mem_size_t)pgsz *
                            lgrp_info->info_mem_free;
                        return (size);
                case LGRP_MEM_SZ_INSTALLED:
                        size = (lgrp_mem_size_t)pgsz *
                            lgrp_info->info_mem_install;
                        return (size);
                default:
                        errno = EINVAL;
                        return (-1);
                }

        case LGRP_CONTENT_ALL:
                /*
                 * Get memory contained within this lgroup (and its children)
                 */
                /*
                 * Check whether given lgroup contains any lgroups with CPU
                 * resources
                 */
                if (lgrp_info->info_rset == NULL)
                        return (0);

                rset = &lgrp_info->info_rset[LGRP_RSRC_MEM *
                    BT_BITOUL(nlgrps_max)];

                /*
                 * Add up memory in lgroup resources
                 */
                size = 0;
                for (i = 0; i < nlgrps_max; i++) {
                        if (!BT_TEST(rset, i))
                                continue;

                        lgrp_info = &snap->ss_info[i];
                        switch (type) {
                        case LGRP_MEM_SZ_FREE:
                                size += (lgrp_mem_size_t)pgsz *
                                    lgrp_info->info_mem_free;
                                break;
                        case LGRP_MEM_SZ_INSTALLED:
                                size += (lgrp_mem_size_t)pgsz *
                                    lgrp_info->info_mem_install;
                                break;
                        default:
                                errno = EINVAL;
                                return (-1);
                        }

                }

                return (size);

        default:
                errno = EINVAL;
                return (-1);
        }
}


/*
 * Get resources for a particuliar lgroup
 */
int
lgrp_resources(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_id_t *lgrps,
    uint_t count, lgrp_rsrc_t type)
{
        int                     i;
        lgrp_info_t             *lgrp_info;
        int                     nlgrps;
        int                     nlgrps_max;
        ulong_t                 *rset;
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        /*
         * Check for valid arguments
         */
        if (snap == NULL || snap->ss_magic != cookie ||
            lgrp < 0 || lgrp == LGRP_NONE ||
            (type != LGRP_RSRC_CPU && type != LGRP_RSRC_MEM)) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * See whether given lgroup exists
         */
        nlgrps_max = snap->ss_nlgrps_max;
        if (lgrp >= nlgrps_max || snap->ss_info == NULL ||
            !BT_TEST(snap->ss_lgrpset, lgrp)) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Get lgroup info
         */
        lgrp_info = &snap->ss_info[lgrp];

        /*
         * Count number lgroups contained within this lgroup and
         * copy as many lgroup IDs into array that will fit
         */
        rset = &lgrp_info->info_rset[type * BT_BITOUL(nlgrps_max)];
        nlgrps = 0;
        for (i = 0; i < snap->ss_nlgrps_max; i++)
                if (BT_TEST(rset, i)) {
                        if (lgrps != NULL && nlgrps < count)
                                lgrps[nlgrps] = i;
                        nlgrps++;
                }

        return (nlgrps);
}


/*
 * Finish using lgroup interface
 */
int
lgrp_fini(lgrp_cookie_t cookie)
{
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        if (snap == NULL || snap->ss_magic != cookie) {
                errno = EINVAL;
                return (-1);
        }

        bzero(snap, snap->ss_size);
        free(snap);
        snap = NULL;

        return (0);
}


/*
 * Return latency between "from" and "to" lgroups
 *
 * This latency number can only be used for relative comparison
 * between lgroups on the running system, cannot be used across platforms,
 * and may not reflect the actual latency.  It is platform and implementation
 * specific, so platform gets to decide its value.  It would be nice if the
 * number was at least proportional to make comparisons more meaningful though.
 */
int
lgrp_latency(lgrp_id_t from, lgrp_id_t to)
{
        lgrp_cookie_t           cookie;
        int                     latency;

        cookie = lgrp_init(LGRP_VIEW_OS);
        latency = lgrp_latency_cookie(cookie, from, to, LGRP_LAT_CPU_TO_MEM);
        (void) lgrp_fini(cookie);

        return (latency);
}


/*
 * Return latency between "from" and "to" lgroups
 *
 * This latency number can only be used for relative comparison
 * between lgroups on the running system, cannot be used across platforms,
 * and may not reflect the actual latency.  It is platform and implementation
 * specific, so platform gets to decide its value.  It would be nice if the
 * number was at least proportional to make comparisons more meaningful though.
 */
int
lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to,
    lgrp_lat_between_t between)
{
        lgrp_info_t             *lgrp_info;
        lgrp_mem_size_t         nbytes;
        int                     ncpus;
        int                     nlgrps_max;
        lgrp_snapshot_header_t  *snap;

        snap = (lgrp_snapshot_header_t *)cookie;

        /*
         * Check for valid snapshot, lgroup, and between flag
         */
        if (snap == NULL || snap->ss_magic != cookie || from < 0 || to < 0 ||
            between != LGRP_LAT_CPU_TO_MEM) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * Check whether lgroups exist
         */
        nlgrps_max = snap->ss_nlgrps_max;
        if (from >= nlgrps_max || to >= nlgrps_max) {
                errno = ESRCH;
                return (-1);
        }

        /*
         * Check whether "from" lgroup has any CPUs
         */
        ncpus = lgrp_cpus(cookie, from, NULL, 0, LGRP_CONTENT_HIERARCHY);
        if (ncpus <= 0) {
                if (ncpus == 0)
                        errno = ESRCH;
                return (-1);
        }

        /*
         * Check whether "to" lgroup has any memory
         */
        nbytes = lgrp_mem_size(cookie, to, LGRP_MEM_SZ_INSTALLED,
            LGRP_CONTENT_HIERARCHY);
        if (nbytes <= 0) {
                if (nbytes == 0)
                        errno = ESRCH;
                return (-1);
        }

        if (from == to) {
                lgrp_info = &snap->ss_info[from];
                return (lgrp_info->info_latency);
        }

        return (snap->ss_latencies[from][to]);
}