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