1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2017 Intel Corporation. All rights reserved.
3 * All rights reserved.
4 */
5
6 #include "spdk/cpuset.h"
7 #include "spdk/log.h"
8
9 struct spdk_cpuset *
spdk_cpuset_alloc(void)10 spdk_cpuset_alloc(void)
11 {
12 return (struct spdk_cpuset *)calloc(1, sizeof(struct spdk_cpuset));
13 }
14
15 void
spdk_cpuset_free(struct spdk_cpuset * set)16 spdk_cpuset_free(struct spdk_cpuset *set)
17 {
18 free(set);
19 }
20
21 bool
spdk_cpuset_equal(const struct spdk_cpuset * set1,const struct spdk_cpuset * set2)22 spdk_cpuset_equal(const struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
23 {
24 assert(set1 != NULL);
25 assert(set2 != NULL);
26 return memcmp(set1->cpus, set2->cpus, sizeof(set2->cpus)) == 0;
27 }
28
29 void
spdk_cpuset_copy(struct spdk_cpuset * dst,const struct spdk_cpuset * src)30 spdk_cpuset_copy(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
31 {
32 assert(dst != NULL);
33 assert(src != NULL);
34 memcpy(&dst->cpus, &src->cpus, sizeof(src->cpus));
35 }
36
37 void
spdk_cpuset_negate(struct spdk_cpuset * set)38 spdk_cpuset_negate(struct spdk_cpuset *set)
39 {
40 unsigned int i;
41 assert(set != NULL);
42 for (i = 0; i < sizeof(set->cpus); i++) {
43 set->cpus[i] = ~set->cpus[i];
44 }
45 }
46
47 void
spdk_cpuset_and(struct spdk_cpuset * dst,const struct spdk_cpuset * src)48 spdk_cpuset_and(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
49 {
50 unsigned int i;
51 assert(dst != NULL);
52 assert(src != NULL);
53 for (i = 0; i < sizeof(src->cpus); i++) {
54 dst->cpus[i] &= src->cpus[i];
55 }
56 }
57
58 void
spdk_cpuset_or(struct spdk_cpuset * dst,const struct spdk_cpuset * src)59 spdk_cpuset_or(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
60 {
61 unsigned int i;
62 assert(dst != NULL);
63 assert(src != NULL);
64 for (i = 0; i < sizeof(src->cpus); i++) {
65 dst->cpus[i] |= src->cpus[i];
66 }
67 }
68
69 void
spdk_cpuset_xor(struct spdk_cpuset * dst,const struct spdk_cpuset * src)70 spdk_cpuset_xor(struct spdk_cpuset *dst, const struct spdk_cpuset *src)
71 {
72 unsigned int i;
73 assert(dst != NULL);
74 assert(src != NULL);
75 for (i = 0; i < sizeof(src->cpus); i++) {
76 dst->cpus[i] ^= src->cpus[i];
77 }
78 }
79
80 void
spdk_cpuset_zero(struct spdk_cpuset * set)81 spdk_cpuset_zero(struct spdk_cpuset *set)
82 {
83 assert(set != NULL);
84 memset(set->cpus, 0, sizeof(set->cpus));
85 }
86
87 void
spdk_cpuset_set_cpu(struct spdk_cpuset * set,uint32_t cpu,bool state)88 spdk_cpuset_set_cpu(struct spdk_cpuset *set, uint32_t cpu, bool state)
89 {
90 assert(set != NULL);
91 assert(cpu < sizeof(set->cpus) * 8);
92 if (state) {
93 set->cpus[cpu / 8] |= (1U << (cpu % 8));
94 } else {
95 set->cpus[cpu / 8] &= ~(1U << (cpu % 8));
96 }
97 }
98
99 bool
spdk_cpuset_get_cpu(const struct spdk_cpuset * set,uint32_t cpu)100 spdk_cpuset_get_cpu(const struct spdk_cpuset *set, uint32_t cpu)
101 {
102 assert(set != NULL);
103 assert(cpu < sizeof(set->cpus) * 8);
104 return (set->cpus[cpu / 8] >> (cpu % 8)) & 1U;
105 }
106
107 void
spdk_cpuset_for_each_cpu(const struct spdk_cpuset * set,void (* fn)(void * ctx,uint32_t cpu),void * ctx)108 spdk_cpuset_for_each_cpu(const struct spdk_cpuset *set,
109 void (*fn)(void *ctx, uint32_t cpu), void *ctx)
110 {
111 uint8_t n;
112 unsigned int i, j;
113 for (i = 0; i < sizeof(set->cpus); i++) {
114 n = set->cpus[i];
115 for (j = 0; j < 8; j++) {
116 if (n & (1 << j)) {
117 fn(ctx, i * 8 + j);
118 }
119 }
120 }
121 }
122
123 static void
count_fn(void * ctx,uint32_t cpu)124 count_fn(void *ctx, uint32_t cpu)
125 {
126 uint32_t *count = ctx;
127
128 (*count)++;
129 }
130
131 uint32_t
spdk_cpuset_count(const struct spdk_cpuset * set)132 spdk_cpuset_count(const struct spdk_cpuset *set)
133 {
134 uint32_t count = 0;
135
136 spdk_cpuset_for_each_cpu(set, count_fn, &count);
137 return count;
138 }
139
140 const char *
spdk_cpuset_fmt(struct spdk_cpuset * set)141 spdk_cpuset_fmt(struct spdk_cpuset *set)
142 {
143 uint32_t lcore, lcore_max = 0;
144 int val, i, n;
145 char *ptr;
146 static const char *hex = "0123456789abcdef";
147
148 assert(set != NULL);
149
150 for (lcore = 0; lcore < sizeof(set->cpus) * 8; lcore++) {
151 if (spdk_cpuset_get_cpu(set, lcore)) {
152 lcore_max = lcore;
153 }
154 }
155
156 ptr = set->str;
157 n = lcore_max / 8;
158 val = set->cpus[n];
159
160 /* Store first number only if it is not leading zero */
161 if ((val & 0xf0) != 0) {
162 *(ptr++) = hex[(val & 0xf0) >> 4];
163 }
164 *(ptr++) = hex[val & 0x0f];
165
166 for (i = n - 1; i >= 0; i--) {
167 val = set->cpus[i];
168 *(ptr++) = hex[(val & 0xf0) >> 4];
169 *(ptr++) = hex[val & 0x0f];
170 }
171 *ptr = '\0';
172
173 return set->str;
174 }
175
176 static int
hex_value(uint8_t c)177 hex_value(uint8_t c)
178 {
179 #define V(x, y) [x] = y + 1
180 static const int8_t val[256] = {
181 V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
182 V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
183 V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
184 V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
185 };
186 #undef V
187
188 return val[c] - 1;
189 }
190
191 static int
parse_list(const char * mask,struct spdk_cpuset * set)192 parse_list(const char *mask, struct spdk_cpuset *set)
193 {
194 char *end;
195 const char *ptr = mask;
196 uint32_t lcore;
197 uint32_t lcore_min, lcore_max;
198
199 spdk_cpuset_zero(set);
200 lcore_min = UINT32_MAX;
201
202 ptr++;
203 end = (char *)ptr;
204 do {
205 while (isblank(*ptr)) {
206 ptr++;
207 }
208 if (*ptr == '\0' || *ptr == ']' || *ptr == '-' || *ptr == ',') {
209 goto invalid_character;
210 }
211
212 errno = 0;
213 lcore = strtoul(ptr, &end, 10);
214 if (errno) {
215 SPDK_ERRLOG("Conversion of core mask in '%s' failed\n", mask);
216 return -1;
217 }
218
219 if (lcore >= sizeof(set->cpus) * 8) {
220 SPDK_ERRLOG("Core number %" PRIu32 " is out of range in '%s'\n", lcore, mask);
221 return -1;
222 }
223
224 while (isblank(*end)) {
225 end++;
226 }
227
228 if (*end == '-') {
229 lcore_min = lcore;
230 } else if (*end == ',' || *end == ']') {
231 lcore_max = lcore;
232 if (lcore_min == UINT32_MAX) {
233 lcore_min = lcore;
234 }
235 if (lcore_min > lcore_max) {
236 SPDK_ERRLOG("Invalid range of CPUs (%" PRIu32 " > %" PRIu32 ")\n",
237 lcore_min, lcore_max);
238 return -1;
239 }
240 for (lcore = lcore_min; lcore <= lcore_max; lcore++) {
241 spdk_cpuset_set_cpu(set, lcore, true);
242 }
243 lcore_min = UINT32_MAX;
244 } else {
245 goto invalid_character;
246 }
247
248 ptr = end + 1;
249
250 } while (*end != ']');
251
252 return 0;
253
254 invalid_character:
255 if (*end == '\0') {
256 SPDK_ERRLOG("Unexpected end of core list '%s'\n", mask);
257 } else {
258 SPDK_ERRLOG("Parsing of core list '%s' failed on character '%c'\n", mask, *end);
259 }
260 return -1;
261 }
262
263 static int
parse_mask(const char * mask,struct spdk_cpuset * set,size_t len)264 parse_mask(const char *mask, struct spdk_cpuset *set, size_t len)
265 {
266 int i, j;
267 char c;
268 int val;
269 uint32_t lcore = 0;
270
271 if (mask[0] == '0' && (mask[1] == 'x' || mask[1] == 'X')) {
272 mask += 2;
273 len -= 2;
274 }
275
276 spdk_cpuset_zero(set);
277 for (i = len - 1; i >= 0; i--) {
278 c = mask[i];
279 if (c == ',') {
280 /* Linux puts comma delimiters in its cpumasks, just skip them. */
281 continue;
282 }
283 val = hex_value(c);
284 if (val < 0) {
285 /* Invalid character */
286 SPDK_ERRLOG("Invalid character in core mask '%s' (%c)\n", mask, c);
287 return -1;
288 }
289 for (j = 0; j < 4 && lcore < SPDK_CPUSET_SIZE; j++, lcore++) {
290 if ((1 << j) & val) {
291 spdk_cpuset_set_cpu(set, lcore, true);
292 }
293 }
294 }
295
296 return 0;
297 }
298
299 int
spdk_cpuset_parse(struct spdk_cpuset * set,const char * mask)300 spdk_cpuset_parse(struct spdk_cpuset *set, const char *mask)
301 {
302 int ret;
303 size_t len;
304
305 if (mask == NULL || set == NULL) {
306 return -1;
307 }
308
309 while (isblank(*mask)) {
310 mask++;
311 }
312
313 len = strlen(mask);
314 while (len > 0 && isblank(mask[len - 1])) {
315 len--;
316 }
317
318 if (len == 0) {
319 return -1;
320 }
321
322 if (mask[0] == '[') {
323 ret = parse_list(mask, set);
324 } else {
325 ret = parse_mask(mask, set, len);
326 }
327
328 return ret;
329 }
330