blob: fc27ac967a6c5a9ad3471c821b0a53c142a636a2 [file] [log] [blame] [edit]
/*****************************************************************************\
* xcpuinfo.c - cpuinfo related primitives
*****************************************************************************
* Copyright (C) 2009 CEA/DAM/DIF
* Written by Matthieu Hautreux <matthieu.hautreux@cea.fr>
*
* This file is part of SLURM, a resource management program.
* For details, see <http://www.schedmd.com/slurmdocs/>.
* Please also read the included file: DISCLAIMER.
*
* SLURM is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with SLURM; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#if HAVE_STDINT_H
# include <stdint.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "slurm/slurm.h"
#include "slurm/slurm_errno.h"
#include "src/common/log.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/slurmd/slurmd/get_mach_stat.h"
#include "xcpuinfo.h"
static char* _cpuinfo_path = "/proc/cpuinfo";
static int _compute_block_map(uint16_t numproc,
uint16_t **block_map, uint16_t **block_map_inv);
static int _chk_cpuinfo_str(char *buffer, char *keyword, char **valptr);
static int _chk_cpuinfo_uint32(char *buffer, char *keyword, uint32_t *val);
static int _ranges_conv(char* lrange, char** prange, int mode);
static int _range_to_map(char* range, uint16_t *map, uint16_t map_size,
int add_threads);
static int _map_to_range(uint16_t *map, uint16_t map_size, char** prange);
bool initialized = false;
uint16_t procs, sockets, cores, threads=1;
uint16_t block_map_size;
uint16_t *block_map, *block_map_inv;
/*
* get_procs - Return the count of procs on this system
* Input: procs - buffer for the CPU count
* Output: procs - filled in with CPU count, "1" if error
* return code - 0 if no error, otherwise errno
*/
extern int
get_procs(uint16_t *procs)
{
#ifdef LPAR_INFO_FORMAT2
/* AIX 5.3 only */
lpar_info_format2_t info;
*procs = 1;
if (lpar_get_info(LPAR_INFO_FORMAT2, &info, sizeof(info)) != 0) {
error("lpar_get_info() failed");
return EINVAL;
}
*procs = (uint16_t) info.online_vcpus;
#else /* !LPAR_INFO_FORMAT2 */
# ifdef _SC_NPROCESSORS_ONLN
int my_proc_tally;
*procs = 1;
my_proc_tally = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (my_proc_tally < 1) {
error ("get_procs: error running sysconf(_SC_NPROCESSORS_ONLN)");
return EINVAL;
}
*procs = (uint16_t) my_proc_tally;
# else
# ifdef HAVE_SYSCTLBYNAME
int ncpu;
size_t len = sizeof(ncpu);
*procs = 1;
if (sysctlbyname("hw.ncpus", &ncpu, &len, NULL, 0) == -1) {
error("get_procs: error running sysctl(HW_NCPU)");
return EINVAL;
}
*procs = (uint16_t) ncpu;
# else /* !HAVE_SYSCTLBYNAME */
*procs = 1;
# endif /* HAVE_SYSCTLBYNAME */
# endif /* _SC_NPROCESSORS_ONLN */
#endif /* LPAR_INFO_FORMAT2 */
return 0;
}
/*
* get_cpuinfo - Return detailed cpuinfo on this system
* Input: numproc - number of processors on the system
* Output: p_sockets - number of physical processor sockets
* p_cores - total number of physical CPU cores
* p_threads - total number of hardware execution threads
* block_map - asbtract->physical block distribution map
* block_map_inv - physical->abstract block distribution map (inverse)
* return code - 0 if no error, otherwise errno
* NOTE: User must xfree block_map and block_map_inv
*/
typedef struct cpuinfo {
uint16_t seen;
uint32_t cpuid;
uint32_t physid;
uint16_t physcnt;
uint32_t coreid;
uint16_t corecnt;
uint16_t siblings;
uint16_t cores;
} cpuinfo_t;
static cpuinfo_t *cpuinfo = NULL; /* array of CPU information for get_cpuinfo */
/* Note: file static for qsort/_compare_cpus*/
extern int
get_cpuinfo(uint16_t numproc,
uint16_t *p_sockets, uint16_t *p_cores, uint16_t *p_threads,
uint16_t *block_map_size,
uint16_t **block_map, uint16_t **block_map_inv)
{
int retval;
uint16_t numcpu = 0; /* number of cpus seen */
uint16_t numphys = 0; /* number of unique "physical id"s */
uint16_t numcores = 0; /* number of unique "cores id"s */
uint16_t maxsibs = 0; /* maximum value of "siblings" */
uint16_t maxcores = 0; /* maximum value of "cores" */
uint16_t minsibs = 0xffff; /* minimum value of "siblings" */
uint16_t mincores = 0xffff; /* minimum value of "cores" */
uint32_t maxcpuid = 0; /* maximum CPU ID ("processor") */
uint32_t maxphysid = 0; /* maximum "physical id" */
uint32_t maxcoreid = 0; /* maximum "core id" */
uint32_t mincpuid = 0xffffffff;/* minimum CPU ID ("processor") */
uint32_t minphysid = 0xffffffff;/* minimum "physical id" */
uint32_t mincoreid = 0xffffffff;/* minimum "core id" */
int i;
#if defined (__sun)
#if defined (_LP64)
int64_t curcpu, val, sockets, cores, threads;
#else
int32_t curcpu, val, sockets, cores, threads;
#endif
int32_t chip_id, core_id, ncore_per_chip, ncpu_per_chip;
#else
FILE *cpu_info_file;
char buffer[128];
uint16_t curcpu, sockets, cores, threads;
#endif
*p_sockets = numproc; /* initially all single core/thread */
*p_cores = 1;
*p_threads = 1;
*block_map_size = 0;
*block_map = NULL;
*block_map_inv = NULL;
#if defined (__sun)
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
kc = kstat_open();
if (kc == NULL) {
error ("get speed: kstat error %d", errno);
return errno;
}
#else
cpu_info_file = fopen(_cpuinfo_path, "r");
if (cpu_info_file == NULL) {
error ("get_cpuinfo: error %d opening %s",
errno, _cpuinfo_path);
return errno;
}
#endif
/* Note: assumes all processor IDs are within [0:numproc-1] */
/* treats physical/core IDs as tokens, not indices */
if (cpuinfo)
memset(cpuinfo, 0, numproc * sizeof(cpuinfo_t));
else
cpuinfo = xmalloc(numproc * sizeof(cpuinfo_t));
#if defined (__sun)
ksp = kstat_lookup(kc, "cpu_info", -1, NULL);
for (; ksp != NULL; ksp = ksp->ks_next) {
if (strcmp(ksp->ks_module, "cpu_info"))
continue;
numcpu++;
kstat_read(kc, ksp, NULL);
knp = kstat_data_lookup(ksp, "chip_id");
chip_id = knp->value.l;
knp = kstat_data_lookup(ksp, "core_id");
core_id = knp->value.l;
knp = kstat_data_lookup(ksp, "ncore_per_chip");
ncore_per_chip = knp->value.l;
knp = kstat_data_lookup(ksp, "ncpu_per_chip");
ncpu_per_chip = knp->value.l;
if (chip_id >= numproc) {
debug("cpuid is %ld (> %d), ignored", curcpu, numproc);
continue;
}
cpuinfo[chip_id].seen = 1;
cpuinfo[chip_id].cpuid = chip_id;
maxcpuid = MAX(maxcpuid, chip_id);
mincpuid = MIN(mincpuid, chip_id);
for (i = 0; i < numproc; i++) {
if ((cpuinfo[i].coreid == core_id) &&
(cpuinfo[i].corecnt))
break;
}
if (i == numproc) {
numcores++;
} else {
cpuinfo[i].corecnt++;
}
if (chip_id < numproc) {
cpuinfo[chip_id].corecnt++;
cpuinfo[chip_id].coreid = core_id;
}
maxcoreid = MAX(maxcoreid, core_id);
mincoreid = MIN(mincoreid, core_id);
if (ncore_per_chip > numproc) {
debug("cores is %u (> %d), ignored",
ncore_per_chip, numproc);
continue;
}
if (chip_id < numproc)
cpuinfo[chip_id].cores = ncore_per_chip;
maxcores = MAX(maxcores, ncore_per_chip);
mincores = MIN(mincores, ncore_per_chip);
}
#else
curcpu = 0;
while (fgets(buffer, sizeof(buffer), cpu_info_file) != NULL) {
uint32_t val;
if (_chk_cpuinfo_uint32(buffer, "processor", &val)) {
numcpu++;
curcpu = val;
if (val >= numproc) { /* out of bounds, ignore */
debug("cpuid is %u (> %d), ignored",
val, numproc);
continue;
}
cpuinfo[val].seen = 1;
cpuinfo[val].cpuid = val;
maxcpuid = MAX(maxcpuid, val);
mincpuid = MIN(mincpuid, val);
} else if (_chk_cpuinfo_uint32(buffer, "physical id", &val)) {
/* see if the ID has already been seen */
for (i=0; i<numproc; i++) {
if ((cpuinfo[i].physid == val)
&& (cpuinfo[i].physcnt))
break;
}
if (i == numproc) { /* new ID... */
numphys++; /* ...increment total */
} else { /* existing ID... */
cpuinfo[i].physcnt++; /* ...update ID cnt */
}
if (curcpu < numproc) {
cpuinfo[curcpu].physcnt++;
cpuinfo[curcpu].physid = val;
}
maxphysid = MAX(maxphysid, val);
minphysid = MIN(minphysid, val);
} else if (_chk_cpuinfo_uint32(buffer, "core id", &val)) {
/* see if the ID has already been seen */
for (i = 0; i < numproc; i++) {
if ((cpuinfo[i].coreid == val)
&& (cpuinfo[i].corecnt))
break;
}
if (i == numproc) { /* new ID... */
numcores++; /* ...increment total */
} else { /* existing ID... */
cpuinfo[i].corecnt++; /* ...update ID cnt */
}
if (curcpu < numproc) {
cpuinfo[curcpu].corecnt++;
cpuinfo[curcpu].coreid = val;
}
maxcoreid = MAX(maxcoreid, val);
mincoreid = MIN(mincoreid, val);
} else if (_chk_cpuinfo_uint32(buffer, "siblings", &val)) {
/* Note: this value is a count, not an index */
if (val > numproc) { /* out of bounds, ignore */
debug("siblings is %u (> %d), ignored",
val, numproc);
continue;
}
if (curcpu < numproc)
cpuinfo[curcpu].siblings = val;
maxsibs = MAX(maxsibs, val);
minsibs = MIN(minsibs, val);
} else if (_chk_cpuinfo_uint32(buffer, "cpu cores", &val)) {
/* Note: this value is a count, not an index */
if (val > numproc) { /* out of bounds, ignore */
debug("cores is %u (> %d), ignored",
val, numproc);
continue;
}
if (curcpu < numproc)
cpuinfo[curcpu].cores = val;
maxcores = MAX(maxcores, val);
mincores = MIN(mincores, val);
}
}
fclose(cpu_info_file);
#endif
/*** Sanity check ***/
if (minsibs == 0) minsibs = 1; /* guaranteee non-zero */
if (maxsibs == 0) {
minsibs = 1;
maxsibs = 1;
}
if (maxcores == 0) { /* no core data */
mincores = 0;
maxcores = 0;
}
/*** Compute Sockets/Cores/Threads ***/
if ((minsibs == maxsibs) && /* homogeneous system */
(mincores == maxcores)) {
sockets = numphys; /* unique "physical id" */
if (sockets <= 1) { /* verify single socket */
sockets = numcpu / maxsibs; /* maximum "siblings" */
}
if (sockets == 0)
sockets = 1; /* guarantee non-zero */
cores = numcores / sockets; /* unique "core id" */
cores = MAX(maxcores, cores); /* maximum "cpu cores" */
if (cores == 0) {
cores = numcpu / sockets; /* assume multi-core */
if (cores > 1) {
debug3("Warning: cpuinfo missing 'core id' or "
"'cpu cores' but assuming multi-core");
}
}
if (cores == 0)
cores = 1; /* guarantee non-zero */
threads = numcpu / (sockets * cores); /* solve for threads */
if (threads == 0)
threads = 1; /* guarantee non-zero */
} else { /* heterogeneous system */
sockets = numcpu;
cores = 1; /* one core per socket */
threads = 1; /* one core per core */
}
*p_sockets = sockets; /* update output parameters */
*p_cores = cores;
*p_threads = threads;
#if DEBUG_DETAIL
/*** Display raw data ***/
debug3("");
debug3("numcpu: %u", numcpu);
debug3("numphys: %u", numphys);
debug3("numcores: %u", numcores);
debug3("cores: %u->%u", mincores, maxcores);
debug3("sibs: %u->%u", minsibs, maxsibs);
debug3("cpuid: %u->%u", mincpuid, maxcpuid);
debug3("physid: %u->%u", minphysid, maxphysid);
debug3("coreid: %u->%u", mincoreid, maxcoreid);
for (i = 0; i <= maxcpuid; i++) {
debug3("CPU %d:", i);
debug3(" seen: %u", cpuinfo[i].seen);
debug3(" physid: %u", cpuinfo[i].physid);
debug3(" physcnt: %u", cpuinfo[i].physcnt);
debug3(" siblings: %u", cpuinfo[i].siblings);
debug3(" cores: %u", cpuinfo[i].cores);
debug3(" coreid: %u", cpuinfo[i].coreid);
debug3(" corecnt: %u", cpuinfo[i].corecnt);
debug3("");
}
debug3("");
debug3("Sockets: %u", sockets);
debug3("Cores per socket: %u", cores);
debug3("Threads per core: %u", threads);
#endif
*block_map_size = numcpu;
retval = _compute_block_map(*block_map_size, block_map, block_map_inv);
xfree(cpuinfo); /* done with raw cpuinfo data */
return retval;
}
/* _chk_cpuinfo_str
* check a line of cpuinfo data (buffer) for a keyword. If it
* exists, return the string value for that keyword in *valptr.
* Input: buffer - single line of cpuinfo data
* keyword - keyword to check for
* Output: valptr - string value corresponding to keyword
* return code - true if keyword found, false if not found
*/
static int _chk_cpuinfo_str(char *buffer, char *keyword, char **valptr)
{
char *ptr;
if (strncmp(buffer, keyword, strlen(keyword)))
return false;
ptr = strstr(buffer, ":");
if (ptr != NULL)
ptr++;
*valptr = ptr;
return true;
}
/* _chk_cpuinfo_uint32
* check a line of cpuinfo data (buffer) for a keyword. If it
* exists, return the uint16 value for that keyword in *valptr.
* Input: buffer - single line of cpuinfo data
* keyword - keyword to check for
* Output: valptr - uint32 value corresponding to keyword
* return code - true if keyword found, false if not found
*/
static int _chk_cpuinfo_uint32(char *buffer, char *keyword, uint32_t *val)
{
char *valptr;
if (_chk_cpuinfo_str(buffer, keyword, &valptr)) {
*val = strtoul(valptr, (char **)NULL, 10);
return true;
} else {
return false;
}
}
/*
* _compute_block_map - Compute abstract->machine block mapping (and inverse)
* allows computation of CPU ID masks for an abstract block distribution
* of logical processors which can then be mapped the IDs used in the
* actual machine processor ID ordering (which can be BIOS/OS dependendent)
* Input: numproc - number of processors on the system
* cpu - array of cpuinfo (file static for qsort/_compare_cpus)
* Output: block_map, block_map_inv - asbtract->physical block distribution map
* return code - 0 if no error, otherwise errno
* NOTE: User must free block_map and block_map_inv
*
* For example, given a system with 8 logical processors arranged as:
*
* Sockets: 4
* Cores per socket: 2
* Threads per core: 1
*
* and a logical CPU ID assignment of:
*
* Machine logical CPU ID assignment:
* Logical CPU ID: 0 1 2 3 4 5 6 7
* Physical Socket ID: 0 1 3 2 0 1 3 2
*
* The block_map would be:
*
* Abstract -> Machine logical CPU ID block mapping:
* Input: (Abstract ID) 0 1 2 3 4 5 6 7
* Output: (Machine ID) 0 4 1 5 3 7 2 6 <--- block_map[]
* Physical Socket ID: 0 0 1 1 2 2 3 3
*
* and it's inverse would be:
*
* Machine -> Abstract logical CPU ID block mapping: (inverse)
* Input: (Machine ID) 0 1 2 3 4 5 6 7
* Output: (Abstract ID) 0 2 6 4 1 3 7 5 <--- block_map_inv[]
* Physical Socket ID: 0 1 3 2 0 1 3 2
*/
/* physical cpu comparison with void * arguments to allow use with
* libc qsort()
*/
static int _icmp16(uint16_t a, uint16_t b)
{
if (a < b) {
return -1;
} else if (a == b) {
return 0;
} else {
return 1;
}
}
static int _icmp32(uint32_t a, uint32_t b)
{
if (a < b) {
return -1;
} else if (a == b) {
return 0;
} else {
return 1;
}
}
static int _compare_cpus(const void *a1, const void *b1) {
uint16_t *a = (uint16_t *) a1;
uint16_t *b = (uint16_t *) b1;
int cmp;
cmp = -1 * _icmp16(cpuinfo[*a].seen,cpuinfo[*b].seen); /* seen to front */
if (cmp != 0)
return cmp;
cmp = _icmp32(cpuinfo[*a].physid, cpuinfo[*b].physid); /* key 1: physid */
if (cmp != 0)
return cmp;
cmp = _icmp32(cpuinfo[*a].coreid, cpuinfo[*b].coreid); /* key 2: coreid */
if (cmp != 0)
return cmp;
cmp = _icmp32(cpuinfo[*a].cpuid, cpuinfo[*b].cpuid); /* key 3: cpu id */
return cmp;
}
static int _compute_block_map(uint16_t numproc,
uint16_t **block_map, uint16_t **block_map_inv)
{
uint16_t i;
/* Compute abstract->machine block mapping (and inverse) */
if (block_map) {
*block_map = xmalloc(numproc * sizeof(uint16_t));
for (i = 0; i < numproc; i++) {
(*block_map)[i] = i;
}
qsort(*block_map, numproc, sizeof(uint16_t), &_compare_cpus);
}
if (block_map_inv) {
*block_map_inv = xmalloc(numproc * sizeof(uint16_t));
for (i = 0; i < numproc; i++) {
uint16_t idx = (*block_map)[i];
(*block_map_inv)[idx] = i;
}
}
#if DEBUG_DETAIL
/* Display the mapping tables */
debug3("\nMachine logical CPU ID assignment:");
debug3("Logical CPU ID: ");
for (i = 0; i < numproc; i++) {
debug3("%3d", i);
}
debug3("");
debug3("Physical Socket ID: ");
for (i = 0; i < numproc; i++) {
debug3("%3u", cpuinfo[i].physid);
}
debug3("");
if (block_map) {
debug3("\nAbstract -> Machine logical CPU ID block mapping:");
debug3("Input: (Abstract ID) ");
for (i = 0; i < numproc; i++) {
debug3("%3d", i);
}
debug3("");
debug3("Output: (Machine ID) ");
for (i = 0; i < numproc; i++) {
debug3("%3u", (*block_map)[i]);
}
debug3("");
debug3("Physical Socket ID: ");
for (i = 0; i < numproc; i++) {
uint16_t id = (*block_map)[i];
debug3("%3u", cpuinfo[id].physid);
}
debug3("");
}
if (block_map_inv) {
debug3("\nMachine -> Abstract logical CPU ID block mapping: "
"(inverse)");
debug3("Input: (Machine ID) ");
for (i = 0; i < numproc; i++) {
debug3("%3d", i);
}
debug3("");
debug3("Output: (Abstract ID)");
for (i = 0; i < numproc; i++) {
debug3("%3u", (*block_map_inv)[i]);
}
debug3("");
debug3("Physical Socket ID: ");
for (i = 0; i < numproc; i++) {
debug3("%3u", cpuinfo[i].physid);
}
debug3("");
}
#endif
return 0;
}
int _ranges_conv(char* lrange,char** prange,int mode);
/* for testing purpose */
/* uint16_t procs=8, sockets=2, cores=2, threads=2; */
/* uint16_t block_map_size=8; */
/* uint16_t block_map[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; */
/* uint16_t block_map_inv[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; */
/* xcpuinfo_abs_to_mac("0,2,4,6",&mach); */
/* xcpuinfo_mac_to_abs(mach,&abs); */
int
xcpuinfo_init(void)
{
if ( initialized )
return XCPUINFO_SUCCESS;
if ( get_procs(&procs) )
return XCPUINFO_ERROR;
if ( get_cpuinfo(procs,&sockets,&cores,&threads,
&block_map_size,&block_map,&block_map_inv) )
return XCPUINFO_ERROR;
initialized = true ;
return XCPUINFO_SUCCESS;
}
int
xcpuinfo_fini(void)
{
if ( ! initialized )
return XCPUINFO_SUCCESS;
initialized = false ;
procs = sockets = cores = threads = 0;
block_map_size = 0;
xfree(block_map);
xfree(block_map_inv);
return XCPUINFO_SUCCESS;
}
int
xcpuinfo_abs_to_mac(char* lrange,char** prange)
{
return _ranges_conv(lrange,prange,0);
}
int
xcpuinfo_mac_to_abs(char* lrange,char** prange)
{
return _ranges_conv(lrange,prange,1);
}
int
xcpuinfo_abs_to_map(char* lrange,uint16_t **map,uint16_t *map_size)
{
*map_size = block_map_size;
*map = (uint16_t*) xmalloc(block_map_size*sizeof(uint16_t));
/* abstract range does not already include the hyperthreads */
return _range_to_map(lrange,*map,*map_size,1);
}
int
xcpuinfo_map_to_mac(uint16_t *map,uint16_t map_size,char** range)
{
return _map_to_range(map,map_size,range);
}
int
xcpuinfo_mac_to_map(char* lrange,uint16_t **map,uint16_t *map_size)
{
*map_size = block_map_size;
*map = (uint16_t*) xmalloc(block_map_size*sizeof(uint16_t));
/* machine range already includes the hyperthreads */
return _range_to_map(lrange,*map,*map_size,0);
}
int
xcpuinfo_absmap_to_macmap(uint16_t *amap,uint16_t amap_size,
uint16_t **bmap,uint16_t *bmap_size)
{
/* int i; */
/* abstract to machine conversion using block map */
uint16_t *map_out;
*bmap_size = amap_size;
map_out = (uint16_t*) xmalloc(amap_size*sizeof(uint16_t));
*bmap = map_out;
return XCPUINFO_SUCCESS;
}
int
xcpuinfo_macmap_to_absmap(uint16_t *amap,uint16_t amap_size,
uint16_t **bmap,uint16_t *bmap_size)
{
int i;
/* machine to abstract conversion using inverted block map */
uint16_t *cmap;
cmap = block_map_inv;
*bmap_size = amap_size;
*bmap = (uint16_t*) xmalloc(amap_size*sizeof(uint16_t));
for( i = 0 ; i < amap_size ; i++) {
if ( amap[i] )
(*bmap)[cmap[i]]=1;
else
(*bmap)[cmap[i]]=0;
}
return XCPUINFO_SUCCESS;
}
/*
* set to 1 each element of already allocated map of size
* map_size if they are present in the input range
* if add_thread does not equal 0, the input range is a treated
* as a core range, and it will be mapped to an array of uint16_t
* that will include all the hyperthreads associated to the cores.
*/
static int
_range_to_map(char* range,uint16_t *map,uint16_t map_size,int add_threads)
{
int bad_nb=0;
int num_fl=0;
int con_fl=0;
int last=0;
char *dup;
char *p;
char *s=NULL;
uint16_t start=0,end=0,i;
/* duplicate input range */
dup = xstrdup(range);
p = dup;
while ( ! last ) {
if ( isdigit(*p) ) {
if ( !num_fl ) {
num_fl++;
s=p;
}
}
else if ( *p == '-' ) {
if ( s && num_fl ) {
*p = '\0';
start = (uint16_t) atoi(s);
con_fl=1;
num_fl=0;
s=NULL;
}
}
else if ( *p == ',' || *p == '\0') {
if ( *p == '\0' )
last = 1;
if ( s && num_fl ) {
*p = '\0';
end = (uint16_t) atoi(s);
if ( !con_fl )
start = end ;
con_fl=2;
num_fl=0;
s=NULL;
}
}
else {
bad_nb++;
break;
}
if ( con_fl == 2 ) {
if ( add_threads ) {
start = start * threads;
end = (end+1)*threads - 1 ;
}
for( i = start ; i <= end && i < map_size ; i++) {
map[i]=1;
}
con_fl=0;
}
p++;
}
xfree(dup);
if ( bad_nb > 0 ) {
/* bad format for input range */
return XCPUINFO_ERROR;
}
return XCPUINFO_SUCCESS;
}
/*
* allocate and build a range of ids using an input map
* having printable element set to 1
*/
static int
_map_to_range(uint16_t *map,uint16_t map_size,char** prange)
{
size_t len;
int num_fl=0;
int con_fl=0;
char id[12];
char *str;
uint16_t start=0,end=0,i;
str = xstrdup("");
for ( i = 0 ; i < map_size ; i++ ) {
if ( map[i] ) {
num_fl=1;
end=i;
if ( !con_fl ) {
start=end;
con_fl=1;
}
}
else if ( num_fl ) {
if ( start < end ) {
sprintf(id,"%u-%u,",start,end);
xstrcat(str,id);
}
else {
sprintf(id,"%u,",start);
xstrcat(str,id);
}
con_fl = num_fl = 0;
}
}
if ( num_fl ) {
if ( start < end ) {
sprintf(id,"%u-%u,",start,end);
xstrcat(str,id);
}
else {
sprintf(id,"%u,",start);
xstrcat(str,id);
}
}
len = strlen(str);
if ( len > 0 ) {
str[len-1]='\0';
}
else {
xfree(str);
return XCPUINFO_ERROR;
}
if ( prange != NULL )
*prange = str;
else
xfree(str);
return XCPUINFO_SUCCESS;
}
/*
* convert a range into an other one according to
* a modus operandi being 0 or 1 for abstract to machine
* or machine to abstract representation of cores
*/
static int
_ranges_conv(char* lrange,char** prange,int mode)
{
int fstatus;
int i;
uint16_t *amap;
uint16_t *map;
uint16_t *map_out;
/* init internal data if not already done */
if ( xcpuinfo_init() != XCPUINFO_SUCCESS )
return XCPUINFO_ERROR;
if ( mode ) {
/* machine to abstract conversion */
amap = block_map_inv;
}
else {
/* abstract to machine conversion */
amap = block_map;
}
/* allocate map for local work */
map = (uint16_t*) xmalloc(block_map_size*sizeof(uint16_t));
map_out = (uint16_t*) xmalloc(block_map_size*sizeof(uint16_t));
/* extract the input map */
fstatus = _range_to_map(lrange,map,block_map_size,!mode);
if ( fstatus ) {
goto exit;
}
/* do the conversion (see src/slurmd/slurmd/get_mach_stat.c) */
for( i = 0 ; i < block_map_size ; i++) {
if ( map[i] )
map_out[amap[i]]=1;
}
/* build the ouput range */
fstatus = _map_to_range(map_out,block_map_size,prange);
exit:
xfree(map);
xfree(map_out);
return fstatus;
}