blob: d7476279fd4c9e31871bb0f5eada13fa8e1244d7 [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include "bitmask.h"
#include "cpuset.h"
#include "common.h"
#include "cpuinfo.h"
#if HAVE_LINUX_MEMPOLICY_H
#define CPUINFO_FILE "/proc/cpuinfo"
#define SCHEDSTAT_FILE "/proc/schedstat"
#define CGROUPINFO_FILE "/proc/cgroups"
#define SYS_CPU_DIR "/sys/devices/system/cpu"
#define LIST_PRESENT_CPU_FILE "/sys/devices/system/cpu/present"
#define LIST_ONLINE_CPU_FILE "/sys/devices/system/cpu/online"
struct cpuinfo *cpus;
int ncpus;
int cpus_nbits;
/* get cpu_baseinfo from /proc/cpuinfo */
static int get_cpu_baseinfo(void)
{
FILE *fp = NULL;
char buf[BUFFSIZE];
char *istr = NULL, *valstr = NULL, *saveptr = NULL;
int ci = 0;
int data = 0;
/* get the number of cpus including offline cpus */
if (!ncpus) {
ncpus = get_ncpus();
if (ncpus <= 0)
return -1;
}
if (cpus != NULL) {
free(cpus);
cpus = NULL;
}
/* allocate the memory space for cpus */
cpus = (struct cpuinfo*)malloc(sizeof(*cpus) * ncpus);
if (cpus == NULL)
return -1;
memset(cpus, 0, sizeof(*cpus) * ncpus);
/* open file /proc/cpuinfo */
if ((fp = fopen(CPUINFO_FILE, "r")) == NULL)
return -1;
/* get cpuinfo */
while (fgets(buf, sizeof(buf), fp) != NULL) {
istr = strtok_r(buf, "\t", &saveptr);
valstr = index(saveptr, ':');
if (valstr == NULL)
continue;
valstr++;
sscanf(valstr, " %d\n", &data);
if (!strcmp(istr, "processor")) {
if (data >= ncpus) {
warnx("Warn: wrong cpu index");
fclose(fp);
return -1;
}
ci = data;
cpus[ci].online = 1;
}
}
fclose(fp);
return 0;
}
/*
* get the cpu bitmask of the online processors
*
* return value: 0 - success
* -1 - failed
*/
int online_cpumask(struct bitmask *cpumask)
{
FILE *fp = NULL;
char buf[BUFFSIZE];
int i;
if (cpumask == NULL)
return -1;
/*
* open file /sys/devices/system/cpu/online and get online
* cpulist.
*/
if ((fp = fopen(LIST_ONLINE_CPU_FILE, "r")) == NULL) {
if (get_cpu_baseinfo() != 0)
return -1;
for (i = 0; i < ncpus; i++) {
if (cpus[i].online)
bitmask_setbit(cpumask, i);
}
} else {
if (fgets(buf, sizeof(buf), fp) == NULL) {
fclose(fp);
return -1;
}
fclose(fp);
/* parse present cpu list to bitmap */
buf[strlen(buf) - 1] = '\0';
if (bitmask_parselist(buf, cpumask) != 0)
return -1;
}
return 0;
}
/*
* get the cpu bitmask of the present processors including offline CPUs
*
* return value: 0 - success
* -1 - failed
*/
int present_cpumask(struct bitmask *cpumask)
{
FILE *fp = NULL;
char buf[BUFFSIZE];
char c_relpath[PATH_MAX];
int cpu = -1;
if (cpumask == NULL)
return -1;
/*
* open file /sys/devices/system/cpu/present and get present
* cpulist.
*/
if ((fp = fopen(LIST_PRESENT_CPU_FILE, "r")) == NULL) {
while_each_childdir(SYS_CPU_DIR, "/", c_relpath,
sizeof(c_relpath)) {
if (!strncmp(c_relpath + 1, "cpu", 3)
&& sscanf(c_relpath + 4, "%d", &cpu) > 0) {
if (cpu >= 0)
bitmask_setbit(cpumask, cpu);
}
} end_while_each_childdir
} else {
if (fgets(buf, sizeof(buf), fp) == NULL) {
fclose(fp);
return -1;
}
fclose(fp);
/* parse present cpu list to bitmap */
buf[strlen(buf) - 1] = '\0';
if (bitmask_parselist(buf, cpumask) != 0)
return -1;
}
return 0;
}
/*
* get the number of the processors including offline CPUs
* We get this number from /sys/devices/system/cpu/present.
* By analyzing the present cpu list, we get the number of all cpus
*/
int get_ncpus(void)
{
struct bitmask *bmp = NULL;
int n = 0;
/* get the bitmask's len */
cpus_nbits = cpuset_cpus_nbits();
if (cpus_nbits <= 0)
return -1;
/* allocate the space for bitmask */
bmp = bitmask_alloc(cpus_nbits);
if (bmp == NULL)
return -1;
if (present_cpumask(bmp)) {
bitmask_free(bmp);
return -1;
}
/* Number of highest set bit +1 is the number of the CPUs */
n = bitmask_last(bmp) + 1;
bitmask_free(bmp);
return n;
}
/* get the sched domain's info for each cpu */
static int get_sched_domains(void)
{
FILE *fp = NULL;
char buf[BUFFSIZE];
char str1[20], str2[BUFFSIZE];
int ci = 0;
/* get the bitmask's len */
if (!cpus_nbits) {
cpus_nbits = cpuset_cpus_nbits();
if (cpus_nbits <= 0) {
warnx("get cpus nbits failed.");
return -1;
}
}
/* open file /proc/schedstat */
if ((fp = fopen(SCHEDSTAT_FILE, "r")) == NULL)
return -1;
/* get cpuinfo */
while (fgets(buf, sizeof(buf), fp) != NULL) {
sscanf(buf, "%s %s", str1, str2);
if (!strncmp(str1, "cpu", 3)) {
ci = atoi(str1 + 3);
if (ci < 0 || ci >= ncpus) {
fprintf(stderr, "Warn: wrong cpu index");
fclose(fp);
return -1;
}
} else if (!strncmp(str1, "domain", 6)) {
if (!cpus[ci].sched_domain) {
cpus[ci].sched_domain = bitmask_alloc(
cpus_nbits);
if (!cpus[ci].sched_domain)
return -1;
}
if (bitmask_parsehex(str2, cpus[ci].sched_domain))
return -1;
}
}
fclose(fp);
return 0;
}
int getcpuinfo(void)
{
int i;
int node = -1;
/* get the number of cpus including offline cpus */
if (!ncpus) {
ncpus = get_ncpus();
if (ncpus <= 0)
return -1;
}
if (cpus == NULL) {
if (get_cpu_baseinfo() != 0) {
warn("get base infomation of cpus from /proc/cpuinfo "
"failed.");
return -1;
}
}
/* which node is every cpu belong to? */
for (i = 0; i < ncpus; i++) {
node = cpuset_cpu2node(i);
if (node == -1)
warnx("cpu2node failed(cpu = %d)", i);
cpus[i].nodeid = node;
}
/* get sched domain's infomation for each cpu */
if (get_sched_domains()) {
warnx("get sched domain's info for each cpu failed.");
return -1;
}
return 0;
}
/* get the number of the cpuset groups */
static int get_num_cpusets(void)
{
FILE *fp = NULL;
char buf[BUFFSIZE];
char subsys_name[BUFFSIZE];
int num_cgroups = 0;
int hierarchy;
int enabled;
/* open file /proc/cgroups and get num cpusets */
if ((fp = fopen(CGROUPINFO_FILE, "r")) == NULL)
return -1;
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (!strncmp(buf, "cpuset", 6)) {
sscanf(buf, "%s\t%d\t%d\t%d\n", subsys_name,
&hierarchy, &num_cgroups, &enabled);
}
}
fclose(fp);
return num_cgroups;
}
static struct cpuset **cpusets;
static int ncpusets;
static int find_domain_cpusets(char *relpath)
{
struct cpuset *cp = NULL;
char c_relpath[PATH_MAX];
int ret = 0;
if (relpath == NULL) {
errno = -EFAULT;
return -1;
}
cp = cpuset_alloc();
if (cp == NULL) {
errno = -ENOMEM;
return -1;
}
if (cpuset_query(cp, relpath)) {
cpuset_free(cp);
return -1;
}
if (cpuset_cpus_weight(cp) == 0)
return 0;
if (cpuset_cpus_weight(cp) > 0
&& cpuset_get_iopt(cp, "sched_load_balance") == 1) {
cpusets[ncpusets] = cp;
ncpusets++;
return 0;
}
while_each_childdir(cpuset_mountpoint(), relpath, c_relpath,
sizeof(c_relpath)) {
if ((ret = find_domain_cpusets(c_relpath)))
break;
} end_while_each_childdir;
return ret;
}
struct bitmask **domains;
int ndomains;
int partition_domains(void)
{
int num_cpusets = 0;
int i, j;
struct bitmask *cpusa = NULL, *cpusb = NULL, *cpusc = NULL;
int *flg = NULL;
int ret = 0;
num_cpusets = get_num_cpusets();
if (num_cpusets == 0) {
warnx("cpuset subsystem is't compiled into kernel.");
return -1;
}
if (!cpus_nbits) {
cpus_nbits = cpuset_cpus_nbits();
if (!cpus_nbits) {
warnx("nbits of cpus is wrong.");
return -1;
}
}
cpusa = bitmask_alloc(cpus_nbits);
if (cpusa == NULL) {
warnx("bitmask_alloc for partition domains failed.");
return -1;
}
cpusb = bitmask_alloc(cpus_nbits);
if (cpusb == NULL) {
warnx("bitmask_alloc for partition domains failed.");
ret = -1;
goto errcpusb;
}
cpusc = bitmask_alloc(cpus_nbits);
if (cpusb == NULL) {
warnx("bitmask_alloc for partition domains failed.");
ret = -1;
goto errcpusc;
}
cpusets = (struct cpuset **)malloc(num_cpusets * sizeof(*cpusets));
if (cpusets == NULL) {
warnx("alloc cpusets space failed.");
ret = -1;
goto errcpusets;
}
if ((ret = find_domain_cpusets("/"))) {
warnx("find domain cpusets failed.");
goto errfindcpusets;
}
flg = (int *)malloc(num_cpusets * sizeof(int));
if (flg == NULL) {
warnx("alloc flg failed.");
ret = -1;
goto errfindcpusets;
}
memset(flg, 0, num_cpusets * sizeof(int));
ndomains = ncpusets;
restart:
for (i = 0; i < ncpusets; i++) {
struct cpuset *cpa = cpusets[i];
if (flg[i])
continue;
cpuset_getcpus(cpa, cpusa);
for (j = i + 1; j < ncpusets; j++) {
struct cpuset *cpb = cpusets[j];
if (flg[j])
continue;
cpuset_getcpus(cpb, cpusb);
if (bitmask_intersects(cpusa, cpusb)) {
bitmask_or(cpusc, cpusa, cpusb);
cpuset_setcpus(cpa, cpusc);
flg[j] = 1;
ndomains--;
goto restart;
}
}
}
domains = (struct bitmask **)malloc(ndomains * sizeof(*domains));
if (domains == NULL) {
warnx("alloc domains space failed.");
ret = -1;
goto errdomains;
}
for (i = 0, j = 0; i < ncpusets; i++) {
if (flg[i])
continue;
domains[j] = bitmask_alloc(cpus_nbits);
if (cpuset_getcpus(cpusets[i], domains[j])) {
warnx("cpuset getcpus failed.");
ret = -1;
goto errgetdomains;
}
j++;
}
goto errdomains;
errgetdomains:
for (i = 0; i < j; i++)
bitmask_free(domains[i]);
free(domains);
domains = NULL;
errdomains:
free(flg);
errfindcpusets:
for (i = 0; i < ncpusets; i++)
cpuset_free(cpusets[i]);
free(cpusets);
cpusets = NULL;
ncpusets = 0;
errcpusets:
bitmask_free(cpusc);
errcpusc:
bitmask_free(cpusb);
errcpusb:
bitmask_free(cpusa);
return ret;
}
#endif