]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cpu-set-util.c
c5277f11af8e190961575992569812f3a992074c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
9 #include "cpu-set-util.h"
10 #include "extract-word.h"
11 #include "hexdecoct.h"
13 #include "parse-util.h"
14 #include "string-util.h"
16 /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */
17 #define CPU_SET_MAX_NCPU 8192
19 char* cpu_set_to_string(const CPUSet
*c
) {
20 _cleanup_free_
char *str
= NULL
;
24 for (size_t i
= 0; i
< c
->allocated
* 8; i
++) {
25 if (!CPU_ISSET_S(i
, c
->allocated
, c
->set
))
28 if (strextendf_with_separator(&str
, " ", "%zu", i
) < 0)
32 return TAKE_PTR(str
) ?: strdup("");
35 static int add_range(char **str
, size_t start
, size_t end
) {
40 return strextendf_with_separator(str
, " ", "%zu", start
);
42 return strextendf_with_separator(str
, " ", "%zu-%zu", start
, end
);
45 char* 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;
52 for (size_t i
= 0; i
< c
->allocated
* 8; i
++) {
53 if (CPU_ISSET_S(i
, c
->allocated
, c
->set
)) {
63 if (in_range
&& add_range(&str
, start
, end
) < 0)
69 if (in_range
&& add_range(&str
, start
, end
) < 0)
72 return TAKE_PTR(str
) ?: strdup("");
75 char* cpu_set_to_mask_string(const CPUSet
*c
) {
76 _cleanup_free_
char *str
= NULL
;
77 bool found_nonzero
= false;
82 /* Return CPU set in hexadecimal bitmap mask, e.g.
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" */
97 for (size_t i
= c
->allocated
* 8; i
> 0; ) {
100 for (int j
= (i
% 32 ?: 32) - 1; j
>= 0; j
--)
101 if (CPU_ISSET_S(--i
, c
->allocated
, c
->set
))
104 if (!found_nonzero
) {
108 r
= strextendf_with_separator(&str
, ",", "%" PRIx32
, m
);
110 r
= strextendf_with_separator(&str
, ",", "%08" PRIx32
, m
);
114 found_nonzero
= true;
117 return TAKE_PTR(str
) ?: strdup("0");
120 void cpu_set_done(CPUSet
*c
) {
129 int cpu_set_realloc(CPUSet
*c
, size_t n
) {
132 if (n
> CPU_SET_MAX_NCPU
)
135 n
= CPU_ALLOC_SIZE(n
);
136 if (n
<= c
->allocated
)
139 if (!GREEDY_REALLOC0(c
->set
, DIV_ROUND_UP(n
, sizeof(cpu_set_t
))))
146 int cpu_set_add(CPUSet
*c
, size_t i
) {
151 /* cpu_set_realloc() has similar check, but for avoiding overflow. */
152 if (i
>= CPU_SET_MAX_NCPU
)
155 r
= cpu_set_realloc(c
, i
+ 1);
159 CPU_SET_S(i
, c
->allocated
, c
->set
);
163 int cpu_set_add_set(CPUSet
*c
, const CPUSet
*src
) {
169 r
= cpu_set_realloc(c
, src
->allocated
* 8);
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
);
180 static int cpu_set_add_range(CPUSet
*c
, size_t start
, size_t end
) {
184 assert(start
<= end
);
186 /* cpu_set_realloc() has similar check, but for avoiding overflow. */
187 if (end
>= CPU_SET_MAX_NCPU
)
190 r
= cpu_set_realloc(c
, end
+ 1);
194 for (size_t i
= start
; i
<= end
; i
++)
195 CPU_SET_S(i
, c
->allocated
, c
->set
);
200 int cpu_set_add_all(CPUSet
*c
) {
203 long m
= sysconf(_SC_NPROCESSORS_ONLN
);
209 return cpu_set_add_range(c
, 0, m
- 1);
212 int config_parse_cpu_set(
214 const char *filename
,
217 unsigned section_line
,
219 int ltype
, /* 0 when used as conf parser, 1 when used as usual parser */
224 CPUSet
*c
= ASSERT_PTR(data
);
225 int r
, level
= ltype
? LOG_DEBUG
: LOG_DEBUG
;
226 bool critical
= ltype
;
228 assert(critical
|| lvalue
);
230 if (isempty(rvalue
)) {
235 _cleanup_(cpu_set_done
) CPUSet cpuset
= {};
236 for (const char *p
= rvalue
;;) {
237 _cleanup_free_
char *word
= NULL
;
239 r
= extract_first_word(&p
, &word
, WHITESPACE
",", EXTRACT_UNQUOTE
);
241 return log_oom_full(level
);
244 return log_debug_errno(r
, "Failed to parse CPU set: %s", rvalue
);
246 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse %s= setting, ignoring assignment: %s",
253 unsigned lower
, upper
;
254 r
= parse_range(word
, &lower
, &upper
);
257 return log_debug_errno(r
, "Failed to parse CPU range: %s", word
);
259 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
260 "Failed to parse CPU range, ignoring assignment: %s", word
);
266 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
267 "Invalid CPU range (%u > %u): %s",
270 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
271 "Invalid CPU range (%u > %u), ignoring assignment: %s",
276 r
= cpu_set_add_range(&cpuset
, lower
, upper
);
278 return log_oom_full(level
);
281 return log_debug_errno(r
, "Failed to set CPU(s) '%s': %m", word
);
283 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
284 "Failed to set CPU(s), ignoring assignment: %s", word
);
289 *c
= TAKE_STRUCT(cpuset
);
293 r
= cpu_set_add_set(c
, &cpuset
);
295 return log_oom_full(level
);
301 int parse_cpu_set(const char *s
, CPUSet
*ret
) {
302 _cleanup_(cpu_set_done
) CPUSet c
= {};
308 r
= config_parse_cpu_set(
310 /* filename = */ NULL
,
312 /* section = */ NULL
,
313 /* section_line = */ 0,
318 /* userdata = */ NULL
);
322 *ret
= TAKE_STRUCT(c
);
326 int cpus_in_affinity_mask(void) {
337 if (sched_getaffinity(0, CPU_ALLOC_SIZE(n
), c
) >= 0) {
340 k
= CPU_COUNT_S(CPU_ALLOC_SIZE(n
), c
);
360 int cpu_set_to_dbus(const CPUSet
*c
, uint8_t **ret
, size_t *ret_size
) {
365 uint8_t *buf
= new0(uint8_t, c
->allocated
);
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);
374 *ret_size
= c
->allocated
;
378 int cpu_set_from_dbus(const uint8_t *bits
, size_t size
, CPUSet
*ret
) {
379 _cleanup_(cpu_set_done
) CPUSet c
= {};
382 assert(bits
|| size
== 0);
385 r
= cpu_set_realloc(&c
, size
* 8);
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
);
393 *ret
= TAKE_STRUCT(c
);