/* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * lgroup interface */ #include #include #include #include #include #include #include #include #include /* * 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]); }