/**
** Determine HAVEGE environment
**
** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com
** Copyright 2009-2014 Gary Wuertz gary@issiweb.com
** Copyright 2011-2012 BenEleventh Consulting manolson@beneleventh.com
**
** This program 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 3 of the License, or
** (at your option) any later version.
**
** This program 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 this program. If not, see .
**
*/
/**
* This compile unit implements automatic tuning for the havege algorithm. Two
* general methods are used CPUID (intel specific) and VFS (Linux specific). The
* best result of the available methods is returned.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include "havegetune.h"
/**
* Text representations of build options
*/
#define BUILD_CLOCK 'C'
#define BUILD_DIAGS 'D'
#define BUILD_CPUID 'I'
#define BUILD_THREADS 'M'
#define BUILD_OLT 'T'
#define BUILD_VFS 'V'
/**
* Text representations of TOPO_MAP sources
*/
static const char *topoReps[] = {
"D", /* SRC_DEFAULT 0x00001 */
"P", /* SRC_PARAM 0x00002 */
"A6", /* SRC_CPUID_AMD6 0x00004 */
"A5", /* SRC_CPUID_AMD5 0x00008 */
"L2", /* SRC_CPUID_INTEL2 0x00010 */
"L4", /* SRC_CPUID_INTEL4 0x00020 */
"V", /* SRC_VFS_INDEX 0x00040 */
"", /* not used 0x00080 */
"C", /* SRC_CPUID_PRESENT 0x00100 */
"H", /* SRC_CPUID_HT 0x00200 */
"A", /* SRC_CPUID_AMD 0x00400 */
"A8", /* SRC_CPUID_AMD8 0x00800 */
"B", /* SRC_CPUID_LEAFB 0x01000 */
"4", /* SRC_CPUID_LEAF4 0x02000 */
"VS", /* SRC_VFS_STATUS 0x04000 */
"VO" /* SRC_VFS_ONLINE 0x08000 */
"VI", /* SRC_VFS_CPUINFO 0x10000 */
"VC", /* SRC_VFS_CPUDIR 0x20000 */
0
};
/**
* Local debugging
*/
#if 0
#define TUNE_DEBUG(...) fprintf(stderr,__VA_ARGS__)
#define TUNE_DUMP 1
static void cfg_dump(HOST_CFG *anchor);
#else
#define TUNE_DEBUG(...)
#endif
/**
* Local prototypes
*/
#ifdef TUNING_VFS_ENABLE
static void cfg_bitClear(TOPO_MAP *m);
static int cfg_bitCount(TOPO_MAP *m);
#endif
static void cfg_bitDecode(char *dest, const char **reps, H_UINT value, H_UINT size);
#if 0
static void cfg_bitDisplay(TOPO_MAP *m);
#endif
static int cfg_bitIntersect(TOPO_MAP *m, TOPO_MAP *t);
static void cfg_bitMerge(TOPO_MAP *m,TOPO_MAP *t);
static int cfg_bitNext(TOPO_MAP *m, int n);
static void cfg_bitSet(TOPO_MAP *m, int n);
static void cfg_cacheAdd(HOST_CFG *anchor, H_UINT src, H_UINT cpu,
H_UINT level, H_UINT type, H_UINT kb);
static void cfg_cpuAdd(HOST_CFG *anchor, H_UINT src, CPU_INST *INST);
/**
* If cpuid not present, no need to generate the code
*/
#ifndef CPUID
#undef TUNING_CPUID_ENABLE
#endif
#ifdef TUNING_CPUID_ENABLE
/************************* CPUID support ***************************************/
/**
* Register names
*/
typedef enum {
EAX, EBX, ECX, EDX
} CPUID_REGNAMES;
#define CPUID_CONFIG(a) cpuid_config(a)
#define CPUID_VENDOR(r) *((H_UINT *)s) = regs[r];s+= sizeof(H_UINT)
/**
* Local CPUID prototypes
*/
#if defined (GCC_VERSION) && GCC_VERSION >= 40400
static void cpuid(int fn, int sfn, H_UINT *regs) __attribute__((optimize(0)));
#else
static void cpuid(int fn, int sfn, H_UINT *regs);
#endif
static void cpuid_config(HOST_CFG *anchor);
static void cpuid_configAmd(HOST_CFG *anchor, CPU_INST *w);
static void cpuid_configIntel(HOST_CFG *anchor, CPU_INST *w);
static void cpuid_configIntel2(HOST_CFG *anchor, CPU_INST *w, H_UINT *regs);
static void cpuid_configIntel4(HOST_CFG *anchor, CPU_INST *w, H_UINT *regs);
#else
#define CPUID_CONFIG(a)
#endif
/************************ /CPUID support ***************************************/
/************************ VFS support ***************************************/
#ifdef TUNING_VFS_ENABLE
#define VFS_LINESIZE 256
/**
* Filter function used by configuration
*/
typedef int (*pFilter)(HOST_CFG *pAnchor, char *input);
typedef int (*pDirFilter)(HOST_CFG *pAnchor, char *input, H_UINT *pArg);
/**
* Definitions
*/
#define VFS_CONFIG(a) vfs_config(a)
/**
* Local filesystem prototypes
*/
static void vfs_config(HOST_CFG *anchor);
static int vfs_configCpuDir(HOST_CFG *anchor, char *input, H_UINT *pArg);
static int vfs_configCpuInfo(HOST_CFG *anchor, char *input);
static int vfs_configDir(HOST_CFG *pAnchor, char *path, pDirFilter filter, H_UINT *pArg);
static int vfs_configFile(HOST_CFG *anchor, char *path, pFilter filter);
static int vfs_configInfoCache(HOST_CFG *pAnchor, char *input, H_UINT *pArg);
static int vfs_configOnline(HOST_CFG *anchor, char *input);
static int vfs_configInt(HOST_CFG *anchor, char *input);
static int vfs_configStatus(HOST_CFG *anchor, char *input);
static int vfs_configType(HOST_CFG *anchor, char *input);
static void vfs_parseList(TOPO_MAP *map, char *input);
static void vfs_parseMask(TOPO_MAP *map, char *input);
#else
#define VFS_CONFIG(a)
#endif
/************************* /VFS support ***************************************/
/**
* Get tuning values for collector
*/
void havege_tune( /* RETURN: none */
HOST_CFG *anchor, /* OUT: tuning info */
H_PARAMS *param) /* IN: config parameters */
{
char *bp = anchor->buildOpts;
int i;
/**
* Capture build options
*/
#ifdef __GNUC__
bp += snprintf(bp, 24, "gcc %d.%d.%d ", __GNUC__ ,__GNUC_MINOR__ , __GNUC_PATCHLEVEL__);
#endif
#if defined(ENABLE_CLOCK_GETTIME)
*bp++ = BUILD_CLOCK;
#endif
#if defined(RAW_IN_ENABLE) || defined(RAW_OUT_ENABLE)
*bp++ = BUILD_DIAGS;
#endif
#ifdef TUNING_CPUID_ENABLE
*bp++ = BUILD_CPUID;
#endif
#if NUMBER_CORES>1
*bp++ = BUILD_THREADS;
#endif
#ifdef ONLINE_TESTS_ENABLE
*bp++ = BUILD_OLT;
#endif
#ifdef TUNING_VFS_ENABLE
*bp++ = BUILD_VFS;
#endif
*bp = 0;
/**
* Virtual file system setup
*/
anchor->procfs = param->procFs==NULL? "/proc" : param->procFs;
anchor->sysfs = param->sysFs==NULL? "/sys" : param->sysFs;
/**
* The order determines preference
*/
if (param->icacheSize != 0)
cfg_cacheAdd(anchor, SRC_PARAM, -1, 1, 'I', param->icacheSize);
if (param->dcacheSize != 0)
cfg_cacheAdd(anchor, SRC_PARAM, -1, 1, 'D', param->dcacheSize);
/**
* Bypass configuration if entirely specified
*/
if (param->icacheSize == 0 || param->dcacheSize == 0) {
CPUID_CONFIG(anchor);
VFS_CONFIG(anchor);
cfg_cacheAdd(anchor, SRC_DEFAULT, -1, 1, 'I', GENERIC_ICACHE);
cfg_cacheAdd(anchor, SRC_DEFAULT, -1, 1, 'D', GENERIC_DCACHE);
}
/**
* Make sure there is at least 1 cpu instance. cpus configuration is considered
* homogeneous so only the first will be reported/used.
*/
if (0 == anchor->ctCpu)
cfg_cpuAdd(anchor, 0, NULL);
cfg_bitDecode(anchor->cpuOpts, topoReps, anchor->cpus[0].cpuMap.source, SZ_CPUREP);
#ifdef TUNE_DUMP
cfg_dump(anchor);
#endif
anchor->d_tune = anchor->i_tune = MAX_CACHES+2;
for (i=0;ictCache;i++) {
if (anchor->caches[i].level==1) {
switch(anchor->caches[i].type) {
case 'I': case 'T':
if (i < (int)anchor->i_tune)
anchor->i_tune = i;
break;
case 'D':
if (i < (int)anchor->d_tune)
anchor->d_tune = i;
break;
}
}
}
cfg_bitDecode(anchor->icacheOpts, topoReps,
anchor->caches[anchor->i_tune].cpuMap.source, SZ_CACHEREP);
cfg_bitDecode(anchor->dcacheOpts, topoReps,
anchor->caches[anchor->d_tune].cpuMap.source, SZ_CACHEREP);
TUNE_DEBUG("havege_tune %d/%d\n", anchor->i_tune, anchor->d_tune);
}
#ifdef TUNING_VFS_ENABLE
/**
* Return number of bits set in map
*/
static void cfg_bitClear( /* RETURN : None */
TOPO_MAP *m) /* IN: bitmap */
{
memset(&m->bits[0], 0, MAX_BIT_IDX * sizeof(H_UINT));
}
/**
* Return number of bits set in map
*/
static int cfg_bitCount( /* RETURN : None */
TOPO_MAP *m) /* IN: bitmap */
{
int n, ct=0;
for(n=-1;(n=cfg_bitNext(m,n))!=-1;ct++) ;
return ct;
}
#endif
/**
* decode bit representation
*/
static void cfg_bitDecode( /* RETURN: None */
char *dest, /* OUT: target */
const char **reps, /* IN: codes */
H_UINT value, /* IN: value to decode */
H_UINT size) /* IN: max range */
{
H_UINT i=0;
const char *s;
size -= 1;
while(value!= 0 && *reps != 0) {
s = *reps++;
if ((value & 1) != 0) {
if (i>0 && i < size)
dest[i++] = ' ';
while(*s != 0 && i < size)
dest[i++] = *s++;
}
value >>= 1;
}
dest[i] = 0;
}
#if 0
/**
* Display topo bit map - cpuset(7) convention is big-endian
*/
static void cfg_bitDisplay( /* RETURN : None */
TOPO_MAP *m) /* IN: bitmap */
{
int n;
for(n=m->msw;n>=0 && n < (int)MAX_BIT_IDX;n--)
printf(" %08x", m->bits[n]);
}
#endif
/**
* Test if maps intersect
*/
static int cfg_bitIntersect( /* RETURN: None */
TOPO_MAP *m, /* OUT: bitmap */
TOPO_MAP *t) /* IN: bit to set */
{
H_UINT i;
for (i=0;i < MAX_BIT_IDX;i++)
if (0!=(m->bits[i] & t->bits[i]))
return 1;
return 0;
}
/**
* Merge two maps
*/
static void cfg_bitMerge( /* RETURN: None */
TOPO_MAP *m, /* OUT: bitmap */
TOPO_MAP *t) /* IN: bits to set */
{
int i;
for (i=0;i<(int)MAX_BIT_IDX;i++) {
m->bits[i] |= t->bits[i];
if (0 != m->bits[i] && i > m->msw)
m->msw = i;
}
}
/**
* Find next bit in topo bit map
*/
static int cfg_bitNext ( /* RETURN: index of next bit or -1 */
TOPO_MAP *m, /* IN: bitmap */
int n) /* IN: prev bit use -1 for first */
{
int bit, word;
bit = (n+1) % BITS_PER_H_UINT;
for(word = (n+1) / BITS_PER_H_UINT;word <= m->msw && word < (int)MAX_BIT_IDX;word++) {
for(;bit<(int)BITS_PER_H_UINT; bit++)
if (m->bits[word] & 1<< bit)
return word * BITS_PER_H_UINT + bit;
bit = 0;
}
return -1;
}
/**
* Set a bit in the topo bit map
*/
static void cfg_bitSet( /* RETURN: None */
TOPO_MAP *m, /* OUT: bitmap */
int n) /* IN: bit to set */
{
int word;
word = n / BITS_PER_H_UINT;
if (word < (int)MAX_BIT_IDX) {
if (word > m->msw)
m->msw = word;
m->bits[word] |= 1 << (n % BITS_PER_H_UINT);
}
}
/**
* Add cache description to configuration
*/
static void cfg_cacheAdd( /* RETURN: None */
HOST_CFG *anchor, /* IN-OUT: configuration */
H_UINT src, /* IN: source */
H_UINT cpu, /* IN: use -1 for all */
H_UINT level, /* IN: cache level */
H_UINT type, /* IN: cache type */
H_UINT kb) /* IN: cache size in kb */
{
int i;
TUNE_DEBUG("cacheAdd(%x, %d,%d,%d,%c)\n", src, cpu, level, kb, type);
if (3 < level || 0 ==kb) return;
for(i = 0;i < anchor->ctCache;i++)
if (anchor->caches[i].level == level &&
anchor->caches[i].type == type &&
anchor->caches[i].size == kb)
break;
if (i >= MAX_CACHES) return;
if (-1 == (int)cpu)
cfg_bitMerge(&anchor->caches[i].cpuMap, &anchor->pCacheInfo);
else cfg_bitSet(&anchor->caches[i].cpuMap, cpu);
anchor->caches[i].cpuMap.source |= src;
if (i < anchor->ctCache) return;
anchor->caches[i].level = level;
anchor->caches[i].type = type;
anchor->caches[i].size = kb;
anchor->ctCache += 1;
}
/**
* Add cpu description to configuration
*/
static void cfg_cpuAdd( /* RETURN: None */
HOST_CFG *anchor, /* IN-OUT: configuration */
H_UINT src, /* IN: source */
CPU_INST *inst) /* IN: instance */
{
int i=0;
TUNE_DEBUG("cpuAdd(%x)\n", src);
if (NULL==inst) {
cfg_bitSet(&anchor->cpus[i].cpuMap, 0);
anchor->ctCpu = 1;
return;
}
for(i=0;i < anchor->ctCpu;i++)
if (0 != cfg_bitIntersect(&anchor->cpus[i].cpuMap, &inst->cpuMap)) {
cfg_bitMerge(&anchor->cpus[i].cpuMap, &inst->cpuMap);
anchor->cpus[i].cpuMap.source |= src;
return;
}
if (i >= MAX_CPUS) return;
memcpy(&anchor->cpus[i], inst, sizeof(CPU_INST));
anchor->cpus[i].cpuMap.source = src;
anchor->ctCpu += 1;
}
#ifdef TUNE_DUMP
/**
* Diagnostic dump
*/
static void cfg_dump( /* RETURN: None */
HOST_CFG *anchor) /* IN: configuration */
{
int i;
for(i=0;i < anchor->ctCpu;i++)
printf("Cpu: %08x\n",
anchor->caches[i].cpuMap.source
);
for(i = 0;i < anchor->ctCache;i++)
printf("Cache: %08x %d, %c, %d\n",
anchor->caches[i].cpuMap.source,
anchor->caches[i].level,
anchor->caches[i].type,
anchor->caches[i].size
);
}
#endif
#ifdef TUNING_CPUID_ENABLE
/************************* CPUID support ***************************************/
/**
* Wrapper around the cpuid macro to assist in debugging
*/
static void cpuid( /* RETURN: none */
int fn, /* IN: function code */
int sfn, /* IN: subfunction */
H_UINT *regs) /* IN-OUT: Workspace */
{
regs[2] = sfn;
CPUID(fn, regs);
}
/**
* Get configuration
*/
static void cpuid_config( /* RETURN: none */
HOST_CFG *anchor) /* IN-OUT: result */
{
CPU_INST wsp;
H_UINT regs[4];
char *s;
if (HASCPUID(regs)) {
memset(&wsp, 0, sizeof(CPU_INST));
wsp.flags |= SRC_CPUID_PRESENT;
cpuid(0x00,0,regs);
wsp.maxFn = regs[EAX];
s = wsp.vendor;
CPUID_VENDOR(EBX);
CPUID_VENDOR(EDX);
CPUID_VENDOR(ECX);
if (regs[EBX] == 0x68747541)
wsp.flags |= SRC_CPUID_AMD;
(void)cpuid(0x80000000,0,regs);
wsp.maxFnx = regs[EAX];
(void)cpuid(0x01,0,regs);
wsp.signature = regs[EAX];
if ((regs[EDX] & (1<<28)) != 0)
wsp.flags |= SRC_CPUID_HT;
TUNE_DEBUG("cpuid_config %s fn=%d fnx=%x flags=%x\n", (char *)wsp.vendor,
wsp.maxFn, wsp.maxFnx, wsp.flags);
if (0!=(wsp.flags & SRC_CPUID_AMD))
cpuid_configAmd(anchor, &wsp);
else cpuid_configIntel(anchor, &wsp);
}
}
/**
* AMD cpuid Configuration. Reference: Publication 25481, Revision 2.34, Sept 2010
*/
static void cpuid_configAmd(
HOST_CFG *anchor, /* IN-OUT: result */
CPU_INST *w) /* IN-OUT: Workspace */
{
H_UINT regs[4];
int i, n;
switch((w->maxFnx&15)) {
case 8:
cpuid(0x80000008,0,regs);
n = 1 + (regs[ECX] & 0xff);
for(i=0;icpuMap, i);
cfg_cpuAdd(anchor, SRC_CPUID_AMD8, w);
/* fallthrough */
case 6:
cpuid(0x80000006,0,regs);
cfg_cacheAdd(anchor, SRC_CPUID_AMD6, -1, 2, 'U', (regs[ECX]>>16) & 0xffff);
cfg_cacheAdd(anchor, SRC_CPUID_AMD6, -1, 3, 'U', ((regs[EDX]>>18) & 0x3fff)<<9);
/* fallthrough */
case 5:
cpuid(0x80000005,0,regs);
cfg_cacheAdd(anchor, SRC_CPUID_AMD5, -1, 1, 'D', (regs[ECX]>>24) & 0xff);
cfg_cacheAdd(anchor, SRC_CPUID_AMD5, -1, 1, 'I', (regs[EDX]>>24) & 0xff);
break;
}
}
/**
* Intel cpuid configuration. Reference: Publication 241618
* Processor Identification and CPUID Instruction
* Application node 485, Jan 2011
*/
static void cpuid_configIntel(
HOST_CFG *anchor, /* IN-OUT: result */
CPU_INST *w) /* IN-OUT: Workspace */
{
H_UINT regs[4];
if (w->maxFn >=0x0b) {
regs[ECX] = 0;
cpuid(0x0b,0,regs);
if (regs[EBX]!=0)
w->flags |= SRC_CPUID_LEAFB;
}
#if 0
if (w->flags & SRC_CPUID_HT)
;
else
;
#endif
if (w->maxFn >=4)
cpuid_configIntel4(anchor, w, regs);
if (w->maxFn >= 2)
cpuid_configIntel2(anchor, w, regs);
}
/**
* Configure caches using cpuid leaf 2. This is a legacy method that only determines
* level 1 cache. Still needed because trace cache is not reported elsewhere.
*/
static void cpuid_configIntel2(
HOST_CFG *anchor, /* IN-OUT: result */
CPU_INST *w, /* IN-OUT: Workspace */
H_UINT *regs) /* IN-OUT: registers */
{
/* L1 and Trace as per Intel application note 485, May 2012 */
static const H_UINT defs[] = {
0x06, 'I', 8 , /* 4-way set assoc, 32 byte line size */
0x08, 'I', 16 , /* 4-way set assoc, 32 byte line size */
0x09, 'I', 32 , /* 4-way set assoc, 64 byte line size + */
0x0a, 'D', 8 , /* 2 way set assoc, 32 byte line size */
0x0c, 'D', 16 , /* 4-way set assoc, 32 byte line size */
0x0d, 'D', 16 , /* 4-way set assoc, 64 byte line size + */
0x0e, 'D', 24 , /* 6-way set assoc, 64 byte line size */
0x10, 'D', 16 , /* 4-way set assoc, 64 byte line size */
0x15, 'I', 16 , /* 4-way set assoc, 64 byte line size */
0x2c, 'D', 32 , /* 8-way set assoc, 64 byte line size */
0x30, 'I', 32 , /* 8-way set assoc, 64 byte line size */
0x60, 'D', 16 , /* 8-way set assoc, sectored cache, 64 byte line size */
0x66, 'D', 8 , /* 4-way set assoc, sectored cache, 64 byte line size */
0x67, 'D', 16 , /* 4-way set assoc, sectored cache, 64 byte line size */
0x68, 'D', 32 , /* 4-way set assoc, sectored cache, 64 byte line size */
0x70, 'T', 12 , /* 8-way set assoc, trace cache */
0x71, 'T', 16 , /* 8-way set assoc, trace cache */
0x72, 'T', 32 , /* 8-way set assoc, trace cache */
0x73, 'T', 64 , /* 8-way set assoc, trace cache */
0x00, 0, 0 /* sentinel */
};
H_UINT i, j, n, m;
cpuid(0x02,0,regs);
n = regs[EAX]&0xff;
while(n--) {
for (i=0;i<4;i++) {
if (0==(regs[i] & 0x80000000)) {
while(0 != regs[i]) {
m = regs[i] & 0xff;
if (m==0xff)
w->flags |= SRC_CPUID_LEAF4;
else for (j=0;0 != defs[j];j += 3)
if (defs[j]==m) {
cfg_cacheAdd(anchor, SRC_CPUID_INTEL2, -1, 1, defs[j+1], defs[j+2]);
break;
}
regs[i]>>=8;
}
}
}
if (n) cpuid(0x02,0,regs);
}
}
/**
* Configure caches using cpuid leaf 4
*/
static void cpuid_configIntel4(
HOST_CFG *anchor, /* IN-OUT: result */
CPU_INST *w, /* IN-OUT: Workspace */
H_UINT *regs) /* IN-OUT: registers */
{
H_UINT level, type;
H_UINT i, j, lineSz, nParts, nWays, sz;
for(i=0;i>26);
for(j=0;jcpuMap, j);
cfg_cpuAdd(anchor, SRC_CPUID_INTEL4, w);
}
switch(regs[EAX] & 31) {
case 0: type = 0 ; break;
case 1: type = 'D'; break;
case 2: type = 'I'; break;
case 3: type = 'U'; break;
default: type = '?'; break;
}
if (0==type) break;
regs[EAX] >>= 5;
level = (H_UINT)(regs[EAX] & 7);
lineSz = 1 + (regs[EBX] & 0xfff);
regs[EBX] >>= 12;
nParts = 1 + (regs[EBX] & 0x3ff);
regs[EBX] >>= 10;
nWays = 1 + regs[EBX];
sz = (nWays * nParts * lineSz * (regs[ECX]+1)) / 1024;
cfg_cacheAdd(anchor, SRC_CPUID_INTEL4, -1, level, type, sz);
}
}
#endif
/************************* CPUID support ***************************************/
/************************* VFS support ***************************************/
#ifdef TUNING_VFS_ENABLE
/**
* Get configuration
*/
static void vfs_config(
HOST_CFG *anchor) /* IN-OUT: result */
{
char path[FILENAME_MAX];
CPU_INST inst;
H_UINT args[2];
int n;
args[0] = args[1] = 0;
snprintf(path, FILENAME_MAX, "%s/self/status", anchor->procfs);
if (-1 != vfs_configFile(anchor, path, vfs_configStatus))
args[0] |= SRC_VFS_STATUS;
snprintf(path, FILENAME_MAX, "%s/devices/system/cpu/online", anchor->sysfs);
if (-1 != vfs_configFile(anchor, path, vfs_configOnline))
args[0] |= SRC_VFS_ONLINE;
snprintf(path, FILENAME_MAX, "%s/cpuinfo", anchor->procfs);
if (-1 != vfs_configFile(anchor, path, vfs_configCpuInfo))
args[0] |= SRC_VFS_CPUINFO;
snprintf(path, FILENAME_MAX, "%s/devices/system/cpu", anchor->sysfs);
if (-1 != vfs_configDir(anchor, path, vfs_configCpuDir, args)) {
memset(&inst, 0, sizeof(CPU_INST));
args[0] |= SRC_VFS_CPUDIR;
for(n=-1;(n=cfg_bitNext(&anchor->pCpuInfo,n))!=-1;)
cfg_bitSet(&inst.cpuMap, n);
if (cfg_bitCount(&inst.cpuMap)>0)
cfg_cpuAdd(anchor, SRC_VFS_CPUINFO, &inst);
}
for(n=-1;(n=cfg_bitNext(&anchor->pCacheInfo,n))!=-1;) {
snprintf(path, FILENAME_MAX, "%s/devices/system/cpu/cpu%d/cache", anchor->sysfs, n);
args[1] = n;
vfs_configDir(anchor, path, vfs_configInfoCache, args);
}
}
/**
* Call back to get cpus and from cpu subdirectories
*/
static int vfs_configCpuDir(
HOST_CFG *anchor, /* IN-OUT: result */
char *input, /* filename */
H_UINT *pArg) /* parameter */
{
(void)pArg;
if (strlen(input)> 3) {
char term[32];
int cpu;
cpu = atoi(input+3);
(void)snprintf(term, 32, "cpu%d", cpu);
if (!strcmp(term, input))
cfg_bitSet(&anchor->pCacheInfo, cpu);
}
return 0;
}
/**
* Call back to get cpus from cpuinfo
*/
static int vfs_configCpuInfo(
HOST_CFG *anchor, /* IN-OUT: result */
char *input) /* input text */
{
char *s = strchr(input, ':');
if (NULL != s) {
char key[32], value[32];
*s++ = '\0';
if (1==sscanf(input, "%31s", key) && 1==sscanf(s, "%31s", value)) {
if (!strcmp("processor",key))
cfg_bitSet(&anchor->pCpuInfo, atoi(value));
}
}
return 0;
}
/**
* Process a configuration directory
*/
static int vfs_configDir(
HOST_CFG *pAnchor, /* IN-OUT: result */
char *path, /* IN: directory path */
pDirFilter filter, /* IN: entry filter */
H_UINT *pArg) /* IN: filter arg */
{
DIR *d;
int rv=-1;
if (NULL != (d = opendir(path))) {
struct dirent *ent;
while (NULL!=(ent = readdir(d))) {
if (0!=(rv = (*filter)(pAnchor, ent->d_name, pArg)))
break;
}
(void)closedir(d);
}
return rv;
}
/**
* Process a configuration file
*/
static int vfs_configFile(
HOST_CFG *pAnchor, /* IN-OUT: result */
char *path, /* IN: file path */
pFilter filter) /* IN: input filter */
{
FILE *f;
int rv=-1;
if (NULL != (f = fopen(path, "rb"))) {
char buf[VFS_LINESIZE];
while(fgets(buf, VFS_LINESIZE, f))
if (0!=(rv = (*filter)(pAnchor, buf)))
break;
fclose(f);
}
return rv;
}
/**
* Call back to get cache details
*/
static int vfs_configInfoCache(
HOST_CFG *pAnchor, /* IN-OUT: result */
char *input, /* IN: path name */
H_UINT *pArgs)
{
if (strlen(input)> 5) {
char path[FILENAME_MAX];
int idx;
idx = atoi(input+5);
(void)snprintf(path, 32, "index%d", idx);
if (!strcmp(path, input)) {
int plen, ctype, level, size;
plen = snprintf(path, FILENAME_MAX, "%s/devices/system/cpu/cpu%d/cache/index%d/level",
pAnchor->sysfs, pArgs[1], idx) - 5;
level = vfs_configFile(pAnchor, path, vfs_configInt);
strcpy(path+plen, "type");
ctype = vfs_configFile(pAnchor, path, vfs_configType);
strcpy(path+plen, "size");
size = vfs_configFile(pAnchor, path, vfs_configInt);
if (size == -1)
size = ctype == 'I' ? GENERIC_ICACHE : GENERIC_DCACHE;
cfg_cacheAdd(pAnchor, SRC_VFS_INDEX, pArgs[1], level, ctype, size);
}
}
return 0;
}
/**
* Call back to get cpus from online file
*/
static int vfs_configOnline(
HOST_CFG *anchor, /* IN-OUT: result */
char *input) /* IN: input text */
{
vfs_parseList(&anchor->pOnline, input);
return 1;
}
/**
* Call back to return a single integer input
*/
static int vfs_configInt(
HOST_CFG *anchor, /* IN-OUT: result */
char *input) /* IN: input text */
{
(void)anchor;
return atoi(input);
}
/**
* Call back to get cpus and memory from status file
*/
static int vfs_configStatus(
HOST_CFG *anchor, /* IN-OUT: result */
char *input) /* IN: input text */
{
char *s = strchr(input, ':');
if (NULL != s) {
char key[32], value[VFS_LINESIZE-32];
*s++ = '\0';
if (1==sscanf(input, "%31s", key) && 1==sscanf(s, "%223s", value)) {
if (!strcmp("Cpus_allowed", key))
vfs_parseMask(&anchor->pAllowed, value);
else if (!strcmp("Mems_allowed", key))
vfs_parseMask(&anchor->mAllowed, value);
}
}
return 0;
}
/**
* Call back to return the first byte of input
*/
int vfs_configType(
HOST_CFG *anchor, /* IN-OUT: result */
char *input) /* IN: input text */
{
(void) anchor;
return input[0];
}
/**
* Parse the List format described in cpuset(7)
*/
static void vfs_parseList( /* RETURN: None */
TOPO_MAP *map, /* OUT: bit map */
char *input) /* IN: list text */
{
char *term, c;
int bounds[2], n;
cfg_bitClear(map);
for(term = strtok(input, ",");term != NULL;term = strtok(NULL, ",")) {
n = bounds[0] = bounds[1] = 0;
while(0 != (int)(c = *term++))
switch(c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
bounds[n] = bounds[n] * 10 + (int)(c - '0');
break;
case '-':
n = 1;
}
cfg_bitSet(map, bounds[0]);
if (0 != n)
while(++bounds[0] <= bounds[1])
cfg_bitSet(map, bounds[0]);
}
}
/**
* Parse the Mask format described in cpuset(7)
*/
static void vfs_parseMask( /* RETURN: None */
TOPO_MAP *map, /* OUT: bit map */
char *input) /* IN: list text */
{
char *term, c;
int m, n;
cfg_bitClear(map);
for(term = strtok(input, ",");term != NULL;term = strtok(NULL, ",")) {
m = 0;
while(0 != (int)(c = *term++))
switch(c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
m = m * 16 + (int)(c - '0');
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
m = m * 16 + (int)(c - 'A') + 10;
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
m = m * 16 + (int)(c - 'a') + 10;
}
for(n=map->msw;n>=0;n--)
map->bits[n+1] = map->bits[n];
if (0 != map->bits[map->msw+1])
map->msw++;
map->bits[0] = 0;
for(n=0;n<32;n++)
if (m & (1<