• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
get_cpu_baseinfo(void)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  */
online_cpumask(struct bitmask * cpumask)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  */
present_cpumask(struct bitmask * cpumask)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  */
get_ncpus(void)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 */
get_sched_domains(void)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 
getcpuinfo(void)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 */
get_num_cpusets(void)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 
find_domain_cpusets(char * relpath)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 
partition_domains(void)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