]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/cpuset.c
Merge branch 'meson-build-dmesg' of https://github.com/jwillikers/util-linux
[thirdparty/util-linux.git] / lib / cpuset.c
CommitLineData
ff5a6d20 1/*
79feaa60
KZ
2 * SPDX-License-Identifier: LGPL-2.1-or-later
3 *
ff5a6d20
KZ
4 * Terminology:
5 *
6 * cpuset - (libc) cpu_set_t data structure represents set of CPUs
7 * cpumask - string with hex mask (e.g. "0x00000001")
8 * cpulist - string with CPU ranges (e.g. "0-3,5,7,8")
9 *
10 * Based on code from taskset.c and Linux kernel.
11 *
0f23ee0c
KZ
12 * This file may be redistributed under the terms of the
13 * GNU Lesser General Public License.
14 *
ff5a6d20
KZ
15 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
16 */
17
efcb71f8
KZ
18#include <stdio.h>
19#include <stdlib.h>
20#include <unistd.h>
efcb71f8
KZ
21#include <sched.h>
22#include <errno.h>
23#include <string.h>
24#include <ctype.h>
319563bf 25#ifdef HAVE_SYS_SYSCALL_H
bae91ecf 26#include <sys/syscall.h>
319563bf 27#endif
efcb71f8
KZ
28
29#include "cpuset.h"
eb76ca98 30#include "c.h"
efcb71f8
KZ
31
32static inline int val_to_char(int v)
33{
34 if (v >= 0 && v < 10)
35 return '0' + v;
35e82b29 36 if (v >= 10 && v < 16)
efcb71f8 37 return ('a' - 10) + v;
35e82b29 38 return -1;
efcb71f8
KZ
39}
40
efcb71f8
KZ
41static inline int char_to_val(int c)
42{
43 int cl;
44
efcb71f8
KZ
45 if (c >= '0' && c <= '9')
46 return c - '0';
35e82b29
SK
47 cl = tolower(c);
48 if (cl >= 'a' && cl <= 'f')
efcb71f8 49 return cl + (10 - 'a');
35e82b29 50 return -1;
efcb71f8
KZ
51}
52
53static const char *nexttoken(const char *q, int sep)
54{
55 if (q)
56 q = strchr(q, sep);
57 if (q)
58 q++;
59 return q;
60}
61
bae91ecf
KZ
62/*
63 * Number of bits in a CPU bitmask on current system
64 */
65int get_max_number_of_cpus(void)
66{
4797b1e5 67#ifdef SYS_sched_getaffinity
bae91ecf
KZ
68 int n, cpus = 2048;
69 size_t setsize;
70 cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL);
71
72 if (!set)
73 return -1; /* error */
74
75 for (;;) {
76 CPU_ZERO_S(setsize, set);
77
78 /* the library version does not return size of cpumask_t */
79 n = syscall(SYS_sched_getaffinity, 0, setsize, set);
80
81 if (n < 0 && errno == EINVAL && cpus < 1024 * 1024) {
82 cpuset_free(set);
83 cpus *= 2;
84 set = cpuset_alloc(cpus, &setsize, NULL);
85 if (!set)
86 return -1; /* error */
87 continue;
88 }
89 cpuset_free(set);
90 return n * 8;
91 }
4797b1e5 92#endif
bae91ecf
KZ
93 return -1;
94}
95
ff5a6d20
KZ
96/*
97 * Allocates a new set for ncpus and returns size in bytes and size in bits
98 */
99cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits)
100{
101 cpu_set_t *set = CPU_ALLOC(ncpus);
102
103 if (!set)
104 return NULL;
105 if (setsize)
106 *setsize = CPU_ALLOC_SIZE(ncpus);
107 if (nbits)
108 *nbits = cpuset_nbits(CPU_ALLOC_SIZE(ncpus));
109 return set;
110}
111
112void cpuset_free(cpu_set_t *set)
113{
114 CPU_FREE(set);
115}
116
ee32c514
KZ
117#if !HAVE_DECL_CPU_ALLOC
118/* Please, use CPU_COUNT_S() macro. This is fallback */
119int __cpuset_count_s(size_t setsize, const cpu_set_t *set)
120{
121 int s = 0;
122 const __cpu_mask *p = set->__bits;
123 const __cpu_mask *end = &set->__bits[setsize / sizeof (__cpu_mask)];
124
125 while (p < end) {
126 __cpu_mask l = *p++;
127
128 if (l == 0)
129 continue;
130# if LONG_BIT > 32
131 l = (l & 0x5555555555555555ul) + ((l >> 1) & 0x5555555555555555ul);
132 l = (l & 0x3333333333333333ul) + ((l >> 2) & 0x3333333333333333ul);
133 l = (l & 0x0f0f0f0f0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0f0f0f0f0ful);
134 l = (l & 0x00ff00ff00ff00fful) + ((l >> 8) & 0x00ff00ff00ff00fful);
135 l = (l & 0x0000ffff0000fffful) + ((l >> 16) & 0x0000ffff0000fffful);
136 l = (l & 0x00000000fffffffful) + ((l >> 32) & 0x00000000fffffffful);
137# else
138 l = (l & 0x55555555ul) + ((l >> 1) & 0x55555555ul);
139 l = (l & 0x33333333ul) + ((l >> 2) & 0x33333333ul);
140 l = (l & 0x0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0ful);
141 l = (l & 0x00ff00fful) + ((l >> 8) & 0x00ff00fful);
142 l = (l & 0x0000fffful) + ((l >> 16) & 0x0000fffful);
143# endif
144 s += l;
145 }
146 return s;
147}
148#endif
149
ff5a6d20
KZ
150/*
151 * Returns human readable representation of the cpuset. The output format is
152 * a list of CPUs with ranges (for example, "0,1,3-9").
153 */
154char *cpulist_create(char *str, size_t len,
155 cpu_set_t *set, size_t setsize)
efcb71f8 156{
8a929ed7 157 size_t i;
efcb71f8
KZ
158 char *ptr = str;
159 int entry_made = 0;
ff5a6d20 160 size_t max = cpuset_nbits(setsize);
efcb71f8 161
ff5a6d20
KZ
162 for (i = 0; i < max; i++) {
163 if (CPU_ISSET_S(i, setsize, set)) {
8a929ed7
KZ
164 int rlen;
165 size_t j, run = 0;
efcb71f8 166 entry_made = 1;
ff5a6d20
KZ
167 for (j = i + 1; j < max; j++) {
168 if (CPU_ISSET_S(j, setsize, set))
efcb71f8
KZ
169 run++;
170 else
171 break;
172 }
173 if (!run)
e3ca1312 174 rlen = snprintf(ptr, len, "%zu,", i);
efcb71f8 175 else if (run == 1) {
e3ca1312 176 rlen = snprintf(ptr, len, "%zu,%zu,", i, i + 1);
efcb71f8
KZ
177 i++;
178 } else {
e3ca1312 179 rlen = snprintf(ptr, len, "%zu-%zu,", i, i + run);
efcb71f8
KZ
180 i += run;
181 }
06fa5817 182 if (rlen < 0 || (size_t) rlen >= len)
ff5a6d20
KZ
183 return NULL;
184 ptr += rlen;
06fa5817 185 len -= rlen;
efcb71f8
KZ
186 }
187 }
188 ptr -= entry_made;
ff5a6d20 189 *ptr = '\0';
efcb71f8
KZ
190
191 return str;
192}
193
ff5a6d20
KZ
194/*
195 * Returns string with CPU mask.
196 */
197char *cpumask_create(char *str, size_t len,
198 cpu_set_t *set, size_t setsize)
efcb71f8 199{
efcb71f8 200 char *ptr = str;
ff5a6d20
KZ
201 char *ret = NULL;
202 int cpu;
efcb71f8 203
ff5a6d20 204 for (cpu = cpuset_nbits(setsize) - 4; cpu >= 0; cpu -= 4) {
efcb71f8 205 char val = 0;
ff5a6d20 206
8a929ed7 207 if (len == (size_t) (ptr - str))
ff5a6d20
KZ
208 break;
209
210 if (CPU_ISSET_S(cpu, setsize, set))
efcb71f8 211 val |= 1;
ff5a6d20 212 if (CPU_ISSET_S(cpu + 1, setsize, set))
efcb71f8 213 val |= 2;
ff5a6d20 214 if (CPU_ISSET_S(cpu + 2, setsize, set))
efcb71f8 215 val |= 4;
ff5a6d20 216 if (CPU_ISSET_S(cpu + 3, setsize, set))
efcb71f8 217 val |= 8;
ff5a6d20 218
efcb71f8
KZ
219 if (!ret && val)
220 ret = ptr;
221 *ptr++ = val_to_char(val);
222 }
ff5a6d20 223 *ptr = '\0';
efcb71f8
KZ
224 return ret ? ret : ptr - 1;
225}
226
ff5a6d20 227/*
68845fed 228 * Parses string with CPUs mask.
ff5a6d20
KZ
229 */
230int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize)
efcb71f8
KZ
231{
232 int len = strlen(str);
233 const char *ptr = str + len - 1;
ff5a6d20 234 int cpu = 0;
efcb71f8
KZ
235
236 /* skip 0x, it's all hex anyway */
237 if (len > 1 && !memcmp(str, "0x", 2L))
238 str += 2;
239
ff5a6d20
KZ
240 CPU_ZERO_S(setsize, set);
241
efcb71f8 242 while (ptr >= str) {
bae91ecf
KZ
243 char val;
244
245 /* cpu masks in /sys uses comma as a separator */
246 if (*ptr == ',')
247 ptr--;
248
249 val = char_to_val(*ptr);
efcb71f8
KZ
250 if (val == (char) -1)
251 return -1;
252 if (val & 1)
ff5a6d20 253 CPU_SET_S(cpu, setsize, set);
efcb71f8 254 if (val & 2)
ff5a6d20 255 CPU_SET_S(cpu + 1, setsize, set);
efcb71f8 256 if (val & 4)
ff5a6d20 257 CPU_SET_S(cpu + 2, setsize, set);
efcb71f8 258 if (val & 8)
ff5a6d20 259 CPU_SET_S(cpu + 3, setsize, set);
efcb71f8 260 ptr--;
ff5a6d20 261 cpu += 4;
efcb71f8
KZ
262 }
263
264 return 0;
265}
266
65bcbf10
KZ
267static int nextnumber(const char *str, char **end, unsigned int *result)
268{
269 errno = 0;
270 if (str == NULL || *str == '\0' || !isdigit(*str))
271 return -EINVAL;
272 *result = (unsigned int) strtoul(str, end, 10);
273 if (errno)
274 return -errno;
275 if (str == *end)
276 return -EINVAL;
277 return 0;
278}
279
ff5a6d20 280/*
68845fed 281 * Parses string with list of CPU ranges.
59fb133a
HC
282 * Returns 0 on success.
283 * Returns 1 on error.
284 * Returns 2 if fail is set and a cpu number passed in the list doesn't fit
285 * into the cpu_set. If fail is not set cpu numbers that do not fit are
286 * ignored and 0 is returned instead.
ff5a6d20 287 */
b16f615a 288int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
efcb71f8 289{
2ef4a8d5 290 const size_t max = cpuset_nbits(setsize);
efcb71f8 291 const char *p, *q;
65bcbf10 292 char *end = NULL;
ff5a6d20 293
f27ce071 294 q = str;
ff5a6d20 295 CPU_ZERO_S(setsize, set);
efcb71f8
KZ
296
297 while (p = q, q = nexttoken(q, ','), p) {
298 unsigned int a; /* beginning of range */
299 unsigned int b; /* end of range */
300 unsigned int s; /* stride */
301 const char *c1, *c2;
302
65bcbf10 303 if (nextnumber(p, &end, &a) != 0)
efcb71f8
KZ
304 return 1;
305 b = a;
306 s = 1;
65bcbf10 307 p = end;
efcb71f8
KZ
308
309 c1 = nexttoken(p, '-');
310 c2 = nexttoken(p, ',');
65bcbf10 311
efcb71f8 312 if (c1 != NULL && (c2 == NULL || c1 < c2)) {
65bcbf10 313 if (nextnumber(c1, &end, &b) != 0)
efcb71f8 314 return 1;
65bcbf10
KZ
315
316 c1 = end && *end ? nexttoken(end, ':') : NULL;
317
289127f5 318 if (c1 != NULL && (c2 == NULL || c1 < c2)) {
65bcbf10 319 if (nextnumber(c1, &end, &s) != 0)
efcb71f8 320 return 1;
72232a26
HC
321 if (s == 0)
322 return 1;
efcb71f8
KZ
323 }
324 }
325
326 if (!(a <= b))
327 return 1;
328 while (a <= b) {
566b1d34
TW
329 if (a >= max) {
330 if (fail)
331 return 2;
332 else
333 break;
334 }
ff5a6d20 335 CPU_SET_S(a, setsize, set);
efcb71f8
KZ
336 a += s;
337 }
338 }
339
65bcbf10 340 if (end && *end)
f27ce071 341 return 1;
efcb71f8
KZ
342 return 0;
343}
125b6a91 344
e8f7acb0 345#ifdef TEST_PROGRAM_CPUSET
125b6a91 346
125b6a91
KZ
347#include <getopt.h>
348
349int main(int argc, char *argv[])
350{
ff5a6d20
KZ
351 cpu_set_t *set;
352 size_t setsize, buflen, nbits;
125b6a91
KZ
353 char *buf, *mask = NULL, *range = NULL;
354 int ncpus = 2048, rc, c;
355
6c7d5ae9 356 static const struct option longopts[] = {
71f08e97
SK
357 { "ncpus", 1, NULL, 'n' },
358 { "mask", 1, NULL, 'm' },
359 { "range", 1, NULL, 'r' },
360 { NULL, 0, NULL, 0 }
125b6a91
KZ
361 };
362
363 while ((c = getopt_long(argc, argv, "n:m:r:", longopts, NULL)) != -1) {
364 switch(c) {
365 case 'n':
366 ncpus = atoi(optarg);
367 break;
368 case 'm':
369 mask = strdup(optarg);
370 break;
371 case 'r':
372 range = strdup(optarg);
373 break;
374 default:
375 goto usage_err;
376 }
377 }
378
379 if (!mask && !range)
380 goto usage_err;
381
ff5a6d20 382 set = cpuset_alloc(ncpus, &setsize, &nbits);
125b6a91
KZ
383 if (!set)
384 err(EXIT_FAILURE, "failed to allocate cpu set");
385
ff5a6d20
KZ
386 /*
387 fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n",
388 ncpus, nbits, setsize);
389 */
390
391 buflen = 7 * nbits;
392 buf = malloc(buflen);
125b6a91
KZ
393 if (!buf)
394 err(EXIT_FAILURE, "failed to allocate cpu set buffer");
395
396 if (mask)
ff5a6d20 397 rc = cpumask_parse(mask, set, setsize);
125b6a91 398 else
b16f615a 399 rc = cpulist_parse(range, set, setsize, 0);
125b6a91
KZ
400
401 if (rc)
402 errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
403
ff5a6d20
KZ
404 printf("%-15s = %15s ", mask ? : range,
405 cpumask_create(buf, buflen, set, setsize));
406 printf("[%s]\n", cpulist_create(buf, buflen, set, setsize));
125b6a91
KZ
407
408 free(buf);
41cd1503 409 free(mask);
9da2972c 410 free(range);
ff5a6d20 411 cpuset_free(set);
125b6a91
KZ
412
413 return EXIT_SUCCESS;
414
415usage_err:
416 fprintf(stderr,
65bcbf10 417 "usage: %s [--ncpus <num>] --mask <mask> | --range <list>\n",
125b6a91
KZ
418 program_invocation_short_name);
419 exit(EXIT_FAILURE);
420}
421#endif