#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>
extern lgrp_id_t _lgrp_home_fast(void);
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);
static lgrp_gen_t
lgrp_generation(lgrp_view_t view)
{
return (_lgrpsys(LGRP_SYS_GENERATION, view, NULL));
}
int
lgrp_version(int version)
{
return (_lgrpsys(LGRP_SYS_VERSION, version, NULL));
}
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));
}
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));
}
lgrp_id_t
lgrp_home(idtype_t idtype, id_t id)
{
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));
}
static int
lgrp_snapshot(void *buf, size_t bufsize)
{
return (_lgrpsys(LGRP_SYS_SNAPSHOT, bufsize, buf));
}
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;
if (i == snap->ss_root)
continue;
lgrp_info = &snap->ss_info[i];
if (lgrp_info == NULL || lgrp_info->info_lgrpid == LGRP_NONE)
continue;
if (lgrp_info->info_parents == NULL)
lgrp_info->info_parents =
(ulong_t *)((uintptr_t)snap->ss_parents +
(i * BT_SIZEOFMAP(nlgrpsmax)));
orphan = 1;
parents = lgrp_info->info_parents;
for (j = 0; j < BT_BITOUL(nlgrpsmax); j++)
if (parents[j] != 0) {
orphan = 0;
break;
}
if (orphan) {
BT_SET(parents, root->info_lgrpid);
if (root->info_children) {
BT_SET(root->info_children, i);
}
}
}
return (0);
}
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;
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);
}
}
}
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];
if (ncpus == 0 || lgrp_info->info_ncpus == 0)
return;
if (cpus == NULL && ncpus == -1) {
lgrp_info->info_ncpus = 0;
return;
}
lgrp_cpus = lgrp_info->info_cpuids;
lgrp_ncpus = lgrp_info->info_ncpus;
i = 0;
for (count = 0; count < lgrp_ncpus; count++) {
for (j = 0; j < ncpus; j++)
if (lgrp_cpus[i] == cpus[j])
break;
if (j < ncpus) {
i++;
continue;
}
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--;
}
}
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);
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);
}
}
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);
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);
}
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);
}
lgrp_cookie_t
lgrp_init(lgrp_view_t view)
{
ssize_t bufsize;
uint_t gen;
int i;
lgrp_snapshot_header_t *snap;
if (view != LGRP_VIEW_OS && view != LGRP_VIEW_CALLER) {
errno = EINVAL;
return (LGRP_COOKIE_NONE);
}
snap = NULL;
while (snap == NULL) {
gen = lgrp_generation(view);
bufsize = lgrp_snapshot(NULL, 0);
if (bufsize <= 0) {
if (errno == ENOMEM)
return (LGRP_COOKIE_NONE);
snap = NULL;
continue;
}
snap = malloc(bufsize);
if (snap == NULL)
return (LGRP_COOKIE_NONE);
bzero(snap, bufsize);
bufsize = lgrp_snapshot(snap, bufsize);
if (bufsize <= 0) {
free(snap);
if (errno == ENOMEM)
return (LGRP_COOKIE_NONE);
snap = NULL;
continue;
}
if (gen == lgrp_generation(view))
break;
free(snap);
snap = NULL;
}
snap->ss_gen = gen;
snap->ss_view = 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;
}
if (snap->ss_levels > 1)
(void) parent_orphans(snap);
if (view == LGRP_VIEW_CALLER)
(void) prune_tree(snap);
else {
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);
}
int
lgrp_cookie_stale(lgrp_cookie_t cookie)
{
psetid_t pset;
lgrp_snapshot_header_t *snap;
snap = (lgrp_snapshot_header_t *)cookie;
if (snap == NULL || snap->ss_magic != cookie) {
errno = EINVAL;
return (-1);
}
if (snap->ss_gen != lgrp_generation(snap->ss_view))
return (1);
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);
}
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);
}
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);
}
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);
}
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;
if (snap == NULL || snap->ss_magic != cookie ||
lgrp < 0 || lgrp == LGRP_NONE) {
errno = EINVAL;
return (-1);
}
nlgrps_max = snap->ss_nlgrps_max;
if (lgrp >= nlgrps_max || !BT_TEST(snap->ss_lgrpset, lgrp)) {
errno = ESRCH;
return (-1);
}
if (lgrp == snap->ss_root || snap->ss_levels == 1) {
if (parents == NULL || count < 1)
return (0);
return (0);
}
if (snap->ss_parents == NULL) {
errno = ESRCH;
return (-1);
}
lgrp_parents = &snap->ss_parents[lgrp * BT_BITOUL(nlgrps_max)];
if (lgrp_parents == NULL) {
errno = ESRCH;
return (-1);
}
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);
}
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;
if (snap == NULL || snap->ss_magic != cookie ||
lgrp < 0 || lgrp == LGRP_NONE) {
errno = EINVAL;
return (-1);
}
nlgrps_max = snap->ss_nlgrps_max;
if (lgrp >= nlgrps_max || !BT_TEST(snap->ss_lgrpset, lgrp)) {
errno = ESRCH;
return (-1);
}
if (snap->ss_levels == 1) {
if (children == NULL || count < 1)
return (0);
return (0);
}
if (snap->ss_children == NULL) {
errno = ESRCH;
return (-1);
}
lgrp_children = &snap->ss_children[lgrp * BT_BITOUL(nlgrps_max)];
if (lgrp_children == NULL)
return (0);
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);
}
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;
lgrp_info = &snap->ss_info[lgrp];
if (lgrp_info == NULL) {
errno = ESRCH;
return (-1);
}
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)];
total = 0;
for (i = 0; i < nlgrps_max; i++) {
if (!BT_TEST(rset, i))
continue;
lgrp_info = &snap->ss_info[i];
cpus = lgrp_info->info_cpuids;
ncpus = lgrp_info->info_ncpus;
total += ncpus;
if (cpuids && *cpuids && count) {
for (j = 0; j < ncpus; j++) {
if (*count) {
**cpuids = cpus[j];
(*cpuids)++;
(*count)--;
}
}
}
}
return (total);
}
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;
if (snap == NULL || snap->ss_magic != cookie ||
lgrp < 0 || lgrp == LGRP_NONE ||
(content != LGRP_CONTENT_DIRECT &&
content != LGRP_CONTENT_HIERARCHY)) {
errno = EINVAL;
return (-1);
}
if (lgrp >= snap->ss_nlgrps_max || snap->ss_info == NULL ||
!BT_TEST(snap->ss_lgrpset, lgrp)) {
errno = ESRCH;
return (-1);
}
lgrp_info = &snap->ss_info[lgrp];
switch (content) {
case LGRP_CONTENT_DIRECT:
cpus = lgrp_info->info_cpuids;
ncpus = lgrp_info->info_ncpus;
if (cpuids == NULL)
return (ncpus);
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);
}
}
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;
if (snap == NULL || snap->ss_magic != cookie ||
lgrp < 0 || lgrp == LGRP_NONE) {
errno = EINVAL;
return (-1);
}
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();
lgrp_info = &snap->ss_info[lgrp];
switch (content) {
case LGRP_CONTENT_DIRECT:
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:
if (lgrp_info->info_rset == NULL)
return (0);
rset = &lgrp_info->info_rset[LGRP_RSRC_MEM *
BT_BITOUL(nlgrps_max)];
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);
}
}
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;
if (snap == NULL || snap->ss_magic != cookie ||
lgrp < 0 || lgrp == LGRP_NONE ||
(type != LGRP_RSRC_CPU && type != LGRP_RSRC_MEM)) {
errno = EINVAL;
return (-1);
}
nlgrps_max = snap->ss_nlgrps_max;
if (lgrp >= nlgrps_max || snap->ss_info == NULL ||
!BT_TEST(snap->ss_lgrpset, lgrp)) {
errno = ESRCH;
return (-1);
}
lgrp_info = &snap->ss_info[lgrp];
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);
}
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);
}
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);
}
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;
if (snap == NULL || snap->ss_magic != cookie || from < 0 || to < 0 ||
between != LGRP_LAT_CPU_TO_MEM) {
errno = EINVAL;
return (-1);
}
nlgrps_max = snap->ss_nlgrps_max;
if (from >= nlgrps_max || to >= nlgrps_max) {
errno = ESRCH;
return (-1);
}
ncpus = lgrp_cpus(cookie, from, NULL, 0, LGRP_CONTENT_HIERARCHY);
if (ncpus <= 0) {
if (ncpus == 0)
errno = ESRCH;
return (-1);
}
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]);
}