xref: /spdk/lib/util/cpuset.c (revision 95d6c9fac17572b107042103439aafd696d60b0e)
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 *
10 spdk_cpuset_alloc(void)
11 {
12 	return (struct spdk_cpuset *)calloc(1, sizeof(struct spdk_cpuset));
13 }
14 
15 void
16 spdk_cpuset_free(struct spdk_cpuset *set)
17 {
18 	free(set);
19 }
20 
21 bool
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
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
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
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
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
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
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
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
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
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
124 count_fn(void *ctx, uint32_t cpu)
125 {
126 	uint32_t *count = ctx;
127 
128 	(*count)++;
129 }
130 
131 uint32_t
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 *
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
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
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
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
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