]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cpu-set-util.c
hwdb: Add mapping for Xiaomi Mipad 2 bottom bezel capacitive buttons
[thirdparty/systemd.git] / src / shared / cpu-set-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
618234a5 2
11c3a366
TA
3#include <errno.h>
4#include <stddef.h>
a832893f 5#include <stdio.h>
11c3a366
TA
6#include <syslog.h>
7
b5efdb8a 8#include "alloc-util.h"
6bedfcbb 9#include "cpu-set-util.h"
b070c7c0
MS
10#include "dirent-util.h"
11#include "errno-util.h"
84ac7bea 12#include "extract-word.h"
b070c7c0 13#include "fd-util.h"
0f30bf58 14#include "hexdecoct.h"
11c3a366
TA
15#include "log.h"
16#include "macro.h"
0985c7c4 17#include "memory-util.h"
93cc7779 18#include "parse-util.h"
b070c7c0 19#include "stat-util.h"
b11d6a7b 20#include "string-util.h"
b070c7c0 21#include "strv.h"
618234a5 22
0985c7c4 23char* cpu_set_to_string(const CPUSet *a) {
a832893f 24 _cleanup_free_ char *str = NULL;
319a4f4b 25 size_t len = 0;
a832893f
ZJS
26 int i, r;
27
0985c7c4
ZJS
28 for (i = 0; (size_t) i < a->allocated * 8; i++) {
29 if (!CPU_ISSET_S(i, a->allocated, a->set))
a832893f
ZJS
30 continue;
31
319a4f4b 32 if (!GREEDY_REALLOC(str, len + 1 + DECIMAL_STR_MAX(int)))
a832893f
ZJS
33 return NULL;
34
35 r = sprintf(str + len, len > 0 ? " %d" : "%d", i);
36 assert_se(r > 0);
37 len += r;
38 }
39
40 return TAKE_PTR(str) ?: strdup("");
41}
42
71b28519
MS
43char *cpu_set_to_range_string(const CPUSet *set) {
44 unsigned range_start = 0, range_end;
45 _cleanup_free_ char *str = NULL;
71b28519 46 bool in_range = false;
319a4f4b 47 size_t len = 0;
71b28519
MS
48 int r;
49
50 for (unsigned i = 0; i < set->allocated * 8; i++)
51 if (CPU_ISSET_S(i, set->allocated, set->set)) {
52 if (in_range)
53 range_end++;
54 else {
55 range_start = range_end = i;
56 in_range = true;
57 }
58 } else if (in_range) {
59 in_range = false;
60
319a4f4b 61 if (!GREEDY_REALLOC(str, len + 2 + 2 * DECIMAL_STR_MAX(unsigned)))
71b28519
MS
62 return NULL;
63
71923237 64 if (range_end > range_start)
c0f86d66 65 r = sprintf(str + len, len > 0 ? " %u-%u" : "%u-%u", range_start, range_end);
1f57a176 66 else
c0f86d66 67 r = sprintf(str + len, len > 0 ? " %u" : "%u", range_start);
71b28519
MS
68 assert_se(r > 0);
69 len += r;
70 }
71
72 if (in_range) {
319a4f4b 73 if (!GREEDY_REALLOC(str, len + 2 + 2 * DECIMAL_STR_MAX(int)))
71b28519
MS
74 return NULL;
75
71923237 76 if (range_end > range_start)
c0f86d66 77 r = sprintf(str + len, len > 0 ? " %u-%u" : "%u-%u", range_start, range_end);
1f57a176 78 else
c0f86d66 79 r = sprintf(str + len, len > 0 ? " %u" : "%u", range_start);
71b28519
MS
80 assert_se(r > 0);
81 }
82
83 return TAKE_PTR(str) ?: strdup("");
84}
85
0f30bf58
RRZ
86char* cpu_set_to_mask_string(const CPUSet *a) {
87 _cleanup_free_ char *str = NULL;
88 size_t len = 0;
89 bool found_nonzero = false;
90
91 assert(a);
92
93 /* Return CPU set in hexadecimal bitmap mask, e.g.
94 * CPU 0 -> "1"
95 * CPU 1 -> "2"
96 * CPU 0,1 -> "3"
97 * CPU 0-3 -> "f"
98 * CPU 0-7 -> "ff"
99 * CPU 4-7 -> "f0"
100 * CPU 7 -> "80"
101 * None -> "0"
102 *
103 * When there are more than 32 CPUs, separate every 32 CPUs by comma, e.g.
104 * CPU 0-47 -> "ffff,ffffffff"
105 * CPU 0-63 -> "ffffffff,ffffffff"
106 * CPU 0-71 -> "ff,ffffffff,ffffffff" */
107
108 for (ssize_t i = a->allocated * 8; i >= 0; i -= 4) {
109 uint8_t m = 0;
110
111 for (size_t j = 0; j < 4; j++)
112 if (CPU_ISSET_S(i + j, a->allocated, a->set))
113 m |= 1U << j;
114
115 if (!found_nonzero)
116 found_nonzero = m > 0;
117
118 if (!found_nonzero && m == 0)
119 /* Skip leading zeros */
120 continue;
121
122 if (!GREEDY_REALLOC(str, len + 3))
123 return NULL;
124
125 str[len++] = hexchar(m);
126 if (i >= 4 && i % 32 == 0)
127 /* Separate by comma for each 32 CPUs. */
128 str[len++] = ',';
129 str[len] = 0;
130 }
131
132 return TAKE_PTR(str) ?: strdup("0");
133}
134
135CPUSet* cpu_set_free(CPUSet *c) {
136 if (!c)
137 return c;
138
139 cpu_set_reset(c);
140 return mfree(c);
141}
142
167a776d 143int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) {
0985c7c4
ZJS
144 size_t need;
145
146 assert(cpu_set);
147
148 need = CPU_ALLOC_SIZE(ncpus);
149 if (need > cpu_set->allocated) {
150 cpu_set_t *t;
151
152 t = realloc(cpu_set->set, need);
153 if (!t)
154 return -ENOMEM;
155
156 memzero((uint8_t*) t + cpu_set->allocated, need - cpu_set->allocated);
157
158 cpu_set->set = t;
159 cpu_set->allocated = need;
160 }
161
162 return 0;
163}
164
332d387f 165int cpu_set_add(CPUSet *cpu_set, unsigned cpu) {
0985c7c4
ZJS
166 int r;
167
168 if (cpu >= 8192)
169 /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */
170 return -ERANGE;
171
172 r = cpu_set_realloc(cpu_set, cpu + 1);
173 if (r < 0)
174 return r;
175
176 CPU_SET_S(cpu, cpu_set->allocated, cpu_set->set);
177 return 0;
178}
179
180int cpu_set_add_all(CPUSet *a, const CPUSet *b) {
181 int r;
182
183 /* Do this backwards, so if we fail, we fail before changing anything. */
184 for (unsigned cpu_p1 = b->allocated * 8; cpu_p1 > 0; cpu_p1--)
185 if (CPU_ISSET_S(cpu_p1 - 1, b->allocated, b->set)) {
186 r = cpu_set_add(a, cpu_p1 - 1);
187 if (r < 0)
188 return r;
189 }
190
e2b2fb7f 191 return 1;
0985c7c4
ZJS
192}
193
194int parse_cpu_set_full(
618234a5 195 const char *rvalue,
0985c7c4 196 CPUSet *cpu_set,
6d8a29b2 197 bool warn,
618234a5
LP
198 const char *unit,
199 const char *filename,
200 unsigned line,
201 const char *lvalue) {
202
0985c7c4 203 _cleanup_(cpu_set_reset) CPUSet c = {};
99534007 204 const char *p = ASSERT_PTR(rvalue);
618234a5 205
cfc28ee2
DT
206 assert(cpu_set);
207
618234a5
LP
208 for (;;) {
209 _cleanup_free_ char *word = NULL;
0985c7c4 210 unsigned cpu_lower, cpu_upper;
618234a5
LP
211 int r;
212
4ec85141 213 r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_UNQUOTE);
6d8a29b2
YW
214 if (r == -ENOMEM)
215 return warn ? log_oom() : -ENOMEM;
a26662ce 216 if (r < 0)
6d8a29b2 217 return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, rvalue) : r;
618234a5
LP
218 if (r == 0)
219 break;
220
a26662ce
FB
221 r = parse_range(word, &cpu_lower, &cpu_upper);
222 if (r < 0)
6d8a29b2 223 return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word) : r;
032cf8e4 224
6d8a29b2
YW
225 if (cpu_lower > cpu_upper) {
226 if (warn)
0985c7c4
ZJS
227 log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring.",
228 word, cpu_lower, cpu_upper);
229
230 /* Make sure something is allocated, to distinguish this from the empty case */
231 r = cpu_set_realloc(&c, 1);
232 if (r < 0)
233 return r;
032cf8e4
YW
234 }
235
0985c7c4
ZJS
236 for (unsigned cpu_p1 = MIN(cpu_upper, UINT_MAX-1) + 1; cpu_p1 > cpu_lower; cpu_p1--) {
237 r = cpu_set_add(&c, cpu_p1 - 1);
238 if (r < 0)
239 return warn ? log_syntax(unit, LOG_ERR, filename, line, r,
240 "Cannot add CPU %u to set: %m", cpu_p1 - 1) : r;
241 }
032cf8e4
YW
242 }
243
088d71f8 244 *cpu_set = TAKE_STRUCT(c);
0985c7c4
ZJS
245
246 return 0;
247}
248
249int parse_cpu_set_extend(
250 const char *rvalue,
251 CPUSet *old,
252 bool warn,
253 const char *unit,
254 const char *filename,
255 unsigned line,
256 const char *lvalue) {
257
258 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
259 int r;
260
cfc28ee2
DT
261 assert(old);
262
0985c7c4
ZJS
263 r = parse_cpu_set_full(rvalue, &cpuset, true, unit, filename, line, lvalue);
264 if (r < 0)
265 return r;
266
267 if (!cpuset.set) {
268 /* An empty assignment resets the CPU list */
269 cpu_set_reset(old);
270 return 0;
271 }
272
273 if (!old->set) {
088d71f8 274 *old = TAKE_STRUCT(cpuset);
e2b2fb7f 275 return 1;
0985c7c4 276 }
032cf8e4 277
0985c7c4 278 return cpu_set_add_all(old, &cpuset);
032cf8e4 279}
f44b3035
ZJS
280
281int cpus_in_affinity_mask(void) {
282 size_t n = 16;
283 int r;
284
285 for (;;) {
286 cpu_set_t *c;
287
288 c = CPU_ALLOC(n);
289 if (!c)
290 return -ENOMEM;
291
292 if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
293 int k;
294
295 k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
296 CPU_FREE(c);
297
298 if (k <= 0)
299 return -EINVAL;
300
301 return k;
302 }
303
304 r = -errno;
305 CPU_FREE(c);
306
307 if (r != -EINVAL)
308 return r;
309 if (n > SIZE_MAX/2)
310 return -ENOMEM;
311 n *= 2;
312 }
313}
c367f996
MS
314
315int cpu_set_to_dbus(const CPUSet *set, uint8_t **ret, size_t *allocated) {
316 uint8_t *out;
317
318 assert(set);
319 assert(ret);
320
321 out = new0(uint8_t, set->allocated);
322 if (!out)
323 return -ENOMEM;
324
325 for (unsigned cpu = 0; cpu < set->allocated * 8; cpu++)
326 if (CPU_ISSET_S(cpu, set->allocated, set->set))
327 out[cpu / 8] |= 1u << (cpu % 8);
328
329 *ret = out;
330 *allocated = set->allocated;
331 return 0;
332}
333
334int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set) {
335 _cleanup_(cpu_set_reset) CPUSet s = {};
336 int r;
337
338 assert(bits);
339 assert(set);
340
341 for (unsigned cpu = size * 8; cpu > 0; cpu--)
342 if (bits[(cpu - 1) / 8] & (1u << ((cpu - 1) % 8))) {
343 r = cpu_set_add(&s, cpu - 1);
344 if (r < 0)
345 return r;
346 }
347
088d71f8 348 *set = TAKE_STRUCT(s);
c367f996
MS
349 return 0;
350}
0f30bf58
RRZ
351
352int cpu_mask_add_all(CPUSet *mask) {
353 long m;
354 int r;
355
356 assert(mask);
357
358 m = sysconf(_SC_NPROCESSORS_ONLN);
359 if (m < 0)
360 return -errno;
361
362 for (unsigned i = 0; i < (unsigned) m; i++) {
363 r = cpu_set_add(mask, i);
364 if (r < 0)
365 return r;
366 }
367
368 return 0;
369}