2 * SPDX-License-Identifier: LGPL-2.1-or-later
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")
10 * Based on code from taskset.c and Linux kernel.
12 * This file may be redistributed under the terms of the
13 * GNU Lesser General Public License.
15 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
25 #ifdef HAVE_SYS_SYSCALL_H
26 #include <sys/syscall.h>
32 static inline int val_to_char(int v
)
36 if (v
>= 10 && v
< 16)
37 return ('a' - 10) + v
;
41 static inline int char_to_val(int c
)
45 if (c
>= '0' && c
<= '9')
48 if (cl
>= 'a' && cl
<= 'f')
49 return cl
+ (10 - 'a');
53 static const char *nexttoken(const char *q
, int sep
)
63 * Number of bits in a CPU bitmask on current system
65 int get_max_number_of_cpus(void)
67 #ifdef SYS_sched_getaffinity
70 cpu_set_t
*set
= cpuset_alloc(cpus
, &setsize
, NULL
);
73 return -1; /* error */
76 CPU_ZERO_S(setsize
, set
);
78 /* the library version does not return size of cpumask_t */
79 n
= syscall(SYS_sched_getaffinity
, 0, setsize
, set
);
81 if (n
< 0 && errno
== EINVAL
&& cpus
< 1024 * 1024) {
84 set
= cpuset_alloc(cpus
, &setsize
, NULL
);
86 return -1; /* error */
97 * Allocates a new set for ncpus and returns size in bytes and size in bits
99 cpu_set_t
*cpuset_alloc(int ncpus
, size_t *setsize
, size_t *nbits
)
101 cpu_set_t
*set
= CPU_ALLOC(ncpus
);
106 *setsize
= CPU_ALLOC_SIZE(ncpus
);
108 *nbits
= cpuset_nbits(CPU_ALLOC_SIZE(ncpus
));
112 void cpuset_free(cpu_set_t
*set
)
117 #if !HAVE_DECL_CPU_ALLOC
118 /* Please, use CPU_COUNT_S() macro. This is fallback */
119 int __cpuset_count_s(size_t setsize
, const cpu_set_t
*set
)
122 const __cpu_mask
*p
= set
->__bits
;
123 const __cpu_mask
*end
= &set
->__bits
[setsize
/ sizeof (__cpu_mask
)];
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
);
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
);
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").
154 char *cpulist_create(char *str
, size_t len
,
155 cpu_set_t
*set
, size_t setsize
)
160 size_t max
= cpuset_nbits(setsize
);
162 for (i
= 0; i
< max
; i
++) {
163 if (CPU_ISSET_S(i
, setsize
, set
)) {
167 for (j
= i
+ 1; j
< max
; j
++) {
168 if (CPU_ISSET_S(j
, setsize
, set
))
174 rlen
= snprintf(ptr
, len
, "%zu,", i
);
176 rlen
= snprintf(ptr
, len
, "%zu,%zu,", i
, i
+ 1);
179 rlen
= snprintf(ptr
, len
, "%zu-%zu,", i
, i
+ run
);
182 if (rlen
< 0 || (size_t) rlen
>= len
)
195 * Returns string with CPU mask.
197 char *cpumask_create(char *str
, size_t len
,
198 cpu_set_t
*set
, size_t setsize
)
204 for (cpu
= cpuset_nbits(setsize
) - 4; cpu
>= 0; cpu
-= 4) {
207 if (len
== (size_t) (ptr
- str
))
210 if (CPU_ISSET_S(cpu
, setsize
, set
))
212 if (CPU_ISSET_S(cpu
+ 1, setsize
, set
))
214 if (CPU_ISSET_S(cpu
+ 2, setsize
, set
))
216 if (CPU_ISSET_S(cpu
+ 3, setsize
, set
))
221 *ptr
++ = val_to_char(val
);
224 return ret
? ret
: ptr
- 1;
228 * Parses string with CPUs mask.
230 int cpumask_parse(const char *str
, cpu_set_t
*set
, size_t setsize
)
232 int len
= strlen(str
);
233 const char *ptr
= str
+ len
- 1;
236 /* skip 0x, it's all hex anyway */
237 if (len
> 1 && !memcmp(str
, "0x", 2L))
240 CPU_ZERO_S(setsize
, set
);
245 /* cpu masks in /sys uses comma as a separator */
249 val
= char_to_val(*ptr
);
250 if (val
== (char) -1)
253 CPU_SET_S(cpu
, setsize
, set
);
255 CPU_SET_S(cpu
+ 1, setsize
, set
);
257 CPU_SET_S(cpu
+ 2, setsize
, set
);
259 CPU_SET_S(cpu
+ 3, setsize
, set
);
267 static int nextnumber(const char *str
, char **end
, unsigned int *result
)
270 if (str
== NULL
|| *str
== '\0' || !isdigit(*str
))
272 *result
= (unsigned int) strtoul(str
, end
, 10);
281 * Parses string with list of CPU ranges.
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.
288 int cpulist_parse(const char *str
, cpu_set_t
*set
, size_t setsize
, int fail
)
290 size_t max
= cpuset_nbits(setsize
);
295 CPU_ZERO_S(setsize
, set
);
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 */
303 if (nextnumber(p
, &end
, &a
) != 0)
309 c1
= nexttoken(p
, '-');
310 c2
= nexttoken(p
, ',');
312 if (c1
!= NULL
&& (c2
== NULL
|| c1
< c2
)) {
313 if (nextnumber(c1
, &end
, &b
) != 0)
316 c1
= end
&& *end
? nexttoken(end
, ':') : NULL
;
318 if (c1
!= NULL
&& (c2
== NULL
|| c1
< c2
)) {
319 if (nextnumber(c1
, &end
, &s
) != 0)
329 if (fail
&& (a
>= max
))
331 CPU_SET_S(a
, setsize
, set
);
341 #ifdef TEST_PROGRAM_CPUSET
345 int main(int argc
, char *argv
[])
348 size_t setsize
, buflen
, nbits
;
349 char *buf
, *mask
= NULL
, *range
= NULL
;
350 int ncpus
= 2048, rc
, c
;
352 static const struct option longopts
[] = {
353 { "ncpus", 1, NULL
, 'n' },
354 { "mask", 1, NULL
, 'm' },
355 { "range", 1, NULL
, 'r' },
359 while ((c
= getopt_long(argc
, argv
, "n:m:r:", longopts
, NULL
)) != -1) {
362 ncpus
= atoi(optarg
);
365 mask
= strdup(optarg
);
368 range
= strdup(optarg
);
378 set
= cpuset_alloc(ncpus
, &setsize
, &nbits
);
380 err(EXIT_FAILURE
, "failed to allocate cpu set");
383 fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n",
384 ncpus, nbits, setsize);
388 buf
= malloc(buflen
);
390 err(EXIT_FAILURE
, "failed to allocate cpu set buffer");
393 rc
= cpumask_parse(mask
, set
, setsize
);
395 rc
= cpulist_parse(range
, set
, setsize
, 0);
398 errx(EXIT_FAILURE
, "failed to parse string: %s", mask
? : range
);
400 printf("%-15s = %15s ", mask
? : range
,
401 cpumask_create(buf
, buflen
, set
, setsize
));
402 printf("[%s]\n", cpulist_create(buf
, buflen
, set
, setsize
));
413 "usage: %s [--ncpus <num>] --mask <mask> | --range <list>\n",
414 program_invocation_short_name
);