]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
618234a5 | 2 | |
a832893f | 3 | #include <stdio.h> |
11c3a366 | 4 | #include <syslog.h> |
69a283c5 | 5 | #include <unistd.h> |
11c3a366 | 6 | |
b5efdb8a | 7 | #include "alloc-util.h" |
df04987a | 8 | #include "bitfield.h" |
6bedfcbb | 9 | #include "cpu-set-util.h" |
84ac7bea | 10 | #include "extract-word.h" |
0f30bf58 | 11 | #include "hexdecoct.h" |
11c3a366 | 12 | #include "log.h" |
93cc7779 | 13 | #include "parse-util.h" |
b11d6a7b | 14 | #include "string-util.h" |
618234a5 | 15 | |
8de95eaf YW |
16 | /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */ |
17 | #define CPU_SET_MAX_NCPU 8192 | |
18 | ||
2d461342 | 19 | char* cpu_set_to_string(const CPUSet *c) { |
a832893f | 20 | _cleanup_free_ char *str = NULL; |
a832893f | 21 | |
2d461342 YW |
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)) | |
a832893f ZJS |
26 | continue; |
27 | ||
2d461342 | 28 | if (strextendf_with_separator(&str, " ", "%zu", i) < 0) |
a832893f | 29 | return NULL; |
a832893f ZJS |
30 | } |
31 | ||
32 | return TAKE_PTR(str) ?: strdup(""); | |
33 | } | |
34 | ||
2d461342 YW |
35 | static 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 | ||
45 | char* cpu_set_to_range_string(const CPUSet *c) { | |
71b28519 | 46 | _cleanup_free_ char *str = NULL; |
2d461342 | 47 | size_t start = 0, end; |
71b28519 | 48 | bool in_range = false; |
71b28519 | 49 | |
2d461342 YW |
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)) { | |
71b28519 | 54 | if (in_range) |
2d461342 | 55 | end++; |
71b28519 | 56 | else { |
2d461342 | 57 | start = end = i; |
71b28519 MS |
58 | in_range = true; |
59 | } | |
2d461342 | 60 | continue; |
71b28519 MS |
61 | } |
62 | ||
2d461342 | 63 | if (in_range && add_range(&str, start, end) < 0) |
71b28519 MS |
64 | return NULL; |
65 | ||
2d461342 | 66 | in_range = false; |
71b28519 MS |
67 | } |
68 | ||
2d461342 YW |
69 | if (in_range && add_range(&str, start, end) < 0) |
70 | return NULL; | |
71 | ||
71b28519 MS |
72 | return TAKE_PTR(str) ?: strdup(""); |
73 | } | |
74 | ||
df04987a | 75 | char* cpu_set_to_mask_string(const CPUSet *c) { |
0f30bf58 | 76 | _cleanup_free_ char *str = NULL; |
0f30bf58 | 77 | bool found_nonzero = false; |
df04987a | 78 | int r; |
0f30bf58 | 79 | |
df04987a | 80 | assert(c); |
0f30bf58 RRZ |
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 | ||
df04987a YW |
97 | for (size_t i = c->allocated * 8; i > 0; ) { |
98 | uint32_t m = 0; | |
0f30bf58 | 99 | |
df04987a YW |
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); | |
0f30bf58 | 103 | |
df04987a YW |
104 | if (!found_nonzero) { |
105 | if (m == 0) | |
106 | continue; | |
0f30bf58 | 107 | |
df04987a YW |
108 | r = strextendf_with_separator(&str, ",", "%" PRIx32, m); |
109 | } else | |
110 | r = strextendf_with_separator(&str, ",", "%08" PRIx32, m); | |
111 | if (r < 0) | |
0f30bf58 RRZ |
112 | return NULL; |
113 | ||
df04987a | 114 | found_nonzero = true; |
0f30bf58 RRZ |
115 | } |
116 | ||
117 | return TAKE_PTR(str) ?: strdup("0"); | |
118 | } | |
119 | ||
296fe3d5 YW |
120 | void cpu_set_done(CPUSet *c) { |
121 | assert(c); | |
122 | ||
123 | if (c->set) | |
124 | CPU_FREE(c->set); | |
125 | ||
126 | *c = (CPUSet) {}; | |
127 | } | |
128 | ||
8de95eaf YW |
129 | int cpu_set_realloc(CPUSet *c, size_t n) { |
130 | assert(c); | |
0985c7c4 | 131 | |
8de95eaf YW |
132 | if (n > CPU_SET_MAX_NCPU) |
133 | return -ERANGE; | |
0985c7c4 | 134 | |
8de95eaf YW |
135 | n = CPU_ALLOC_SIZE(n); |
136 | if (n <= c->allocated) | |
137 | return 0; | |
0985c7c4 | 138 | |
8de95eaf YW |
139 | if (!GREEDY_REALLOC0(c->set, DIV_ROUND_UP(n, sizeof(cpu_set_t)))) |
140 | return -ENOMEM; | |
0985c7c4 | 141 | |
8de95eaf | 142 | c->allocated = n; |
0985c7c4 ZJS |
143 | return 0; |
144 | } | |
145 | ||
8de95eaf | 146 | int cpu_set_add(CPUSet *c, size_t i) { |
0985c7c4 ZJS |
147 | int r; |
148 | ||
8de95eaf YW |
149 | assert(c); |
150 | ||
151 | /* cpu_set_realloc() has similar check, but for avoiding overflow. */ | |
152 | if (i >= CPU_SET_MAX_NCPU) | |
0985c7c4 ZJS |
153 | return -ERANGE; |
154 | ||
8de95eaf | 155 | r = cpu_set_realloc(c, i + 1); |
0985c7c4 ZJS |
156 | if (r < 0) |
157 | return r; | |
158 | ||
8de95eaf | 159 | CPU_SET_S(i, c->allocated, c->set); |
0985c7c4 ZJS |
160 | return 0; |
161 | } | |
162 | ||
f4093f43 | 163 | int cpu_set_add_set(CPUSet *c, const CPUSet *src) { |
0985c7c4 ZJS |
164 | int r; |
165 | ||
8de95eaf YW |
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); | |
0985c7c4 | 176 | |
e2b2fb7f | 177 | return 1; |
0985c7c4 ZJS |
178 | } |
179 | ||
8de95eaf YW |
180 | static 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 | ||
f4093f43 | 200 | int cpu_set_add_all(CPUSet *c) { |
8de95eaf YW |
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 | ||
fe3ada07 | 212 | int config_parse_cpu_set( |
618234a5 LP |
213 | const char *unit, |
214 | const char *filename, | |
215 | unsigned line, | |
fe3ada07 YW |
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) { | |
618234a5 | 223 | |
fe3ada07 YW |
224 | CPUSet *c = ASSERT_PTR(data); |
225 | int r, level = ltype ? LOG_DEBUG : LOG_DEBUG; | |
226 | bool critical = ltype; | |
618234a5 | 227 | |
fe3ada07 | 228 | assert(critical || lvalue); |
cfc28ee2 | 229 | |
fe3ada07 YW |
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;;) { | |
618234a5 | 237 | _cleanup_free_ char *word = NULL; |
618234a5 | 238 | |
4ec85141 | 239 | r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_UNQUOTE); |
6d8a29b2 | 240 | if (r == -ENOMEM) |
fe3ada07 YW |
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 | } | |
618234a5 LP |
250 | if (r == 0) |
251 | break; | |
252 | ||
fe3ada07 YW |
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); | |
032cf8e4 | 258 | |
fe3ada07 YW |
259 | log_syntax(unit, LOG_WARNING, filename, line, r, |
260 | "Failed to parse CPU range, ignoring assignment: %s", word); | |
261 | continue; | |
262 | } | |
0985c7c4 | 263 | |
fe3ada07 YW |
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; | |
032cf8e4 YW |
274 | } |
275 | ||
fe3ada07 YW |
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); | |
0985c7c4 | 285 | } |
032cf8e4 YW |
286 | } |
287 | ||
fe3ada07 YW |
288 | if (!c->set) { |
289 | *c = TAKE_STRUCT(cpuset); | |
290 | return 1; | |
291 | } | |
0985c7c4 | 292 | |
fe3ada07 YW |
293 | r = cpu_set_add_set(c, &cpuset); |
294 | if (r == -ENOMEM) | |
295 | return log_oom_full(level); | |
296 | assert(r >= 0); | |
0985c7c4 | 297 | |
fe3ada07 YW |
298 | return 1; |
299 | } | |
0985c7c4 | 300 | |
fe3ada07 YW |
301 | int parse_cpu_set(const char *s, CPUSet *ret) { |
302 | _cleanup_(cpu_set_done) CPUSet c = {}; | |
0985c7c4 ZJS |
303 | int r; |
304 | ||
fe3ada07 YW |
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); | |
0985c7c4 ZJS |
319 | if (r < 0) |
320 | return r; | |
321 | ||
fe3ada07 YW |
322 | *ret = TAKE_STRUCT(c); |
323 | return 0; | |
032cf8e4 | 324 | } |
f44b3035 ZJS |
325 | |
326 | int 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 | } | |
c367f996 | 359 | |
c4afb3c3 YW |
360 | int cpu_set_to_dbus(const CPUSet *c, uint8_t **ret, size_t *ret_size) { |
361 | assert(c); | |
c367f996 | 362 | assert(ret); |
c4afb3c3 | 363 | assert(ret_size); |
c367f996 | 364 | |
c4afb3c3 YW |
365 | uint8_t *buf = new0(uint8_t, c->allocated); |
366 | if (!buf) | |
c367f996 MS |
367 | return -ENOMEM; |
368 | ||
c4afb3c3 YW |
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); | |
c367f996 | 372 | |
c4afb3c3 YW |
373 | *ret = buf; |
374 | *ret_size = c->allocated; | |
c367f996 MS |
375 | return 0; |
376 | } | |
377 | ||
c4afb3c3 | 378 | int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *ret) { |
296fe3d5 | 379 | _cleanup_(cpu_set_done) CPUSet c = {}; |
c367f996 MS |
380 | int r; |
381 | ||
c4afb3c3 YW |
382 | assert(bits || size == 0); |
383 | assert(ret); | |
c367f996 | 384 | |
c4afb3c3 YW |
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); | |
c367f996 | 392 | |
c4afb3c3 | 393 | *ret = TAKE_STRUCT(c); |
c367f996 MS |
394 | return 0; |
395 | } |