]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cpu-set-util.c
Move cpus_in_affinity_mask() to cpu-set-util.[ch]
[thirdparty/systemd.git] / src / shared / cpu-set-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
84ac7bea 10#include "extract-word.h"
11c3a366
TA
11#include "log.h"
12#include "macro.h"
0985c7c4 13#include "memory-util.h"
93cc7779 14#include "parse-util.h"
b11d6a7b 15#include "string-util.h"
618234a5 16
0985c7c4 17char* cpu_set_to_string(const CPUSet *a) {
a832893f
ZJS
18 _cleanup_free_ char *str = NULL;
19 size_t allocated = 0, len = 0;
20 int i, r;
21
0985c7c4
ZJS
22 for (i = 0; (size_t) i < a->allocated * 8; i++) {
23 if (!CPU_ISSET_S(i, a->allocated, a->set))
a832893f
ZJS
24 continue;
25
26 if (!GREEDY_REALLOC(str, allocated, len + 1 + DECIMAL_STR_MAX(int)))
27 return NULL;
28
29 r = sprintf(str + len, len > 0 ? " %d" : "%d", i);
30 assert_se(r > 0);
31 len += r;
32 }
33
34 return TAKE_PTR(str) ?: strdup("");
35}
36
618234a5
LP
37cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
38 cpu_set_t *c;
39 unsigned n = 1024;
40
41 /* Allocates the cpuset in the right size */
42
43 for (;;) {
44 c = CPU_ALLOC(n);
45 if (!c)
46 return NULL;
47
48 if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
49 CPU_ZERO_S(CPU_ALLOC_SIZE(n), c);
50
51 if (ncpus)
52 *ncpus = n;
53
54 return c;
55 }
56
57 CPU_FREE(c);
58
59 if (errno != EINVAL)
60 return NULL;
61
62 n *= 2;
63 }
64}
65
0985c7c4
ZJS
66static int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) {
67 size_t need;
68
69 assert(cpu_set);
70
71 need = CPU_ALLOC_SIZE(ncpus);
72 if (need > cpu_set->allocated) {
73 cpu_set_t *t;
74
75 t = realloc(cpu_set->set, need);
76 if (!t)
77 return -ENOMEM;
78
79 memzero((uint8_t*) t + cpu_set->allocated, need - cpu_set->allocated);
80
81 cpu_set->set = t;
82 cpu_set->allocated = need;
83 }
84
85 return 0;
86}
87
88static int cpu_set_add(CPUSet *cpu_set, unsigned cpu) {
89 int r;
90
91 if (cpu >= 8192)
92 /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */
93 return -ERANGE;
94
95 r = cpu_set_realloc(cpu_set, cpu + 1);
96 if (r < 0)
97 return r;
98
99 CPU_SET_S(cpu, cpu_set->allocated, cpu_set->set);
100 return 0;
101}
102
103int cpu_set_add_all(CPUSet *a, const CPUSet *b) {
104 int r;
105
106 /* Do this backwards, so if we fail, we fail before changing anything. */
107 for (unsigned cpu_p1 = b->allocated * 8; cpu_p1 > 0; cpu_p1--)
108 if (CPU_ISSET_S(cpu_p1 - 1, b->allocated, b->set)) {
109 r = cpu_set_add(a, cpu_p1 - 1);
110 if (r < 0)
111 return r;
112 }
113
114 return 0;
115}
116
117int parse_cpu_set_full(
618234a5 118 const char *rvalue,
0985c7c4 119 CPUSet *cpu_set,
6d8a29b2 120 bool warn,
618234a5
LP
121 const char *unit,
122 const char *filename,
123 unsigned line,
124 const char *lvalue) {
125
0985c7c4 126 _cleanup_(cpu_set_reset) CPUSet c = {};
6d8a29b2 127 const char *p = rvalue;
618234a5 128
0985c7c4 129 assert(p);
618234a5
LP
130
131 for (;;) {
132 _cleanup_free_ char *word = NULL;
0985c7c4 133 unsigned cpu_lower, cpu_upper;
618234a5
LP
134 int r;
135
6d8a29b2
YW
136 r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_QUOTES);
137 if (r == -ENOMEM)
138 return warn ? log_oom() : -ENOMEM;
a26662ce 139 if (r < 0)
6d8a29b2 140 return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, rvalue) : r;
618234a5
LP
141 if (r == 0)
142 break;
143
a26662ce
FB
144 r = parse_range(word, &cpu_lower, &cpu_upper);
145 if (r < 0)
6d8a29b2 146 return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word) : r;
032cf8e4 147
6d8a29b2
YW
148 if (cpu_lower > cpu_upper) {
149 if (warn)
0985c7c4
ZJS
150 log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring.",
151 word, cpu_lower, cpu_upper);
152
153 /* Make sure something is allocated, to distinguish this from the empty case */
154 r = cpu_set_realloc(&c, 1);
155 if (r < 0)
156 return r;
032cf8e4
YW
157 }
158
0985c7c4
ZJS
159 for (unsigned cpu_p1 = MIN(cpu_upper, UINT_MAX-1) + 1; cpu_p1 > cpu_lower; cpu_p1--) {
160 r = cpu_set_add(&c, cpu_p1 - 1);
161 if (r < 0)
162 return warn ? log_syntax(unit, LOG_ERR, filename, line, r,
163 "Cannot add CPU %u to set: %m", cpu_p1 - 1) : r;
164 }
032cf8e4
YW
165 }
166
0985c7c4
ZJS
167 /* On success, transfer ownership to the output variable */
168 *cpu_set = c;
169 c = (CPUSet) {};
170
171 return 0;
172}
173
174int parse_cpu_set_extend(
175 const char *rvalue,
176 CPUSet *old,
177 bool warn,
178 const char *unit,
179 const char *filename,
180 unsigned line,
181 const char *lvalue) {
182
183 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
184 int r;
185
186 r = parse_cpu_set_full(rvalue, &cpuset, true, unit, filename, line, lvalue);
187 if (r < 0)
188 return r;
189
190 if (!cpuset.set) {
191 /* An empty assignment resets the CPU list */
192 cpu_set_reset(old);
193 return 0;
194 }
195
196 if (!old->set) {
197 *old = cpuset;
198 cpuset = (CPUSet) {};
199 return 0;
200 }
032cf8e4 201
0985c7c4 202 return cpu_set_add_all(old, &cpuset);
032cf8e4 203}
f44b3035
ZJS
204
205int cpus_in_affinity_mask(void) {
206 size_t n = 16;
207 int r;
208
209 for (;;) {
210 cpu_set_t *c;
211
212 c = CPU_ALLOC(n);
213 if (!c)
214 return -ENOMEM;
215
216 if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
217 int k;
218
219 k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
220 CPU_FREE(c);
221
222 if (k <= 0)
223 return -EINVAL;
224
225 return k;
226 }
227
228 r = -errno;
229 CPU_FREE(c);
230
231 if (r != -EINVAL)
232 return r;
233 if (n > SIZE_MAX/2)
234 return -ENOMEM;
235 n *= 2;
236 }
237}