xref: /spdk/lib/util/cpuset.c (revision b78e763c1af2ace4c19d2932065a43357e3f5d3e)
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_and(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
72 {
73 	unsigned int i;
74 	assert(set1 != NULL);
75 	assert(set2 != NULL);
76 	for (i = 0; i < sizeof(set2->cpus); i++) {
77 		set1->cpus[i] &= set2->cpus[i];
78 	}
79 }
80 
81 void
82 spdk_cpuset_or(struct spdk_cpuset *set1, const struct spdk_cpuset *set2)
83 {
84 	unsigned int i;
85 	assert(set1 != NULL);
86 	assert(set2 != NULL);
87 	for (i = 0; i < sizeof(set2->cpus); i++) {
88 		set1->cpus[i] |= set2->cpus[i];
89 	}
90 }
91 
92 void
93 spdk_cpuset_zero(struct spdk_cpuset *set)
94 {
95 	assert(set != NULL);
96 	memset(set->cpus, 0, sizeof(set->cpus));
97 }
98 
99 void
100 spdk_cpuset_set_cpu(struct spdk_cpuset *set, uint32_t cpu, bool state)
101 {
102 	assert(set != NULL);
103 	assert(cpu < sizeof(set->cpus) * 8);
104 	if (state) {
105 		set->cpus[cpu / 8] |= (1U << (cpu % 8));
106 	} else {
107 		set->cpus[cpu / 8] &= ~(1U << (cpu % 8));
108 	}
109 }
110 
111 bool
112 spdk_cpuset_get_cpu(const struct spdk_cpuset *set, uint32_t cpu)
113 {
114 	assert(set != NULL);
115 	assert(cpu < sizeof(set->cpus) * 8);
116 	return (set->cpus[cpu / 8] >> (cpu % 8)) & 1U;
117 }
118 
119 uint32_t
120 spdk_cpuset_count(const struct spdk_cpuset *set)
121 {
122 	uint32_t count = 0;
123 	uint8_t n;
124 	unsigned int i;
125 	for (i = 0; i < sizeof(set->cpus); i++) {
126 		n = set->cpus[i];
127 		while (n) {
128 			n &= (n - 1);
129 			count++;
130 		}
131 	}
132 	return count;
133 }
134 
135 const char *
136 spdk_cpuset_fmt(struct spdk_cpuset *set)
137 {
138 	uint32_t lcore, lcore_max = 0;
139 	int val, i, n;
140 	char *ptr;
141 	static const char *hex = "0123456789abcdef";
142 
143 	assert(set != NULL);
144 
145 	for (lcore = 0; lcore < sizeof(set->cpus) * 8; lcore++) {
146 		if (spdk_cpuset_get_cpu(set, lcore)) {
147 			lcore_max = lcore;
148 		}
149 	}
150 
151 	ptr = set->str;
152 	n = lcore_max / 8;
153 	val = set->cpus[n];
154 
155 	/* Store first number only if it is not leading zero */
156 	if ((val & 0xf0) != 0) {
157 		*(ptr++) = hex[(val & 0xf0) >> 4];
158 	}
159 	*(ptr++) = hex[val & 0x0f];
160 
161 	for (i = n - 1; i >= 0; i--) {
162 		val = set->cpus[i];
163 		*(ptr++) = hex[(val & 0xf0) >> 4];
164 		*(ptr++) = hex[val & 0x0f];
165 	}
166 	*ptr = '\0';
167 
168 	return set->str;
169 }
170 
171 static int
172 hex_value(uint8_t c)
173 {
174 #define V(x, y) [x] = y + 1
175 	static const int8_t val[256] = {
176 		V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
177 		V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
178 		V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
179 		V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
180 	};
181 #undef V
182 
183 	return val[c] - 1;
184 }
185 
186 static int
187 parse_list(const char *mask, struct spdk_cpuset *set)
188 {
189 	char *end;
190 	const char *ptr = mask;
191 	uint32_t lcore;
192 	uint32_t lcore_min, lcore_max;
193 
194 	spdk_cpuset_zero(set);
195 	lcore_min = UINT32_MAX;
196 
197 	ptr++;
198 	end = (char *)ptr;
199 	do {
200 		while (isblank(*ptr)) {
201 			ptr++;
202 		}
203 		if (*ptr == '\0' || *ptr == ']' || *ptr == '-' || *ptr == ',') {
204 			goto invalid_character;
205 		}
206 
207 		errno = 0;
208 		lcore = strtoul(ptr, &end, 10);
209 		if (errno) {
210 			SPDK_ERRLOG("Conversion of core mask in '%s' failed\n", mask);
211 			return -1;
212 		}
213 
214 		if (lcore >= sizeof(set->cpus) * 8) {
215 			SPDK_ERRLOG("Core number %" PRIu32 " is out of range in '%s'\n", lcore, mask);
216 			return -1;
217 		}
218 
219 		while (isblank(*end)) {
220 			end++;
221 		}
222 
223 		if (*end == '-') {
224 			lcore_min = lcore;
225 		} else if (*end == ',' || *end == ']') {
226 			lcore_max = lcore;
227 			if (lcore_min == UINT32_MAX) {
228 				lcore_min = lcore;
229 			}
230 			if (lcore_min > lcore_max) {
231 				SPDK_ERRLOG("Invalid range of CPUs (%" PRIu32 " > %" PRIu32 ")\n",
232 					    lcore_min, lcore_max);
233 				return -1;
234 			}
235 			for (lcore = lcore_min; lcore <= lcore_max; lcore++) {
236 				spdk_cpuset_set_cpu(set, lcore, true);
237 			}
238 			lcore_min = UINT32_MAX;
239 		} else {
240 			goto invalid_character;
241 		}
242 
243 		ptr = end + 1;
244 
245 	} while (*end != ']');
246 
247 	return 0;
248 
249 invalid_character:
250 	if (*end == '\0') {
251 		SPDK_ERRLOG("Unexpected end of core list '%s'\n", mask);
252 	} else {
253 		SPDK_ERRLOG("Parsing of core list '%s' failed on character '%c'\n", mask, *end);
254 	}
255 	return -1;
256 }
257 
258 static int
259 parse_mask(const char *mask, struct spdk_cpuset *set, size_t len)
260 {
261 	int i, j;
262 	char c;
263 	int val;
264 	uint32_t lcore = 0;
265 
266 	if (mask[0] == '0' && (mask[1] == 'x' || mask[1] == 'X')) {
267 		mask += 2;
268 		len -= 2;
269 	}
270 
271 	spdk_cpuset_zero(set);
272 	for (i = len - 1; i >= 0; i--) {
273 		c = mask[i];
274 		val = hex_value(c);
275 		if (val < 0) {
276 			/* Invalid character */
277 			SPDK_ERRLOG("Invalid character in core mask '%s' (%c)\n", mask, c);
278 			return -1;
279 		}
280 		for (j = 0; j < 4 && lcore < sizeof(set->cpus); j++, lcore++) {
281 			if ((1 << j) & val) {
282 				spdk_cpuset_set_cpu(set, lcore, true);
283 			}
284 		}
285 	}
286 
287 	return 0;
288 }
289 
290 int
291 spdk_cpuset_parse(struct spdk_cpuset *set, const char *mask)
292 {
293 	int ret;
294 	size_t len;
295 
296 	if (mask == NULL || set == NULL) {
297 		return -1;
298 	}
299 
300 	while (isblank(*mask)) {
301 		mask++;
302 	}
303 
304 	len = strlen(mask);
305 	while (len > 0 && isblank(mask[len - 1])) {
306 		len--;
307 	}
308 
309 	if (len == 0) {
310 		return -1;
311 	}
312 
313 	if (mask[0] == '[') {
314 		ret = parse_list(mask, set);
315 	} else {
316 		ret = parse_mask(mask, set, len);
317 	}
318 
319 	return ret;
320 }
321