Home | History | Annotate | Download | only in cpuset_lib
      1 #include "config.h"
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <sys/types.h>
      6 #include <dirent.h>
      7 #include <err.h>
      8 #include <errno.h>
      9 
     10 #include "bitmask.h"
     11 #include "cpuset.h"
     12 #include "common.h"
     13 #include "cpuinfo.h"
     14 
     15 #if HAVE_LINUX_MEMPOLICY_H
     16 
     17 #define CPUINFO_FILE		"/proc/cpuinfo"
     18 #define SCHEDSTAT_FILE		"/proc/schedstat"
     19 #define CGROUPINFO_FILE		"/proc/cgroups"
     20 #define SYS_CPU_DIR		"/sys/devices/system/cpu"
     21 #define LIST_PRESENT_CPU_FILE	"/sys/devices/system/cpu/present"
     22 #define LIST_ONLINE_CPU_FILE	"/sys/devices/system/cpu/online"
     23 
     24 struct cpuinfo *cpus;
     25 int ncpus;
     26 int cpus_nbits;
     27 
     28 /* get cpu_baseinfo from /proc/cpuinfo */
     29 static int get_cpu_baseinfo(void)
     30 {
     31 	FILE *fp = NULL;
     32 	char buf[BUFFSIZE];
     33 	char *istr = NULL, *valstr = NULL, *saveptr = NULL;
     34 	int ci = 0;
     35 	int data = 0;
     36 
     37 	/* get the number of cpus including offline cpus */
     38 	if (!ncpus) {
     39 		ncpus = get_ncpus();
     40 		if (ncpus <= 0)
     41 			return -1;
     42 	}
     43 
     44 	if (cpus != NULL) {
     45 		free(cpus);
     46 		cpus = NULL;
     47 	}
     48 
     49 	/* allocate the memory space for cpus */
     50 	cpus = malloc(sizeof(*cpus) * ncpus);
     51 	if (cpus == NULL)
     52 		return -1;
     53 	memset(cpus, 0, sizeof(*cpus) * ncpus);
     54 
     55 	/* open file /proc/cpuinfo */
     56 	if ((fp = fopen(CPUINFO_FILE, "r")) == NULL)
     57 		return -1;
     58 
     59 	/* get cpuinfo */
     60 	while (fgets(buf, sizeof(buf), fp) != NULL) {
     61 		istr = strtok_r(buf, "\t", &saveptr);
     62 		valstr = index(saveptr, ':');
     63 		if (valstr == NULL)
     64 			continue;
     65 		valstr++;
     66 		sscanf(valstr, " %d\n", &data);
     67 		if (!strcmp(istr, "processor")) {
     68 			if (data >= ncpus) {
     69 				warnx("Warn: wrong cpu index");
     70 				fclose(fp);
     71 				return -1;
     72 			}
     73 			ci = data;
     74 			cpus[ci].online = 1;
     75 		}
     76 	}
     77 
     78 	fclose(fp);
     79 	return 0;
     80 }
     81 
     82 /*
     83  * get the cpu bitmask of the online processors
     84  *
     85  * return value: 0  - success
     86  *               -1 - failed
     87  */
     88 int online_cpumask(struct bitmask *cpumask)
     89 {
     90 	FILE *fp = NULL;
     91 	char buf[BUFFSIZE];
     92 	int i;
     93 
     94 	if (cpumask == NULL)
     95 		return -1;
     96 	/*
     97 	 * open file /sys/devices/system/cpu/online and get online
     98 	 * cpulist.
     99 	 */
    100 	if ((fp = fopen(LIST_ONLINE_CPU_FILE, "r")) == NULL) {
    101 		if (get_cpu_baseinfo() != 0)
    102 			return -1;
    103 		for (i = 0; i < ncpus; i++) {
    104 			if (cpus[i].online)
    105 				bitmask_setbit(cpumask, i);
    106 		}
    107 	} else {
    108 		if (fgets(buf, sizeof(buf), fp) == NULL) {
    109 			fclose(fp);
    110 			return -1;
    111 		}
    112 		fclose(fp);
    113 
    114 		/* parse present cpu list to bitmap */
    115 		buf[strlen(buf) - 1] = '\0';
    116 		if (bitmask_parselist(buf, cpumask) != 0)
    117 			return -1;
    118 	}
    119 
    120 	return 0;
    121 }
    122 
    123 /*
    124  * get the cpu bitmask of the present processors including offline CPUs
    125  *
    126  * return value: 0  - success
    127  *               -1 - failed
    128  */
    129 int present_cpumask(struct bitmask *cpumask)
    130 {
    131 	FILE *fp = NULL;
    132 	char buf[BUFFSIZE];
    133 	char c_relpath[PATH_MAX];
    134 	int cpu = -1;
    135 
    136 	if (cpumask == NULL)
    137 		return -1;
    138 	/*
    139 	 * open file /sys/devices/system/cpu/present and get present
    140 	 * cpulist.
    141 	 */
    142 	if ((fp = fopen(LIST_PRESENT_CPU_FILE, "r")) == NULL) {
    143 		while_each_childdir(SYS_CPU_DIR, "/", c_relpath,
    144 				    sizeof(c_relpath)) {
    145 			if (!strncmp(c_relpath + 1, "cpu", 3)
    146 			    && sscanf(c_relpath + 4, "%d", &cpu) > 0) {
    147 				if (cpu >= 0)
    148 					bitmask_setbit(cpumask, cpu);
    149 			}
    150 		}
    151 	end_while_each_childdir} else {
    152 		if (fgets(buf, sizeof(buf), fp) == NULL) {
    153 			fclose(fp);
    154 			return -1;
    155 		}
    156 		fclose(fp);
    157 
    158 		/* parse present cpu list to bitmap */
    159 		buf[strlen(buf) - 1] = '\0';
    160 		if (bitmask_parselist(buf, cpumask) != 0)
    161 			return -1;
    162 	}
    163 
    164 	return 0;
    165 }
    166 
    167 /*
    168  * get the number of the processors including offline CPUs
    169  * We get this number from /sys/devices/system/cpu/present.
    170  * By analyzing the present cpu list, we get the number of all cpus
    171  */
    172 int get_ncpus(void)
    173 {
    174 	struct bitmask *bmp = NULL;
    175 	int n = 0;
    176 
    177 	/* get the bitmask's len */
    178 	cpus_nbits = cpuset_cpus_nbits();
    179 	if (cpus_nbits <= 0)
    180 		return -1;
    181 
    182 	/* allocate the space for bitmask */
    183 	bmp = bitmask_alloc(cpus_nbits);
    184 	if (bmp == NULL)
    185 		return -1;
    186 
    187 	if (present_cpumask(bmp)) {
    188 		bitmask_free(bmp);
    189 		return -1;
    190 	}
    191 
    192 	/* Number of highest set bit +1 is the number of the CPUs */
    193 	n = bitmask_last(bmp) + 1;
    194 	bitmask_free(bmp);
    195 
    196 	return n;
    197 }
    198 
    199 /* get the sched domain's info for each cpu */
    200 static int get_sched_domains(void)
    201 {
    202 	FILE *fp = NULL;
    203 	char buf[BUFFSIZE];
    204 	char str1[20], str2[BUFFSIZE];
    205 	int ci = 0;
    206 
    207 	/* get the bitmask's len */
    208 	if (!cpus_nbits) {
    209 		cpus_nbits = cpuset_cpus_nbits();
    210 		if (cpus_nbits <= 0) {
    211 			warnx("get cpus nbits failed.");
    212 			return -1;
    213 		}
    214 	}
    215 
    216 	/* open file /proc/schedstat */
    217 	if ((fp = fopen(SCHEDSTAT_FILE, "r")) == NULL)
    218 		return -1;
    219 
    220 	/* get cpuinfo */
    221 	while (fgets(buf, sizeof(buf), fp) != NULL) {
    222 		sscanf(buf, "%s %s", str1, str2);
    223 		if (!strncmp(str1, "cpu", 3)) {
    224 			ci = atoi(str1 + 3);
    225 			if (ci < 0 || ci >= ncpus) {
    226 				fprintf(stderr, "Warn: wrong cpu index");
    227 				fclose(fp);
    228 				return -1;
    229 			}
    230 		} else if (!strncmp(str1, "domain", 6)) {
    231 			if (!cpus[ci].sched_domain) {
    232 				cpus[ci].sched_domain =
    233 				    bitmask_alloc(cpus_nbits);
    234 				if (!cpus[ci].sched_domain) {
    235 					fclose(fp);
    236 					return -1;
    237 				}
    238 			}
    239 			if (bitmask_parsehex(str2, cpus[ci].sched_domain)) {
    240 				fclose(fp);
    241 				return -1;
    242 			}
    243 		}
    244 	}
    245 
    246 	fclose(fp);
    247 	return 0;
    248 }
    249 
    250 int getcpuinfo(void)
    251 {
    252 	int i;
    253 	int node = -1;
    254 
    255 	/* get the number of cpus including offline cpus */
    256 	if (!ncpus) {
    257 		ncpus = get_ncpus();
    258 		if (ncpus <= 0)
    259 			return -1;
    260 	}
    261 
    262 	if (cpus == NULL) {
    263 		if (get_cpu_baseinfo() != 0) {
    264 			warn("get base infomation of cpus from /proc/cpuinfo "
    265 			     "failed.");
    266 			return -1;
    267 		}
    268 	}
    269 
    270 	/* which node is every cpu belong to? */
    271 	for (i = 0; i < ncpus; i++) {
    272 		node = cpuset_cpu2node(i);
    273 		if (node == -1)
    274 			warnx("cpu2node failed(cpu = %d)", i);
    275 		cpus[i].nodeid = node;
    276 	}
    277 
    278 	/* get sched domain's infomation for each cpu */
    279 	if (get_sched_domains()) {
    280 		warnx("get sched domain's info for each cpu failed.");
    281 		return -1;
    282 	}
    283 
    284 	return 0;
    285 }
    286 
    287 /* get the number of the cpuset groups */
    288 static int get_num_cpusets(void)
    289 {
    290 	FILE *fp = NULL;
    291 	char buf[BUFFSIZE];
    292 	char subsys_name[BUFFSIZE];
    293 	int num_cgroups = 0;
    294 	int hierarchy;
    295 	int enabled;
    296 
    297 	/* open file /proc/cgroups and get num cpusets */
    298 	if ((fp = fopen(CGROUPINFO_FILE, "r")) == NULL)
    299 		return -1;
    300 
    301 	while (fgets(buf, sizeof(buf), fp) != NULL) {
    302 		if (!strncmp(buf, "cpuset", 6)) {
    303 			sscanf(buf, "%s\t%d\t%d\t%d\n", subsys_name,
    304 			       &hierarchy, &num_cgroups, &enabled);
    305 		}
    306 	}
    307 
    308 	fclose(fp);
    309 
    310 	return num_cgroups;
    311 }
    312 
    313 static struct cpuset **cpusets;
    314 static int ncpusets;
    315 
    316 static int find_domain_cpusets(char *relpath)
    317 {
    318 	struct cpuset *cp = NULL;
    319 	char c_relpath[PATH_MAX];
    320 	int ret = 0;
    321 
    322 	if (relpath == NULL) {
    323 		errno = -EFAULT;
    324 		return -1;
    325 	}
    326 
    327 	cp = cpuset_alloc();
    328 	if (cp == NULL) {
    329 		errno = -ENOMEM;
    330 		return -1;
    331 	}
    332 
    333 	if (cpuset_query(cp, relpath)) {
    334 		cpuset_free(cp);
    335 		return -1;
    336 	}
    337 
    338 	if (cpuset_cpus_weight(cp) == 0)
    339 		return 0;
    340 
    341 	if (cpuset_cpus_weight(cp) > 0
    342 	    && cpuset_get_iopt(cp, "sched_load_balance") == 1) {
    343 		cpusets[ncpusets] = cp;
    344 		ncpusets++;
    345 		return 0;
    346 	}
    347 
    348 	while_each_childdir(cpuset_mountpoint(), relpath, c_relpath,
    349 			    sizeof(c_relpath)) {
    350 		if ((ret = find_domain_cpusets(c_relpath)))
    351 			break;
    352 	}
    353 	end_while_each_childdir;
    354 
    355 	return ret;
    356 }
    357 
    358 struct bitmask **domains;
    359 int ndomains;
    360 
    361 int partition_domains(void)
    362 {
    363 	int num_cpusets = 0;
    364 	int i, j;
    365 	struct bitmask *cpusa = NULL, *cpusb = NULL, *cpusc = NULL;
    366 	int *flg = NULL;
    367 	int ret = 0;
    368 
    369 	num_cpusets = get_num_cpusets();
    370 	if (num_cpusets == 0) {
    371 		warnx("cpuset subsystem is't compiled into kernel.");
    372 		return -1;
    373 	}
    374 
    375 	if (!cpus_nbits) {
    376 		cpus_nbits = cpuset_cpus_nbits();
    377 		if (!cpus_nbits) {
    378 			warnx("nbits of cpus is wrong.");
    379 			return -1;
    380 		}
    381 	}
    382 
    383 	cpusa = bitmask_alloc(cpus_nbits);
    384 	if (cpusa == NULL) {
    385 		warnx("bitmask_alloc for partition domains failed.");
    386 		return -1;
    387 	}
    388 
    389 	cpusb = bitmask_alloc(cpus_nbits);
    390 	if (cpusb == NULL) {
    391 		warnx("bitmask_alloc for partition domains failed.");
    392 		ret = -1;
    393 		goto errcpusb;
    394 	}
    395 
    396 	cpusc = bitmask_alloc(cpus_nbits);
    397 	if (cpusb == NULL) {
    398 		warnx("bitmask_alloc for partition domains failed.");
    399 		ret = -1;
    400 		goto errcpusc;
    401 	}
    402 
    403 	cpusets = malloc(num_cpusets * sizeof(*cpusets));
    404 	if (cpusets == NULL) {
    405 		warnx("alloc cpusets space failed.");
    406 		ret = -1;
    407 		goto errcpusets;
    408 	}
    409 
    410 	if ((ret = find_domain_cpusets("/"))) {
    411 		warnx("find domain cpusets failed.");
    412 		goto errfindcpusets;
    413 	}
    414 
    415 	flg = malloc(num_cpusets * sizeof(int));
    416 	if (flg == NULL) {
    417 		warnx("alloc flg failed.");
    418 		ret = -1;
    419 		goto errfindcpusets;
    420 	}
    421 	memset(flg, 0, num_cpusets * sizeof(int));
    422 
    423 	ndomains = ncpusets;
    424 restart:
    425 	for (i = 0; i < ncpusets; i++) {
    426 		struct cpuset *cpa = cpusets[i];
    427 
    428 		if (flg[i])
    429 			continue;
    430 
    431 		cpuset_getcpus(cpa, cpusa);
    432 
    433 		for (j = i + 1; j < ncpusets; j++) {
    434 			struct cpuset *cpb = cpusets[j];
    435 
    436 			if (flg[j])
    437 				continue;
    438 
    439 			cpuset_getcpus(cpb, cpusb);
    440 			if (bitmask_intersects(cpusa, cpusb)) {
    441 				bitmask_or(cpusc, cpusa, cpusb);
    442 				cpuset_setcpus(cpa, cpusc);
    443 				flg[j] = 1;
    444 				ndomains--;
    445 				goto restart;
    446 			}
    447 		}
    448 	}
    449 
    450 	domains = malloc(ndomains * sizeof(*domains));
    451 	if (domains == NULL) {
    452 		warnx("alloc domains space failed.");
    453 		ret = -1;
    454 		goto errdomains;
    455 	}
    456 
    457 	for (i = 0, j = 0; i < ncpusets; i++) {
    458 		if (flg[i])
    459 			continue;
    460 		domains[j] = bitmask_alloc(cpus_nbits);
    461 		if (cpuset_getcpus(cpusets[i], domains[j])) {
    462 			warnx("cpuset getcpus failed.");
    463 			ret = -1;
    464 			goto errgetdomains;
    465 		}
    466 		j++;
    467 	}
    468 	goto errdomains;
    469 
    470 errgetdomains:
    471 	for (i = 0; i < j; i++)
    472 		bitmask_free(domains[i]);
    473 	free(domains);
    474 	domains = NULL;
    475 errdomains:
    476 	free(flg);
    477 errfindcpusets:
    478 	for (i = 0; i < ncpusets; i++)
    479 		cpuset_free(cpusets[i]);
    480 	free(cpusets);
    481 	cpusets = NULL;
    482 	ncpusets = 0;
    483 errcpusets:
    484 	bitmask_free(cpusc);
    485 errcpusc:
    486 	bitmask_free(cpusb);
    487 errcpusb:
    488 	bitmask_free(cpusa);
    489 	return ret;
    490 }
    491 
    492 #endif
    493