1 /*
2 * cpuset user library implementation.
3 *
4 * Copyright (c) 2006-2007 Silicon Graphics, Inc. All rights reserved.
5 *
6 * Paul Jackson <pj@sgi.com>
7 */
8
9 /*
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #define _GNU_SOURCE /* need to see pread() and syscall() */
26 #include <unistd.h>
27
28 #include <ctype.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <fts.h>
33 #include <limits.h>
34 #include <signal.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <sys/syscall.h>
41 #include <sys/types.h>
42 #include <time.h>
43 #include <utime.h>
44 #include <sys/utsname.h> /* for cpuset_would_crash_kernel() */
45
46 #include "bitmask.h"
47 #include "cpuset.h"
48 #include "common.h"
49 #include "test.h"
50 #include "lapi/syscalls.h"
51 #include "config.h"
52
53 #if HAVE_LINUX_MEMPOLICY_H
54 #include <linux/mempolicy.h>
55
56 /* Bump version, and update Change History, when libcpuset API changes */
57 #define CPUSET_VERSION 3
58
59 /*
60 * For a history of what changed in each version, see the "Change
61 * History" section, at the end of the libcpuset master document.
62 */
63
cpuset_version(void)64 int cpuset_version(void)
65 {
66 return CPUSET_VERSION;
67 }
68
69 struct cpuset {
70 struct bitmask *cpus;
71 struct bitmask *mems;
72 char cpu_exclusive;
73 char mem_exclusive;
74 char mem_hardwall;
75 char notify_on_release;
76 char memory_migrate;
77 char memory_pressure_enabled;
78 char memory_spread_page;
79 char memory_spread_slab;
80 char sched_load_balance;
81 int sched_relax_domain_level;
82
83 /*
84 * Each field 'x' above gets an 'x_valid' field below.
85 * The apply_cpuset_settings() will only set those fields whose
86 * corresponding *_valid flags are set. The cpuset_alloc()
87 * routine clears these flags as part of the clear in calloc(),
88 * and the various cpuset_set*() routines set these flags when
89 * setting the corresponding value.
90 *
91 * The purpose of these valid fields is to ensure that when
92 * we create a new cpuset, we don't accidentally overwrite
93 * some non-zero kernel default, such as an inherited
94 * memory_spread_* flag, just because the user application
95 * code didn't override the default zero settings resulting
96 * from the calloc() call in cpuset_alloc().
97 *
98 * The choice of 'char' for the type of the flags above,
99 * but a bitfield for the flags below, is somewhat capricious.
100 */
101 unsigned cpus_valid:1;
102 unsigned mems_valid:1;
103 unsigned cpu_exclusive_valid:1;
104 unsigned mem_exclusive_valid:1;
105 unsigned mem_hardwall_valid:1;
106 unsigned notify_on_release_valid:1;
107 unsigned memory_migrate_valid:1;
108 unsigned memory_pressure_enabled_valid:1;
109 unsigned memory_spread_page_valid:1;
110 unsigned memory_spread_slab_valid:1;
111 unsigned sched_load_balance_valid:1;
112 unsigned sched_relax_domain_level_valid:1;
113
114 /*
115 * if the relative variable was modified, use following flags
116 * to put a mark
117 */
118 unsigned cpus_dirty:1;
119 unsigned mems_dirty:1;
120 unsigned cpu_exclusive_dirty:1;
121 unsigned mem_exclusive_dirty:1;
122 unsigned mem_hardwall_dirty:1;
123 unsigned notify_on_release_dirty:1;
124 unsigned memory_migrate_dirty:1;
125 unsigned memory_pressure_enabled_dirty:1;
126 unsigned memory_spread_page_dirty:1;
127 unsigned memory_spread_slab_dirty:1;
128 unsigned sched_load_balance_dirty:1;
129 unsigned sched_relax_domain_level_dirty:1;
130 };
131
132 /* Presumed cpuset file system mount point */
133 static const char *cpusetmnt = "/dev/cpuset";
134
135 /* Stashed copy of cpunodemap[], mapping each cpu to its node. */
136 static const char *mapfile = "/var/run/cpunodemap";
137
138 /* The primary source for the cpunodemap[] is available below here. */
139 static const char *sysdevices = "/sys/devices/system";
140
141 /* small buffer size - for reading boolean flags or map file (1 or 2 ints) */
142 #define SMALL_BUFSZ 16
143
144 /*
145 * The 'mask_size_file' is used to ferrit out the kernel cpumask_t
146 * and nodemask_t sizes. The lines in this file that begin with the
147 * strings 'cpumask_prefix' and 'nodemask_prefix' display a cpumask
148 * and nodemask string, respectively. The lengths of these strings
149 * reflect the kernel's internal cpumask_t and nodemask_t sizes,
150 * which sizes are needed to correctly call the sched_setaffinity
151 * and set_mempolicy system calls, and to size user level
152 * bitmasks to match the kernels.
153 */
154
155 static const char *mask_size_file = "/proc/self/status";
156 static const char *cpumask_prefix = "Cpus_allowed:\t";
157 static const char *nodemask_prefix = "Mems_allowed:\t";
158
159 /*
160 * Sizes of kernel cpumask_t and nodemask_t bitmaps, in bits.
161 *
162 * The first time we need these, we parse the Cpus_allowed and
163 * Mems_allowed lines from mask_size_file ("/proc/self/status").
164 */
165
166 static int cpumask_sz;
167 static int nodemask_sz;
168
169 /*
170 * These defaults only kick in if we fail to size the kernel
171 * cpumask and nodemask by reading the Cpus_allowed and
172 * Mems_allowed fields from the /proc/self/status file.
173 */
174
175 #define DEFCPUBITS (512)
176 #define DEFNODEBITS (DEFCPUBITS/2)
177
178 /*
179 * Arch-neutral API for obtaining NUMA distances between CPUs
180 * and Memory Nodes, via the files:
181 * /sys/devices/system/node/nodeN/distance
182 * which have lines such as:
183 * 46 66 10 20
184 * which say that for cpu on node N (from the path above), the
185 * distance to nodes 0, 1, 2, and 3 are 44, 66, 10, and 20,
186 * respectively.
187 */
188
189 static const char *distance_directory = "/sys/devices/system/node";
190
191 /*
192 * Someday, we should disable, then later discard, the SN code
193 * marked ALTERNATE_SN_DISTMAP.
194 */
195
196 #define ALTERNATE_SN_DISTMAP 1
197 #ifdef ALTERNATE_SN_DISTMAP
198
199 /*
200 * Alternative SN (SGI ia64) architecture specific API for obtaining
201 * NUMA distances between CPUs and Memory Nodes is via the file
202 * /proc/sgi_sn/sn_topology, which has lines such as:
203 *
204 * node 2 001c14#0 local asic SHub_1.1, nasid 0x4, dist 46:66:10:20
205 *
206 * which says that for each CPU on node 2, the distance to nodes
207 * 0, 1, 2 and 3 are 46, 66, 10 and 20, respectively.
208 *
209 * This file has other lines as well, which start with other
210 * keywords than "node". Ignore these other lines.
211 */
212
213 static const char *sn_topology = "/proc/sgi_sn/sn_topology";
214 static const char *sn_top_node_prefix = "node ";
215
216 #endif
217
218 /*
219 * Check that cpusets supported, /dev/cpuset mounted.
220 * If ok, return 0.
221 * If not, return -1 and set errno:
222 * ENOSYS - kernel doesn't support cpusets
223 * ENODEV - /dev/cpuset not mounted
224 */
225
226 static enum {
227 check_notdone,
228 check_enosys,
229 check_enodev,
230 check_ok
231 } check_state = check_notdone;
232
check(void)233 static int check(void)
234 {
235 if (check_state == check_notdone) {
236 struct stat statbuf;
237
238 if (stat("/proc/self/cpuset", &statbuf) < 0) {
239 check_state = check_enosys;
240 goto done;
241 }
242
243 if (stat("/dev/cpuset/tasks", &statbuf) < 0) {
244 check_state = check_enodev;
245 goto done;
246 }
247
248 check_state = check_ok;
249 }
250 done:
251 switch (check_state) {
252 case check_enosys:
253 errno = ENOSYS;
254 return -1;
255 case check_enodev:
256 errno = ENODEV;
257 return -1;
258 default:
259 break;
260 }
261 return 0;
262 }
263
chomp(char * s)264 static void chomp(char *s)
265 {
266 char *t;
267
268 for (t = s + strlen(s) - 1; t >= s; t--) {
269 if (*t == '\n' || *t == '\r')
270 *t = '\0';
271 else
272 break;
273 }
274 }
275
276 /*
277 * Determine number of bytes in a seekable open file, without
278 * assuming that stat(2) on that file has a useful size.
279 * Has side affect of leaving the file rewound to the beginnning.
280 */
filesize(FILE * fp)281 static int filesize(FILE * fp)
282 {
283 int sz = 0;
284 rewind(fp);
285 while (fgetc(fp) != EOF)
286 sz++;
287 rewind(fp);
288 return sz;
289 }
290
291 /* Are strings s1 and s2 equal? */
streq(const char * s1,const char * s2)292 static int streq(const char *s1, const char *s2)
293 {
294 return strcmp(s1, s2) == 0;
295 }
296
297 /* Is string 'pre' a prefix of string 's'? */
strprefix(const char * s,const char * pre)298 static int strprefix(const char *s, const char *pre)
299 {
300 return strncmp(s, pre, strlen(pre)) == 0;
301 }
302
303 /*
304 * char *flgets(char *buf, int buflen, FILE *fp)
305 *
306 * Obtain one line from input file fp. Copy up to first
307 * buflen-1 chars of line into buffer buf, discarding any remainder
308 * of line. Stop reading at newline, discarding newline.
309 * Nul terminate result and return pointer to buffer buf
310 * on success, or NULL if nothing more to read or failure.
311 */
312
flgets(char * buf,int buflen,FILE * fp)313 static char *flgets(char *buf, int buflen, FILE * fp)
314 {
315 int c = -1;
316 char *bp;
317
318 bp = buf;
319 while ((--buflen > 0) && ((c = getc(fp)) >= 0)) {
320 if (c == '\n')
321 goto newline;
322 *bp++ = c;
323 }
324 if ((c < 0) && (bp == buf))
325 return NULL;
326
327 if (c > 0) {
328 while ((c = getc(fp)) >= 0) {
329 if (c == '\n')
330 break;
331 }
332 }
333
334 newline:
335 *bp++ = '\0';
336 return buf;
337 }
338
339 /*
340 * sgetc(const char *inputbuf, int *offsetptr)
341 *
342 * Return next char from nul-terminated input buffer inputbuf,
343 * starting at offset *offsetptr. Increment *offsetptr.
344 * If next char would be nul ('\0'), return EOF and don't
345 * increment *offsetptr.
346 */
347
sgetc(const char * inputbuf,int * offsetptr)348 static int sgetc(const char *inputbuf, int *offsetptr)
349 {
350 char c;
351
352 if ((c = inputbuf[*offsetptr]) != 0) {
353 *offsetptr = *offsetptr + 1;
354 return c;
355 } else {
356 return EOF;
357 }
358 }
359
360 /*
361 * char *slgets(char *buf, int buflen, const char *inputbuf, int *offsetptr)
362 *
363 * Obtain next line from nul-terminated input buffer 'inputbuf',
364 * starting at offset *offsetptr. Copy up to first buflen-1
365 * chars of line into output buffer buf, discarding any remainder
366 * of line. Stop reading at newline, discarding newline.
367 * Nul terminate result and return pointer to output buffer
368 * buf on success, or NULL if nothing more to read.
369 */
370
slgets(char * buf,int buflen,const char * inputbuf,int * offsetptr)371 static char *slgets(char *buf, int buflen, const char *inputbuf, int *offsetptr)
372 {
373 int c = -1;
374 char *bp;
375
376 bp = buf;
377 while ((--buflen > 0) && ((c = sgetc(inputbuf, offsetptr)) >= 0)) {
378 if (c == '\n')
379 goto newline;
380 *bp++ = c;
381 }
382 if ((c < 0) && (bp == buf))
383 return NULL;
384
385 if (c > 0) {
386 while ((c = sgetc(inputbuf, offsetptr)) >= 0) {
387 if (c == '\n')
388 break;
389 }
390 }
391
392 newline:
393 *bp++ = '\0';
394 return buf;
395 }
396
397 /*
398 * time_t get_mtime(char *path)
399 *
400 * Return modtime of file at location path, else return 0.
401 */
402
get_mtime(const char * path)403 static time_t get_mtime(const char *path)
404 {
405 struct stat statbuf;
406
407 if (stat(path, &statbuf) != 0)
408 return 0;
409 return statbuf.st_mtime;
410 }
411
412 /*
413 * int set_mtime(const char *path, time_t mtime)
414 *
415 * Set modtime of file 'path' to 'mtime'. Return 0 on success,
416 * or -1 on error, setting errno.
417 */
418
set_mtime(const char * path,time_t mtime)419 static int set_mtime(const char *path, time_t mtime)
420 {
421 struct utimbuf times;
422
423 times.actime = mtime;
424 times.modtime = mtime;
425 return utime(path, ×);
426 }
427
428 /*
429 * True if two pathnames resolve to same file.
430 * False if either path can not be stat'd,
431 * or if the two paths resolve to a different file.
432 */
433
samefile(const char * path1,const char * path2)434 static int samefile(const char *path1, const char *path2)
435 {
436 struct stat sb1, sb2;
437
438 if (stat(path1, &sb1) != 0)
439 return 0;
440 if (stat(path2, &sb2) != 0)
441 return 0;
442 return sb1.st_ino == sb2.st_ino && sb1.st_dev == sb2.st_dev;
443 }
444
445 #define slash(c) (*(c) == '/')
446 #define eocomp(c) (slash(c) || !*(c))
447 #define dot1(c) (*(c) == '.' && eocomp(c+1))
448
449 /* In place path compression. Remove extra dots and slashes. */
pathcomp(char * p)450 static char *pathcomp(char *p)
451 {
452 char *a = p;
453 char *b = p;
454
455 if (!p || !*p)
456 return p;
457 if (slash(p))
458 *b++ = *a++;
459 for (;;) {
460 if (slash(a))
461 while (slash(++a))
462 continue;
463 if (!*a) {
464 if (b == p)
465 *b++ = '.';
466 *b = '\0';
467 return (p);
468 } else if (dot1(a)) {
469 a++;
470 } else {
471 if ((b != p) && !slash(b - 1))
472 *b++ = '/';
473 while (!eocomp(a))
474 *b++ = *a++;
475 }
476 }
477 }
478
479 #undef slash
480 #undef eocomp
481 #undef dot1
482
483 /*
484 * pathcat2(buf, buflen, name1, name2)
485 *
486 * Return buf, of length buflen, with name1/name2 stored in it.
487 */
488
pathcat2(char * buf,int buflen,const char * name1,const char * name2)489 static char *pathcat2(char *buf, int buflen, const char *name1,
490 const char *name2)
491 {
492 (void)snprintf(buf, buflen, "%s/%s", name1, name2);
493 return pathcomp(buf);
494 }
495
496 /*
497 * pathcat3(buf, buflen, name1, name2, name3)
498 *
499 * Return buf, of length buflen, with name1/name2/name3 stored in it.
500 */
501
pathcat3(char * buf,int buflen,const char * name1,const char * name2,const char * name3)502 static char *pathcat3(char *buf, int buflen, const char *name1,
503 const char *name2, const char *name3)
504 {
505 (void)snprintf(buf, buflen, "%s/%s/%s", name1, name2, name3);
506 return pathcomp(buf);
507 }
508
509 /*
510 * fullpath(buf, buflen, name)
511 *
512 * Put full path of cpuset 'name' in buffer 'buf'. If name
513 * starts with a slash (``/``) character, then this a path
514 * relative to ``/dev/cpuset``, otherwise it is relative to
515 * the current tasks cpuset. Return 0 on success, else
516 * -1 on error, setting errno.
517 */
518
fullpath(char * buf,int buflen,const char * name)519 static int fullpath(char *buf, int buflen, const char *name)
520 {
521 int len;
522
523 /* easy case */
524 if (*name == '/') {
525 pathcat2(buf, buflen, cpusetmnt, name);
526 pathcomp(buf);
527 return 0;
528 }
529
530 /* hard case */
531 snprintf(buf, buflen, "%s/", cpusetmnt);
532 len = strlen(buf);
533 if (cpuset_getcpusetpath(0, buf + len, buflen - len) == NULL)
534 return -1;
535 if (strlen(buf) >= buflen - 1 - strlen(name)) {
536 errno = E2BIG;
537 return -1;
538 }
539 strcat(buf, "/");
540 strcat(buf, name);
541 pathcomp(buf);
542 return 0;
543 }
544
545 /*
546 * fullpath2(buf, buflen, name1, name2)
547 *
548 * Like fullpath(), only concatenate two pathname components on end.
549 */
550
fullpath2(char * buf,int buflen,const char * name1,const char * name2)551 static int fullpath2(char *buf, int buflen, const char *name1,
552 const char *name2)
553 {
554 if (fullpath(buf, buflen, name1) < 0)
555 return -1;
556 if (strlen(buf) >= buflen - 1 - strlen(name2)) {
557 errno = E2BIG;
558 return -1;
559 }
560 strcat(buf, "/");
561 strcat(buf, name2);
562 pathcomp(buf);
563 return 0;
564 }
565
566 /*
567 * Convert the string length of an ascii hex mask to the number
568 * of bits represented by that mask.
569 *
570 * The cpumask and nodemask values in /proc/self/status are in an
571 * ascii format that uses 9 characters for each 32 bits of mask.
572 */
s2nbits(const char * s)573 static int s2nbits(const char *s)
574 {
575 return strlen(s) * 32 / 9;
576 }
577
update_mask_sizes(void)578 static void update_mask_sizes(void)
579 {
580 FILE *fp = NULL;
581 char *buf = NULL;
582 int fsize;
583
584 if ((fp = fopen(mask_size_file, "r")) == NULL)
585 goto done;
586 fsize = filesize(fp);
587 if ((buf = malloc(fsize)) == NULL)
588 goto done;
589
590 /*
591 * Beware: mask sizing arithmetic is fussy.
592 * The trailing newline left by fgets() is required.
593 */
594 while (fgets(buf, fsize, fp)) {
595 if (strprefix(buf, cpumask_prefix))
596 cpumask_sz = s2nbits(buf + strlen(cpumask_prefix));
597 if (strprefix(buf, nodemask_prefix))
598 nodemask_sz = s2nbits(buf + strlen(nodemask_prefix));
599 }
600 done:
601 free(buf);
602 if (fp != NULL)
603 fclose(fp);
604 if (cpumask_sz == 0)
605 cpumask_sz = DEFCPUBITS;
606 if (nodemask_sz == 0)
607 nodemask_sz = DEFNODEBITS;
608 }
609
610 /* Allocate a new struct cpuset */
cpuset_alloc(void)611 struct cpuset *cpuset_alloc(void)
612 {
613 struct cpuset *cp = NULL;
614 int nbits;
615
616 if ((cp = calloc(1, sizeof(struct cpuset))) == NULL)
617 goto err;
618
619 nbits = cpuset_cpus_nbits();
620 if ((cp->cpus = bitmask_alloc(nbits)) == NULL)
621 goto err;
622
623 nbits = cpuset_mems_nbits();
624 if ((cp->mems = bitmask_alloc(nbits)) == NULL)
625 goto err;
626
627 return cp;
628 err:
629 if (cp && cp->cpus)
630 bitmask_free(cp->cpus);
631 if (cp && cp->mems)
632 bitmask_free(cp->mems);
633 free(cp);
634 return NULL;
635 }
636
637 /* Free struct cpuset *cp */
cpuset_free(struct cpuset * cp)638 void cpuset_free(struct cpuset *cp)
639 {
640 if (!cp)
641 return;
642 if (cp->cpus)
643 bitmask_free(cp->cpus);
644 if (cp->mems)
645 bitmask_free(cp->mems);
646 free(cp);
647 }
648
649 /* Number of bits in a CPU bitmask on current system */
cpuset_cpus_nbits(void)650 int cpuset_cpus_nbits(void)
651 {
652 if (cpumask_sz == 0)
653 update_mask_sizes();
654 return cpumask_sz;
655 }
656
657 /* Number of bits in a Memory bitmask on current system */
cpuset_mems_nbits(void)658 int cpuset_mems_nbits(void)
659 {
660 if (nodemask_sz == 0)
661 update_mask_sizes();
662 return nodemask_sz;
663 }
664
665 /* Set CPUs in cpuset cp to bitmask cpus */
cpuset_setcpus(struct cpuset * cp,const struct bitmask * cpus)666 int cpuset_setcpus(struct cpuset *cp, const struct bitmask *cpus)
667 {
668 if (cp->cpus)
669 bitmask_free(cp->cpus);
670 cp->cpus = bitmask_alloc(bitmask_nbits(cpus));
671 if (cp->cpus == NULL)
672 return -1;
673 bitmask_copy(cp->cpus, cpus);
674 cp->cpus_valid = 1;
675 cp->cpus_dirty = 1;
676 return 0;
677 }
678
679 /* Set Memory Nodes in cpuset cp to bitmask mems */
cpuset_setmems(struct cpuset * cp,const struct bitmask * mems)680 int cpuset_setmems(struct cpuset *cp, const struct bitmask *mems)
681 {
682 if (cp->mems)
683 bitmask_free(cp->mems);
684 cp->mems = bitmask_alloc(bitmask_nbits(mems));
685 if (cp->mems == NULL)
686 return -1;
687 bitmask_copy(cp->mems, mems);
688 cp->mems_valid = 1;
689 cp->mems_dirty = 1;
690 return 0;
691 }
692
693 /* Set integer value optname of cpuset cp */
cpuset_set_iopt(struct cpuset * cp,const char * optionname,int value)694 int cpuset_set_iopt(struct cpuset *cp, const char *optionname, int value)
695 {
696 if (streq(optionname, "cpu_exclusive")) {
697 cp->cpu_exclusive = ! !value;
698 cp->cpu_exclusive_valid = 1;
699 cp->cpu_exclusive_dirty = 1;
700 } else if (streq(optionname, "mem_exclusive")) {
701 cp->mem_exclusive = ! !value;
702 cp->mem_exclusive_valid = 1;
703 cp->mem_exclusive_dirty = 1;
704 } else if (streq(optionname, "mem_hardwall")) {
705 cp->mem_hardwall = ! !value;
706 cp->mem_hardwall_valid = 1;
707 cp->mem_hardwall_dirty = 1;
708 } else if (streq(optionname, "notify_on_release")) {
709 cp->notify_on_release = ! !value;
710 cp->notify_on_release_valid = 1;
711 cp->notify_on_release_dirty = 1;
712 } else if (streq(optionname, "memory_pressure_enabled")) {
713 cp->memory_pressure_enabled = ! !value;
714 cp->memory_pressure_enabled_valid = 1;
715 cp->memory_pressure_enabled_dirty = 1;
716 } else if (streq(optionname, "memory_migrate")) {
717 cp->memory_migrate = ! !value;
718 cp->memory_migrate_valid = 1;
719 cp->memory_migrate_dirty = 1;
720 } else if (streq(optionname, "memory_spread_page")) {
721 cp->memory_spread_page = ! !value;
722 cp->memory_spread_page_valid = 1;
723 cp->memory_spread_page_dirty = 1;
724 } else if (streq(optionname, "memory_spread_slab")) {
725 cp->memory_spread_slab = ! !value;
726 cp->memory_spread_slab_valid = 1;
727 cp->memory_spread_slab_dirty = 1;
728 } else if (streq(optionname, "sched_load_balance")) {
729 cp->sched_load_balance = ! !value;
730 cp->sched_load_balance_valid = 1;
731 cp->sched_load_balance_dirty = 1;
732 } else if (streq(optionname, "sched_relax_domain_level")) {
733 cp->sched_relax_domain_level = value;
734 cp->sched_relax_domain_level_valid = 1;
735 cp->sched_relax_domain_level_dirty = 1;
736 } else
737 return -2; /* optionname not recognized */
738 return 0;
739 }
740
741 /* [optional] Set string value optname */
cpuset_set_sopt(UNUSED struct cpuset * cp,UNUSED const char * optionname,UNUSED const char * value)742 int cpuset_set_sopt(UNUSED struct cpuset *cp, UNUSED const char *optionname,
743 UNUSED const char *value)
744 {
745 return -2; /* For now, all string options unrecognized */
746 }
747
748 /* Return handle for reading memory_pressure. */
cpuset_open_memory_pressure(const char * cpusetpath)749 int cpuset_open_memory_pressure(const char *cpusetpath)
750 {
751 char buf[PATH_MAX];
752
753 fullpath2(buf, sizeof(buf), cpusetpath, "memory_pressure");
754 return open(buf, O_RDONLY);
755 }
756
757 /* Return current memory_pressure of cpuset. */
cpuset_read_memory_pressure(int han)758 int cpuset_read_memory_pressure(int han)
759 {
760 char buf[SMALL_BUFSZ];
761
762 if (pread(han, buf, sizeof(buf), 0L) < 0)
763 return -1;
764 return atoi(buf);
765 }
766
767 /* Close handle for reading memory pressure. */
cpuset_close_memory_pressure(int han)768 void cpuset_close_memory_pressure(int han)
769 {
770 close(han);
771 }
772
773 /*
774 * Resolve cpuset pointer (to that of current task if cp == NULL).
775 *
776 * If cp not NULL, just return it. If cp is NULL, return pointer
777 * to temporary cpuset for current task, and set *cp_tofree to
778 * pointer to that same temporary cpuset, to be freed later.
779 *
780 * Return NULL and set errno on error. Errors can occur when
781 * resolving the current tasks cpuset.
782 */
resolve_cp(const struct cpuset * cp,struct cpuset ** cp_tofree)783 static const struct cpuset *resolve_cp(const struct cpuset *cp,
784 struct cpuset **cp_tofree)
785 {
786 const struct cpuset *rcp;
787
788 if (cp) {
789 rcp = cp;
790 } else {
791 struct cpuset *cp1 = cpuset_alloc();
792 if (cp1 == NULL)
793 goto err;
794 if (cpuset_cpusetofpid(cp1, 0) < 0) {
795 cpuset_free(cp1);
796 goto err;
797 }
798 *cp_tofree = cp1;
799 rcp = cp1;
800 }
801 return rcp;
802 err:
803 return NULL;
804 }
805
806 /* Write CPUs in cpuset cp (current task if cp == NULL) to bitmask cpus */
cpuset_getcpus(const struct cpuset * cp,struct bitmask * cpus)807 int cpuset_getcpus(const struct cpuset *cp, struct bitmask *cpus)
808 {
809 struct cpuset *cp_tofree = NULL;
810 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
811
812 if (!cp1)
813 goto err;
814 if (cp1->cpus == NULL) {
815 errno = EINVAL;
816 goto err;
817 }
818 bitmask_copy(cpus, cp1->cpus);
819 cpuset_free(cp_tofree);
820 return 0;
821 err:
822 cpuset_free(cp_tofree);
823 return -1;
824 }
825
826 /* Write Memory Nodes in cp (current task if cp == NULL) to bitmask mems */
cpuset_getmems(const struct cpuset * cp,struct bitmask * mems)827 int cpuset_getmems(const struct cpuset *cp, struct bitmask *mems)
828 {
829 struct cpuset *cp_tofree = NULL;
830 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
831
832 if (!cp1)
833 goto err;
834 if (cp1->mems == NULL) {
835 errno = EINVAL;
836 goto err;
837 }
838 bitmask_copy(mems, cp1->mems);
839 cpuset_free(cp_tofree);
840 return 0;
841 err:
842 cpuset_free(cp_tofree);
843 return -1;
844 }
845
846 /* Return number of CPUs in cpuset cp (current task if cp == NULL) */
cpuset_cpus_weight(const struct cpuset * cp)847 int cpuset_cpus_weight(const struct cpuset *cp)
848 {
849 struct cpuset *cp_tofree = NULL;
850 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
851 int w = -1;
852
853 if (!cp1)
854 goto err;
855 if (cp1->cpus == NULL) {
856 errno = EINVAL;
857 goto err;
858 }
859 w = bitmask_weight(cp1->cpus);
860 /* fall into ... */
861 err:
862 cpuset_free(cp_tofree);
863 return w;
864 }
865
866 /* Return number of Memory Nodes in cpuset cp (current task if cp == NULL) */
cpuset_mems_weight(const struct cpuset * cp)867 int cpuset_mems_weight(const struct cpuset *cp)
868 {
869 struct cpuset *cp_tofree = NULL;
870 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
871 int w = -1;
872
873 if (!cp1)
874 goto err;
875 if (cp1->mems == NULL) {
876 errno = EINVAL;
877 goto err;
878 }
879 w = bitmask_weight(cp1->mems);
880 /* fall into ... */
881 err:
882 cpuset_free(cp_tofree);
883 return w;
884 }
885
886 /* Return integer value of option optname in cp */
cpuset_get_iopt(const struct cpuset * cp,const char * optionname)887 int cpuset_get_iopt(const struct cpuset *cp, const char *optionname)
888 {
889 if (streq(optionname, "cpu_exclusive"))
890 return cp->cpu_exclusive;
891 else if (streq(optionname, "mem_exclusive"))
892 return cp->mem_exclusive;
893 else if (streq(optionname, "mem_hardwall"))
894 return cp->mem_hardwall;
895 else if (streq(optionname, "notify_on_release"))
896 return cp->notify_on_release;
897 else if (streq(optionname, "memory_pressure_enabled"))
898 return cp->memory_pressure_enabled;
899 else if (streq(optionname, "memory_migrate"))
900 return cp->memory_migrate;
901 else if (streq(optionname, "memory_spread_page"))
902 return cp->memory_spread_page;
903 else if (streq(optionname, "memory_spread_slab"))
904 return cp->memory_spread_slab;
905 else if (streq(optionname, "sched_load_balance"))
906 return cp->sched_load_balance;
907 else if (streq(optionname, "sched_relax_domain_level"))
908 return cp->sched_relax_domain_level;
909 else
910 return -2; /* optionname not recognized */
911 }
912
913 /* [optional] Return string value of optname */
cpuset_get_sopt(UNUSED const struct cpuset * cp,UNUSED const char * optionname)914 const char *cpuset_get_sopt(UNUSED const struct cpuset *cp,
915 UNUSED const char *optionname)
916 {
917 return NULL; /* For now, all string options unrecognized */
918 }
919
read_flag(const char * filepath,char * flagp)920 static int read_flag(const char *filepath, char *flagp)
921 {
922 char buf[SMALL_BUFSZ]; /* buffer a "0" or "1" flag line */
923 int fd = -1;
924
925 if ((fd = open(filepath, O_RDONLY)) < 0)
926 goto err;
927 if (read(fd, buf, sizeof(buf)) < 1)
928 goto err;
929 if (atoi(buf))
930 *flagp = 1;
931 else
932 *flagp = 0;
933 close(fd);
934 return 0;
935 err:
936 if (fd >= 0)
937 close(fd);
938 return -1;
939 }
940
load_flag(const char * path,char * flagp,const char * flag)941 static int load_flag(const char *path, char *flagp, const char *flag)
942 {
943 char buf[PATH_MAX];
944
945 pathcat2(buf, sizeof(buf), path, flag);
946 return read_flag(buf, flagp);
947 }
948
read_number(const char * filepath,int * numberp)949 static int read_number(const char *filepath, int *numberp)
950 {
951 char buf[SMALL_BUFSZ];
952 int fd = -1;
953
954 if ((fd = open(filepath, O_RDONLY)) < 0)
955 goto err;
956 if (read(fd, buf, sizeof(buf)) < 1)
957 goto err;
958 *numberp = atoi(buf);
959 close(fd);
960 return 0;
961 err:
962 if (fd >= 0)
963 close(fd);
964 return -1;
965 }
966
load_number(const char * path,int * numberp,const char * file)967 static int load_number(const char *path, int *numberp, const char *file)
968 {
969 char buf[PATH_MAX];
970
971 pathcat2(buf, sizeof(buf), path, file);
972 return read_number(buf, numberp);
973 }
974
read_mask(const char * filepath,struct bitmask ** bmpp,int nbits)975 static int read_mask(const char *filepath, struct bitmask **bmpp, int nbits)
976 {
977 FILE *fp = NULL;
978 char *buf = NULL;
979 int buflen;
980 struct bitmask *bmp = NULL;
981
982 if ((fp = fopen(filepath, "r")) == NULL)
983 goto err;
984 buflen = filesize(fp) + 1; /* + 1 for nul term */
985 if ((buf = malloc(buflen)) == NULL)
986 goto err;
987 if (flgets(buf, buflen, fp) == NULL)
988 goto err;
989 fclose(fp);
990 fp = NULL;
991
992 if ((bmp = bitmask_alloc(nbits)) == NULL)
993 goto err;
994 if (*buf && bitmask_parselist(buf, bmp) < 0)
995 goto err;
996 if (*bmpp)
997 bitmask_free(*bmpp);
998 *bmpp = bmp;
999 free(buf);
1000 buf = NULL;
1001 return 0;
1002 err:
1003 if (buf != NULL)
1004 free(buf);
1005 if (fp != NULL)
1006 fclose(fp);
1007 if (bmp != NULL)
1008 bitmask_free(bmp);
1009 return -1;
1010 }
1011
load_mask(const char * path,struct bitmask ** bmpp,int nbits,const char * mask)1012 static int load_mask(const char *path, struct bitmask **bmpp,
1013 int nbits, const char *mask)
1014 {
1015 char buf[PATH_MAX];
1016
1017 pathcat2(buf, sizeof(buf), path, mask);
1018 return read_mask(buf, bmpp, nbits);
1019 }
1020
1021 /* Write string to file at given filepath. Create or truncate file. */
write_string_file(const char * filepath,const char * str)1022 static int write_string_file(const char *filepath, const char *str)
1023 {
1024 int fd = -1;
1025
1026 if ((fd = open(filepath, O_WRONLY | O_CREAT, 0644)) < 0)
1027 goto err;
1028 if (write(fd, str, strlen(str)) < 0)
1029 goto err;
1030 close(fd);
1031 return 0;
1032 err:
1033 if (fd >= 0)
1034 close(fd);
1035 return -1;
1036 }
1037
1038 /* Size and allocate buffer. Write bitmask into it. Caller must free */
sprint_mask_buf(const struct bitmask * bmp)1039 static char *sprint_mask_buf(const struct bitmask *bmp)
1040 {
1041 char *buf = NULL;
1042 int buflen;
1043 char c;
1044
1045 /* First bitmask_displaylist() call just to get the length */
1046 buflen = bitmask_displaylist(&c, 1, bmp) + 1; /* "+ 1" for nul */
1047 if ((buf = malloc(buflen)) == NULL)
1048 return NULL;
1049 bitmask_displaylist(buf, buflen, bmp);
1050 return buf;
1051 }
1052
exists_flag(const char * path,const char * flag)1053 static int exists_flag(const char *path, const char *flag)
1054 {
1055 char buf[PATH_MAX];
1056 struct stat statbuf;
1057 int rc;
1058
1059 pathcat2(buf, sizeof(buf), path, flag);
1060 rc = (stat(buf, &statbuf) == 0);
1061 errno = 0;
1062 return rc;
1063 }
1064
store_flag(const char * path,const char * flag,int val)1065 static int store_flag(const char *path, const char *flag, int val)
1066 {
1067 char buf[PATH_MAX];
1068
1069 pathcat2(buf, sizeof(buf), path, flag);
1070 return write_string_file(buf, val ? "1" : "0");
1071 }
1072
store_number(const char * path,const char * file,int val)1073 static int store_number(const char *path, const char *file, int val)
1074 {
1075 char buf[PATH_MAX];
1076 char data[SMALL_BUFSZ];
1077
1078 memset(data, 0, sizeof(data));
1079 pathcat2(buf, sizeof(buf), path, file);
1080 snprintf(data, sizeof(data), "%d", val);
1081 return write_string_file(buf, data);
1082 }
1083
store_mask(const char * path,const char * mask,const struct bitmask * bmp)1084 static int store_mask(const char *path, const char *mask,
1085 const struct bitmask *bmp)
1086 {
1087 char maskpath[PATH_MAX];
1088 char *bp = NULL;
1089 int rc;
1090
1091 if (bmp == NULL)
1092 return 0;
1093 pathcat2(maskpath, sizeof(maskpath), path, mask);
1094 if ((bp = sprint_mask_buf(bmp)) == NULL)
1095 return -1;
1096 rc = write_string_file(maskpath, bp);
1097 free(bp);
1098 return rc;
1099 }
1100
1101 /*
1102 * Return 1 if 'cpu' is online, else 0 if offline. Tests the file
1103 * /sys/devices/system/cpu/cpuN/online file for 0 or 1 contents
1104 * were N == cpu number.
1105 */
1106
cpu_online(unsigned int cpu)1107 char cpu_online(unsigned int cpu)
1108 {
1109 char online;
1110 char cpupath[PATH_MAX];
1111
1112 (void)snprintf(cpupath, sizeof(cpupath),
1113 "/sys/devices/system/cpu/cpu%d/online", cpu);
1114 if (read_flag(cpupath, &online) < 0)
1115 return 0; /* oops - guess that cpu's not there */
1116 return online;
1117 }
1118
1119 /*
1120 * The cpunodemap maps each cpu in [0 ... cpuset_cpus_nbits()),
1121 * to the node on which that cpu resides or cpuset_mems_nbits().
1122 *
1123 * To avoid every user having to recalculate this relation
1124 * from various clues in the sysfs file system (below the
1125 * path /sys/devices/system) a copy of this map is kept at
1126 * /var/run/cpunodemap.
1127 *
1128 * The system automatically cleans out files below
1129 * /var/run on each system reboot (see the init script
1130 * /etc/rc.d/boot.d/S*boot.localnet), so we don't have to worry
1131 * about stale data in this file across reboots. If the file
1132 * is missing, let the first process that needs it, and has
1133 * permission to write in the /var/run directory, rebuild it.
1134 *
1135 * If using this cached data, remember the mtime of the mapfile
1136 * the last time we read it in case something like a hotplug
1137 * event results in the file being removed and rebuilt, so we
1138 * can detect if we're using a stale cache, and need to reload.
1139 *
1140 * The mtime of this file is set to the time when we did
1141 * the recalculation of the map, from the clues beneath
1142 * /sys/devices/system. This is done so that a program
1143 * won't see the mapfile it just wrote as being newer than what
1144 * it just wrote out (store_map) and read the same map back in
1145 * (load_file).
1146 */
1147
1148 /*
1149 * Hold flockfile(stdin) while using cpunodemap for posix thread safety.
1150 *
1151 * Note on locking and flockfile(FILE *):
1152 *
1153 * We use flockfile() and funlockfile() instead of directly
1154 * calling pthread_mutex_lock and pthread_mutex_unlock on
1155 * a pthread_mutex_t, because this avoids forcing the app
1156 * to link with libpthread. The glibc implementation of
1157 * flockfile/funlockfile will fall back to no-ops if libpthread
1158 * doesn't happen to be linked.
1159 *
1160 * Since flockfile already has the moderately convoluted
1161 * combination of weak and strong symbols required to accomplish
1162 * this, it is easier to use flockfile() on some handy FILE *
1163 * stream as a surrogate for pthread locking than it is to so
1164 * re-invent that wheel.
1165 *
1166 * Forcing all apps that use cpusets to link with libpthread
1167 * would force non-transparent initialization on apps that
1168 * might not be prepared to handle it.
1169 *
1170 * The application using libcpuset should never notice this
1171 * odd use of flockfile(), because we never return to the
1172 * application from any libcpuset call with any such lock held.
1173 * We just use this locking for guarding some non-atomic cached
1174 * data updates and accesses, internal to some libcpuset calls.
1175 * Also, flockfile() allows recursive nesting, so if the app
1176 * calls libcpuset holding such a file lock, we won't deadlock
1177 * if we go to acquire the same lock. We'll just get the lock
1178 * and increment its counter while we hold it.
1179 */
1180
1181 static struct cpunodemap {
1182 int *map; /* map[cpumask_sz]: maps cpu to its node */
1183 time_t mtime; /* modtime of mapfile when last read */
1184 } cpunodemap;
1185
1186 /*
1187 * rebuild_map() - Rebuild cpunodemap[] from scratch.
1188 *
1189 * Situation:
1190 * Neither our in-memory cpunodemap[] array nor the
1191 * cache of it in mapfile is current.
1192 * Action:
1193 * Rebuild it from first principles and the information
1194 * available below /sys/devices/system.
1195 */
1196
rebuild_map(void)1197 static void rebuild_map(void)
1198 {
1199 char buf[PATH_MAX];
1200 DIR *dir1, *dir2;
1201 struct dirent *dent1, *dent2;
1202 int ncpus = cpuset_cpus_nbits();
1203 int nmems = cpuset_mems_nbits();
1204 unsigned int cpu, mem;
1205
1206 for (cpu = 0; cpu < (unsigned int)ncpus; cpu++)
1207 cpunodemap.map[cpu] = -1;
1208 pathcat2(buf, sizeof(buf), sysdevices, "node");
1209 if ((dir1 = opendir(buf)) == NULL)
1210 return;
1211 while ((dent1 = readdir(dir1)) != NULL) {
1212 if (sscanf(dent1->d_name, "node%u", &mem) < 1)
1213 continue;
1214 pathcat3(buf, sizeof(buf), sysdevices, "node", dent1->d_name);
1215 if ((dir2 = opendir(buf)) == NULL)
1216 continue;
1217 while ((dent2 = readdir(dir2)) != NULL) {
1218 if (sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
1219 continue;
1220 if (cpu >= (unsigned int)ncpus
1221 || mem >= (unsigned int)nmems)
1222 continue;
1223 cpunodemap.map[cpu] = mem;
1224 }
1225 closedir(dir2);
1226 }
1227 closedir(dir1);
1228 cpunodemap.mtime = time(0);
1229 }
1230
1231 /*
1232 * load_map() - Load cpunodemap[] from mapfile.
1233 *
1234 * Situation:
1235 * The cpunodemap in mapfile is more recent than
1236 * what we have in the cpunodemap[] array.
1237 * Action:
1238 * Reload the cpunodemap[] array from the file.
1239 */
1240
load_map(void)1241 static void load_map(void)
1242 {
1243 char buf[SMALL_BUFSZ]; /* buffer 1 line of mapfile */
1244 FILE *mapfp; /* File stream on mapfile */
1245 int ncpus = cpuset_cpus_nbits();
1246 int nmems = cpuset_mems_nbits();
1247 unsigned int cpu, mem;
1248
1249 if ((cpunodemap.map = calloc(ncpus, sizeof(int))) == NULL)
1250 return;
1251 cpunodemap.mtime = get_mtime(mapfile);
1252 if ((mapfp = fopen(mapfile, "r")) == NULL)
1253 return;
1254 for (cpu = 0; cpu < (unsigned int)ncpus; cpu++)
1255 cpunodemap.map[cpu] = nmems;
1256 while (flgets(buf, sizeof(buf), mapfp) != NULL) {
1257 if (sscanf(buf, "%u %u", &cpu, &mem) < 2)
1258 continue;
1259 if (cpu >= (unsigned int)ncpus || mem >= (unsigned int)nmems)
1260 continue;
1261 cpunodemap.map[cpu] = mem;
1262 }
1263 fclose(mapfp);
1264 }
1265
1266 /*
1267 * store_map() - Write cpunodemap[] out to mapfile.
1268 *
1269 * Situation:
1270 * The cpunodemap in the cpunodemap[] array is
1271 * more recent than the one in mapfile.
1272 * Action:
1273 * Write cpunodemap[] out to mapfile.
1274 */
1275
store_map(void)1276 static void store_map(void)
1277 {
1278 char buf[PATH_MAX];
1279 int fd = -1;
1280 FILE *mapfp = NULL;
1281 int ncpus = cpuset_cpus_nbits();
1282 int nmems = cpuset_mems_nbits();
1283 unsigned int cpu, mem;
1284
1285 snprintf(buf, sizeof(buf), "%s.%s", mapfile, "XXXXXX");
1286 if ((fd = mkstemp(buf)) < 0)
1287 goto err;
1288 if ((mapfp = fdopen(fd, "w")) == NULL)
1289 goto err;
1290 for (cpu = 0; cpu < (unsigned int)ncpus; cpu++) {
1291 mem = cpunodemap.map[cpu];
1292 if (mem < (unsigned int)nmems)
1293 fprintf(mapfp, "%u %u\n", cpu, mem);
1294 }
1295 fclose(mapfp);
1296 set_mtime(buf, cpunodemap.mtime);
1297 if (rename(buf, mapfile) < 0)
1298 goto err;
1299 /* mkstemp() creates mode 0600 - change to world readable */
1300 (void)chmod(mapfile, 0444);
1301 return;
1302 err:
1303 if (mapfp != NULL) {
1304 fclose(mapfp);
1305 fd = -1;
1306 }
1307 if (fd >= 0)
1308 close(fd);
1309 (void)unlink(buf);
1310 }
1311
1312 /*
1313 * Load and gain thread safe access to the <cpu, node> map.
1314 *
1315 * Return 0 on success with flockfile(stdin) held.
1316 * Each successful get_map() call must be matched with a
1317 * following put_map() call to release the lock.
1318 *
1319 * On error, return -1 with errno set and no lock held.
1320 */
1321
get_map(void)1322 static int get_map(void)
1323 {
1324 time_t file_mtime;
1325
1326 flockfile(stdin);
1327
1328 if (cpunodemap.map == NULL) {
1329 cpunodemap.map = calloc(cpuset_cpus_nbits(), sizeof(int));
1330 if (cpunodemap.map == NULL)
1331 goto err;
1332 }
1333
1334 /* If no one has a good cpunodemap, rebuild from scratch */
1335 file_mtime = get_mtime(mapfile);
1336 if (cpunodemap.mtime == 0 && file_mtime == 0)
1337 rebuild_map();
1338
1339 /* If either cpunodemap[] or mapfile newer, update other with it */
1340 file_mtime = get_mtime(mapfile);
1341 if (cpunodemap.mtime < file_mtime)
1342 load_map();
1343 else if (cpunodemap.mtime > file_mtime)
1344 store_map();
1345 return 0;
1346 err:
1347 funlockfile(stdin);
1348 return -1;
1349 }
1350
put_map(void)1351 static void put_map(void)
1352 {
1353 funlockfile(stdin);
1354 }
1355
1356 /* Set cpus to those local to Memory Nodes mems */
cpuset_localcpus(const struct bitmask * mems,struct bitmask * cpus)1357 int cpuset_localcpus(const struct bitmask *mems, struct bitmask *cpus)
1358 {
1359 int ncpus = cpuset_cpus_nbits();
1360 unsigned int cpu;
1361
1362 if (check() < 0)
1363 return -1;
1364
1365 get_map();
1366 bitmask_clearall(cpus);
1367 for (cpu = 0; cpu < (unsigned int)ncpus; cpu++) {
1368 if (bitmask_isbitset(mems, cpunodemap.map[cpu]))
1369 bitmask_setbit(cpus, cpu);
1370 }
1371 put_map();
1372 return 0;
1373 }
1374
1375 /* Set mems to those local to CPUs cpus */
cpuset_localmems(const struct bitmask * cpus,struct bitmask * mems)1376 int cpuset_localmems(const struct bitmask *cpus, struct bitmask *mems)
1377 {
1378 int ncpus = cpuset_cpus_nbits();
1379 unsigned int cpu;
1380
1381 if (check() < 0)
1382 return -1;
1383
1384 get_map();
1385 bitmask_clearall(mems);
1386 for (cpu = 0; cpu < (unsigned int)ncpus; cpu++) {
1387 if (bitmask_isbitset(cpus, cpu))
1388 bitmask_setbit(mems, cpunodemap.map[cpu]);
1389 }
1390 put_map();
1391 return 0;
1392 }
1393
1394 /*
1395 * distmap[]
1396 *
1397 * Array of ints of size cpumask_sz by nodemask_sz.
1398 *
1399 * Element distmap[cpu][mem] is the distance between CPU cpu
1400 * and Memory Node mem. Distances are weighted to roughly
1401 * approximate the cost of memory references, and scaled so that
1402 * the distance from a CPU to its local Memory Node is ten (10).
1403 *
1404 * The first call to cpuset_cpumemdist() builds this map, from
1405 * whatever means the kernel provides to obtain these distances.
1406 *
1407 * These distances derive from ACPI SLIT table entries, which are
1408 * eight bits in size.
1409 *
1410 * Hold flockfile(stdout) while using distmap for posix thread safety.
1411 */
1412
1413 typedef unsigned char distmap_entry_t; /* type of distmap[] entries */
1414
1415 static distmap_entry_t *distmap; /* maps <cpu, mem> to distance */
1416
1417 #define DISTMAP_MAX UCHAR_MAX /* maximum value in distmap[] */
1418
1419 #define I(i,j) ((i) * nmems + (j)) /* 2-D array index simulation */
1420
1421 /*
1422 * Parse arch neutral lines from 'distance' files of form:
1423 *
1424 * 46 66 10 20
1425 *
1426 * The lines contain a space separated list of distances, which is parsed
1427 * into array dists[] of each nodes distance from the specified node.
1428 *
1429 * Result is placed in distmap[ncpus][nmems]:
1430 *
1431 * For each cpu c on node:
1432 * For each node position n in list of distances:
1433 * distmap[c][n] = dists[n]
1434 */
1435
parse_distmap_line(unsigned int node,char * buf)1436 static int parse_distmap_line(unsigned int node, char *buf)
1437 {
1438 char *p, *q;
1439 int ncpus = cpuset_cpus_nbits();
1440 int nmems = cpuset_mems_nbits();
1441 unsigned int c, n;
1442 distmap_entry_t *dists = NULL;
1443 struct bitmask *cpus = NULL, *mems = NULL;
1444 int ret = -1;
1445
1446 p = buf;
1447 if ((dists = calloc(nmems, sizeof(*dists))) == NULL)
1448 goto err;
1449 for (n = 0; n < (unsigned int)nmems; n++)
1450 dists[n] = DISTMAP_MAX;
1451
1452 for (n = 0; n < (unsigned int)nmems && *p; n++, p = q) {
1453 unsigned int d;
1454
1455 if ((p = strpbrk(p, "0123456789")) == NULL)
1456 break;
1457 d = strtoul(p, &q, 10);
1458 if (p == q)
1459 break;
1460 if (d < DISTMAP_MAX)
1461 dists[n] = (distmap_entry_t) d;
1462 }
1463
1464 if ((mems = bitmask_alloc(nmems)) == NULL)
1465 goto err;
1466 bitmask_setbit(mems, node);
1467
1468 if ((cpus = bitmask_alloc(ncpus)) == NULL)
1469 goto err;
1470 cpuset_localcpus(mems, cpus);
1471
1472 for (c = bitmask_first(cpus); c < (unsigned int)ncpus;
1473 c = bitmask_next(cpus, c + 1))
1474 for (n = 0; n < (unsigned int)nmems; n++)
1475 distmap[I(c, n)] = dists[n];
1476 ret = 0;
1477 /* fall into ... */
1478 err:
1479 bitmask_free(mems);
1480 bitmask_free(cpus);
1481 free(dists);
1482 return ret;
1483 }
1484
parse_distance_file(unsigned int node,const char * path)1485 static int parse_distance_file(unsigned int node, const char *path)
1486 {
1487 FILE *fp;
1488 char *buf = NULL;
1489 int buflen;
1490
1491 if ((fp = fopen(path, "r")) == NULL)
1492 goto err;
1493
1494 buflen = filesize(fp);
1495
1496 if ((buf = malloc(buflen)) == NULL)
1497 goto err;
1498
1499 if (flgets(buf, buflen, fp) == NULL)
1500 goto err;
1501
1502 if (parse_distmap_line(node, buf) < 0)
1503 goto err;
1504
1505 free(buf);
1506 fclose(fp);
1507 return 0;
1508 err:
1509 free(buf);
1510 if (fp)
1511 fclose(fp);
1512 return -1;
1513 }
1514
build_distmap(void)1515 static void build_distmap(void)
1516 {
1517 static int tried_before = 0;
1518 int ncpus = cpuset_cpus_nbits();
1519 int nmems = cpuset_mems_nbits();
1520 int c, m;
1521 DIR *dir = NULL;
1522 struct dirent *dent;
1523
1524 if (tried_before)
1525 goto err;
1526 tried_before = 1;
1527
1528 if ((distmap = calloc(ncpus * nmems, sizeof(*distmap))) == NULL)
1529 goto err;
1530
1531 for (c = 0; c < ncpus; c++)
1532 for (m = 0; m < nmems; m++)
1533 distmap[I(c, m)] = DISTMAP_MAX;
1534
1535 if ((dir = opendir(distance_directory)) == NULL)
1536 goto err;
1537 while ((dent = readdir(dir)) != NULL) {
1538 char buf[PATH_MAX];
1539 unsigned int node;
1540
1541 if (sscanf(dent->d_name, "node%u", &node) < 1)
1542 continue;
1543 pathcat3(buf, sizeof(buf), distance_directory, dent->d_name,
1544 "distance");
1545 if (parse_distance_file(node, buf) < 0)
1546 goto err;
1547 }
1548 closedir(dir);
1549 return;
1550 err:
1551 if (dir)
1552 closedir(dir);
1553 free(distmap);
1554 distmap = NULL;
1555 }
1556
1557 #ifdef ALTERNATE_SN_DISTMAP
1558
1559 /*
1560 * Parse SN architecture specific line of form:
1561 *
1562 * node 3 001c14#1 local asic SHub_1.1, nasid 0x6, dist 66:46:20:10
1563 *
1564 * Second field is node number. The "dist" field is the colon separated list
1565 * of distances, which is parsed into array dists[] of each nodes distance
1566 * from that node.
1567 *
1568 * Result is placed in distmap[ncpus][nmems]:
1569 *
1570 * For each cpu c on that node:
1571 * For each node position n in list of distances:
1572 * distmap[c][n] = dists[n]
1573 */
1574
parse_distmap_line_sn(char * buf)1575 static void parse_distmap_line_sn(char *buf)
1576 {
1577 char *p, *pend, *q;
1578 int ncpus = cpuset_cpus_nbits();
1579 int nmems = cpuset_mems_nbits();
1580 unsigned long c, n, node;
1581 distmap_entry_t *dists = NULL;
1582 struct bitmask *cpus = NULL, *mems = NULL;
1583
1584 if ((p = strchr(buf, ' ')) == NULL)
1585 goto err;
1586 if ((node = strtoul(p, &q, 10)) >= (unsigned int)nmems)
1587 goto err;
1588 if ((p = strstr(q, " dist ")) == NULL)
1589 goto err;
1590 p += strlen(" dist ");
1591 if ((pend = strchr(p, ' ')) != NULL)
1592 *pend = '\0';
1593 if ((dists = calloc(nmems, sizeof(*dists))) == NULL)
1594 goto err;
1595 for (n = 0; n < (unsigned int)nmems; n++)
1596 dists[n] = DISTMAP_MAX;
1597
1598 for (n = 0; n < (unsigned int)nmems && *p; n++, p = q) {
1599 unsigned long d;
1600
1601 if ((p = strpbrk(p, "0123456789")) == NULL)
1602 break;
1603 d = strtoul(p, &q, 10);
1604 if (p == q)
1605 break;
1606 if (d < DISTMAP_MAX)
1607 dists[n] = (distmap_entry_t) d;
1608 }
1609
1610 if ((mems = bitmask_alloc(nmems)) == NULL)
1611 goto err;
1612 bitmask_setbit(mems, node);
1613
1614 if ((cpus = bitmask_alloc(ncpus)) == NULL)
1615 goto err;
1616 cpuset_localcpus(mems, cpus);
1617
1618 for (c = bitmask_first(cpus); c < (unsigned int)ncpus;
1619 c = bitmask_next(cpus, c + 1))
1620 for (n = 0; n < (unsigned int)nmems; n++)
1621 distmap[I(c, n)] = dists[n];
1622 /* fall into ... */
1623 err:
1624 bitmask_free(mems);
1625 bitmask_free(cpus);
1626 free(dists);
1627 }
1628
build_distmap_sn(void)1629 static void build_distmap_sn(void)
1630 {
1631 int ncpus = cpuset_cpus_nbits();
1632 int nmems = cpuset_mems_nbits();
1633 int c, m;
1634 static int tried_before = 0;
1635 FILE *fp = NULL;
1636 char *buf = NULL;
1637 int buflen;
1638
1639 if (tried_before)
1640 goto err;
1641 tried_before = 1;
1642
1643 if ((fp = fopen(sn_topology, "r")) == NULL)
1644 goto err;
1645
1646 if ((distmap = calloc(ncpus * nmems, sizeof(*distmap))) == NULL)
1647 goto err;
1648
1649 for (c = 0; c < ncpus; c++)
1650 for (m = 0; m < nmems; m++)
1651 distmap[I(c, m)] = DISTMAP_MAX;
1652
1653 buflen = filesize(fp);
1654 if ((buf = malloc(buflen)) == NULL)
1655 goto err;
1656
1657 while (flgets(buf, buflen, fp) != NULL)
1658 if (strprefix(buf, sn_top_node_prefix))
1659 parse_distmap_line_sn(buf);
1660
1661 free(buf);
1662 fclose(fp);
1663 return;
1664 err:
1665 free(buf);
1666 free(distmap);
1667 distmap = NULL;
1668 if (fp)
1669 fclose(fp);
1670 }
1671
1672 #endif
1673
1674 /* [optional] Hardware distance from CPU to Memory Node */
cpuset_cpumemdist(int cpu,int mem)1675 unsigned int cpuset_cpumemdist(int cpu, int mem)
1676 {
1677 int ncpus = cpuset_cpus_nbits();
1678 int nmems = cpuset_mems_nbits();
1679 distmap_entry_t r = DISTMAP_MAX;
1680
1681 flockfile(stdout);
1682
1683 if (check() < 0)
1684 goto err;
1685
1686 if (distmap == NULL)
1687 build_distmap();
1688
1689 #ifdef ALTERNATE_SN_DISTMAP
1690 if (distmap == NULL)
1691 build_distmap_sn();
1692 #endif
1693
1694 if (distmap == NULL)
1695 goto err;
1696
1697 if (cpu < 0 || cpu >= ncpus || mem < 0 || mem >= nmems)
1698 goto err;
1699
1700 r = distmap[I(cpu, mem)];
1701 /* fall into ... */
1702 err:
1703 funlockfile(stdout);
1704 return r;
1705 }
1706
1707 /* [optional] Return Memory Node closest to cpu */
cpuset_cpu2node(int cpu)1708 int cpuset_cpu2node(int cpu)
1709 {
1710 int ncpus = cpuset_cpus_nbits();
1711 int nmems = cpuset_mems_nbits();
1712 struct bitmask *cpus = NULL, *mems = NULL;
1713 int r = -1;
1714
1715 if (check() < 0)
1716 goto err;
1717
1718 if ((cpus = bitmask_alloc(ncpus)) == NULL)
1719 goto err;
1720 bitmask_setbit(cpus, cpu);
1721
1722 if ((mems = bitmask_alloc(nmems)) == NULL)
1723 goto err;
1724 cpuset_localmems(cpus, mems);
1725 r = bitmask_first(mems);
1726 /* fall into ... */
1727 err:
1728 bitmask_free(cpus);
1729 bitmask_free(mems);
1730 return r;
1731 }
1732
apply_cpuset_settings(const char * path,const struct cpuset * cp)1733 static int apply_cpuset_settings(const char *path, const struct cpuset *cp)
1734 {
1735 if (cp->cpu_exclusive_valid && cp->cpu_exclusive_dirty) {
1736 if (store_flag(path, "cpu_exclusive", cp->cpu_exclusive) < 0)
1737 goto err;
1738 }
1739
1740 if (cp->mem_exclusive_valid && cp->mem_exclusive_dirty) {
1741 if (store_flag(path, "mem_exclusive", cp->mem_exclusive) < 0)
1742 goto err;
1743 }
1744
1745 if (cp->mem_hardwall_valid && cp->mem_hardwall_dirty) {
1746 if (store_flag(path, "mem_hardwall", cp->mem_hardwall) < 0)
1747 goto err;
1748 }
1749
1750 if (cp->notify_on_release_valid && cp->notify_on_release_dirty) {
1751 if (store_flag(path, "notify_on_release", cp->notify_on_release)
1752 < 0)
1753 goto err;
1754 }
1755
1756 if (cp->memory_migrate_valid &&
1757 cp->memory_migrate_dirty && exists_flag(path, "memory_migrate")) {
1758 if (store_flag(path, "memory_migrate", cp->memory_migrate) < 0)
1759 goto err;
1760 }
1761
1762 if (cp->memory_pressure_enabled_valid &&
1763 cp->memory_pressure_enabled_dirty &&
1764 exists_flag(path, "memory_pressure_enabled")) {
1765 if (store_flag
1766 (path, "memory_pressure_enabled",
1767 cp->memory_pressure_enabled) < 0)
1768 goto err;
1769 }
1770
1771 if (cp->memory_spread_page_valid &&
1772 cp->memory_spread_page_dirty &&
1773 exists_flag(path, "memory_spread_page")) {
1774 if (store_flag
1775 (path, "memory_spread_page", cp->memory_spread_page) < 0)
1776 goto err;
1777 }
1778
1779 if (cp->memory_spread_slab_valid &&
1780 cp->memory_spread_slab_dirty &&
1781 exists_flag(path, "memory_spread_slab")) {
1782 if (store_flag
1783 (path, "memory_spread_slab", cp->memory_spread_slab) < 0)
1784 goto err;
1785 }
1786
1787 if (cp->sched_load_balance_valid &&
1788 cp->sched_load_balance_dirty &&
1789 exists_flag(path, "sched_load_balance")) {
1790 if (store_flag
1791 (path, "sched_load_balance", cp->sched_load_balance) < 0)
1792 goto err;
1793 }
1794
1795 if (cp->sched_relax_domain_level_valid &&
1796 cp->sched_relax_domain_level_dirty &&
1797 exists_flag(path, "sched_relax_domain_level")) {
1798 if (store_number
1799 (path, "sched_relax_domain_level",
1800 cp->sched_relax_domain_level) < 0)
1801 goto err;
1802 }
1803
1804 if (cp->cpus_valid && cp->cpus_dirty) {
1805 if (store_mask(path, "cpus", cp->cpus) < 0)
1806 goto err;
1807 }
1808
1809 if (cp->mems_valid && cp->mems_dirty) {
1810 if (store_mask(path, "mems", cp->mems) < 0)
1811 goto err;
1812 }
1813 return 0;
1814 err:
1815 return -1;
1816 }
1817
1818 /*
1819 * get_siblings() - helper routine for cpuset_would_crash_kernel(), below.
1820 *
1821 * Extract max value of any 'siblings' field in /proc/cpuinfo.
1822 * Cache the result - only need to extract once in lifetime of task.
1823 *
1824 * The siblings field is the number of logical CPUs in a physical
1825 * processor package. It is equal to the product of the number of
1826 * cores in that package, times the number of hyper-threads per core.
1827 * The bug that cpuset_would_crash_kernel() is detecting arises
1828 * when a cpu_exclusive cpuset tries to include just some, not all,
1829 * of the sibling logical CPUs available in a processor package.
1830 *
1831 * In the improbable case that a system has mixed values of siblings
1832 * (some processor packages have more than others, perhaps due to
1833 * partially enabling Hyper-Threading), we take the worse case value,
1834 * the largest siblings value. This might be overkill. I don't know
1835 * if this kernel bug considers each processor package's siblings
1836 * separately or not. But it sure is easier this way ...
1837 *
1838 * This routine takes about 0.7 msecs on a 4 CPU 2.8 MHz Xeon, from
1839 * open to close, the first time called.
1840 */
1841
get_siblings(void)1842 static int get_siblings(void)
1843 {
1844 static int siblings;
1845 char buf[32]; /* big enough for one 'siblings' line */
1846 FILE *fp;
1847
1848 if (siblings)
1849 return siblings;
1850
1851 if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
1852 return 4; /* wing it - /proc not mounted ? */
1853 while (flgets(buf, sizeof(buf), fp) != NULL) {
1854 int s;
1855
1856 if (sscanf(buf, "siblings : %d", &s) < 1)
1857 continue;
1858 if (s > siblings)
1859 siblings = s;
1860 }
1861 fclose(fp);
1862 if (siblings == 0)
1863 siblings = 1; /* old kernel, no siblings, default to 1 */
1864 return siblings;
1865 }
1866
1867 /*
1868 * Some 2.6.16 and 2.6.17 kernel versions have a bug in the dynamic
1869 * scheduler domain code invoked for cpu_exclusive cpusets that causes
1870 * the kernel to freeze, requiring a hardware reset.
1871 *
1872 * On kernels built with CONFIG_SCHED_MC enabled, if a 'cpu_exclusive'
1873 * cpuset is defined where that cpusets 'cpus' are not on package
1874 * boundaries then the kernel will freeze, usually as soon as this
1875 * cpuset is created, requiring a hardware reset.
1876 *
1877 * A cpusets 'cpus' are not on package boundaries if the cpuset
1878 * includes a proper non-empty subset (some, but not all) of the
1879 * logical cpus on a processor package. This requires multiple
1880 * logical CPUs per package, available with either Hyper-Thread or
1881 * Multi-Core support. Without one of these features, there is only
1882 * one logical CPU per physical package, and it's not possible to
1883 * have a proper, non-empty subset of a set of cardinality one.
1884 *
1885 * SUSE SLES10 kernels, as first released, only enable CONFIG_SCHED_MC
1886 * on i386 and x86_64 arch's.
1887 *
1888 * The objective of this routine cpuset_would_crash_kernel() is to
1889 * determine if a proposed cpuset setting would crash the kernel due
1890 * to this bug, so that the caller can avoid the crash.
1891 *
1892 * Ideally we'd check for exactly these conditions here, but computing
1893 * the package (identified by the 'physical id' field of /proc/cpuinfo)
1894 * of each cpu in a cpuset is more effort than it's worth here.
1895 *
1896 * Also there is no obvious way to identify exactly whether the kernel
1897 * one is executing on has this bug, short of trying it, and seeing
1898 * if the kernel just crashed.
1899 *
1900 * So for now, we look for a simpler set of conditions, that meets
1901 * our immediate need - avoid this crash on SUSE SLES10 systems that
1902 * are susceptible to it. We look for the kernel version 2.6.16.*,
1903 * which is the base kernel of SUSE SLES10, and for i386 or x86_64
1904 * processors, which had CONFIG_SCHED_MC enabled.
1905 *
1906 * If these simpler conditions are met, we further simplify the check,
1907 * by presuming that the logical CPUs are numbered on processor
1908 * package boundaries. If each package has S siblings, we assume
1909 * that CPUs numbered N through N + S -1 are on the same package,
1910 * for any CPU N such that N mod S == 0.
1911 *
1912 * Yes, this is a hack, focused on avoiding kernel freezes on
1913 * susceptible SUSE SLES10 systems.
1914 */
1915
cpuset_would_crash_kernel(const struct cpuset * cp)1916 static int cpuset_would_crash_kernel(const struct cpuset *cp)
1917 {
1918 static int susceptible_system = -1;
1919
1920 if (!cp->cpu_exclusive)
1921 goto ok;
1922
1923 if (susceptible_system == -1) {
1924 struct utsname u;
1925 int rel_2_6_16, arch_i386, arch_x86_64;
1926
1927 if (uname(&u) < 0)
1928 goto fail;
1929 rel_2_6_16 = strprefix(u.release, "2.6.16.");
1930 arch_i386 = streq(u.machine, "i386");
1931 arch_x86_64 = streq(u.machine, "x86_64");
1932 susceptible_system = rel_2_6_16 && (arch_i386 || arch_x86_64);
1933 }
1934
1935 if (susceptible_system) {
1936 int ncpus = cpuset_cpus_nbits();
1937 int siblings = get_siblings();
1938 unsigned int cpu;
1939
1940 for (cpu = 0; cpu < (unsigned int)ncpus; cpu += siblings) {
1941 int s, num_set = 0;
1942
1943 for (s = 0; s < siblings; s++) {
1944 if (bitmask_isbitset(cp->cpus, cpu + s))
1945 num_set++;
1946 }
1947
1948 /* If none or all siblings set, we're still ok */
1949 if (num_set == 0 || num_set == siblings)
1950 continue;
1951
1952 /* Found one that would crash kernel. Fail. */
1953 errno = ENXIO;
1954 goto fail;
1955 }
1956 }
1957 /* If not susceptible, or if all ok, fall into "ok" ... */
1958 ok:
1959 return 0; /* would not crash */
1960 fail:
1961 return 1; /* would crash */
1962 }
1963
1964 /* compare two cpuset and mark the dirty variable */
mark_dirty_variable(struct cpuset * cp1,const struct cpuset * cp2)1965 static void mark_dirty_variable(struct cpuset *cp1, const struct cpuset *cp2)
1966 {
1967 if (cp1->cpu_exclusive_valid &&
1968 cp1->cpu_exclusive != cp2->cpu_exclusive)
1969 cp1->cpu_exclusive_dirty = 1;
1970
1971 if (cp1->mem_exclusive_valid &&
1972 cp1->mem_exclusive != cp2->mem_exclusive)
1973 cp1->mem_exclusive_dirty = 1;
1974
1975 if (cp1->mem_hardwall_valid && cp1->mem_hardwall != cp2->mem_hardwall)
1976 cp1->mem_hardwall_dirty = 1;
1977
1978 if (cp1->notify_on_release_valid &&
1979 cp1->notify_on_release != cp2->notify_on_release)
1980 cp1->notify_on_release_dirty = 1;
1981
1982 if (cp1->memory_migrate_valid &&
1983 cp1->memory_migrate != cp2->memory_migrate)
1984 cp1->memory_migrate_dirty = 1;
1985
1986 if (cp1->memory_pressure_enabled_valid &&
1987 cp1->memory_pressure_enabled != cp2->memory_pressure_enabled)
1988 cp1->memory_pressure_enabled_dirty = 1;
1989
1990 if (cp1->memory_spread_page_valid &&
1991 cp1->memory_spread_page != cp2->memory_spread_page)
1992 cp1->memory_spread_page_dirty = 1;
1993
1994 if (cp1->memory_spread_slab_valid &&
1995 cp1->memory_spread_slab != cp2->memory_spread_slab)
1996 cp1->memory_spread_slab_dirty = 1;
1997
1998 if (cp1->sched_load_balance_valid &&
1999 cp1->sched_load_balance != cp2->sched_load_balance)
2000 cp1->sched_load_balance_dirty = 1;
2001
2002 if (cp1->sched_relax_domain_level_valid &&
2003 cp1->sched_relax_domain_level != cp2->sched_relax_domain_level)
2004 cp1->sched_relax_domain_level_dirty = 1;
2005
2006 if (cp1->cpus_valid && !bitmask_equal(cp1->cpus, cp2->cpus))
2007 cp1->cpus_dirty = 1;
2008 if (cp1->mems_valid && !bitmask_equal(cp1->mems, cp2->mems))
2009 cp1->mems_dirty = 1;
2010 }
2011
2012 /* Create (if new set) or modify cpuset 'cp' at location 'relpath' */
cr_or_mod(const char * relpath,const struct cpuset * cp,int new)2013 static int cr_or_mod(const char *relpath, const struct cpuset *cp, int new)
2014 {
2015 char buf[PATH_MAX];
2016 int do_rmdir_on_err = 0;
2017 int do_restore_cp_sav_on_err = 0;
2018 struct cpuset *cp_sav = NULL;
2019 int sav_errno;
2020
2021 if (check() < 0)
2022 goto err;
2023
2024 if (cpuset_would_crash_kernel(cp))
2025 goto err;
2026
2027 fullpath(buf, sizeof(buf), relpath);
2028
2029 if (new) {
2030 if (mkdir(buf, 0755) < 0)
2031 goto err;
2032 /* we made it, so we should remove it on error */
2033 do_rmdir_on_err = 1;
2034 }
2035
2036 if ((cp_sav = cpuset_alloc()) == NULL)
2037 goto err;
2038 if (cpuset_query(cp_sav, relpath) < 0)
2039 goto err;
2040 /* we have old settings to restore on error */
2041 do_restore_cp_sav_on_err = 1;
2042
2043 /* check which variable need to restore on error */
2044 mark_dirty_variable(cp_sav, cp);
2045
2046 if (apply_cpuset_settings(buf, cp) < 0)
2047 goto err;
2048
2049 cpuset_free(cp_sav);
2050 return 0;
2051 err:
2052 sav_errno = errno;
2053 if (do_restore_cp_sav_on_err)
2054 (void)apply_cpuset_settings(buf, cp_sav);
2055 if (cp_sav)
2056 cpuset_free(cp_sav);
2057 if (do_rmdir_on_err)
2058 (void)rmdir(buf);
2059 errno = sav_errno;
2060 return -1;
2061 }
2062
2063 /* Create cpuset 'cp' at location 'relpath' */
cpuset_create(const char * relpath,const struct cpuset * cp)2064 int cpuset_create(const char *relpath, const struct cpuset *cp)
2065 {
2066 return cr_or_mod(relpath, cp, 1);
2067 }
2068
2069 /* Delete cpuset at location 'path' (if empty) */
cpuset_delete(const char * relpath)2070 int cpuset_delete(const char *relpath)
2071 {
2072 char buf[PATH_MAX];
2073
2074 if (check() < 0)
2075 goto err;
2076
2077 fullpath(buf, sizeof(buf), relpath);
2078 if (rmdir(buf) < 0)
2079 goto err;
2080
2081 return 0;
2082 err:
2083 return -1;
2084 }
2085
2086 /* Set cpuset cp to the cpuset at location 'path' */
cpuset_query(struct cpuset * cp,const char * relpath)2087 int cpuset_query(struct cpuset *cp, const char *relpath)
2088 {
2089 char buf[PATH_MAX];
2090
2091 if (check() < 0)
2092 goto err;
2093
2094 fullpath(buf, sizeof(buf), relpath);
2095
2096 if (load_flag(buf, &cp->cpu_exclusive, "cpu_exclusive") < 0)
2097 goto err;
2098 cp->cpu_exclusive_valid = 1;
2099
2100 if (load_flag(buf, &cp->mem_exclusive, "mem_exclusive") < 0)
2101 goto err;
2102 cp->mem_exclusive_valid = 1;
2103
2104 if (load_flag(buf, &cp->notify_on_release, "notify_on_release") < 0)
2105 goto err;
2106 cp->notify_on_release_valid = 1;
2107
2108 if (exists_flag(buf, "memory_migrate")) {
2109 if (load_flag(buf, &cp->memory_migrate, "memory_migrate") < 0)
2110 goto err;
2111 cp->memory_migrate_valid = 1;
2112 }
2113
2114 if (exists_flag(buf, "mem_hardwall")) {
2115 if (load_flag(buf, &cp->mem_hardwall, "mem_hardwall") < 0)
2116 goto err;
2117 cp->mem_hardwall_valid = 1;
2118 }
2119
2120 if (exists_flag(buf, "memory_pressure_enabled")) {
2121 if (load_flag
2122 (buf, &cp->memory_pressure_enabled,
2123 "memory_pressure_enabled") < 0)
2124 goto err;
2125 cp->memory_pressure_enabled_valid = 1;
2126 }
2127
2128 if (exists_flag(buf, "memory_spread_page")) {
2129 if (load_flag
2130 (buf, &cp->memory_spread_page, "memory_spread_page") < 0)
2131 goto err;
2132 cp->memory_spread_page_valid = 1;
2133 }
2134
2135 if (exists_flag(buf, "memory_spread_slab")) {
2136 if (load_flag
2137 (buf, &cp->memory_spread_slab, "memory_spread_slab") < 0)
2138 goto err;
2139 cp->memory_spread_slab_valid = 1;
2140 }
2141
2142 if (exists_flag(buf, "sched_load_balance")) {
2143 if (load_flag
2144 (buf, &cp->sched_load_balance, "sched_load_balance") < 0)
2145 goto err;
2146 cp->sched_load_balance_valid = 1;
2147 }
2148
2149 if (exists_flag(buf, "sched_relax_domain_level")) {
2150 if (load_number
2151 (buf, &cp->sched_relax_domain_level,
2152 "sched_relax_domain_level") < 0)
2153 goto err;
2154 cp->sched_relax_domain_level_valid = 1;
2155 }
2156
2157 if (load_mask(buf, &cp->cpus, cpuset_cpus_nbits(), "cpus") < 0)
2158 goto err;
2159 cp->cpus_valid = 1;
2160
2161 if (load_mask(buf, &cp->mems, cpuset_mems_nbits(), "mems") < 0)
2162 goto err;
2163 cp->mems_valid = 1;
2164
2165 return 0;
2166 err:
2167 return -1;
2168 }
2169
2170 /* Modify cpuset at location 'relpath' to values of 'cp' */
cpuset_modify(const char * relpath,const struct cpuset * cp)2171 int cpuset_modify(const char *relpath, const struct cpuset *cp)
2172 {
2173 return cr_or_mod(relpath, cp, 0);
2174 }
2175
2176 /* Get cpuset path of pid into buf */
cpuset_getcpusetpath(pid_t pid,char * buf,size_t size)2177 char *cpuset_getcpusetpath(pid_t pid, char *buf, size_t size)
2178 {
2179 int fd; /* dual use: cpuset file for pid and self */
2180 int rc; /* dual use: snprintf and read return codes */
2181
2182 if (check() < 0)
2183 return NULL;
2184
2185 /* borrow result buf[] to build cpuset file path */
2186 if (pid == 0)
2187 rc = snprintf(buf, size, "/proc/self/cpuset");
2188 else
2189 rc = snprintf(buf, size, "/proc/%d/cpuset", pid);
2190 if (rc >= (int)size) {
2191 errno = E2BIG;
2192 return NULL;
2193 }
2194 if ((fd = open(buf, O_RDONLY)) < 0) {
2195 int e = errno;
2196 if (e == ENOENT)
2197 e = ESRCH;
2198 if ((fd = open("/proc/self/cpuset", O_RDONLY)) < 0)
2199 e = ENOSYS;
2200 else
2201 close(fd);
2202 errno = e;
2203 return NULL;
2204 }
2205 rc = read(fd, buf, size);
2206 close(fd);
2207 if (rc < 0)
2208 return NULL;
2209 if (rc >= (int)size) {
2210 errno = E2BIG;
2211 return NULL;
2212 }
2213 buf[rc] = 0;
2214 chomp(buf);
2215 return buf;
2216
2217 }
2218
2219 /* Get cpuset 'cp' of pid */
cpuset_cpusetofpid(struct cpuset * cp,pid_t pid)2220 int cpuset_cpusetofpid(struct cpuset *cp, pid_t pid)
2221 {
2222 char buf[PATH_MAX];
2223
2224 if (cpuset_getcpusetpath(pid, buf, sizeof(buf)) == NULL)
2225 return -1;
2226 if (cpuset_query(cp, buf) < 0)
2227 return -1;
2228 return 0;
2229 }
2230
2231 /* [optional] Return mountpoint of cpuset filesystem */
cpuset_mountpoint(void)2232 const char *cpuset_mountpoint(void)
2233 {
2234 if (check() < 0) {
2235 switch (errno) {
2236 case ENODEV:
2237 return "[cpuset filesystem not mounted]";
2238 default:
2239 return "[cpuset filesystem not supported]";
2240 }
2241 }
2242 return cpusetmnt;
2243 }
2244
2245 /* Return true if path is a directory. */
isdir(const char * path)2246 static int isdir(const char *path)
2247 {
2248 struct stat statbuf;
2249
2250 if (stat(path, &statbuf) < 0)
2251 return 0;
2252 return S_ISDIR(statbuf.st_mode);
2253 }
2254
2255 /*
2256 * [optional] cpuset_collides_exclusive() - True if would collide exclusive.
2257 *
2258 * Return true iff the specified cpuset would overlap with any
2259 * sibling cpusets in either cpus or mems, where either this
2260 * cpuset or the sibling is cpu_exclusive or mem_exclusive.
2261 *
2262 * cpuset_create() fails with errno == EINVAL if the requested cpuset
2263 * would overlap with any sibling, where either one is cpu_exclusive or
2264 * mem_exclusive. This is a common, and not obvious error. The
2265 * following routine checks for this particular case, so that code
2266 * creating cpusets can better identify the situation, perhaps to issue
2267 * a more informative error message.
2268 *
2269 * Can also be used to diagnose cpuset_modify failures. This
2270 * routine ignores any existing cpuset with the same path as the
2271 * given 'cpusetpath', and only looks for exclusive collisions with
2272 * sibling cpusets of that path.
2273 *
2274 * In case of any error, returns (0) -- does not collide. Presumably
2275 * any actual attempt to create or modify a cpuset will encounter the
2276 * same error, and report it usefully.
2277 *
2278 * This routine is not particularly efficient; most likely code creating or
2279 * modifying a cpuset will want to try the operation first, and then if that
2280 * fails with errno EINVAL, perhaps call this routine to determine if an
2281 * exclusive cpuset collision caused the error.
2282 */
2283
cpuset_collides_exclusive(const char * cpusetpath,const struct cpuset * cp1)2284 int cpuset_collides_exclusive(const char *cpusetpath, const struct cpuset *cp1)
2285 {
2286 char parent[PATH_MAX];
2287 char *p;
2288 char *pathcopy = NULL;
2289 char *base;
2290 DIR *dir = NULL;
2291 struct dirent *dent;
2292 struct cpuset *cp2 = NULL;
2293 struct bitmask *cpus1 = NULL, *cpus2 = NULL;
2294 struct bitmask *mems1 = NULL, *mems2 = NULL;
2295 int ret;
2296
2297 if (check() < 0)
2298 goto err;
2299
2300 fullpath(parent, sizeof(parent), cpusetpath);
2301 if (streq(parent, cpusetmnt))
2302 goto err; /* only one cpuset root - can't collide */
2303 pathcopy = strdup(parent);
2304 p = strrchr(parent, '/');
2305 if (!p)
2306 goto err; /* huh? - impossible - run and hide */
2307 *p = 0; /* now parent is dirname of fullpath */
2308
2309 p = strrchr(pathcopy, '/');
2310 base = p + 1; /* now base is basename of fullpath */
2311 if (!*base)
2312 goto err; /* this is also impossible - run away */
2313
2314 if ((dir = opendir(parent)) == NULL)
2315 goto err;
2316 if ((cp2 = cpuset_alloc()) == NULL)
2317 goto err;
2318 if ((cpus1 = bitmask_alloc(cpuset_cpus_nbits())) == NULL)
2319 goto err;
2320 if ((cpus2 = bitmask_alloc(cpuset_cpus_nbits())) == NULL)
2321 goto err;
2322 if ((mems1 = bitmask_alloc(cpuset_mems_nbits())) == NULL)
2323 goto err;
2324 if ((mems2 = bitmask_alloc(cpuset_mems_nbits())) == NULL)
2325 goto err;
2326
2327 while ((dent = readdir(dir)) != NULL) {
2328 char child[PATH_MAX];
2329
2330 if (streq(dent->d_name, ".") || streq(dent->d_name, ".."))
2331 continue;
2332 if (streq(dent->d_name, base))
2333 continue;
2334 pathcat2(child, sizeof(child), parent, dent->d_name);
2335 if (!isdir(child))
2336 continue;
2337 if (cpuset_query(cp2, child + strlen(cpusetmnt)) < 0)
2338 goto err;
2339 if (cp1->cpu_exclusive || cp2->cpu_exclusive) {
2340 cpuset_getcpus(cp1, cpus1);
2341 cpuset_getcpus(cp2, cpus2);
2342 if (bitmask_intersects(cpus1, cpus2))
2343 goto collides;
2344 }
2345 if (cp1->mem_exclusive || cp2->mem_exclusive) {
2346 cpuset_getmems(cp1, mems1);
2347 cpuset_getmems(cp2, mems2);
2348 if (bitmask_intersects(mems1, mems2))
2349 goto collides;
2350 }
2351 }
2352 err:
2353 /* error, or did not collide */
2354 ret = 0;
2355 goto done;
2356 collides:
2357 /* collides */
2358 ret = 1;
2359 /* fall into ... */
2360 done:
2361 if (dir)
2362 closedir(dir);
2363 cpuset_free(cp2);
2364 free(pathcopy);
2365 bitmask_free(cpus1);
2366 bitmask_free(cpus2);
2367 bitmask_free(mems1);
2368 bitmask_free(mems2);
2369 return ret;
2370 }
2371
2372 /*
2373 * [optional] cpuset_nuke() - Remove cpuset anyway possible
2374 *
2375 * Remove a cpuset, including killing tasks in it, and
2376 * removing any descendent cpusets and killing their tasks.
2377 *
2378 * Tasks can take a long time (minutes on some configurations)
2379 * to exit. Loop up to 'seconds' seconds, trying to kill them.
2380 *
2381 * How we do it:
2382 * 1) First, kill all the pids, looping until there are
2383 * no more pids in this cpuset or below, or until the
2384 * 'seconds' timeout limit is exceeded.
2385 * 2) Then depth first recursively rmdir the cpuset directories.
2386 * 3) If by this point the original cpuset is gone, we succeeded.
2387 *
2388 * If the timeout is exceeded, and tasks still exist, fail with
2389 * errno == ETIME.
2390 *
2391 * We sleep a variable amount of time. After the first attempt to
2392 * kill all the tasks in the cpuset or its descendents, we sleep 1
2393 * second, the next time 2 seconds, increasing 1 second each loop
2394 * up to a max of 10 seconds. If more loops past 10 are required
2395 * to kill all the tasks, we sleep 10 seconds each subsequent loop.
2396 * In any case, before the last loop, we sleep however many seconds
2397 * remain of the original timeout 'seconds' requested. The total
2398 * time of all sleeps will be no more than the requested 'seconds'.
2399 *
2400 * If the cpuset started out empty of any tasks, or if the passed in
2401 * 'seconds' was zero, then this routine will return quickly, having
2402 * not slept at all. Otherwise, this routine will at a minimum send
2403 * a SIGKILL to all the tasks in this cpuset subtree, then sleep one
2404 * second, before looking to see if any tasks remain. If tasks remain
2405 * in the cpuset subtree, and a longer 'seconds' timeout was requested
2406 * (more than one), it will continue to kill remaining tasks and sleep,
2407 * in a loop, for as long as time and tasks remain.
2408 *
2409 * The signal sent for the kill is hardcoded to SIGKILL (9). If some
2410 * other signal should be sent first, use a separate code loop,
2411 * perhaps based on cpuset_init_pidlist and cpuset_get_pidlist, to
2412 * scan the task pids in a cpuset. If SIGKILL should -not- be sent,
2413 * this cpuset_nuke() routine can still be called to recursively
2414 * remove a cpuset subtree, by specifying a timeout of zero 'seconds'.
2415 *
2416 * On success, returns 0 with errno == 0.
2417 *
2418 * On failure, returns -1, with errno possibly one of:
2419 * EACCES - search permission denied on intervening directory
2420 * ETIME - timed out - tasks remain after 'seconds' timeout
2421 * EMFILE - too many open files
2422 * ENODEV - /dev/cpuset not mounted
2423 * ENOENT - component of cpuset path doesn't exist
2424 * ENOMEM - out of memory
2425 * ENOSYS - kernel doesn't support cpusets
2426 * ENOTDIR - component of cpuset path is not a directory
2427 * EPERM - lacked permission to kill a task
2428 * EPERM - lacked permission to read cpusets or files therein
2429 */
2430
2431 void cpuset_fts_reverse(struct cpuset_fts_tree *cs_tree);
2432
cpuset_nuke(const char * relpath,unsigned int seconds)2433 int cpuset_nuke(const char *relpath, unsigned int seconds)
2434 {
2435 unsigned int secs_left = seconds; /* total sleep seconds left */
2436 unsigned int secs_loop = 1; /* how much sleep next loop */
2437 unsigned int secs_slept; /* seconds slept in sleep() */
2438 struct cpuset_pidlist *pl = NULL; /* pids in cpuset subtree */
2439 struct cpuset_fts_tree *cs_tree;
2440 const struct cpuset_fts_entry *cs_entry;
2441 int ret, sav_errno = 0;
2442
2443 if (check() < 0)
2444 return -1;
2445
2446 if (seconds == 0)
2447 goto rmdir_cpusets;
2448
2449 while (1) {
2450 int plen, j;
2451
2452 if ((pl = cpuset_init_pidlist(relpath, 1)) == NULL) {
2453 /* missing cpuset is as good as if already nuked */
2454 if (errno == ENOENT) {
2455 ret = 0;
2456 goto no_more_cpuset;
2457 }
2458
2459 /* other problems reading cpuset are bad news */
2460 sav_errno = errno;
2461 goto failed;
2462 }
2463
2464 if ((plen = cpuset_pidlist_length(pl)) == 0)
2465 goto rmdir_cpusets;
2466
2467 for (j = 0; j < plen; j++) {
2468 pid_t pid;
2469
2470 if ((pid = cpuset_get_pidlist(pl, j)) > 1) {
2471 if (kill(pid, SIGKILL) < 0 && errno != ESRCH) {
2472 sav_errno = errno;
2473 goto failed;
2474 }
2475 }
2476 }
2477
2478 if (secs_left == 0)
2479 goto took_too_long;
2480
2481 cpuset_freepidlist(pl);
2482 pl = NULL;
2483
2484 secs_slept = secs_loop - sleep(secs_loop);
2485
2486 /* Ensure forward progress */
2487 if (secs_slept == 0)
2488 secs_slept = 1;
2489
2490 /* Ensure sane sleep() return (unnecessary?) */
2491 if (secs_slept > secs_loop)
2492 secs_slept = secs_loop;
2493
2494 secs_left -= secs_slept;
2495
2496 if (secs_loop < 10)
2497 secs_loop++;
2498
2499 secs_loop = MIN(secs_left, secs_loop);
2500 }
2501
2502 took_too_long:
2503 sav_errno = ETIME;
2504 /* fall into ... */
2505 failed:
2506 cpuset_freepidlist(pl);
2507 errno = sav_errno;
2508 return -1;
2509
2510 rmdir_cpusets:
2511 /* Let's try removing cpuset(s) now. */
2512 cpuset_freepidlist(pl);
2513
2514 if ((cs_tree = cpuset_fts_open(relpath)) == NULL && errno != ENOENT)
2515 return -1;
2516 ret = 0;
2517 cpuset_fts_reverse(cs_tree); /* rmdir's must be done bottom up */
2518 while ((cs_entry = cpuset_fts_read(cs_tree)) != NULL) {
2519 char buf[PATH_MAX];
2520
2521 fullpath(buf, sizeof(buf), cpuset_fts_get_path(cs_entry));
2522 if (rmdir(buf) < 0 && errno != ENOENT) {
2523 sav_errno = errno;
2524 ret = -1;
2525 }
2526 }
2527 cpuset_fts_close(cs_tree);
2528 /* fall into ... */
2529 no_more_cpuset:
2530 if (ret == 0)
2531 errno = 0;
2532 else
2533 errno = sav_errno;
2534 return ret;
2535 }
2536
2537 /*
2538 * When recursively reading all the tasks files from a subtree,
2539 * chain together the read results, one pidblock per tasks file,
2540 * containing the raw unprocessed ascii as read(2) in. After
2541 * we gather up this raw data, we then go back to count how
2542 * many pid's there are in total, allocate an array of pid_t
2543 * of that size, and transform the raw ascii data into this
2544 * array of pid_t's.
2545 */
2546
2547 struct pidblock {
2548 char *buf;
2549 int buflen;
2550 struct pidblock *next;
2551 };
2552
2553 /*
2554 * Chain the raw contents of a file onto the pbhead list.
2555 *
2556 * We malloc "+ 1" extra byte for a nul-terminator, so that
2557 * the strtoul() loop in pid_transform() won't scan past
2558 * the end of pb->buf[] and accidentally find more pids.
2559 */
add_pidblock(const char * file,struct pidblock ** ppbhead)2560 static void add_pidblock(const char *file, struct pidblock **ppbhead)
2561 {
2562 FILE *fp = NULL;
2563 struct pidblock *pb = NULL;
2564 int fsz;
2565
2566 if ((fp = fopen(file, "r")) == NULL)
2567 goto err;
2568 fsz = filesize(fp);
2569 if (fsz == 0)
2570 goto err;
2571 if ((pb = calloc(1, sizeof(*pb))) == NULL)
2572 goto err;
2573 pb->buflen = fsz;
2574 if ((pb->buf = malloc(pb->buflen + 1)) == NULL)
2575 goto err;
2576 if (fread(pb->buf, 1, pb->buflen, fp) > 0) {
2577 pb->buf[pb->buflen] = '\0';
2578 pb->next = *ppbhead;
2579 *ppbhead = pb;
2580 }
2581 fclose(fp);
2582 return;
2583 err:
2584 if (fp)
2585 fclose(fp);
2586 free(pb);
2587 }
2588
read_task_file(const char * relpath,struct pidblock ** ppbhead)2589 static void read_task_file(const char *relpath, struct pidblock **ppbhead)
2590 {
2591 char buf[PATH_MAX];
2592
2593 fullpath2(buf, sizeof(buf), relpath, "tasks");
2594 add_pidblock(buf, ppbhead);
2595 }
2596
2597 struct cpuset_pidlist {
2598 pid_t *pids;
2599 int npids;
2600 };
2601
2602 /* Count how many pids in buf (one per line - just count newlines) */
pidcount(const char * buf,int buflen)2603 static int pidcount(const char *buf, int buflen)
2604 {
2605 int n = 0;
2606 const char *cp;
2607
2608 for (cp = buf; cp < buf + buflen; cp++) {
2609 if (*cp == '\n')
2610 n++;
2611 }
2612 return n;
2613 }
2614
2615 /* Transform one-per-line ascii pids in pb to pid_t entries in pl */
pid_transform(struct pidblock * pb,struct cpuset_pidlist * pl,int n)2616 static int pid_transform(struct pidblock *pb, struct cpuset_pidlist *pl, int n)
2617 {
2618 char *a, *b;
2619
2620 for (a = pb->buf; a < pb->buf + pb->buflen; a = b) {
2621 pid_t p = strtoul(a, &b, 10);
2622 if (a == b)
2623 break;
2624 pl->pids[n++] = p;
2625 }
2626 return n;
2627 }
2628
free_pidblocks(struct pidblock * pbhead)2629 static void free_pidblocks(struct pidblock *pbhead)
2630 {
2631 struct pidblock *pb, *nextpb;
2632
2633 for (pb = pbhead; pb; pb = nextpb) {
2634 nextpb = pb->next;
2635 free(pb->buf);
2636 free(pb);
2637 }
2638 }
2639
2640 /* numeric comparison routine for qsort */
numericsort(const void * m1,const void * m2)2641 static int numericsort(const void *m1, const void *m2)
2642 {
2643 pid_t p1 = *(pid_t *) m1;
2644 pid_t p2 = *(pid_t *) m2;
2645
2646 return p1 - p2;
2647 }
2648
2649 /* Return list pids in cpuset 'path' */
cpuset_init_pidlist(const char * relpath,int recursiveflag)2650 struct cpuset_pidlist *cpuset_init_pidlist(const char *relpath,
2651 int recursiveflag)
2652 {
2653 struct pidblock *pb = NULL;
2654 struct cpuset_pidlist *pl = NULL;
2655 struct pidblock *pbhead = NULL;
2656 int n;
2657
2658 if (check() < 0)
2659 goto err;
2660
2661 if (recursiveflag) {
2662 struct cpuset_fts_tree *cs_tree;
2663 const struct cpuset_fts_entry *cs_entry;
2664
2665 if ((cs_tree = cpuset_fts_open(relpath)) == NULL)
2666 goto err;
2667 while ((cs_entry = cpuset_fts_read(cs_tree)) != NULL) {
2668 if (cpuset_fts_get_info(cs_entry) != CPUSET_FTS_CPUSET)
2669 continue;
2670 read_task_file(cpuset_fts_get_path(cs_entry), &pbhead);
2671 }
2672 cpuset_fts_close(cs_tree);
2673 } else {
2674 read_task_file(relpath, &pbhead);
2675 }
2676
2677 if ((pl = calloc(1, sizeof(*pl))) == NULL)
2678 goto err;
2679 pl->npids = 0;
2680 for (pb = pbhead; pb; pb = pb->next)
2681 pl->npids += pidcount(pb->buf, pb->buflen);
2682 if ((pl->pids = calloc(pl->npids, sizeof(pid_t))) == NULL)
2683 goto err;
2684 n = 0;
2685 for (pb = pbhead; pb; pb = pb->next)
2686 n = pid_transform(pb, pl, n);
2687 free_pidblocks(pbhead);
2688 qsort(pl->pids, pl->npids, sizeof(pid_t), numericsort);
2689 return pl;
2690 err:
2691 cpuset_freepidlist(pl);
2692 free_pidblocks(pbhead);
2693 return NULL;
2694 }
2695
2696 /* Return number of elements in pidlist */
cpuset_pidlist_length(const struct cpuset_pidlist * pl)2697 int cpuset_pidlist_length(const struct cpuset_pidlist *pl)
2698 {
2699 if (pl)
2700 return pl->npids;
2701 else
2702 return 0;
2703 }
2704
2705 /* Return i'th element of pidlist */
cpuset_get_pidlist(const struct cpuset_pidlist * pl,int i)2706 pid_t cpuset_get_pidlist(const struct cpuset_pidlist * pl, int i)
2707 {
2708 if (pl && i >= 0 && i < pl->npids)
2709 return pl->pids[i];
2710 else
2711 return (pid_t) - 1;
2712 }
2713
2714 /* Free pidlist */
cpuset_freepidlist(struct cpuset_pidlist * pl)2715 void cpuset_freepidlist(struct cpuset_pidlist *pl)
2716 {
2717 if (pl && pl->pids)
2718 free(pl->pids);
2719 free(pl);
2720 }
2721
__cpuset_move(pid_t pid,const char * path)2722 static int __cpuset_move(pid_t pid, const char *path)
2723 {
2724 char buf[SMALL_BUFSZ];
2725
2726 snprintf(buf, sizeof(buf), "%u", pid);
2727 return write_string_file(path, buf);
2728 }
2729
2730 /* Move task (pid == 0 for current) to a cpuset */
cpuset_move(pid_t pid,const char * relpath)2731 int cpuset_move(pid_t pid, const char *relpath)
2732 {
2733 char buf[PATH_MAX];
2734
2735 if (check() < 0)
2736 return -1;
2737
2738 if (pid == 0)
2739 pid = getpid();
2740
2741 fullpath2(buf, sizeof(buf), relpath, "tasks");
2742 return __cpuset_move(pid, buf);
2743 }
2744
2745 /* Move all tasks in pidlist to a cpuset */
cpuset_move_all(struct cpuset_pidlist * pl,const char * relpath)2746 int cpuset_move_all(struct cpuset_pidlist *pl, const char *relpath)
2747 {
2748 int i;
2749 char buf[PATH_MAX];
2750 int ret;
2751
2752 if (check() < 0)
2753 return -1;
2754
2755 fullpath2(buf, sizeof(buf), relpath, "tasks");
2756
2757 ret = 0;
2758 for (i = 0; i < pl->npids; i++)
2759 if (__cpuset_move(pl->pids[i], buf) < 0)
2760 ret = -1;
2761 return ret;
2762 }
2763
2764 /*
2765 * [optional] cpuset_move_cpuset_tasks() - Move all tasks in a
2766 * cpuset to another cpuset
2767 *
2768 * Move all tasks in cpuset fromrelpath to cpuset torelpath. This may
2769 * race with tasks being added to or forking into fromrelpath. Loop
2770 * repeatedly, reading the tasks file of cpuset fromrelpath and writing
2771 * any task pid's found there to the tasks file of cpuset torelpath,
2772 * up to ten attempts, or until the tasks file of cpuset fromrelpath
2773 * is empty, or until fromrelpath is no longer present.
2774 *
2775 * Returns 0 with errno == 0 if able to empty the tasks file of cpuset
2776 * fromrelpath. Of course it is still possible that some independent
2777 * task could add another task to cpuset fromrelpath at the same time
2778 * that such a successful result is being returned, so there can be
2779 * no guarantee that a successful return means that fromrelpath is
2780 * still empty of tasks.
2781 *
2782 * We are careful to allow for the possibility that the cpuset
2783 * fromrelpath might disappear out from under us, perhaps because it
2784 * has notify_on_release set and gets automatically removed as soon
2785 * as we detach its last task from it. Consider a missing fromrelpath
2786 * to be a successful move.
2787 *
2788 * If called with fromrelpath and torelpath pathnames that evaluate to
2789 * the same cpuset, then treat that as if cpuset_reattach() was called,
2790 * rebinding each task in this cpuset one time, and return success or
2791 * failure depending on the return of that cpuset_reattach() call.
2792 *
2793 * On failure, returns -1, with errno possibly one of:
2794 * EACCES - search permission denied on intervening directory
2795 * ENOTEMPTY - tasks remain after multiple attempts to move them
2796 * EMFILE - too many open files
2797 * ENODEV - /dev/cpuset not mounted
2798 * ENOENT - component of cpuset path doesn't exist
2799 * ENOMEM - out of memory
2800 * ENOSYS - kernel doesn't support cpusets
2801 * ENOTDIR - component of cpuset path is not a directory
2802 * EPERM - lacked permission to kill a task
2803 * EPERM - lacked permission to read cpusets or files therein
2804 *
2805 * This is an [optional] function. Use cpuset_function to invoke it.
2806 */
2807
2808 #define NUMBER_MOVE_TASK_ATTEMPTS 10
2809
cpuset_move_cpuset_tasks(const char * fromrelpath,const char * torelpath)2810 int cpuset_move_cpuset_tasks(const char *fromrelpath, const char *torelpath)
2811 {
2812 char fromfullpath[PATH_MAX];
2813 char tofullpath[PATH_MAX];
2814 int i;
2815 struct cpuset_pidlist *pl = NULL;
2816 int sav_errno;
2817
2818 fullpath(fromfullpath, sizeof(fromfullpath), fromrelpath);
2819 fullpath(tofullpath, sizeof(tofullpath), torelpath);
2820
2821 if (samefile(fromfullpath, tofullpath))
2822 return cpuset_reattach(fromrelpath);
2823
2824 for (i = 0; i < NUMBER_MOVE_TASK_ATTEMPTS; i++) {
2825 int plen, j;
2826
2827 if ((pl = cpuset_init_pidlist(fromrelpath, 0)) == NULL) {
2828 /* missing cpuset is as good as if all moved */
2829 if (errno == ENOENT)
2830 goto no_more_cpuset;
2831
2832 /* other problems reading cpuset are bad news */
2833 sav_errno = errno;
2834 goto failed;
2835 }
2836
2837 if ((plen = cpuset_pidlist_length(pl)) == 0)
2838 goto no_more_pids;
2839
2840 for (j = 0; j < plen; j++) {
2841 pid_t pid;
2842
2843 pid = cpuset_get_pidlist(pl, j);
2844 if (cpuset_move(pid, torelpath) < 0) {
2845 /* missing task is as good as if moved */
2846 if (errno == ESRCH)
2847 continue;
2848
2849 /* other per-task errors are bad news */
2850 sav_errno = errno;
2851 goto failed;
2852 }
2853 }
2854
2855 cpuset_freepidlist(pl);
2856 pl = NULL;
2857 }
2858
2859 sav_errno = ENOTEMPTY;
2860 /* fall into ... */
2861 failed:
2862 cpuset_freepidlist(pl);
2863 errno = sav_errno;
2864 return -1;
2865
2866 no_more_pids:
2867 no_more_cpuset:
2868 /* Success - all tasks (or entire cpuset ;) gone. */
2869 cpuset_freepidlist(pl);
2870 errno = 0;
2871 return 0;
2872 }
2873
2874 /* Migrate task (pid == 0 for current) to a cpuset (moves task and memory) */
cpuset_migrate(pid_t pid,const char * relpath)2875 int cpuset_migrate(pid_t pid, const char *relpath)
2876 {
2877 char buf[PATH_MAX];
2878 char buf2[PATH_MAX];
2879 char memory_migrate_flag;
2880 int r;
2881
2882 if (check() < 0)
2883 return -1;
2884
2885 if (pid == 0)
2886 pid = getpid();
2887
2888 fullpath(buf2, sizeof(buf2), relpath);
2889
2890 if (load_flag(buf2, &memory_migrate_flag, "memory_migrate") < 0)
2891 return -1;
2892 if (store_flag(buf2, "memory_migrate", 1) < 0)
2893 return -1;
2894
2895 fullpath2(buf, sizeof(buf), relpath, "tasks");
2896
2897 r = __cpuset_move(pid, buf);
2898
2899 store_flag(buf2, "memory_migrate", memory_migrate_flag);
2900 return r;
2901 }
2902
2903 /* Migrate all tasks in pidlist to a cpuset (moves task and memory) */
cpuset_migrate_all(struct cpuset_pidlist * pl,const char * relpath)2904 int cpuset_migrate_all(struct cpuset_pidlist *pl, const char *relpath)
2905 {
2906 int i;
2907 char buf[PATH_MAX];
2908 char buf2[PATH_MAX];
2909 char memory_migrate_flag;
2910 int ret;
2911
2912 if (check() < 0)
2913 return -1;
2914
2915 fullpath(buf2, sizeof(buf2), relpath);
2916
2917 if (load_flag(buf2, &memory_migrate_flag, "memory_migrate") < 0)
2918 return -1;
2919 if (store_flag(buf2, "memory_migrate", 1) < 0)
2920 return -1;
2921
2922 fullpath2(buf, sizeof(buf), relpath, "tasks");
2923
2924 ret = 0;
2925 for (i = 0; i < pl->npids; i++)
2926 if (__cpuset_move(pl->pids[i], buf) < 0)
2927 ret = -1;
2928
2929 if (store_flag(buf2, "memory_migrate", memory_migrate_flag) < 0)
2930 ret = -1;
2931 return ret;
2932 }
2933
2934 /* Rebind cpus_allowed of each task in cpuset 'path' */
cpuset_reattach(const char * relpath)2935 int cpuset_reattach(const char *relpath)
2936 {
2937 struct cpuset_pidlist *pl;
2938 int rc;
2939
2940 if ((pl = cpuset_init_pidlist(relpath, 0)) == NULL)
2941 return -1;
2942 rc = cpuset_move_all(pl, relpath);
2943 cpuset_freepidlist(pl);
2944 return rc;
2945 }
2946
2947 /* Map cpuset relative cpu number to system wide cpu number */
cpuset_c_rel_to_sys_cpu(const struct cpuset * cp,int cpu)2948 int cpuset_c_rel_to_sys_cpu(const struct cpuset *cp, int cpu)
2949 {
2950 struct cpuset *cp_tofree = NULL;
2951 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
2952 int pos = -1;
2953
2954 if (!cp1)
2955 goto err;
2956 pos = bitmask_rel_to_abs_pos(cp1->cpus, cpu);
2957 /* fall into ... */
2958 err:
2959 cpuset_free(cp_tofree);
2960 return pos;
2961 }
2962
2963 /* Map system wide cpu number to cpuset relative cpu number */
cpuset_c_sys_to_rel_cpu(const struct cpuset * cp,int cpu)2964 int cpuset_c_sys_to_rel_cpu(const struct cpuset *cp, int cpu)
2965 {
2966 struct cpuset *cp_tofree = NULL;
2967 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
2968 int pos = -1;
2969
2970 if (!cp1)
2971 goto err;
2972 pos = bitmask_abs_to_rel_pos(cp1->cpus, cpu);
2973 /* fall into ... */
2974 err:
2975 cpuset_free(cp_tofree);
2976 return pos;
2977 }
2978
2979 /* Map cpuset relative mem number to system wide mem number */
cpuset_c_rel_to_sys_mem(const struct cpuset * cp,int mem)2980 int cpuset_c_rel_to_sys_mem(const struct cpuset *cp, int mem)
2981 {
2982 struct cpuset *cp_tofree = NULL;
2983 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
2984 int pos = -1;
2985
2986 if (!cp1)
2987 goto err;
2988 pos = bitmask_rel_to_abs_pos(cp1->mems, mem);
2989 /* fall into ... */
2990 err:
2991 cpuset_free(cp_tofree);
2992 return pos;
2993 }
2994
2995 /* Map system wide mem number to cpuset relative mem number */
cpuset_c_sys_to_rel_mem(const struct cpuset * cp,int mem)2996 int cpuset_c_sys_to_rel_mem(const struct cpuset *cp, int mem)
2997 {
2998 struct cpuset *cp_tofree = NULL;
2999 const struct cpuset *cp1 = resolve_cp(cp, &cp_tofree);
3000 int pos = -1;
3001
3002 if (!cp1)
3003 goto err;
3004 pos = bitmask_abs_to_rel_pos(cp1->mems, mem);
3005 /* fall into ... */
3006 err:
3007 cpuset_free(cp_tofree);
3008 return pos;
3009 }
3010
3011 /* Map pid's cpuset relative cpu number to system wide cpu number */
cpuset_p_rel_to_sys_cpu(pid_t pid,int cpu)3012 int cpuset_p_rel_to_sys_cpu(pid_t pid, int cpu)
3013 {
3014 struct cpuset *cp;
3015 int rc = -1;
3016
3017 if ((cp = cpuset_alloc()) == NULL)
3018 goto done;
3019 if (cpuset_cpusetofpid(cp, pid) < 0)
3020 goto done;
3021 rc = cpuset_c_rel_to_sys_cpu(cp, cpu);
3022 done:
3023 cpuset_free(cp);
3024 return rc;
3025 }
3026
3027 /* Map system wide cpu number to pid's cpuset relative cpu number */
cpuset_p_sys_to_rel_cpu(pid_t pid,int cpu)3028 int cpuset_p_sys_to_rel_cpu(pid_t pid, int cpu)
3029 {
3030 struct cpuset *cp;
3031 int rc = -1;
3032
3033 if ((cp = cpuset_alloc()) == NULL)
3034 goto done;
3035 if (cpuset_cpusetofpid(cp, pid) < 0)
3036 goto done;
3037 rc = cpuset_c_sys_to_rel_cpu(cp, cpu);
3038 done:
3039 cpuset_free(cp);
3040 return rc;
3041 }
3042
3043 /* Map pid's cpuset relative mem number to system wide mem number */
cpuset_p_rel_to_sys_mem(pid_t pid,int mem)3044 int cpuset_p_rel_to_sys_mem(pid_t pid, int mem)
3045 {
3046 struct cpuset *cp;
3047 int rc = -1;
3048
3049 if ((cp = cpuset_alloc()) == NULL)
3050 goto done;
3051 if (cpuset_cpusetofpid(cp, pid) < 0)
3052 goto done;
3053 rc = cpuset_c_rel_to_sys_mem(cp, mem);
3054 done:
3055 cpuset_free(cp);
3056 return rc;
3057 }
3058
3059 /* Map system wide mem number to pid's cpuset relative mem number */
cpuset_p_sys_to_rel_mem(pid_t pid,int mem)3060 int cpuset_p_sys_to_rel_mem(pid_t pid, int mem)
3061 {
3062 struct cpuset *cp;
3063 int rc = -1;
3064
3065 if ((cp = cpuset_alloc()) == NULL)
3066 goto done;
3067 if (cpuset_cpusetofpid(cp, pid) < 0)
3068 goto done;
3069 rc = cpuset_c_sys_to_rel_mem(cp, mem);
3070 done:
3071 cpuset_free(cp);
3072 return rc;
3073 }
3074
3075 /*
3076 * Override glibc's calls for get/set affinity - they have
3077 * something using cpu_set_t that will die when NR_CPUS > 1024.
3078 * Go directly to the 'real' system calls. Also override calls
3079 * for get_mempolicy and set_mempolicy. None of these
3080 * calls are yet (July 2004) guaranteed to be in all glibc versions
3081 * that we care about.
3082 */
3083
sched_setaffinity(pid_t pid,unsigned len,unsigned long * mask)3084 static int sched_setaffinity(pid_t pid, unsigned len, unsigned long *mask)
3085 {
3086 return ltp_syscall(__NR_sched_setaffinity, pid, len, mask);
3087 }
3088
get_mempolicy(int * policy,unsigned long * nmask,unsigned long maxnode,void * addr,int flags)3089 static int get_mempolicy(int *policy, unsigned long *nmask,
3090 unsigned long maxnode, void *addr, int flags)
3091 {
3092 return ltp_syscall(__NR_get_mempolicy, policy, nmask, maxnode,
3093 addr, flags);
3094 }
3095
set_mempolicy(int mode,unsigned long * nmask,unsigned long maxnode)3096 static int set_mempolicy(int mode, unsigned long *nmask, unsigned long maxnode)
3097 {
3098 return ltp_syscall(__NR_set_mempolicy, mode, nmask, maxnode);
3099 }
3100
3101 struct cpuset_placement {
3102 struct bitmask *cpus;
3103 struct bitmask *mems;
3104 char *path;
3105 };
3106
3107 /* Allocate and fill in a placement struct - cpatures current placement */
cpuset_get_placement(pid_t pid)3108 struct cpuset_placement *cpuset_get_placement(pid_t pid)
3109 {
3110 struct cpuset_placement *plc;
3111 struct cpuset *cp = NULL;
3112 char buf[PATH_MAX];
3113 int nbits;
3114
3115 if ((plc = calloc(1, sizeof(*plc))) == NULL)
3116 goto err;
3117
3118 nbits = cpuset_cpus_nbits();
3119 if ((plc->cpus = bitmask_alloc(nbits)) == NULL)
3120 goto err;
3121
3122 nbits = cpuset_mems_nbits();
3123 if ((plc->mems = bitmask_alloc(nbits)) == NULL)
3124 goto err;
3125
3126 if ((cp = cpuset_alloc()) == NULL)
3127 goto err;
3128 if (cpuset_getcpusetpath(pid, buf, sizeof(buf)) == NULL)
3129 goto err;
3130 if (cpuset_query(cp, buf) < 0)
3131 goto err;
3132
3133 bitmask_copy(plc->cpus, cp->cpus);
3134 bitmask_copy(plc->mems, cp->mems);
3135 plc->path = strdup(buf);
3136
3137 cpuset_free(cp);
3138 return plc;
3139 err:
3140 cpuset_free(cp);
3141 cpuset_free_placement(plc);
3142 return NULL;
3143 }
3144
3145 /* Compare two placement structs - use to detect changes in placement */
cpuset_equal_placement(const struct cpuset_placement * plc1,const struct cpuset_placement * plc2)3146 int cpuset_equal_placement(const struct cpuset_placement *plc1,
3147 const struct cpuset_placement *plc2)
3148 {
3149 return bitmask_equal(plc1->cpus, plc2->cpus) &&
3150 bitmask_equal(plc1->mems, plc2->mems) &&
3151 streq(plc1->path, plc2->path);
3152 }
3153
3154 /* Free a placement struct */
cpuset_free_placement(struct cpuset_placement * plc)3155 void cpuset_free_placement(struct cpuset_placement *plc)
3156 {
3157 if (!plc)
3158 return;
3159 bitmask_free(plc->cpus);
3160 bitmask_free(plc->mems);
3161 free(plc->path);
3162 free(plc);
3163 }
3164
3165 /*
3166 * A cpuset_fts_open() call constructs a linked list of entries
3167 * called a "cpuset_fts_tree", with one entry per cpuset below
3168 * the specified path. The cpuset_fts_read() routine returns the
3169 * next entry on this list. The various cpuset_fts_get_*() calls
3170 * return attributes of the specified entry. The cpuset_fts_close()
3171 * call frees the linked list and all associated data. All cpuset
3172 * entries and attributes for the cpuset_fts_tree returned from a
3173 * given cpuset_fts_open() call remain allocated and unchanged until
3174 * that cpuset_fts_tree is closed by a cpuset_fts_close() call. Any
3175 * subsequent changes to the cpuset filesystem will go unnoticed
3176 * (not affect open cpuset_fts_tree's.)
3177 */
3178
3179 struct cpuset_fts_entry;
3180 void cpuset_fts_rewind(struct cpuset_fts_tree *cs_tree);
3181
3182 struct cpuset_fts_tree {
3183 struct cpuset_fts_entry *head; /* head of linked entry list */
3184 struct cpuset_fts_entry *next; /* cpuset_fts_read() offset */
3185 };
3186
3187 struct cpuset_fts_entry {
3188 struct cpuset_fts_entry *next; /* linked entry list chain */
3189 struct cpuset *cpuset;
3190 struct stat *stat;
3191 char *path;
3192 int info;
3193 int err;
3194 };
3195
3196 /* Open a handle on a cpuset hierarchy. All the real work is done here. */
cpuset_fts_open(const char * cpusetpath)3197 struct cpuset_fts_tree *cpuset_fts_open(const char *cpusetpath)
3198 {
3199 FTS *fts = NULL;
3200 FTSENT *ftsent;
3201 char *path_argv[2];
3202 char buf[PATH_MAX];
3203 struct cpuset_fts_tree *cs_tree = NULL;
3204 struct cpuset_fts_entry *ep; /* the latest new list entry */
3205 struct cpuset_fts_entry **pnlep; /* ptr to next list entry ptr */
3206 char *relpath;
3207 int fts_flags;
3208
3209 fullpath(buf, sizeof(buf), cpusetpath);
3210 path_argv[0] = buf;
3211 path_argv[1] = NULL;
3212
3213 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT | FTS_XDEV;
3214 fts = fts_open(path_argv, fts_flags, NULL);
3215 if (fts == NULL)
3216 goto err;
3217
3218 cs_tree = malloc(sizeof(*cs_tree));
3219 if (cs_tree == NULL)
3220 goto err;
3221 pnlep = &cs_tree->head;
3222 *pnlep = NULL;
3223
3224 while ((ftsent = fts_read(fts)) != NULL) {
3225 if (ftsent->fts_info != FTS_D && ftsent->fts_info != FTS_DNR)
3226 continue;
3227
3228 /* ftsent is a directory (perhaps unreadable) ==> cpuset */
3229 ep = calloc(1, sizeof(*ep));
3230 if (ep == NULL)
3231 goto err;
3232 *pnlep = ep;
3233 pnlep = &ep->next;
3234
3235 /* Set entry's path, and if DNR, error */
3236 relpath = ftsent->fts_path + strlen(cpusetmnt);
3237 if (strlen(relpath) == 0)
3238 relpath = "/";
3239 ep->path = strdup(relpath);
3240 if (ep->path == NULL)
3241 goto err;
3242 if (ftsent->fts_info == FTS_DNR) {
3243 ep->info = CPUSET_FTS_ERR_DNR;
3244 ep->err = ftsent->fts_errno;
3245 continue;
3246 }
3247
3248 /* ftsent is a -readable- cpuset: set entry's stat, etc */
3249 ep->stat = calloc(1, sizeof(struct stat));
3250 if (ep->stat == NULL)
3251 goto err;
3252 if (stat(ftsent->fts_path, ep->stat) < 0) {
3253 ep->info = CPUSET_FTS_ERR_STAT;
3254 ep->err = ftsent->fts_errno;
3255 continue;
3256 }
3257
3258 ep->cpuset = calloc(1, sizeof(struct cpuset));
3259 if (ep->cpuset == NULL)
3260 goto err;
3261 if (cpuset_query(ep->cpuset, relpath) < 0) {
3262 ep->info = CPUSET_FTS_ERR_CPUSET;
3263 ep->err = errno;
3264 continue;
3265 }
3266 ep->info = CPUSET_FTS_CPUSET;
3267 }
3268
3269 (void)fts_close(fts);
3270 cpuset_fts_rewind(cs_tree);
3271 return cs_tree;
3272
3273 err:
3274 if (cs_tree)
3275 cpuset_fts_close(cs_tree);
3276 if (fts)
3277 (void)fts_close(fts);
3278 return NULL;
3279 }
3280
3281 /* Return pointer to next cpuset entry in hierarchy */
cpuset_fts_read(struct cpuset_fts_tree * cs_tree)3282 const struct cpuset_fts_entry *cpuset_fts_read(struct cpuset_fts_tree *cs_tree)
3283 {
3284 const struct cpuset_fts_entry *cs_entry = cs_tree->next;
3285 if (cs_tree->next != NULL) /* seek to next entry */
3286 cs_tree->next = cs_tree->next->next;
3287 return cs_entry;
3288 }
3289
3290 /* Reverse list of cpusets, in place. Simulates pre-order/post-order flip. */
cpuset_fts_reverse(struct cpuset_fts_tree * cs_tree)3291 void cpuset_fts_reverse(struct cpuset_fts_tree *cs_tree)
3292 {
3293 struct cpuset_fts_entry *cs1, *cs2, *cs3;
3294
3295 /*
3296 * At each step, cs1 < cs2 < cs3 and the cs2->next pointer
3297 * is redirected from cs3 to cs1.
3298 */
3299
3300 cs1 = cs2 = NULL;
3301 cs3 = cs_tree->head;
3302 while (cs3) {
3303 cs1 = cs2;
3304 cs2 = cs3;
3305 cs3 = cs3->next;
3306 cs2->next = cs1;
3307 }
3308 cs_tree->head = cs2;
3309 cpuset_fts_rewind(cs_tree);
3310 }
3311
3312 /* Rewind cpuset list to beginning */
cpuset_fts_rewind(struct cpuset_fts_tree * cs_tree)3313 void cpuset_fts_rewind(struct cpuset_fts_tree *cs_tree)
3314 {
3315 cs_tree->next = cs_tree->head;
3316 }
3317
3318 /* Return pointer to nul-terminated cpuset path of entry in hierarchy */
cpuset_fts_get_path(const struct cpuset_fts_entry * cs_entry)3319 const char *cpuset_fts_get_path(const struct cpuset_fts_entry *cs_entry)
3320 {
3321 return cs_entry->path;
3322 }
3323
3324 /* Return pointer to stat(2) structure of a cpuset entry's directory */
cpuset_fts_get_stat(const struct cpuset_fts_entry * cs_entry)3325 const struct stat *cpuset_fts_get_stat(const struct cpuset_fts_entry *cs_entry)
3326 {
3327 return cs_entry->stat;
3328 }
3329
3330 /* Return pointer to cpuset structure of a cpuset entry */
cpuset_fts_get_cpuset(const struct cpuset_fts_entry * cs_entry)3331 const struct cpuset *cpuset_fts_get_cpuset(const struct cpuset_fts_entry
3332 *cs_entry)
3333 {
3334 return cs_entry->cpuset;
3335 }
3336
3337 /* Return value of errno (0 if no error) on attempted cpuset operations */
cpuset_fts_get_errno(const struct cpuset_fts_entry * cs_entry)3338 int cpuset_fts_get_errno(const struct cpuset_fts_entry *cs_entry)
3339 {
3340 return cs_entry->err;
3341 }
3342
3343 /* Return operation identity causing error */
cpuset_fts_get_info(const struct cpuset_fts_entry * cs_entry)3344 int cpuset_fts_get_info(const struct cpuset_fts_entry *cs_entry)
3345 {
3346 return cs_entry->info;
3347 }
3348
3349 /* Close a cpuset hierarchy handle (free's all associated memory) */
cpuset_fts_close(struct cpuset_fts_tree * cs_tree)3350 void cpuset_fts_close(struct cpuset_fts_tree *cs_tree)
3351 {
3352 struct cpuset_fts_entry *cs_entry = cs_tree->head;
3353
3354 while (cs_entry) {
3355 struct cpuset_fts_entry *ep = cs_entry;
3356
3357 cs_entry = cs_entry->next;
3358 free(ep->path);
3359 free(ep->stat);
3360 cpuset_free(ep->cpuset);
3361 free(ep);
3362 }
3363 free(cs_tree);
3364 }
3365
3366 /* Bind current task to cpu (uses sched_setaffinity(2)) */
cpuset_cpubind(int cpu)3367 int cpuset_cpubind(int cpu)
3368 {
3369 struct bitmask *bmp;
3370 int r;
3371
3372 if ((bmp = bitmask_alloc(cpuset_cpus_nbits())) == NULL)
3373 return -1;
3374 bitmask_setbit(bmp, cpu);
3375 r = sched_setaffinity(0, bitmask_nbytes(bmp), bitmask_mask(bmp));
3376 bitmask_free(bmp);
3377 return r;
3378 }
3379
3380 /*
3381 * int cpuset_latestcpu(pid_t pid)
3382 *
3383 * Return most recent CPU on which task pid executed. If pid == 0,
3384 * examine current task.
3385 *
3386 * The last used CPU is visible for a given pid as field #39 (starting
3387 * with #1) in the file /proc/pid/stat. Currently this file has 41
3388 * fields, in which case this is the 3rd to the last field.
3389 *
3390 * Unfortunately field #2 is a command name and might have embedded
3391 * whitespace. So we can't just count white space separated fields.
3392 * Fortunately, this command name is surrounded by parentheses, as
3393 * for example "(sh)", and that closing parenthesis is the last ')'
3394 * character in the line. No remaining fields can have embedded
3395 * whitespace or parentheses. So instead of looking for the 39th
3396 * white space separated field, we can look for the 37th white space
3397 * separated field past the last ')' character on the line.
3398 */
3399
3400 /* Return most recent CPU on which task pid executed */
cpuset_latestcpu(pid_t pid)3401 int cpuset_latestcpu(pid_t pid)
3402 {
3403 char buf[PATH_MAX];
3404 char *bp;
3405 int fd = -1;
3406 int cpu = -1;
3407
3408 if (pid == 0)
3409 snprintf(buf, sizeof(buf), "/proc/self/stat");
3410 else
3411 snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
3412
3413 if ((fd = open(buf, O_RDONLY)) < 0)
3414 goto err;
3415 if (read(fd, buf, sizeof(buf)) < 1)
3416 goto err;
3417 close(fd);
3418
3419 bp = strrchr(buf, ')');
3420 if (bp)
3421 sscanf(bp + 1, "%*s %*u %*u %*u %*u %*u %*u %*u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u " "%*u %*u %*u %*u %*u %*u %*u %*u %u", /* 37th field past ')' */
3422 &cpu);
3423 if (cpu < 0)
3424 errno = EINVAL;
3425 return cpu;
3426 err:
3427 if (fd >= 0)
3428 close(fd);
3429 return -1;
3430 }
3431
3432 /* Bind current task to memory (uses set_mempolicy(2)) */
cpuset_membind(int mem)3433 int cpuset_membind(int mem)
3434 {
3435 struct bitmask *bmp;
3436 int r;
3437
3438 if ((bmp = bitmask_alloc(cpuset_mems_nbits())) == NULL)
3439 return -1;
3440 bitmask_setbit(bmp, mem);
3441 r = set_mempolicy(MPOL_BIND, bitmask_mask(bmp), bitmask_nbits(bmp) + 1);
3442 bitmask_free(bmp);
3443 return r;
3444 }
3445
3446 /* [optional] Return Memory Node holding page at specified addr */
cpuset_addr2node(void * addr)3447 int cpuset_addr2node(void *addr)
3448 {
3449 int node = -1;
3450
3451 if (get_mempolicy(&node, NULL, 0, addr, MPOL_F_NODE | MPOL_F_ADDR)) {
3452 /* I realize this seems redundant, but I _want_ to make sure
3453 * that this value is -1. */
3454 node = -1;
3455 }
3456 return node;
3457 }
3458
3459 /*
3460 * Transform cpuset into Text Format Representation in buffer 'buf',
3461 * of length 'buflen', nul-terminated if space allows. Return number
3462 * of characters that would have been written, if enough space had
3463 * been available, in the same way that snprintf() does.
3464 */
3465
3466 /* Export cpuset settings to a regular file */
cpuset_export(const struct cpuset * cp,char * buf,int buflen)3467 int cpuset_export(const struct cpuset *cp, char *buf, int buflen)
3468 {
3469 char *tmp = NULL;
3470 int n = 0;
3471
3472 if (cp->cpu_exclusive)
3473 n += snprintf(buf + n, MAX(buflen - n, 0), "cpu_exclusive\n");
3474
3475 if (cp->mem_exclusive)
3476 n += snprintf(buf + n, MAX(buflen - n, 0), "mem_exclusive\n");
3477
3478 if (cp->notify_on_release)
3479 n += snprintf(buf + n, MAX(buflen - n, 0),
3480 "notify_on_release\n");
3481
3482 if (cp->memory_pressure_enabled)
3483 n += snprintf(buf + n, MAX(buflen - n, 0),
3484 "memory_pressure_enabled\n");
3485
3486 if (cp->memory_migrate)
3487 n += snprintf(buf + n, MAX(buflen - n, 0), "memory_migrate\n");
3488
3489 if (cp->memory_spread_page)
3490 n += snprintf(buf + n, MAX(buflen - n, 0),
3491 "memory_spread_page\n");
3492
3493 if (cp->memory_spread_slab)
3494 n += snprintf(buf + n, MAX(buflen - n, 0),
3495 "memory_spread_slab\n");
3496
3497 if ((tmp = sprint_mask_buf(cp->cpus)) == NULL)
3498 return -1;
3499 n += snprintf(buf + n, MAX(buflen - n, 0), "cpus %s\n", tmp);
3500 free(tmp);
3501 tmp = NULL;
3502
3503 if ((tmp = sprint_mask_buf(cp->mems)) == NULL)
3504 return -1;
3505 n += snprintf(buf + n, MAX(buflen - n, 0), "mems %s\n", tmp);
3506 free(tmp);
3507 tmp = NULL;
3508
3509 return n;
3510 }
3511
import_list(UNUSED const char * tok,const char * arg,struct bitmask * bmp,char * emsg,int elen)3512 static int import_list(UNUSED const char *tok, const char *arg,
3513 struct bitmask *bmp, char *emsg, int elen)
3514 {
3515 if (bitmask_parselist(arg, bmp) < 0) {
3516 if (emsg)
3517 snprintf(emsg, elen, "Invalid list format: %s", arg);
3518 return -1;
3519 }
3520 return 0;
3521 }
3522
stolower(char * s)3523 static void stolower(char *s)
3524 {
3525 while (*s) {
3526 unsigned char c = *s;
3527 *s = tolower(c);
3528 s++;
3529 }
3530 }
3531
3532 /* Import cpuset settings from a regular file */
cpuset_import(struct cpuset * cp,const char * buf,int * elinenum,char * emsg,int elen)3533 int cpuset_import(struct cpuset *cp, const char *buf, int *elinenum,
3534 char *emsg, int elen)
3535 {
3536 char *linebuf = NULL;
3537 int linebuflen;
3538 int linenum = 0;
3539 int offset = 0;
3540
3541 linebuflen = strlen(buf) + 1;
3542 if ((linebuf = malloc(linebuflen)) == NULL) {
3543 if (emsg)
3544 snprintf(emsg, elen, "Insufficient memory");
3545 goto err;
3546 }
3547
3548 while (slgets(linebuf, linebuflen, buf, &offset)) {
3549 char *tok, *arg;
3550 char *ptr; /* for strtok_r */
3551
3552 linenum++;
3553 if ((tok = strchr(linebuf, '#')) != NULL)
3554 *tok = 0;
3555 if ((tok = strtok_r(linebuf, " \t", &ptr)) == NULL)
3556 continue;
3557 stolower(tok);
3558
3559 arg = strtok_r(0, " \t", &ptr);
3560
3561 if (streq(tok, "cpu_exclusive")) {
3562 cp->cpu_exclusive = 1;
3563 goto eol;
3564 }
3565 if (streq(tok, "mem_exclusive")) {
3566 cp->mem_exclusive = 1;
3567 goto eol;
3568 }
3569 if (streq(tok, "notify_on_release")) {
3570 cp->notify_on_release = 1;
3571 goto eol;
3572 }
3573 if (streq(tok, "memory_pressure_enabled")) {
3574 cp->memory_pressure_enabled = 1;
3575 goto eol;
3576 }
3577 if (streq(tok, "memory_migrate")) {
3578 cp->memory_migrate = 1;
3579 goto eol;
3580 }
3581 if (streq(tok, "memory_spread_page")) {
3582 cp->memory_spread_page = 1;
3583 goto eol;
3584 }
3585 if (streq(tok, "memory_spread_slab")) {
3586 cp->memory_spread_slab = 1;
3587 goto eol;
3588 }
3589 if (streq(tok, "cpu") || streq(tok, "cpus")) {
3590 if (import_list(tok, arg, cp->cpus, emsg, elen) < 0)
3591 goto err;
3592 goto eol;
3593 }
3594 if (streq(tok, "mem") || streq(tok, "mems")) {
3595 if (import_list(tok, arg, cp->mems, emsg, elen) < 0)
3596 goto err;
3597 goto eol;
3598 }
3599 if (emsg)
3600 snprintf(emsg, elen, "Unrecognized token: '%s'", tok);
3601 goto err;
3602 eol:
3603 if ((tok = strtok_r(0, " \t", &ptr)) != NULL) {
3604 if (emsg)
3605 snprintf(emsg, elen, "Surplus token: '%s'",
3606 tok);
3607 goto err;
3608 }
3609 continue;
3610 }
3611
3612 free(linebuf);
3613
3614 if (bitmask_isallclear(cp->cpus) && !bitmask_isallclear(cp->mems))
3615 cpuset_localcpus(cp->mems, cp->cpus);
3616 else if (!bitmask_isallclear(cp->cpus) && bitmask_isallclear(cp->mems))
3617 cpuset_localmems(cp->cpus, cp->mems);
3618
3619 /*
3620 * All cpuset attributes are determined in an import.
3621 * Those that aren't explicitly specified are presumed
3622 * to be unchanged (zero, if it's a freshly allocated
3623 * struct cpuset.)
3624 */
3625
3626 cp->cpus_valid = 1;
3627 cp->mems_valid = 1;
3628 cp->cpu_exclusive_valid = 1;
3629 cp->mem_exclusive_valid = 1;
3630 cp->notify_on_release_valid = 1;
3631 cp->memory_migrate_valid = 1;
3632 cp->memory_pressure_enabled_valid = 1;
3633 cp->memory_spread_page_valid = 1;
3634 cp->memory_spread_slab_valid = 1;
3635
3636 return 0;
3637 err:
3638 if (elinenum)
3639 *elinenum = linenum;
3640 free(linebuf);
3641 return -1;
3642 }
3643
3644 /* Pin current task CPU (and memory) */
cpuset_pin(int relcpu)3645 int cpuset_pin(int relcpu)
3646 {
3647 struct cpuset_placement *plc1 = NULL, *plc2 = NULL;
3648 int cpu, r;
3649
3650 if (check() < 0)
3651 return -1;
3652
3653 do {
3654 cpuset_free_placement(plc1);
3655 plc1 = cpuset_get_placement(0);
3656
3657 r = 0;
3658 if (cpuset_unpin() < 0)
3659 r = -1;
3660 cpu = cpuset_p_rel_to_sys_cpu(0, relcpu);
3661 if (cpuset_cpubind(cpu) < 0)
3662 r = -1;
3663
3664 cpuset_free_placement(plc2);
3665 plc2 = cpuset_get_placement(0);
3666 } while (!cpuset_equal_placement(plc1, plc2));
3667
3668 cpuset_free_placement(plc1);
3669 cpuset_free_placement(plc2);
3670 return r;
3671 }
3672
3673 /* Return number CPUs in current tasks cpuset */
cpuset_size(void)3674 int cpuset_size(void)
3675 {
3676 struct cpuset_placement *plc1 = NULL, *plc2 = NULL;
3677 int r;
3678
3679 if (check() < 0)
3680 return -1;
3681
3682 do {
3683 cpuset_free_placement(plc1);
3684 plc1 = cpuset_get_placement(0);
3685
3686 r = cpuset_cpus_weight(0);
3687
3688 cpuset_free_placement(plc2);
3689 plc2 = cpuset_get_placement(0);
3690 } while (!cpuset_equal_placement(plc1, plc2));
3691
3692 cpuset_free_placement(plc1);
3693 cpuset_free_placement(plc2);
3694 return r;
3695 }
3696
3697 /* Return relative CPU number, within current cpuset, last executed on */
cpuset_where(void)3698 int cpuset_where(void)
3699 {
3700 struct cpuset_placement *plc1 = NULL, *plc2 = NULL;
3701 int r;
3702
3703 if (check() < 0)
3704 return -1;
3705
3706 do {
3707 cpuset_free_placement(plc1);
3708 plc1 = cpuset_get_placement(0);
3709
3710 r = cpuset_p_sys_to_rel_cpu(0, cpuset_latestcpu(0));
3711
3712 cpuset_free_placement(plc2);
3713 plc2 = cpuset_get_placement(0);
3714 } while (!cpuset_equal_placement(plc1, plc2));
3715
3716 cpuset_free_placement(plc1);
3717 cpuset_free_placement(plc2);
3718 return r;
3719 }
3720
3721 /* Undo cpuset_pin - let current task have the run of all CPUs in its cpuset */
cpuset_unpin(void)3722 int cpuset_unpin(void)
3723 {
3724 struct bitmask *cpus = NULL, *mems = NULL;
3725 int r = -1;
3726
3727 if (check() < 0)
3728 goto err;
3729
3730 /*
3731 * Don't need cpuset_*_placement() guard against concurrent
3732 * cpuset migration, because none of the following depends
3733 * on the tasks cpuset placement.
3734 */
3735
3736 if ((cpus = bitmask_alloc(cpuset_cpus_nbits())) == NULL)
3737 goto err;
3738 bitmask_setall(cpus);
3739 if (sched_setaffinity(0, bitmask_nbytes(cpus), bitmask_mask(cpus)) < 0)
3740 goto err;
3741
3742 if ((mems = bitmask_alloc(cpuset_mems_nbits())) == NULL)
3743 goto err;
3744 if (set_mempolicy(MPOL_DEFAULT, bitmask_mask(mems),
3745 bitmask_nbits(mems) + 1) < 0)
3746 goto err;
3747 r = 0;
3748 /* fall into ... */
3749 err:
3750 bitmask_free(cpus);
3751 bitmask_free(mems);
3752 return r;
3753
3754 }
3755
3756 struct cpuset_function_list {
3757 const char *fname;
3758 void *func;
3759 } flist[] = {
3760 {
3761 "cpuset_version", cpuset_version}, {
3762 "cpuset_alloc", cpuset_alloc}, {
3763 "cpuset_free", cpuset_free}, {
3764 "cpuset_cpus_nbits", cpuset_cpus_nbits}, {
3765 "cpuset_mems_nbits", cpuset_mems_nbits}, {
3766 "cpuset_setcpus", cpuset_setcpus}, {
3767 "cpuset_setmems", cpuset_setmems}, {
3768 "cpuset_set_iopt", cpuset_set_iopt}, {
3769 "cpuset_set_sopt", cpuset_set_sopt}, {
3770 "cpuset_getcpus", cpuset_getcpus}, {
3771 "cpuset_getmems", cpuset_getmems}, {
3772 "cpuset_cpus_weight", cpuset_cpus_weight}, {
3773 "cpuset_mems_weight", cpuset_mems_weight}, {
3774 "cpuset_get_iopt", cpuset_get_iopt}, {
3775 "cpuset_get_sopt", cpuset_get_sopt}, {
3776 "cpuset_localcpus", cpuset_localcpus}, {
3777 "cpuset_localmems", cpuset_localmems}, {
3778 "cpuset_cpumemdist", cpuset_cpumemdist}, {
3779 "cpuset_cpu2node", cpuset_cpu2node}, {
3780 "cpuset_addr2node", cpuset_addr2node}, {
3781 "cpuset_create", cpuset_create}, {
3782 "cpuset_delete", cpuset_delete}, {
3783 "cpuset_query", cpuset_query}, {
3784 "cpuset_modify", cpuset_modify}, {
3785 "cpuset_getcpusetpath", cpuset_getcpusetpath}, {
3786 "cpuset_cpusetofpid", cpuset_cpusetofpid}, {
3787 "cpuset_mountpoint", cpuset_mountpoint}, {
3788 "cpuset_collides_exclusive", cpuset_collides_exclusive}, {
3789 "cpuset_nuke", cpuset_nuke}, {
3790 "cpuset_init_pidlist", cpuset_init_pidlist}, {
3791 "cpuset_pidlist_length", cpuset_pidlist_length}, {
3792 "cpuset_get_pidlist", cpuset_get_pidlist}, {
3793 "cpuset_freepidlist", cpuset_freepidlist}, {
3794 "cpuset_move", cpuset_move}, {
3795 "cpuset_move_all", cpuset_move_all}, {
3796 "cpuset_move_cpuset_tasks", cpuset_move_cpuset_tasks}, {
3797 "cpuset_migrate", cpuset_migrate}, {
3798 "cpuset_migrate_all", cpuset_migrate_all}, {
3799 "cpuset_reattach", cpuset_reattach}, {
3800 "cpuset_open_memory_pressure", cpuset_open_memory_pressure}, {
3801 "cpuset_read_memory_pressure", cpuset_read_memory_pressure}, {
3802 "cpuset_close_memory_pressure", cpuset_close_memory_pressure}, {
3803 "cpuset_c_rel_to_sys_cpu", cpuset_c_rel_to_sys_cpu}, {
3804 "cpuset_c_sys_to_rel_cpu", cpuset_c_sys_to_rel_cpu}, {
3805 "cpuset_c_rel_to_sys_mem", cpuset_c_rel_to_sys_mem}, {
3806 "cpuset_c_sys_to_rel_mem", cpuset_c_sys_to_rel_mem}, {
3807 "cpuset_p_rel_to_sys_cpu", cpuset_p_rel_to_sys_cpu}, {
3808 "cpuset_p_sys_to_rel_cpu", cpuset_p_sys_to_rel_cpu}, {
3809 "cpuset_p_rel_to_sys_mem", cpuset_p_rel_to_sys_mem}, {
3810 "cpuset_p_sys_to_rel_mem", cpuset_p_sys_to_rel_mem}, {
3811 "cpuset_get_placement", cpuset_get_placement}, {
3812 "cpuset_equal_placement", cpuset_equal_placement}, {
3813 "cpuset_free_placement", cpuset_free_placement}, {
3814 "cpuset_fts_open", cpuset_fts_open}, {
3815 "cpuset_fts_read", cpuset_fts_read}, {
3816 "cpuset_fts_reverse", cpuset_fts_reverse}, {
3817 "cpuset_fts_rewind", cpuset_fts_rewind}, {
3818 "cpuset_fts_get_path", cpuset_fts_get_path}, {
3819 "cpuset_fts_get_stat", cpuset_fts_get_stat}, {
3820 "cpuset_fts_get_cpuset", cpuset_fts_get_cpuset}, {
3821 "cpuset_fts_get_errno", cpuset_fts_get_errno}, {
3822 "cpuset_fts_get_info", cpuset_fts_get_info}, {
3823 "cpuset_fts_close", cpuset_fts_close}, {
3824 "cpuset_cpubind", cpuset_cpubind}, {
3825 "cpuset_latestcpu", cpuset_latestcpu}, {
3826 "cpuset_membind", cpuset_membind}, {
3827 "cpuset_export", cpuset_export}, {
3828 "cpuset_import", cpuset_import}, {
3829 "cpuset_function", cpuset_function}, {
3830 "cpuset_pin", cpuset_pin}, {
3831 "cpuset_size", cpuset_size}, {
3832 "cpuset_where", cpuset_where}, {
3833 "cpuset_unpin", cpuset_unpin},};
3834
3835 /* Return pointer to a libcpuset.so function, or NULL */
cpuset_function(const char * function_name)3836 void *cpuset_function(const char *function_name)
3837 {
3838 unsigned int i;
3839
3840 for (i = 0; i < sizeof(flist) / sizeof(flist[0]); i++)
3841 if (streq(function_name, flist[i].fname))
3842 return flist[i].func;
3843 return NULL;
3844 }
3845
3846 /* Fortran interface to basic cpuset routines */
cpuset_pin_(int * ptr_relcpu)3847 int cpuset_pin_(int *ptr_relcpu)
3848 {
3849 return cpuset_pin(*ptr_relcpu);
3850 }
3851
cpuset_size_(void)3852 int cpuset_size_(void)
3853 {
3854 return cpuset_size();
3855 }
3856
cpuset_where_(void)3857 int cpuset_where_(void)
3858 {
3859 return cpuset_where();
3860 }
3861
cpuset_unpin_(void)3862 int cpuset_unpin_(void)
3863 {
3864 return cpuset_unpin();
3865 }
3866
3867 #endif /* HAVE_LINUX_MEMPOLICY_H */
3868