mirror of
https://github.com/xmrig/xmrig.git
synced 2025-12-06 23:52:38 -05:00
1647 lines
48 KiB
C
1647 lines
48 KiB
C
/*
|
|
* Copyright © 2009 CNRS
|
|
* Copyright © 2009-2023 Inria. All rights reserved.
|
|
* Copyright © 2009-2010 Université Bordeaux
|
|
* Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved.
|
|
* See COPYING in top-level directory.
|
|
*/
|
|
|
|
#include "private/autogen/config.h"
|
|
#include "hwloc.h"
|
|
#include "private/private.h"
|
|
#include "private/misc.h"
|
|
#include "private/debug.h"
|
|
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
struct hwloc_synthetic_attr_s {
|
|
hwloc_obj_type_t type;
|
|
unsigned depth; /* For caches/groups */
|
|
hwloc_obj_cache_type_t cachetype; /* For caches */
|
|
hwloc_uint64_t memorysize; /* For caches/memory */
|
|
hwloc_uint64_t memorysidecachesize; /* Single level of memory-side-cache in-front of a NUMA node */
|
|
};
|
|
|
|
struct hwloc_synthetic_indexes_s {
|
|
/* the indexes= attribute before parsing */
|
|
const char *string;
|
|
unsigned long string_length;
|
|
/* the array of explicit indexes after parsing */
|
|
unsigned *array;
|
|
|
|
/* used while filling the topology */
|
|
unsigned next; /* id of the next object for that level */
|
|
};
|
|
|
|
struct hwloc_synthetic_level_data_s {
|
|
unsigned arity;
|
|
unsigned long totalwidth;
|
|
|
|
struct hwloc_synthetic_attr_s attr;
|
|
struct hwloc_synthetic_indexes_s indexes;
|
|
|
|
struct hwloc_synthetic_attached_s {
|
|
struct hwloc_synthetic_attr_s attr;
|
|
|
|
struct hwloc_synthetic_attached_s *next;
|
|
} *attached;
|
|
};
|
|
|
|
struct hwloc_synthetic_backend_data_s {
|
|
/* synthetic backend parameters */
|
|
char *string;
|
|
|
|
unsigned long numa_attached_nr;
|
|
struct hwloc_synthetic_indexes_s numa_attached_indexes;
|
|
|
|
#define HWLOC_SYNTHETIC_MAX_DEPTH 128
|
|
struct hwloc_synthetic_level_data_s level[HWLOC_SYNTHETIC_MAX_DEPTH];
|
|
};
|
|
|
|
struct hwloc_synthetic_intlv_loop_s {
|
|
unsigned step;
|
|
unsigned nb;
|
|
unsigned level_depth;
|
|
};
|
|
|
|
static void
|
|
hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data,
|
|
struct hwloc_synthetic_indexes_s *indexes,
|
|
unsigned long total,
|
|
int verbose)
|
|
{
|
|
const char *attr = indexes->string;
|
|
unsigned long length = indexes->string_length;
|
|
unsigned *array = NULL;
|
|
size_t i;
|
|
|
|
if (!attr)
|
|
return;
|
|
|
|
array = calloc(total, sizeof(*array));
|
|
if (!array) {
|
|
if (verbose)
|
|
fprintf(stderr, "Failed to allocate synthetic index array of size %lu\n", total);
|
|
goto out;
|
|
}
|
|
|
|
i = strspn(attr, "0123456789,");
|
|
if (i == length) {
|
|
/* explicit array of indexes */
|
|
|
|
for(i=0; i<total; i++) {
|
|
const char *next;
|
|
unsigned idx = strtoul(attr, (char **) &next, 10);
|
|
if (next == attr) {
|
|
if (verbose)
|
|
fprintf(stderr, "Failed to read synthetic index #%lu at '%s'\n", (unsigned long) i, attr);
|
|
goto out_with_array;
|
|
}
|
|
|
|
array[i] = idx;
|
|
if (i != total-1) {
|
|
if (*next != ',') {
|
|
if (verbose)
|
|
fprintf(stderr, "Missing comma after synthetic index #%lu at '%s'\n", (unsigned long) i, attr);
|
|
goto out_with_array;
|
|
}
|
|
attr = next+1;
|
|
} else {
|
|
attr = next;
|
|
}
|
|
}
|
|
indexes->array = array;
|
|
|
|
} else {
|
|
/* interleaving */
|
|
unsigned nr_loops = 1, cur_loop;
|
|
unsigned minstep = total;
|
|
unsigned long nbs = 1;
|
|
unsigned j, mul;
|
|
const char *tmp;
|
|
struct hwloc_synthetic_intlv_loop_s *loops;
|
|
|
|
tmp = attr;
|
|
while (tmp) {
|
|
tmp = strchr(tmp, ':');
|
|
if (!tmp || tmp >= attr+length)
|
|
break;
|
|
nr_loops++;
|
|
tmp++;
|
|
}
|
|
|
|
/* nr_loops colon-separated fields, but we may need one more at the end */
|
|
loops = malloc((nr_loops+1) * sizeof(*loops));
|
|
if (!loops)
|
|
goto out_with_array;
|
|
|
|
if (*attr >= '0' && *attr <= '9') {
|
|
/* interleaving as x*y:z*t:... */
|
|
unsigned step, nb;
|
|
|
|
tmp = attr;
|
|
cur_loop = 0;
|
|
while (tmp) {
|
|
char *tmp2, *tmp3;
|
|
step = (unsigned) strtol(tmp, &tmp2, 0);
|
|
if (tmp2 == tmp || *tmp2 != '*') {
|
|
if (verbose)
|
|
fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number before '*'\n", tmp);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
if (!step) {
|
|
if (verbose)
|
|
fprintf(stderr, "Invalid interleaving loop with step 0 at '%s'\n", tmp);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
tmp2++;
|
|
nb = (unsigned) strtol(tmp2, &tmp3, 0);
|
|
if (tmp3 == tmp2 || (*tmp3 && *tmp3 != ':' && *tmp3 != ')' && *tmp3 != ' ')) {
|
|
if (verbose)
|
|
fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number between '*' and ':'\n", tmp);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
if (!nb) {
|
|
if (verbose)
|
|
fprintf(stderr, "Invalid interleaving loop with number 0 at '%s'\n", tmp2);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
loops[cur_loop].step = step;
|
|
loops[cur_loop].nb = nb;
|
|
if (step < minstep)
|
|
minstep = step;
|
|
nbs *= nb;
|
|
cur_loop++;
|
|
if (*tmp3 == ')' || *tmp3 == ' ')
|
|
break;
|
|
tmp = (const char*) (tmp3+1);
|
|
}
|
|
|
|
} else {
|
|
/* interleaving as type1:type2:... */
|
|
hwloc_obj_type_t type;
|
|
union hwloc_obj_attr_u attrs;
|
|
int err;
|
|
|
|
/* find level depths for each interleaving loop */
|
|
tmp = attr;
|
|
cur_loop = 0;
|
|
while (tmp) {
|
|
err = hwloc_type_sscanf(tmp, &type, &attrs, sizeof(attrs));
|
|
if (err < 0) {
|
|
if (verbose)
|
|
fprintf(stderr, "Failed to read synthetic index interleaving loop type '%s'\n", tmp);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
if (type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) {
|
|
if (verbose)
|
|
fprintf(stderr, "Misc object type disallowed in synthetic index interleaving loop type '%s'\n", tmp);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
for(i=0; ; i++) {
|
|
if (!data->level[i].arity) {
|
|
loops[cur_loop].level_depth = (unsigned)-1;
|
|
break;
|
|
}
|
|
if (type != data->level[i].attr.type)
|
|
continue;
|
|
if (type == HWLOC_OBJ_GROUP
|
|
&& attrs.group.depth != (unsigned) -1
|
|
&& attrs.group.depth != data->level[i].attr.depth)
|
|
continue;
|
|
loops[cur_loop].level_depth = (unsigned)i;
|
|
break;
|
|
}
|
|
if (loops[cur_loop].level_depth == (unsigned)-1) {
|
|
if (verbose)
|
|
fprintf(stderr, "Failed to find level for synthetic index interleaving loop type '%s'\n",
|
|
tmp);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
tmp = strchr(tmp, ':');
|
|
if (!tmp || tmp > attr+length)
|
|
break;
|
|
tmp++;
|
|
cur_loop++;
|
|
}
|
|
|
|
/* compute actual loop step/nb */
|
|
for(cur_loop=0; cur_loop<nr_loops; cur_loop++) {
|
|
unsigned mydepth = loops[cur_loop].level_depth;
|
|
unsigned prevdepth = 0;
|
|
unsigned step, nb;
|
|
for(i=0; i<nr_loops; i++) {
|
|
if (loops[i].level_depth == mydepth && i != cur_loop) {
|
|
if (verbose)
|
|
fprintf(stderr, "Invalid duplicate interleaving loop type in synthetic index '%s'\n", attr);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
if (loops[i].level_depth < mydepth
|
|
&& loops[i].level_depth > prevdepth)
|
|
prevdepth = loops[i].level_depth;
|
|
}
|
|
step = total / data->level[mydepth].totalwidth; /* number of objects below us */
|
|
nb = data->level[mydepth].totalwidth / data->level[prevdepth].totalwidth; /* number of us within parent */
|
|
|
|
loops[cur_loop].step = step;
|
|
loops[cur_loop].nb = nb;
|
|
assert(nb);
|
|
assert(step);
|
|
if (step < minstep)
|
|
minstep = step;
|
|
nbs *= nb;
|
|
}
|
|
}
|
|
assert(nbs);
|
|
|
|
if (nbs != total) {
|
|
/* one loop of total/nbs steps is missing, add it if it's just the smallest one */
|
|
if (minstep == total/nbs) {
|
|
loops[nr_loops].step = 1;
|
|
loops[nr_loops].nb = total/nbs;
|
|
nr_loops++;
|
|
} else {
|
|
if (verbose)
|
|
fprintf(stderr, "Invalid index interleaving total width %lu instead of %lu\n", nbs, total);
|
|
free(loops);
|
|
goto out_with_array;
|
|
}
|
|
}
|
|
|
|
/* generate the array of indexes */
|
|
mul = 1;
|
|
for(i=0; i<nr_loops; i++) {
|
|
unsigned step = loops[i].step;
|
|
unsigned nb = loops[i].nb;
|
|
for(j=0; j<total; j++)
|
|
array[j] += ((j / step) % nb) * mul;
|
|
mul *= nb;
|
|
}
|
|
|
|
free(loops);
|
|
|
|
/* check that we have the right values (cannot pass total, cannot give duplicate 0) */
|
|
for(j=0; j<total; j++) {
|
|
if (array[j] >= total) {
|
|
if (verbose)
|
|
fprintf(stderr, "Invalid index interleaving generates out-of-range index %u\n", array[j]);
|
|
goto out_with_array;
|
|
}
|
|
if (!array[j] && j) {
|
|
if (verbose)
|
|
fprintf(stderr, "Invalid index interleaving generates duplicate index values\n");
|
|
goto out_with_array;
|
|
}
|
|
}
|
|
|
|
indexes->array = array;
|
|
}
|
|
|
|
return;
|
|
|
|
out_with_array:
|
|
free(array);
|
|
out:
|
|
return;
|
|
}
|
|
|
|
static hwloc_uint64_t
|
|
hwloc_synthetic_parse_memory_attr(const char *attr, const char **endp)
|
|
{
|
|
const char *endptr;
|
|
hwloc_uint64_t size;
|
|
size = strtoull(attr, (char **) &endptr, 0);
|
|
if (!hwloc_strncasecmp(endptr, "TB", 2)) {
|
|
size *= 1000ULL*1000ULL*1000ULL*1000ULL;
|
|
endptr += 2;
|
|
} else if (!hwloc_strncasecmp(endptr, "TiB", 3)) {
|
|
size <<= 40;
|
|
endptr += 3;
|
|
} else if (!hwloc_strncasecmp(endptr, "GB", 2)) {
|
|
size *= 1000ULL*1000ULL*1000ULL;
|
|
endptr += 2;
|
|
} else if (!hwloc_strncasecmp(endptr, "GiB", 3)) {
|
|
size <<= 30;
|
|
endptr += 3;
|
|
} else if (!hwloc_strncasecmp(endptr, "MB", 2)) {
|
|
size *= 1000ULL*1000ULL;
|
|
endptr += 2;
|
|
} else if (!hwloc_strncasecmp(endptr, "MiB", 3)) {
|
|
size <<= 20;
|
|
endptr += 3;
|
|
} else if (!hwloc_strncasecmp(endptr, "kB", 2)) {
|
|
size *= 1000ULL;
|
|
endptr += 2;
|
|
} else if (!hwloc_strncasecmp(endptr, "kiB", 3)) {
|
|
size <<= 10;
|
|
endptr += 3;
|
|
}
|
|
*endp = endptr;
|
|
return size;
|
|
}
|
|
|
|
static int
|
|
hwloc_synthetic_parse_attrs(const char *attrs, const char **next_posp,
|
|
struct hwloc_synthetic_attr_s *sattr,
|
|
struct hwloc_synthetic_indexes_s *sind,
|
|
int verbose)
|
|
{
|
|
hwloc_obj_type_t type = sattr->type;
|
|
const char *next_pos;
|
|
hwloc_uint64_t memorysize = 0;
|
|
const char *index_string = NULL;
|
|
size_t index_string_length = 0;
|
|
|
|
next_pos = (const char *) strchr(attrs, ')');
|
|
if (!next_pos) {
|
|
if (verbose)
|
|
fprintf(stderr, "Missing attribute closing bracket in synthetic string doesn't have a number of objects at '%s'\n", attrs);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
while (')' != *attrs) {
|
|
int iscache = hwloc__obj_type_is_cache(type);
|
|
|
|
if (iscache && !strncmp("size=", attrs, 5)) {
|
|
memorysize = hwloc_synthetic_parse_memory_attr(attrs+5, &attrs);
|
|
|
|
} else if (!iscache && !strncmp("memory=", attrs, 7)) {
|
|
memorysize = hwloc_synthetic_parse_memory_attr(attrs+7, &attrs);
|
|
|
|
} else if (!strncmp("memorysidecachesize=", attrs, 20)) {
|
|
sattr->memorysidecachesize = hwloc_synthetic_parse_memory_attr(attrs+20, &attrs);
|
|
|
|
} else if (!strncmp("indexes=", attrs, 8)) {
|
|
index_string = attrs+8;
|
|
attrs += 8;
|
|
index_string_length = strcspn(attrs, " )");
|
|
attrs += index_string_length;
|
|
|
|
} else {
|
|
size_t length = strcspn(attrs, " )");
|
|
fprintf(stderr, "hwloc/synthetic: Ignoring unknown attribute at '%s'\n", attrs);
|
|
attrs += length;
|
|
}
|
|
|
|
if (' ' == *attrs)
|
|
attrs++;
|
|
else if (')' != *attrs) {
|
|
if (verbose)
|
|
fprintf(stderr, "Missing parameter separator at '%s'\n", attrs);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
sattr->memorysize = memorysize;
|
|
|
|
if (index_string) {
|
|
if (sind->string && verbose)
|
|
fprintf(stderr, "Overwriting duplicate indexes attribute with last occurence\n");
|
|
sind->string = index_string;
|
|
sind->string_length = (unsigned long)index_string_length;
|
|
}
|
|
|
|
*next_posp = next_pos+1;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
hwloc_synthetic_set_default_attrs(struct hwloc_synthetic_attr_s *sattr,
|
|
int *type_count)
|
|
{
|
|
hwloc_obj_type_t type = sattr->type;
|
|
|
|
if (type == HWLOC_OBJ_GROUP) {
|
|
if (sattr->depth == (unsigned)-1)
|
|
sattr->depth = type_count[HWLOC_OBJ_GROUP]--;
|
|
|
|
} else if (hwloc__obj_type_is_cache(type)) {
|
|
if (!sattr->memorysize) {
|
|
if (1 == sattr->depth)
|
|
/* 32KiB in L1 */
|
|
sattr->memorysize = 32*1024;
|
|
else
|
|
/* *4 at each level, starting from 1MiB for L2, unified */
|
|
sattr->memorysize = 256ULL*1024 << (2*sattr->depth);
|
|
}
|
|
|
|
} else if (type == HWLOC_OBJ_NUMANODE && !sattr->memorysize) {
|
|
/* 1GiB in memory nodes. */
|
|
sattr->memorysize = 1024*1024*1024;
|
|
}
|
|
}
|
|
|
|
/* frees level until arity = 0 */
|
|
static void
|
|
hwloc_synthetic_free_levels(struct hwloc_synthetic_backend_data_s *data)
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<HWLOC_SYNTHETIC_MAX_DEPTH; i++) {
|
|
struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
|
|
struct hwloc_synthetic_attached_s **pprev = &curlevel->attached;
|
|
while (*pprev) {
|
|
struct hwloc_synthetic_attached_s *cur = *pprev;
|
|
*pprev = cur->next;
|
|
free(cur);
|
|
}
|
|
free(curlevel->indexes.array);
|
|
if (!curlevel->arity)
|
|
break;
|
|
}
|
|
free(data->numa_attached_indexes.array);
|
|
}
|
|
|
|
/* Read from description a series of integers describing a symmetrical
|
|
topology and update the hwloc_synthetic_backend_data_s accordingly. On
|
|
success, return zero. */
|
|
static int
|
|
hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s *data,
|
|
const char *description)
|
|
{
|
|
const char *pos, *next_pos;
|
|
unsigned long item, count;
|
|
unsigned i;
|
|
int type_count[HWLOC_OBJ_TYPE_MAX];
|
|
unsigned unset;
|
|
int verbose = 0;
|
|
const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE");
|
|
int err;
|
|
unsigned long totalarity = 1;
|
|
|
|
if (env)
|
|
verbose = atoi(env);
|
|
|
|
data->numa_attached_nr = 0;
|
|
data->numa_attached_indexes.array = NULL;
|
|
|
|
/* default values before we add root attributes */
|
|
data->level[0].totalwidth = 1;
|
|
data->level[0].attr.type = HWLOC_OBJ_MACHINE;
|
|
data->level[0].indexes.string = NULL;
|
|
data->level[0].indexes.array = NULL;
|
|
data->level[0].attr.memorysize = 0;
|
|
data->level[0].attr.memorysidecachesize = 0;
|
|
data->level[0].attached = NULL;
|
|
type_count[HWLOC_OBJ_MACHINE] = 1;
|
|
if (*description == '(') {
|
|
err = hwloc_synthetic_parse_attrs(description+1, &description, &data->level[0].attr, &data->level[0].indexes, verbose);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
data->numa_attached_indexes.string = NULL;
|
|
data->numa_attached_indexes.array = NULL;
|
|
|
|
for (pos = description, count = 1; *pos; pos = next_pos) {
|
|
hwloc_obj_type_t type = HWLOC_OBJ_TYPE_NONE;
|
|
union hwloc_obj_attr_u attrs;
|
|
|
|
/* initialize parent arity to 0 so that the levels are not infinite */
|
|
data->level[count-1].arity = 0;
|
|
|
|
while (*pos == ' ' || *pos == '\n')
|
|
pos++;
|
|
|
|
if (!*pos)
|
|
break;
|
|
|
|
if (*pos == '[') {
|
|
/* attached */
|
|
struct hwloc_synthetic_attached_s *attached, **pprev;
|
|
char *attr;
|
|
|
|
pos++;
|
|
|
|
if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string with unknown attached object type at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
if (type != HWLOC_OBJ_NUMANODE) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string with disallowed attached object type at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
data->numa_attached_nr += data->level[count-1].totalwidth;
|
|
|
|
attached = malloc(sizeof(*attached));
|
|
if (attached) {
|
|
attached->attr.type = type;
|
|
attached->attr.memorysize = 0;
|
|
attached->attr.memorysidecachesize = 0;
|
|
/* attached->attr.depth and .cachetype unused */
|
|
attached->next = NULL;
|
|
pprev = &data->level[count-1].attached;
|
|
while (*pprev)
|
|
pprev = &((*pprev)->next);
|
|
*pprev = attached;
|
|
}
|
|
|
|
next_pos = strchr(pos, ']');
|
|
if (!next_pos) {
|
|
if (verbose)
|
|
fprintf(stderr,"Synthetic string doesn't have a closing `]' after attached object type at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
attr = strchr(pos, '(');
|
|
if (attr && attr < next_pos && attached) {
|
|
const char *dummy;
|
|
err = hwloc_synthetic_parse_attrs(attr+1, &dummy, &attached->attr, &data->numa_attached_indexes, verbose);
|
|
if (err < 0)
|
|
goto error;
|
|
}
|
|
|
|
next_pos++;
|
|
continue;
|
|
}
|
|
|
|
/* normal level */
|
|
|
|
/* reset defaults */
|
|
data->level[count].indexes.string = NULL;
|
|
data->level[count].indexes.array = NULL;
|
|
data->level[count].attached = NULL;
|
|
|
|
if (*pos < '0' || *pos > '9') {
|
|
if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) {
|
|
if (!strncmp(pos, "Tile", 4) || !strncmp(pos, "Module", 6)) {
|
|
/* possible future types */
|
|
type = HWLOC_OBJ_GROUP;
|
|
} else {
|
|
/* FIXME: allow generic "Cache" string? would require to deal with possibly duplicate cache levels */
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string with unknown object type at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
}
|
|
if (type == HWLOC_OBJ_MACHINE || type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string with disallowed object type at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
next_pos = strchr(pos, ':');
|
|
if (!next_pos) {
|
|
if (verbose)
|
|
fprintf(stderr,"Synthetic string doesn't have a `:' after object type at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
pos = next_pos + 1;
|
|
}
|
|
|
|
data->level[count].attr.type = type;
|
|
data->level[count].attr.depth = (unsigned) -1;
|
|
data->level[count].attr.cachetype = (hwloc_obj_cache_type_t) -1;
|
|
if (hwloc__obj_type_is_cache(type)) {
|
|
/* these are always initialized */
|
|
data->level[count].attr.depth = attrs.cache.depth;
|
|
data->level[count].attr.cachetype = attrs.cache.type;
|
|
} else if (type == HWLOC_OBJ_GROUP) {
|
|
/* could be -1 but will be set below */
|
|
data->level[count].attr.depth = attrs.group.depth;
|
|
}
|
|
|
|
/* number of normal children */
|
|
item = strtoul(pos, (char **)&next_pos, 0);
|
|
if (next_pos == pos) {
|
|
if (verbose)
|
|
fprintf(stderr,"Synthetic string doesn't have a number of objects at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
if (!item) {
|
|
if (verbose)
|
|
fprintf(stderr,"Synthetic string with disallowed 0 number of objects at '%s'\n", pos);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
totalarity *= item;
|
|
data->level[count].totalwidth = totalarity;
|
|
data->level[count].indexes.string = NULL;
|
|
data->level[count].indexes.array = NULL;
|
|
data->level[count].attr.memorysize = 0;
|
|
data->level[count].attr.memorysidecachesize = 0;
|
|
if (*next_pos == '(') {
|
|
err = hwloc_synthetic_parse_attrs(next_pos+1, &next_pos, &data->level[count].attr, &data->level[count].indexes, verbose);
|
|
if (err < 0)
|
|
goto error;
|
|
}
|
|
|
|
if (count + 1 >= HWLOC_SYNTHETIC_MAX_DEPTH) {
|
|
if (verbose)
|
|
fprintf(stderr,"Too many synthetic levels, max %d\n", HWLOC_SYNTHETIC_MAX_DEPTH);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
if (item > UINT_MAX) {
|
|
if (verbose)
|
|
fprintf(stderr,"Too big arity, max %u\n", UINT_MAX);
|
|
errno = EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
data->level[count-1].arity = (unsigned)item;
|
|
count++;
|
|
}
|
|
|
|
if (data->level[count-1].attr.type != HWLOC_OBJ_TYPE_NONE && data->level[count-1].attr.type != HWLOC_OBJ_PU) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot use non-PU type for last level\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
data->level[count-1].attr.type = HWLOC_OBJ_PU;
|
|
|
|
for(i=HWLOC_OBJ_TYPE_MIN; i<HWLOC_OBJ_TYPE_MAX; i++) {
|
|
type_count[i] = 0;
|
|
}
|
|
for(i=count-1; i>0; i--) {
|
|
hwloc_obj_type_t type = data->level[i].attr.type;
|
|
if (type != HWLOC_OBJ_TYPE_NONE) {
|
|
type_count[type]++;
|
|
}
|
|
}
|
|
|
|
/* sanity checks */
|
|
if (!type_count[HWLOC_OBJ_PU]) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string missing ending number of PUs\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
} else if (type_count[HWLOC_OBJ_PU] > 1) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot have several PU levels\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (type_count[HWLOC_OBJ_PACKAGE] > 1) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot have several package levels\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (type_count[HWLOC_OBJ_DIE] > 1) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot have several die levels\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (type_count[HWLOC_OBJ_NUMANODE] > 1) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot have several NUMA node levels\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (type_count[HWLOC_OBJ_NUMANODE] && data->numa_attached_nr) {
|
|
if (verbose)
|
|
fprintf(stderr,"Synthetic string cannot have NUMA nodes both as a level and attached\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (type_count[HWLOC_OBJ_CORE] > 1) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot have several core levels\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* deal with missing intermediate levels */
|
|
unset = 0;
|
|
for(i=1; i<count-1; i++) {
|
|
if (data->level[i].attr.type == HWLOC_OBJ_TYPE_NONE)
|
|
unset++;
|
|
}
|
|
if (unset && unset != count-2) {
|
|
if (verbose)
|
|
fprintf(stderr, "Synthetic string cannot mix unspecified and specified types for levels\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (unset) {
|
|
/* we want in priority: numa, package, core, up to 3 caches, groups */
|
|
unsigned _count = count;
|
|
unsigned neednuma = 0;
|
|
unsigned needpack = 0;
|
|
unsigned needcore = 0;
|
|
unsigned needcaches = 0;
|
|
unsigned needgroups = 0;
|
|
/* 2 levels for machine and PU */
|
|
_count -= 2;
|
|
|
|
neednuma = (_count >= 1 && !data->numa_attached_nr);
|
|
_count -= neednuma;
|
|
|
|
needpack = (_count >= 1);
|
|
_count -= needpack;
|
|
|
|
needcore = (_count >= 1);
|
|
_count -= needcore;
|
|
|
|
needcaches = (_count > 4 ? 4 : _count);
|
|
_count -= needcaches;
|
|
|
|
needgroups = _count;
|
|
|
|
/* we place them in order: groups, package, numa, caches, core */
|
|
for(i = 0; i < needgroups; i++) {
|
|
unsigned depth = 1 + i;
|
|
data->level[depth].attr.type = HWLOC_OBJ_GROUP;
|
|
type_count[HWLOC_OBJ_GROUP]++;
|
|
}
|
|
if (needpack) {
|
|
unsigned depth = 1 + needgroups;
|
|
data->level[depth].attr.type = HWLOC_OBJ_PACKAGE;
|
|
type_count[HWLOC_OBJ_PACKAGE] = 1;
|
|
}
|
|
if (neednuma) {
|
|
unsigned depth = 1 + needgroups + needpack;
|
|
data->level[depth].attr.type = HWLOC_OBJ_NUMANODE;
|
|
type_count[HWLOC_OBJ_NUMANODE] = 1;
|
|
}
|
|
if (needcaches) {
|
|
/* priority: l2, l1, l3, l1i */
|
|
/* order: l3, l2, l1, l1i */
|
|
unsigned l3depth = 1 + needgroups + needpack + neednuma;
|
|
unsigned l2depth = l3depth + (needcaches >= 3);
|
|
unsigned l1depth = l2depth + 1;
|
|
unsigned l1idepth = l1depth + 1;
|
|
if (needcaches >= 3) {
|
|
data->level[l3depth].attr.type = HWLOC_OBJ_L3CACHE;
|
|
data->level[l3depth].attr.depth = 3;
|
|
data->level[l3depth].attr.cachetype = HWLOC_OBJ_CACHE_UNIFIED;
|
|
type_count[HWLOC_OBJ_L3CACHE] = 1;
|
|
}
|
|
data->level[l2depth].attr.type = HWLOC_OBJ_L2CACHE;
|
|
data->level[l2depth].attr.depth = 2;
|
|
data->level[l2depth].attr.cachetype = HWLOC_OBJ_CACHE_UNIFIED;
|
|
type_count[HWLOC_OBJ_L2CACHE] = 1;
|
|
if (needcaches >= 2) {
|
|
data->level[l1depth].attr.type = HWLOC_OBJ_L1CACHE;
|
|
data->level[l1depth].attr.depth = 1;
|
|
data->level[l1depth].attr.cachetype = HWLOC_OBJ_CACHE_DATA;
|
|
type_count[HWLOC_OBJ_L1CACHE] = 1;
|
|
}
|
|
if (needcaches >= 4) {
|
|
data->level[l1idepth].attr.type = HWLOC_OBJ_L1ICACHE;
|
|
data->level[l1idepth].attr.depth = 1;
|
|
data->level[l1idepth].attr.cachetype = HWLOC_OBJ_CACHE_INSTRUCTION;
|
|
type_count[HWLOC_OBJ_L1ICACHE] = 1;
|
|
}
|
|
}
|
|
if (needcore) {
|
|
unsigned depth = 1 + needgroups + needpack + neednuma + needcaches;
|
|
data->level[depth].attr.type = HWLOC_OBJ_CORE;
|
|
type_count[HWLOC_OBJ_CORE] = 1;
|
|
}
|
|
}
|
|
|
|
/* enforce a NUMA level */
|
|
if (!type_count[HWLOC_OBJ_NUMANODE] && !data->numa_attached_nr) {
|
|
/* insert a NUMA level below the automatic machine root */
|
|
if (verbose)
|
|
fprintf(stderr, "Inserting a NUMA level with a single object at depth 1\n");
|
|
/* move existing levels by one */
|
|
memmove(&data->level[2], &data->level[1], count*sizeof(struct hwloc_synthetic_level_data_s));
|
|
data->level[1].attr.type = HWLOC_OBJ_NUMANODE;
|
|
data->level[1].indexes.string = NULL;
|
|
data->level[1].indexes.array = NULL;
|
|
data->level[1].attr.memorysize = 0;
|
|
data->level[1].attr.memorysidecachesize = 0;
|
|
data->level[1].totalwidth = data->level[0].totalwidth;
|
|
/* update arity to insert a single NUMA node per parent */
|
|
data->level[1].arity = data->level[0].arity;
|
|
data->level[0].arity = 1;
|
|
count++;
|
|
}
|
|
|
|
/* set default attributes that depend on the depth/hierarchy of levels */
|
|
for (i=0; i<count; i++) {
|
|
struct hwloc_synthetic_attached_s *attached;
|
|
struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
|
|
hwloc_synthetic_set_default_attrs(&curlevel->attr, type_count);
|
|
for(attached = curlevel->attached; attached != NULL; attached = attached->next)
|
|
hwloc_synthetic_set_default_attrs(&attached->attr, type_count);
|
|
hwloc_synthetic_process_indexes(data, &curlevel->indexes, curlevel->totalwidth, verbose);
|
|
}
|
|
|
|
hwloc_synthetic_process_indexes(data, &data->numa_attached_indexes, data->numa_attached_nr, verbose);
|
|
|
|
data->string = strdup(description);
|
|
data->level[count-1].arity = 0;
|
|
return 0;
|
|
|
|
error:
|
|
hwloc_synthetic_free_levels(data);
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
hwloc_synthetic_set_attr(struct hwloc_synthetic_attr_s *sattr,
|
|
hwloc_obj_t obj)
|
|
{
|
|
switch (obj->type) {
|
|
case HWLOC_OBJ_GROUP:
|
|
obj->attr->group.kind = HWLOC_GROUP_KIND_SYNTHETIC;
|
|
obj->attr->group.subkind = sattr->depth-1;
|
|
break;
|
|
case HWLOC_OBJ_MACHINE:
|
|
break;
|
|
case HWLOC_OBJ_NUMANODE:
|
|
obj->attr->numanode.local_memory = sattr->memorysize;
|
|
obj->attr->numanode.page_types_len = 1;
|
|
obj->attr->numanode.page_types = malloc(sizeof(*obj->attr->numanode.page_types));
|
|
memset(obj->attr->numanode.page_types, 0, sizeof(*obj->attr->numanode.page_types));
|
|
obj->attr->numanode.page_types[0].size = 4096;
|
|
obj->attr->numanode.page_types[0].count = sattr->memorysize / 4096;
|
|
break;
|
|
case HWLOC_OBJ_MEMCACHE:
|
|
obj->attr->cache.depth = 1;
|
|
obj->attr->cache.linesize = 64;
|
|
obj->attr->cache.type = HWLOC_OBJ_CACHE_UNIFIED;
|
|
obj->attr->cache.size = sattr->memorysidecachesize;
|
|
break;
|
|
case HWLOC_OBJ_PACKAGE:
|
|
case HWLOC_OBJ_DIE:
|
|
break;
|
|
case HWLOC_OBJ_L1CACHE:
|
|
case HWLOC_OBJ_L2CACHE:
|
|
case HWLOC_OBJ_L3CACHE:
|
|
case HWLOC_OBJ_L4CACHE:
|
|
case HWLOC_OBJ_L5CACHE:
|
|
case HWLOC_OBJ_L1ICACHE:
|
|
case HWLOC_OBJ_L2ICACHE:
|
|
case HWLOC_OBJ_L3ICACHE:
|
|
obj->attr->cache.depth = sattr->depth;
|
|
obj->attr->cache.linesize = 64;
|
|
obj->attr->cache.type = sattr->cachetype;
|
|
obj->attr->cache.size = sattr->memorysize;
|
|
break;
|
|
case HWLOC_OBJ_CORE:
|
|
break;
|
|
case HWLOC_OBJ_PU:
|
|
break;
|
|
default:
|
|
/* Should never happen */
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static unsigned
|
|
hwloc_synthetic_next_index(struct hwloc_synthetic_indexes_s *indexes, hwloc_obj_type_t type)
|
|
{
|
|
unsigned os_index = indexes->next++;
|
|
|
|
if (indexes->array)
|
|
os_index = indexes->array[os_index];
|
|
else if (hwloc__obj_type_is_cache(type) || type == HWLOC_OBJ_GROUP)
|
|
/* don't enforce useless os_indexes for Caches and Groups */
|
|
os_index = HWLOC_UNKNOWN_INDEX;
|
|
|
|
return os_index;
|
|
}
|
|
|
|
static void
|
|
hwloc_synthetic_insert_attached(struct hwloc_topology *topology,
|
|
struct hwloc_synthetic_backend_data_s *data,
|
|
struct hwloc_synthetic_attached_s *attached,
|
|
hwloc_bitmap_t set)
|
|
{
|
|
hwloc_obj_t child;
|
|
unsigned attached_os_index;
|
|
|
|
if (!attached)
|
|
return;
|
|
|
|
assert(attached->attr.type == HWLOC_OBJ_NUMANODE);
|
|
|
|
attached_os_index = hwloc_synthetic_next_index(&data->numa_attached_indexes, HWLOC_OBJ_NUMANODE);
|
|
|
|
child = hwloc_alloc_setup_object(topology, attached->attr.type, attached_os_index);
|
|
child->cpuset = hwloc_bitmap_dup(set);
|
|
|
|
child->nodeset = hwloc_bitmap_alloc();
|
|
hwloc_bitmap_set(child->nodeset, attached_os_index);
|
|
|
|
hwloc_synthetic_set_attr(&attached->attr, child);
|
|
|
|
hwloc__insert_object_by_cpuset(topology, NULL, child, "synthetic:attached");
|
|
|
|
if (attached->attr.memorysidecachesize) {
|
|
hwloc_obj_t mscachechild = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MEMCACHE, HWLOC_UNKNOWN_INDEX);
|
|
mscachechild->cpuset = hwloc_bitmap_dup(set);
|
|
mscachechild->nodeset = hwloc_bitmap_dup(child->nodeset);
|
|
hwloc_synthetic_set_attr(&attached->attr, mscachechild);
|
|
hwloc__insert_object_by_cpuset(topology, NULL, mscachechild, "synthetic:attached:mscache");
|
|
}
|
|
|
|
hwloc_synthetic_insert_attached(topology, data, attached->next, set);
|
|
}
|
|
|
|
/*
|
|
* Recursively build objects whose cpu start at first_cpu
|
|
* - level gives where to look in the type, arity and id arrays
|
|
* - the id array is used as a variable to get unique IDs for a given level.
|
|
* - generated memory should be added to *memory_kB.
|
|
* - generated cpus should be added to parent_cpuset.
|
|
* - next cpu number to be used should be returned.
|
|
*/
|
|
static void
|
|
hwloc__look_synthetic(struct hwloc_topology *topology,
|
|
struct hwloc_synthetic_backend_data_s *data,
|
|
int level,
|
|
hwloc_bitmap_t parent_cpuset)
|
|
{
|
|
hwloc_obj_t obj;
|
|
unsigned i;
|
|
struct hwloc_synthetic_level_data_s *curlevel = &data->level[level];
|
|
hwloc_obj_type_t type = curlevel->attr.type;
|
|
hwloc_bitmap_t set;
|
|
unsigned os_index;
|
|
|
|
assert(hwloc__obj_type_is_normal(type) || type == HWLOC_OBJ_NUMANODE);
|
|
assert(type != HWLOC_OBJ_MACHINE);
|
|
|
|
os_index = hwloc_synthetic_next_index(&curlevel->indexes, type);
|
|
|
|
set = hwloc_bitmap_alloc();
|
|
if (!curlevel->arity) {
|
|
hwloc_bitmap_set(set, os_index);
|
|
} else {
|
|
for (i = 0; i < curlevel->arity; i++)
|
|
hwloc__look_synthetic(topology, data, level + 1, set);
|
|
}
|
|
|
|
hwloc_bitmap_or(parent_cpuset, parent_cpuset, set);
|
|
|
|
if (hwloc_filter_check_keep_object_type(topology, type)) {
|
|
obj = hwloc_alloc_setup_object(topology, type, os_index);
|
|
obj->cpuset = hwloc_bitmap_dup(set);
|
|
|
|
if (type == HWLOC_OBJ_NUMANODE) {
|
|
obj->nodeset = hwloc_bitmap_alloc();
|
|
hwloc_bitmap_set(obj->nodeset, os_index);
|
|
}
|
|
|
|
hwloc_synthetic_set_attr(&curlevel->attr, obj);
|
|
|
|
hwloc__insert_object_by_cpuset(topology, NULL, obj, "synthetic");
|
|
|
|
if (type == HWLOC_OBJ_NUMANODE && curlevel->attr.memorysidecachesize) {
|
|
hwloc_obj_t mscachechild = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MEMCACHE, HWLOC_UNKNOWN_INDEX);
|
|
mscachechild->cpuset = hwloc_bitmap_dup(set);
|
|
mscachechild->nodeset = hwloc_bitmap_dup(obj->nodeset);
|
|
hwloc_synthetic_set_attr(&curlevel->attr, mscachechild);
|
|
hwloc__insert_object_by_cpuset(topology, NULL, mscachechild, "synthetic:mscache");
|
|
}
|
|
}
|
|
|
|
hwloc_synthetic_insert_attached(topology, data, curlevel->attached, set);
|
|
|
|
hwloc_bitmap_free(set);
|
|
}
|
|
|
|
static int
|
|
hwloc_look_synthetic(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus)
|
|
{
|
|
/*
|
|
* This backend enforces !topology->is_thissystem by default.
|
|
*/
|
|
|
|
struct hwloc_topology *topology = backend->topology;
|
|
struct hwloc_synthetic_backend_data_s *data = backend->private_data;
|
|
hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
|
|
unsigned i;
|
|
|
|
assert(dstatus->phase == HWLOC_DISC_PHASE_GLOBAL);
|
|
|
|
assert(!topology->levels[0][0]->cpuset);
|
|
|
|
hwloc_alloc_root_sets(topology->levels[0][0]);
|
|
|
|
topology->support.discovery->pu = 1;
|
|
topology->support.discovery->numa = 1; /* we add a single NUMA node if none is given */
|
|
topology->support.discovery->numa_memory = 1; /* specified or default size */
|
|
|
|
/* start with os_index 0 for each level */
|
|
for (i = 0; data->level[i].arity > 0; i++)
|
|
data->level[i].indexes.next = 0;
|
|
data->numa_attached_indexes.next = 0;
|
|
/* ... including the last one */
|
|
data->level[i].indexes.next = 0;
|
|
|
|
/* update first level type according to the synthetic type array */
|
|
topology->levels[0][0]->type = data->level[0].attr.type;
|
|
hwloc_synthetic_set_attr(&data->level[0].attr, topology->levels[0][0]);
|
|
|
|
for (i = 0; i < data->level[0].arity; i++)
|
|
hwloc__look_synthetic(topology, data, 1, cpuset);
|
|
|
|
hwloc_synthetic_insert_attached(topology, data, data->level[0].attached, cpuset);
|
|
|
|
hwloc_bitmap_free(cpuset);
|
|
|
|
hwloc_obj_add_info(topology->levels[0][0], "Backend", "Synthetic");
|
|
hwloc_obj_add_info(topology->levels[0][0], "SyntheticDescription", data->string);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
hwloc_synthetic_backend_disable(struct hwloc_backend *backend)
|
|
{
|
|
struct hwloc_synthetic_backend_data_s *data = backend->private_data;
|
|
hwloc_synthetic_free_levels(data);
|
|
free(data->string);
|
|
free(data);
|
|
}
|
|
|
|
static struct hwloc_backend *
|
|
hwloc_synthetic_component_instantiate(struct hwloc_topology *topology,
|
|
struct hwloc_disc_component *component,
|
|
unsigned excluded_phases __hwloc_attribute_unused,
|
|
const void *_data1,
|
|
const void *_data2 __hwloc_attribute_unused,
|
|
const void *_data3 __hwloc_attribute_unused)
|
|
{
|
|
struct hwloc_backend *backend;
|
|
struct hwloc_synthetic_backend_data_s *data;
|
|
int err;
|
|
|
|
if (!_data1) {
|
|
const char *env = getenv("HWLOC_SYNTHETIC");
|
|
if (env) {
|
|
/* 'synthetic' was given in HWLOC_COMPONENTS without a description */
|
|
_data1 = env;
|
|
} else {
|
|
errno = EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
backend = hwloc_backend_alloc(topology, component);
|
|
if (!backend)
|
|
goto out;
|
|
|
|
data = malloc(sizeof(*data));
|
|
if (!data) {
|
|
errno = ENOMEM;
|
|
goto out_with_backend;
|
|
}
|
|
|
|
err = hwloc_backend_synthetic_init(data, (const char *) _data1);
|
|
if (err < 0)
|
|
goto out_with_data;
|
|
|
|
backend->private_data = data;
|
|
backend->discover = hwloc_look_synthetic;
|
|
backend->disable = hwloc_synthetic_backend_disable;
|
|
backend->is_thissystem = 0;
|
|
|
|
return backend;
|
|
|
|
out_with_data:
|
|
free(data);
|
|
out_with_backend:
|
|
free(backend);
|
|
out:
|
|
return NULL;
|
|
}
|
|
|
|
static struct hwloc_disc_component hwloc_synthetic_disc_component = {
|
|
"synthetic",
|
|
HWLOC_DISC_PHASE_GLOBAL,
|
|
~0,
|
|
hwloc_synthetic_component_instantiate,
|
|
30,
|
|
1,
|
|
NULL
|
|
};
|
|
|
|
const struct hwloc_component hwloc_synthetic_component = {
|
|
HWLOC_COMPONENT_ABI,
|
|
NULL, NULL,
|
|
HWLOC_COMPONENT_TYPE_DISC,
|
|
0,
|
|
&hwloc_synthetic_disc_component
|
|
};
|
|
|
|
static __hwloc_inline int
|
|
hwloc__export_synthetic_update_status(int *ret, char **tmp, ssize_t *tmplen, int res)
|
|
{
|
|
if (res < 0)
|
|
return -1;
|
|
*ret += res;
|
|
if (res >= *tmplen)
|
|
res = *tmplen>0 ? (int)(*tmplen) - 1 : 0;
|
|
*tmp += res;
|
|
*tmplen -= res;
|
|
return 0;
|
|
}
|
|
|
|
static __hwloc_inline void
|
|
hwloc__export_synthetic_add_char(int *ret, char **tmp, ssize_t *tmplen, char c)
|
|
{
|
|
if (*tmplen > 1) {
|
|
(*tmp)[0] = c;
|
|
(*tmp)[1] = '\0';
|
|
(*tmp)++;
|
|
(*tmplen)--;
|
|
}
|
|
(*ret)++;
|
|
}
|
|
|
|
static int
|
|
hwloc__export_synthetic_indexes(hwloc_obj_t *level, unsigned total,
|
|
char *buffer, size_t buflen)
|
|
{
|
|
unsigned step = 1;
|
|
unsigned nr_loops = 0;
|
|
struct hwloc_synthetic_intlv_loop_s *loops = NULL, *tmploops;
|
|
hwloc_obj_t cur;
|
|
unsigned i, j;
|
|
ssize_t tmplen = buflen;
|
|
char *tmp = buffer;
|
|
int res, ret = 0;
|
|
|
|
/* must start with 0 */
|
|
if (level[0]->os_index)
|
|
goto exportall;
|
|
|
|
while (step != total) {
|
|
/* must be a divider of the total */
|
|
if (total % step)
|
|
goto exportall;
|
|
|
|
/* look for os_index == step */
|
|
for(i=1; i<total; i++)
|
|
if (level[i]->os_index == step)
|
|
break;
|
|
if (i == total)
|
|
goto exportall;
|
|
for(j=2; j<total/i; j++)
|
|
if (level[i*j]->os_index != step*j)
|
|
break;
|
|
|
|
nr_loops++;
|
|
tmploops = realloc(loops, nr_loops*sizeof(*loops));
|
|
if (!tmploops)
|
|
goto exportall;
|
|
loops = tmploops;
|
|
loops[nr_loops-1].step = i;
|
|
loops[nr_loops-1].nb = j;
|
|
step *= j;
|
|
}
|
|
|
|
/* check this interleaving */
|
|
for(i=0; i<total; i++) {
|
|
unsigned ind = 0;
|
|
unsigned mul = 1;
|
|
for(j=0; j<nr_loops; j++) {
|
|
ind += (i / loops[j].step) % loops[j].nb * mul;
|
|
mul *= loops[j].nb;
|
|
}
|
|
if (level[i]->os_index != ind)
|
|
goto exportall;
|
|
}
|
|
|
|
/* success, print it */
|
|
for(j=0; j<nr_loops; j++) {
|
|
res = hwloc_snprintf(tmp, tmplen, "%u*%u%s", loops[j].step, loops[j].nb,
|
|
j == nr_loops-1 ? ")" : ":");
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) {
|
|
free(loops);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
free(loops);
|
|
return ret;
|
|
|
|
exportall:
|
|
free(loops);
|
|
|
|
/* dump all indexes */
|
|
cur = level[0];
|
|
while (cur) {
|
|
res = hwloc_snprintf(tmp, tmplen, "%u%s", cur->os_index,
|
|
cur->next_cousin ? "," : ")");
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
cur = cur->next_cousin;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
hwloc__export_synthetic_obj_attr(struct hwloc_topology * topology,
|
|
unsigned long flags,
|
|
hwloc_obj_t obj,
|
|
char *buffer, size_t buflen)
|
|
{
|
|
const char * separator = " ";
|
|
const char * prefix = "(";
|
|
char cachesize[64] = "";
|
|
char memsize[64] = "";
|
|
char memorysidecachesize[64] = "";
|
|
int needindexes = 0;
|
|
|
|
if (hwloc__obj_type_is_cache(obj->type) && obj->attr->cache.size) {
|
|
snprintf(cachesize, sizeof(cachesize), "%ssize=%llu",
|
|
prefix, (unsigned long long) obj->attr->cache.size);
|
|
prefix = separator;
|
|
}
|
|
if (obj->type == HWLOC_OBJ_NUMANODE && obj->attr->numanode.local_memory) {
|
|
snprintf(memsize, sizeof(memsize), "%smemory=%llu",
|
|
prefix, (unsigned long long) obj->attr->numanode.local_memory);
|
|
prefix = separator;
|
|
}
|
|
if (obj->type == HWLOC_OBJ_NUMANODE && !(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1)) {
|
|
hwloc_obj_t memorysidecache = obj->parent;
|
|
hwloc_uint64_t size = 0;
|
|
while (memorysidecache && memorysidecache->type == HWLOC_OBJ_MEMCACHE) {
|
|
size += memorysidecache->attr->cache.size;
|
|
memorysidecache = memorysidecache->parent;
|
|
}
|
|
if (size) {
|
|
snprintf(memorysidecachesize, sizeof(memorysidecachesize), "%smemorysidecachesize=%llu",
|
|
prefix, (unsigned long long) size);
|
|
prefix = separator;
|
|
}
|
|
}
|
|
if (!obj->logical_index /* only display indexes once per level (not for non-first NUMA children, etc.) */
|
|
&& (obj->type == HWLOC_OBJ_PU || obj->type == HWLOC_OBJ_NUMANODE)) {
|
|
hwloc_obj_t cur = obj;
|
|
while (cur) {
|
|
if (cur->os_index != cur->logical_index) {
|
|
needindexes = 1;
|
|
break;
|
|
}
|
|
cur = cur->next_cousin;
|
|
}
|
|
}
|
|
if (*cachesize || *memsize || *memorysidecachesize || needindexes) {
|
|
ssize_t tmplen = buflen;
|
|
char *tmp = buffer;
|
|
int res, ret = 0;
|
|
|
|
res = hwloc_snprintf(tmp, tmplen, "%s%s%s%s", cachesize, memsize, memorysidecachesize, needindexes ? "" : ")");
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
|
|
if (needindexes) {
|
|
unsigned total;
|
|
hwloc_obj_t *level;
|
|
|
|
if (obj->depth < 0) {
|
|
assert(obj->depth == HWLOC_TYPE_DEPTH_NUMANODE);
|
|
total = topology->slevels[HWLOC_SLEVEL_NUMANODE].nbobjs;
|
|
level = topology->slevels[HWLOC_SLEVEL_NUMANODE].objs;
|
|
} else {
|
|
total = topology->level_nbobjects[obj->depth];
|
|
level = topology->levels[obj->depth];
|
|
}
|
|
|
|
res = hwloc_snprintf(tmp, tmplen, "%sindexes=", prefix);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
|
|
res = hwloc__export_synthetic_indexes(level, total, tmp, tmplen);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
}
|
|
return ret;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
hwloc__export_synthetic_obj(struct hwloc_topology * topology, unsigned long flags,
|
|
hwloc_obj_t obj, unsigned arity,
|
|
char *buffer, size_t buflen)
|
|
{
|
|
char aritys[12] = "";
|
|
ssize_t tmplen = buflen;
|
|
char *tmp = buffer;
|
|
int res, ret = 0;
|
|
|
|
/* <type>:<arity>, except for root */
|
|
if (arity != (unsigned)-1)
|
|
snprintf(aritys, sizeof(aritys), ":%u", arity);
|
|
if (hwloc__obj_type_is_cache(obj->type)
|
|
&& (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES)) {
|
|
/* v1 uses generic "Cache" for non-extended type name */
|
|
res = hwloc_snprintf(tmp, tmplen, "Cache%s", aritys);
|
|
|
|
} else if (obj->type == HWLOC_OBJ_PACKAGE
|
|
&& (flags & (HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES
|
|
|HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1))) {
|
|
/* if exporting to v1 or without extended-types, use all-v1-compatible Socket name */
|
|
res = hwloc_snprintf(tmp, tmplen, "Socket%s", aritys);
|
|
|
|
} else if (obj->type == HWLOC_OBJ_DIE
|
|
&& (flags & (HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES
|
|
|HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1))) {
|
|
/* if exporting to v1 or without extended-types, use all-v1-compatible Group name */
|
|
res = hwloc_snprintf(tmp, tmplen, "Group%s", aritys);
|
|
|
|
} else if (obj->type == HWLOC_OBJ_GROUP /* don't export group depth */
|
|
|| flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES) {
|
|
res = hwloc_snprintf(tmp, tmplen, "%s%s", hwloc_obj_type_string(obj->type), aritys);
|
|
} else {
|
|
char types[64];
|
|
hwloc_obj_type_snprintf(types, sizeof(types), obj, 1);
|
|
res = hwloc_snprintf(tmp, tmplen, "%s%s", types, aritys);
|
|
}
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
|
|
if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
|
|
/* obj attributes */
|
|
res = hwloc__export_synthetic_obj_attr(topology, flags, obj, tmp, tmplen);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
hwloc__export_synthetic_memory_children(struct hwloc_topology * topology, unsigned long flags,
|
|
hwloc_obj_t parent,
|
|
char *buffer, size_t buflen,
|
|
int needprefix, int verbose)
|
|
{
|
|
hwloc_obj_t mchild;
|
|
ssize_t tmplen = buflen;
|
|
char *tmp = buffer;
|
|
int res, ret = 0;
|
|
|
|
mchild = parent->memory_first_child;
|
|
if (!mchild)
|
|
return 0;
|
|
|
|
if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) {
|
|
/* v1: export a single NUMA child */
|
|
if (parent->memory_arity > 1) {
|
|
/* not supported */
|
|
if (verbose)
|
|
fprintf(stderr, "Cannot export to synthetic v1 if multiple memory children are attached to the same location.\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (needprefix)
|
|
hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' ');
|
|
|
|
/* ignore memcaches and export the NUMA node */
|
|
while (mchild->type != HWLOC_OBJ_NUMANODE)
|
|
mchild = mchild->memory_first_child;
|
|
res = hwloc__export_synthetic_obj(topology, flags, mchild, 1, tmp, tmplen);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
return ret;
|
|
}
|
|
|
|
while (mchild) {
|
|
/* The core doesn't support shared memcache for now (because ACPI and Linux don't).
|
|
* So, for each mchild here, recurse only in the first children at each level.
|
|
*
|
|
* FIXME: whenever supported by the core, really recurse to export memcaches and numanode,
|
|
* but it requires clever parsing of [ memcache [numa] [numa] ] during import,
|
|
* better attaching of things to describe the hierarchy.
|
|
*/
|
|
hwloc_obj_t numanode = mchild;
|
|
/* Only export the first NUMA node leaf of each memory child.
|
|
* Memcaches are ignored here, they will be summed and exported as a single attribute
|
|
* of the NUMA node in hwloc__export_synthetic_obj().
|
|
*/
|
|
while (numanode && numanode->type != HWLOC_OBJ_NUMANODE) {
|
|
if (verbose && numanode->memory_arity > 1) {
|
|
static int warned = 0;
|
|
if (!warned)
|
|
fprintf(stderr, "Ignoring non-first memory children at non-first level of memory hierarchy.\n");
|
|
warned = 1;
|
|
}
|
|
numanode = numanode->memory_first_child;
|
|
}
|
|
assert(numanode); /* there's always a numanode at the bottom of the memory tree */
|
|
|
|
if (needprefix)
|
|
hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' ');
|
|
|
|
hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, '[');
|
|
|
|
res = hwloc__export_synthetic_obj(topology, flags, numanode, (unsigned)-1, tmp, tmplen);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
|
|
hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ']');
|
|
|
|
needprefix = 1;
|
|
mchild = mchild->next_sibling;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
hwloc_check_memory_symmetric(struct hwloc_topology * topology)
|
|
{
|
|
hwloc_bitmap_t remaining_nodes;
|
|
|
|
remaining_nodes = hwloc_bitmap_dup(hwloc_get_root_obj(topology)->nodeset);
|
|
if (!remaining_nodes)
|
|
/* assume asymmetric */
|
|
return -1;
|
|
|
|
while (!hwloc_bitmap_iszero(remaining_nodes)) {
|
|
unsigned idx;
|
|
hwloc_obj_t node;
|
|
hwloc_obj_t first_parent;
|
|
unsigned i;
|
|
|
|
idx = hwloc_bitmap_first(remaining_nodes);
|
|
node = hwloc_get_numanode_obj_by_os_index(topology, idx);
|
|
assert(node);
|
|
|
|
first_parent = node->parent;
|
|
|
|
/* check whether all object on parent's level have same number of NUMA bits */
|
|
for(i=0; i<hwloc_get_nbobjs_by_depth(topology, first_parent->depth); i++) {
|
|
hwloc_obj_t parent, mchild;
|
|
|
|
parent = hwloc_get_obj_by_depth(topology, first_parent->depth, i);
|
|
assert(parent);
|
|
|
|
/* must have same memory arity */
|
|
if (parent->memory_arity != first_parent->memory_arity)
|
|
goto out_with_bitmap;
|
|
|
|
/* clear children NUMA bits from remaining_nodes */
|
|
mchild = parent->memory_first_child;
|
|
while (mchild) {
|
|
hwloc_bitmap_clr(remaining_nodes, mchild->os_index); /* cannot use parent->nodeset, some normal children may have other NUMA nodes */
|
|
mchild = mchild->next_sibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
hwloc_bitmap_free(remaining_nodes);
|
|
return 0;
|
|
|
|
out_with_bitmap:
|
|
hwloc_bitmap_free(remaining_nodes);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
hwloc_topology_export_synthetic(struct hwloc_topology * topology,
|
|
char *buffer, size_t buflen,
|
|
unsigned long flags)
|
|
{
|
|
hwloc_obj_t obj = hwloc_get_root_obj(topology);
|
|
ssize_t tmplen = buflen;
|
|
char *tmp = buffer;
|
|
int res, ret = 0;
|
|
unsigned arity;
|
|
int needprefix = 0;
|
|
int verbose = 0;
|
|
const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE");
|
|
|
|
if (env)
|
|
verbose = atoi(env);
|
|
|
|
if (!topology->is_loaded) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (flags & ~(HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES
|
|
|HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS
|
|
|HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1
|
|
|HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* TODO: add a flag to ignore symmetric_subtree and I/Os.
|
|
* just assume things are symmetric with the left branches of the tree.
|
|
* but the number of objects per level may be wrong, what to do with OS index array in this case?
|
|
* only allow ignoring symmetric_subtree if the level width remains OK?
|
|
*/
|
|
|
|
/* TODO: add a root object by default, with a prefix such as tree=
|
|
* so that we can backward-compatibly recognize whether there's a root or not.
|
|
* and add a flag to disable it.
|
|
*/
|
|
|
|
/* TODO: flag to force all indexes, not only for PU and NUMA? */
|
|
|
|
if (!obj->symmetric_subtree) {
|
|
if (verbose)
|
|
fprintf(stderr, "Cannot export to synthetic unless topology is symmetric (root->symmetric_subtree must be set).\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)
|
|
&& hwloc_check_memory_symmetric(topology) < 0) {
|
|
if (verbose)
|
|
fprintf(stderr, "Cannot export to synthetic unless memory is attached symmetrically.\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) {
|
|
/* v1 requires all NUMA at the same level */
|
|
hwloc_obj_t node, parent;
|
|
signed pdepth;
|
|
|
|
node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0);
|
|
assert(node);
|
|
parent = node->parent;
|
|
while (!hwloc__obj_type_is_normal(parent->type))
|
|
parent = parent->parent;
|
|
pdepth = parent->depth;
|
|
|
|
while ((node = node->next_cousin) != NULL) {
|
|
parent = node->parent;
|
|
while (!hwloc__obj_type_is_normal(parent->type))
|
|
parent = parent->parent;
|
|
if (parent->depth != pdepth) {
|
|
if (verbose)
|
|
fprintf(stderr, "Cannot export to synthetic v1 if memory is attached to parents at different depths.\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* we're good, start exporting */
|
|
|
|
if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
|
|
/* obj attributes */
|
|
res = hwloc__export_synthetic_obj_attr(topology, flags, obj, tmp, tmplen);
|
|
if (res > 0)
|
|
needprefix = 1;
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) {
|
|
res = hwloc__export_synthetic_memory_children(topology, flags, obj, tmp, tmplen, needprefix, verbose);
|
|
if (res > 0)
|
|
needprefix = 1;
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
}
|
|
|
|
arity = obj->arity;
|
|
while (arity) {
|
|
/* for each level */
|
|
obj = obj->first_child;
|
|
|
|
if (needprefix)
|
|
hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' ');
|
|
|
|
res = hwloc__export_synthetic_obj(topology, flags, obj, arity, tmp, tmplen);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
|
|
if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) {
|
|
res = hwloc__export_synthetic_memory_children(topology, flags, obj, tmp, tmplen, 1, verbose);
|
|
if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
|
|
return -1;
|
|
}
|
|
|
|
/* next level */
|
|
needprefix = 1;
|
|
arity = obj->arity;
|
|
}
|
|
|
|
return ret;
|
|
}
|