xref: /spdk/lib/util/cpuset.c (revision f93b6fb0a4ebcee203e7c44c9e170c20bbce96cc)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/cpuset.h"
35 #include "spdk/log.h"
36 
37 struct spdk_cpuset {
38 	char str[SPDK_CPUSET_SIZE / 4 + 1];
39 	uint8_t cpus[SPDK_CPUSET_SIZE / 8];
40 };
41 
42 struct spdk_cpuset *
43 spdk_cpuset_alloc(void)
44 {
45 	return (struct spdk_cpuset *)calloc(sizeof(struct spdk_cpuset), 1);
46 }
47 
48 void
49 spdk_cpuset_free(struct spdk_cpuset *set)
50 {
51 	free(set);
52 }
53 
54 bool
55 spdk_cpuset_equal(const struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
56 {
57 	assert(set1 != NULL);
58 	assert(set2 != NULL);
59 	return memcmp(set1->cpus, set2->cpus, sizeof(set2->cpus)) == 0;
60 }
61 
62 void
63 spdk_cpuset_copy(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
64 {
65 	assert(set1 != NULL);
66 	assert(set2 != NULL);
67 	memcpy(&set1->cpus, &set2->cpus, sizeof(set2->cpus));
68 }
69 
70 void
71 spdk_cpuset_negate(struct spdk_cpuset *set)
72 {
73 	unsigned int i;
74 	assert(set != NULL);
75 	for (i = 0; i < sizeof(set->cpus); i++) {
76 		set->cpus[i] = ~set->cpus[i];
77 	}
78 }
79 
80 void
81 spdk_cpuset_and(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
82 {
83 	unsigned int i;
84 	assert(set1 != NULL);
85 	assert(set2 != NULL);
86 	for (i = 0; i < sizeof(set2->cpus); i++) {
87 		set1->cpus[i] &= set2->cpus[i];
88 	}
89 }
90 
91 void
92 spdk_cpuset_or(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
93 {
94 	unsigned int i;
95 	assert(set1 != NULL);
96 	assert(set2 != NULL);
97 	for (i = 0; i < sizeof(set2->cpus); i++) {
98 		set1->cpus[i] |= set2->cpus[i];
99 	}
100 }
101 
102 void
103 spdk_cpuset_xor(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
104 {
105 	unsigned int i;
106 	assert(set1 != NULL);
107 	assert(set2 != NULL);
108 	for (i = 0; i < sizeof(set2->cpus); i++) {
109 		set1->cpus[i] ^= set2->cpus[i];
110 	}
111 }
112 
113 void
114 spdk_cpuset_zero(struct spdk_cpuset *set)
115 {
116 	assert(set != NULL);
117 	memset(set->cpus, 0, sizeof(set->cpus));
118 }
119 
120 void
121 spdk_cpuset_set_cpu(struct spdk_cpuset *set, uint32_t cpu, bool state)
122 {
123 	assert(set != NULL);
124 	assert(cpu < sizeof(set->cpus) * 8);
125 	if (state) {
126 		set->cpus[cpu / 8] |= (1U << (cpu % 8));
127 	} else {
128 		set->cpus[cpu / 8] &= ~(1U << (cpu % 8));
129 	}
130 }
131 
132 bool
133 spdk_cpuset_get_cpu(const struct spdk_cpuset *set, uint32_t cpu)
134 {
135 	assert(set != NULL);
136 	assert(cpu < sizeof(set->cpus) * 8);
137 	return (set->cpus[cpu / 8] >> (cpu % 8)) & 1U;
138 }
139 
140 uint32_t
141 spdk_cpuset_count(const struct spdk_cpuset *set)
142 {
143 	uint32_t count = 0;
144 	uint8_t n;
145 	unsigned int i;
146 	for (i = 0; i < sizeof(set->cpus); i++) {
147 		n = set->cpus[i];
148 		while (n) {
149 			n &= (n - 1);
150 			count++;
151 		}
152 	}
153 	return count;
154 }
155 
156 const char *
157 spdk_cpuset_fmt(struct spdk_cpuset *set)
158 {
159 	uint32_t lcore, lcore_max = 0;
160 	int val, i, n;
161 	char *ptr;
162 	static const char *hex = "0123456789abcdef";
163 
164 	assert(set != NULL);
165 
166 	for (lcore = 0; lcore < sizeof(set->cpus) * 8; lcore++) {
167 		if (spdk_cpuset_get_cpu(set, lcore)) {
168 			lcore_max = lcore;
169 		}
170 	}
171 
172 	ptr = set->str;
173 	n = lcore_max / 8;
174 	val = set->cpus[n];
175 
176 	/* Store first number only if it is not leading zero */
177 	if ((val & 0xf0) != 0) {
178 		*(ptr++) = hex[(val & 0xf0) >> 4];
179 	}
180 	*(ptr++) = hex[val & 0x0f];
181 
182 	for (i = n - 1; i >= 0; i--) {
183 		val = set->cpus[i];
184 		*(ptr++) = hex[(val & 0xf0) >> 4];
185 		*(ptr++) = hex[val & 0x0f];
186 	}
187 	*ptr = '\0';
188 
189 	return set->str;
190 }
191 
192 static int
193 hex_value(uint8_t c)
194 {
195 #define V(x, y) [x] = y + 1
196 	static const int8_t val[256] = {
197 		V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
198 		V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
199 		V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
200 		V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
201 	};
202 #undef V
203 
204 	return val[c] - 1;
205 }
206 
207 static int
208 parse_list(const char *mask, struct spdk_cpuset *set)
209 {
210 	char *end;
211 	const char *ptr = mask;
212 	uint32_t lcore;
213 	uint32_t lcore_min, lcore_max;
214 
215 	spdk_cpuset_zero(set);
216 	lcore_min = UINT32_MAX;
217 
218 	ptr++;
219 	end = (char *)ptr;
220 	do {
221 		while (isblank(*ptr)) {
222 			ptr++;
223 		}
224 		if (*ptr == '\0' || *ptr == ']' || *ptr == '-' || *ptr == ',') {
225 			goto invalid_character;
226 		}
227 
228 		errno = 0;
229 		lcore = strtoul(ptr, &end, 10);
230 		if (errno) {
231 			SPDK_ERRLOG("Conversion of core mask in '%s' failed\n", mask);
232 			return -1;
233 		}
234 
235 		if (lcore >= sizeof(set->cpus) * 8) {
236 			SPDK_ERRLOG("Core number %" PRIu32 " is out of range in '%s'\n", lcore, mask);
237 			return -1;
238 		}
239 
240 		while (isblank(*end)) {
241 			end++;
242 		}
243 
244 		if (*end == '-') {
245 			lcore_min = lcore;
246 		} else if (*end == ',' || *end == ']') {
247 			lcore_max = lcore;
248 			if (lcore_min == UINT32_MAX) {
249 				lcore_min = lcore;
250 			}
251 			if (lcore_min > lcore_max) {
252 				SPDK_ERRLOG("Invalid range of CPUs (%" PRIu32 " > %" PRIu32 ")\n",
253 					    lcore_min, lcore_max);
254 				return -1;
255 			}
256 			for (lcore = lcore_min; lcore <= lcore_max; lcore++) {
257 				spdk_cpuset_set_cpu(set, lcore, true);
258 			}
259 			lcore_min = UINT32_MAX;
260 		} else {
261 			goto invalid_character;
262 		}
263 
264 		ptr = end + 1;
265 
266 	} while (*end != ']');
267 
268 	return 0;
269 
270 invalid_character:
271 	if (*end == '\0') {
272 		SPDK_ERRLOG("Unexpected end of core list '%s'\n", mask);
273 	} else {
274 		SPDK_ERRLOG("Parsing of core list '%s' failed on character '%c'\n", mask, *end);
275 	}
276 	return -1;
277 }
278 
279 static int
280 parse_mask(const char *mask, struct spdk_cpuset *set, size_t len)
281 {
282 	int i, j;
283 	char c;
284 	int val;
285 	uint32_t lcore = 0;
286 
287 	if (mask[0] == '0' && (mask[1] == 'x' || mask[1] == 'X')) {
288 		mask += 2;
289 		len -= 2;
290 	}
291 
292 	spdk_cpuset_zero(set);
293 	for (i = len - 1; i >= 0; i--) {
294 		c = mask[i];
295 		val = hex_value(c);
296 		if (val < 0) {
297 			/* Invalid character */
298 			SPDK_ERRLOG("Invalid character in core mask '%s' (%c)\n", mask, c);
299 			return -1;
300 		}
301 		for (j = 0; j < 4 && lcore < sizeof(set->cpus); j++, lcore++) {
302 			if ((1 << j) & val) {
303 				spdk_cpuset_set_cpu(set, lcore, true);
304 			}
305 		}
306 	}
307 
308 	return 0;
309 }
310 
311 int
312 spdk_cpuset_parse(struct spdk_cpuset *set, const char *mask)
313 {
314 	int ret;
315 	size_t len;
316 
317 	if (mask == NULL || set == NULL) {
318 		return -1;
319 	}
320 
321 	while (isblank(*mask)) {
322 		mask++;
323 	}
324 
325 	len = strlen(mask);
326 	while (len > 0 && isblank(mask[len - 1])) {
327 		len--;
328 	}
329 
330 	if (len == 0) {
331 		return -1;
332 	}
333 
334 	if (mask[0] == '[') {
335 		ret = parse_list(mask, set);
336 	} else {
337 		ret = parse_mask(mask, set, len);
338 	}
339 
340 	return ret;
341 }
342