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