]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/cpu-set-util.c
ci: enable arm64 runner for build/unit jobs
[thirdparty/systemd.git] / src / shared / cpu-set-util.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <stdio.h>
4#include <syslog.h>
5#include <unistd.h>
6
7#include "alloc-util.h"
8#include "bitfield.h"
9#include "cpu-set-util.h"
10#include "extract-word.h"
11#include "hexdecoct.h"
12#include "log.h"
13#include "parse-util.h"
14#include "string-util.h"
15
16/* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */
17#define CPU_SET_MAX_NCPU 8192
18
19char* cpu_set_to_string(const CPUSet *c) {
20 _cleanup_free_ char *str = NULL;
21
22 assert(c);
23
24 for (size_t i = 0; i < c->allocated * 8; i++) {
25 if (!CPU_ISSET_S(i, c->allocated, c->set))
26 continue;
27
28 if (strextendf_with_separator(&str, " ", "%zu", i) < 0)
29 return NULL;
30 }
31
32 return TAKE_PTR(str) ?: strdup("");
33}
34
35static int add_range(char **str, size_t start, size_t end) {
36 assert(str);
37 assert(start <= end);
38
39 if (start == end)
40 return strextendf_with_separator(str, " ", "%zu", start);
41
42 return strextendf_with_separator(str, " ", "%zu-%zu", start, end);
43}
44
45char* cpu_set_to_range_string(const CPUSet *c) {
46 _cleanup_free_ char *str = NULL;
47 size_t start = 0, end;
48 bool in_range = false;
49
50 assert(c);
51
52 for (size_t i = 0; i < c->allocated * 8; i++) {
53 if (CPU_ISSET_S(i, c->allocated, c->set)) {
54 if (in_range)
55 end++;
56 else {
57 start = end = i;
58 in_range = true;
59 }
60 continue;
61 }
62
63 if (in_range && add_range(&str, start, end) < 0)
64 return NULL;
65
66 in_range = false;
67 }
68
69 if (in_range && add_range(&str, start, end) < 0)
70 return NULL;
71
72 return TAKE_PTR(str) ?: strdup("");
73}
74
75char* cpu_set_to_mask_string(const CPUSet *c) {
76 _cleanup_free_ char *str = NULL;
77 bool found_nonzero = false;
78 int r;
79
80 assert(c);
81
82 /* Return CPU set in hexadecimal bitmap mask, e.g.
83 * CPU 0 -> "1"
84 * CPU 1 -> "2"
85 * CPU 0,1 -> "3"
86 * CPU 0-3 -> "f"
87 * CPU 0-7 -> "ff"
88 * CPU 4-7 -> "f0"
89 * CPU 7 -> "80"
90 * None -> "0"
91 *
92 * When there are more than 32 CPUs, separate every 32 CPUs by comma, e.g.
93 * CPU 0-47 -> "ffff,ffffffff"
94 * CPU 0-63 -> "ffffffff,ffffffff"
95 * CPU 0-71 -> "ff,ffffffff,ffffffff" */
96
97 for (size_t i = c->allocated * 8; i > 0; ) {
98 uint32_t m = 0;
99
100 for (int j = (i % 32 ?: 32) - 1; j >= 0; j--)
101 if (CPU_ISSET_S(--i, c->allocated, c->set))
102 SET_BIT(m, j);
103
104 if (!found_nonzero) {
105 if (m == 0)
106 continue;
107
108 r = strextendf_with_separator(&str, ",", "%" PRIx32, m);
109 } else
110 r = strextendf_with_separator(&str, ",", "%08" PRIx32, m);
111 if (r < 0)
112 return NULL;
113
114 found_nonzero = true;
115 }
116
117 return TAKE_PTR(str) ?: strdup("0");
118}
119
120void cpu_set_done(CPUSet *c) {
121 assert(c);
122
123 if (c->set)
124 CPU_FREE(c->set);
125
126 *c = (CPUSet) {};
127}
128
129int cpu_set_realloc(CPUSet *c, size_t n) {
130 assert(c);
131
132 if (n > CPU_SET_MAX_NCPU)
133 return -ERANGE;
134
135 n = CPU_ALLOC_SIZE(n);
136 if (n <= c->allocated)
137 return 0;
138
139 if (!GREEDY_REALLOC0(c->set, DIV_ROUND_UP(n, sizeof(cpu_set_t))))
140 return -ENOMEM;
141
142 c->allocated = n;
143 return 0;
144}
145
146int cpu_set_add(CPUSet *c, size_t i) {
147 int r;
148
149 assert(c);
150
151 /* cpu_set_realloc() has similar check, but for avoiding overflow. */
152 if (i >= CPU_SET_MAX_NCPU)
153 return -ERANGE;
154
155 r = cpu_set_realloc(c, i + 1);
156 if (r < 0)
157 return r;
158
159 CPU_SET_S(i, c->allocated, c->set);
160 return 0;
161}
162
163int cpu_set_add_set(CPUSet *c, const CPUSet *src) {
164 int r;
165
166 assert(c);
167 assert(src);
168
169 r = cpu_set_realloc(c, src->allocated * 8);
170 if (r < 0)
171 return r;
172
173 for (size_t i = 0; i < src->allocated * 8; i++)
174 if (CPU_ISSET_S(i, src->allocated, src->set))
175 CPU_SET_S(i, c->allocated, c->set);
176
177 return 1;
178}
179
180static int cpu_set_add_range(CPUSet *c, size_t start, size_t end) {
181 int r;
182
183 assert(c);
184 assert(start <= end);
185
186 /* cpu_set_realloc() has similar check, but for avoiding overflow. */
187 if (end >= CPU_SET_MAX_NCPU)
188 return -ERANGE;
189
190 r = cpu_set_realloc(c, end + 1);
191 if (r < 0)
192 return r;
193
194 for (size_t i = start; i <= end; i++)
195 CPU_SET_S(i, c->allocated, c->set);
196
197 return 0;
198}
199
200int cpu_set_add_all(CPUSet *c) {
201 assert(c);
202
203 long m = sysconf(_SC_NPROCESSORS_ONLN);
204 if (m < 0)
205 return -errno;
206 if (m == 0)
207 return -ENXIO;
208
209 return cpu_set_add_range(c, 0, m - 1);
210}
211
212int config_parse_cpu_set(
213 const char *unit,
214 const char *filename,
215 unsigned line,
216 const char *section,
217 unsigned section_line,
218 const char *lvalue,
219 int ltype, /* 0 when used as conf parser, 1 when used as usual parser */
220 const char *rvalue,
221 void *data,
222 void *userdata) {
223
224 CPUSet *c = ASSERT_PTR(data);
225 int r, level = ltype ? LOG_DEBUG : LOG_DEBUG;
226 bool critical = ltype;
227
228 assert(critical || lvalue);
229
230 if (isempty(rvalue)) {
231 cpu_set_done(c);
232 return 1;
233 }
234
235 _cleanup_(cpu_set_done) CPUSet cpuset = {};
236 for (const char *p = rvalue;;) {
237 _cleanup_free_ char *word = NULL;
238
239 r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_UNQUOTE);
240 if (r == -ENOMEM)
241 return log_oom_full(level);
242 if (r < 0) {
243 if (critical)
244 return log_debug_errno(r, "Failed to parse CPU set: %s", rvalue);
245
246 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= setting, ignoring assignment: %s",
247 lvalue, rvalue);
248 return 0;
249 }
250 if (r == 0)
251 break;
252
253 unsigned lower, upper;
254 r = parse_range(word, &lower, &upper);
255 if (r < 0) {
256 if (critical)
257 return log_debug_errno(r, "Failed to parse CPU range: %s", word);
258
259 log_syntax(unit, LOG_WARNING, filename, line, r,
260 "Failed to parse CPU range, ignoring assignment: %s", word);
261 continue;
262 }
263
264 if (lower > upper) {
265 if (critical)
266 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
267 "Invalid CPU range (%u > %u): %s",
268 lower, upper, word);
269
270 log_syntax(unit, LOG_WARNING, filename, line, r,
271 "Invalid CPU range (%u > %u), ignoring assignment: %s",
272 lower, upper, word);
273 continue;
274 }
275
276 r = cpu_set_add_range(&cpuset, lower, upper);
277 if (r == -ENOMEM)
278 return log_oom_full(level);
279 if (r < 0) {
280 if (critical)
281 return log_debug_errno(r, "Failed to set CPU(s) '%s': %m", word);
282
283 log_syntax(unit, LOG_WARNING, filename, line, r,
284 "Failed to set CPU(s), ignoring assignment: %s", word);
285 }
286 }
287
288 if (!c->set) {
289 *c = TAKE_STRUCT(cpuset);
290 return 1;
291 }
292
293 r = cpu_set_add_set(c, &cpuset);
294 if (r == -ENOMEM)
295 return log_oom_full(level);
296 assert(r >= 0);
297
298 return 1;
299}
300
301int parse_cpu_set(const char *s, CPUSet *ret) {
302 _cleanup_(cpu_set_done) CPUSet c = {};
303 int r;
304
305 assert(s);
306 assert(ret);
307
308 r = config_parse_cpu_set(
309 /* unit = */ NULL,
310 /* filename = */ NULL,
311 /* line = */ 0,
312 /* section = */ NULL,
313 /* section_line = */ 0,
314 /* lvalue = */ NULL,
315 /* ltype = */ 1,
316 /* rvalue = */ s,
317 /* data = */ &c,
318 /* userdata = */ NULL);
319 if (r < 0)
320 return r;
321
322 *ret = TAKE_STRUCT(c);
323 return 0;
324}
325
326int cpus_in_affinity_mask(void) {
327 size_t n = 16;
328 int r;
329
330 for (;;) {
331 cpu_set_t *c;
332
333 c = CPU_ALLOC(n);
334 if (!c)
335 return -ENOMEM;
336
337 if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
338 int k;
339
340 k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
341 CPU_FREE(c);
342
343 if (k <= 0)
344 return -EINVAL;
345
346 return k;
347 }
348
349 r = -errno;
350 CPU_FREE(c);
351
352 if (r != -EINVAL)
353 return r;
354 if (n > SIZE_MAX/2)
355 return -ENOMEM;
356 n *= 2;
357 }
358}
359
360int cpu_set_to_dbus(const CPUSet *c, uint8_t **ret, size_t *ret_size) {
361 assert(c);
362 assert(ret);
363 assert(ret_size);
364
365 uint8_t *buf = new0(uint8_t, c->allocated);
366 if (!buf)
367 return -ENOMEM;
368
369 for (size_t i = 0; i < c->allocated * 8; i++)
370 if (CPU_ISSET_S(i, c->allocated, c->set))
371 SET_BIT(buf[i / 8], i % 8);
372
373 *ret = buf;
374 *ret_size = c->allocated;
375 return 0;
376}
377
378int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *ret) {
379 _cleanup_(cpu_set_done) CPUSet c = {};
380 int r;
381
382 assert(bits || size == 0);
383 assert(ret);
384
385 r = cpu_set_realloc(&c, size * 8);
386 if (r < 0)
387 return r;
388
389 for (size_t i = 0; i < size * 8; i++)
390 if (BIT_SET(bits[i / 8], i % 8))
391 CPU_SET_S(i, c.allocated, c.set);
392
393 *ret = TAKE_STRUCT(c);
394 return 0;
395}