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