xref: /kvmtool/util/bitmap.c (revision 0febaae00bb6f8c5e694f87d6354fbcbe81e6653)
1 /*
2  * Taken from Linux kernel version v5.15.
3  */
4 #include <ctype.h>
5 #include <limits.h>
6 #include <stdlib.h>
7 #include <strings.h>
8 
9 #include "linux/bitmap.h"
10 #include "linux/bitops.h"
11 #include "linux/err.h"
12 
13 /*
14  * Region 9-38:4/10 describes the following bitmap structure:
15  * 0	   9  12    18			38	     N
16  * .........****......****......****..................
17  *	    ^  ^     ^			 ^	     ^
18  *      start  off   group_len	       end	 nbits
19  */
20 struct region {
21 	unsigned int start;
22 	unsigned int off;
23 	unsigned int group_len;
24 	unsigned int end;
25 	unsigned int nbits;
26 };
27 
__bitmap_set(unsigned long * map,unsigned int start,int len)28 void __bitmap_set(unsigned long *map, unsigned int start, int len)
29 {
30 	unsigned long *p = map + BIT_WORD(start);
31 	const unsigned int size = start + len;
32 	int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
33 	unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
34 
35 	while (len - bits_to_set >= 0) {
36 		*p |= mask_to_set;
37 		len -= bits_to_set;
38 		bits_to_set = BITS_PER_LONG;
39 		mask_to_set = ~0UL;
40 		p++;
41 	}
42 	if (len) {
43 		mask_to_set &= BITMAP_LAST_WORD_MASK(size);
44 		*p |= mask_to_set;
45 	}
46 }
47 
bitmap_set_region(const struct region * r,unsigned long * bitmap)48 static void bitmap_set_region(const struct region *r, unsigned long *bitmap)
49 {
50 	unsigned int start;
51 
52 	for (start = r->start; start <= r->end; start += r->group_len)
53 		bitmap_set(bitmap, start, min(r->end - start + 1, r->off));
54 }
55 
__end_of_region(char c)56 static inline bool __end_of_region(char c)
57 {
58 	return isspace(c) || c == ',';
59 }
60 
end_of_str(char c)61 static inline bool end_of_str(char c)
62 {
63 	return c == '\0' || c == '\n';
64 }
65 
end_of_region(char c)66 static inline bool end_of_region(char c)
67 {
68 	return __end_of_region(c) || end_of_str(c);
69 }
70 
71 /*
72  * The format allows commas and whitespaces at the beginning
73  * of the region.
74  */
bitmap_find_region(const char * str)75 static const char *bitmap_find_region(const char *str)
76 {
77 	while (__end_of_region(*str))
78 		str++;
79 
80 	return end_of_str(*str) ? NULL : str;
81 }
82 
bitmap_check_region(const struct region * r)83 static int bitmap_check_region(const struct region *r)
84 {
85 	if (r->start > r->end || r->group_len == 0 || r->off > r->group_len)
86 		return -EINVAL;
87 
88 	if (r->end >= r->nbits)
89 		return -ERANGE;
90 
91 	return 0;
92 }
93 
bitmap_getnum(const char * str,unsigned int * num,unsigned int lastbit)94 static const char *bitmap_getnum(const char *str, unsigned int *num,
95 				 unsigned int lastbit)
96 {
97 	unsigned long long n;
98 	char *endptr;
99 
100 	if (str[0] == 'N') {
101 		*num = lastbit;
102 		return str + 1;
103 	}
104 
105 	n = strtoll(str, &endptr, 10);
106 	/* No digits found. */
107 	if (n == 0 && endptr == str)
108 		return ERR_PTR(-EINVAL);
109 	/* Check for overflows and negative numbers. */
110 	if (n == ULLONG_MAX || n != (unsigned long)n || n != (unsigned int)n)
111 		return ERR_PTR(-EOVERFLOW);
112 
113 	*num = n;
114 	return endptr;
115 }
116 
bitmap_parse_region(const char * str,struct region * r)117 static const char *bitmap_parse_region(const char *str, struct region *r)
118 {
119 	unsigned int lastbit = r->nbits - 1;
120 
121 	if (!strncasecmp(str, "all", 3)) {
122 		r->start = 0;
123 		r->end = lastbit;
124 		str += 3;
125 
126 		goto check_pattern;
127 	}
128 
129 	str = bitmap_getnum(str, &r->start, lastbit);
130 	if (IS_ERR(str))
131 		return str;
132 
133 	if (end_of_region(*str))
134 		goto no_end;
135 
136 	if (*str != '-')
137 		return ERR_PTR(-EINVAL);
138 
139 	str = bitmap_getnum(str + 1, &r->end, lastbit);
140 	if (IS_ERR(str))
141 		return str;
142 
143 check_pattern:
144 	if (end_of_region(*str))
145 		goto no_pattern;
146 
147 	if (*str != ':')
148 		return ERR_PTR(-EINVAL);
149 
150 	str = bitmap_getnum(str + 1, &r->off, lastbit);
151 	if (IS_ERR(str))
152 		return str;
153 
154 	if (*str != '/')
155 		return ERR_PTR(-EINVAL);
156 
157 	return bitmap_getnum(str + 1, &r->group_len, lastbit);
158 
159 no_end:
160 	r->end = r->start;
161 no_pattern:
162 	r->off = r->end + 1;
163 	r->group_len = r->end + 1;
164 
165 	return end_of_str(*str) ? NULL : str;
166 }
167 
168 /**
169  * bitmap_parselist - convert list format ASCII string to bitmap
170  * @buf: read user string from this buffer; must be terminated
171  *    with a \0 or \n.
172  * @maskp: write resulting mask here
173  * @nmaskbits: number of bits in mask to be written
174  *
175  * Input format is a comma-separated list of decimal numbers and
176  * ranges.  Consecutively set bits are shown as two hyphen-separated
177  * decimal numbers, the smallest and largest bit numbers set in
178  * the range.
179  * Optionally each range can be postfixed to denote that only parts of it
180  * should be set. The range will divided to groups of specific size.
181  * From each group will be used only defined amount of bits.
182  * Syntax: range:used_size/group_size
183  * Example: 0-1023:2/256 ==> 0,1,256,257,512,513,768,769
184  * The value 'N' can be used as a dynamically substituted token for the
185  * maximum allowed value; i.e (nmaskbits - 1).  Keep in mind that it is
186  * dynamic, so if system changes cause the bitmap width to change, such
187  * as more cores in a CPU list, then any ranges using N will also change.
188  *
189  * Returns: 0 on success, -errno on invalid input strings. Error values:
190  *
191  *   - ``-EINVAL``: wrong region format
192  *   - ``-EINVAL``: invalid character in string
193  *   - ``-ERANGE``: bit number specified too large for mask
194  *   - ``-EOVERFLOW``: integer overflow in the input parameters
195  */
bitmap_parselist(const char * buf,unsigned long * maskp,int nmaskbits)196 int bitmap_parselist(const char *buf, unsigned long *maskp, int nmaskbits)
197 {
198 	struct region r;
199 	long ret;
200 
201 	r.nbits = nmaskbits;
202 	bitmap_zero(maskp, r.nbits);
203 
204 	while (buf) {
205 		buf = bitmap_find_region(buf);
206 		if (buf == NULL)
207 			return 0;
208 
209 		buf = bitmap_parse_region(buf, &r);
210 		if (IS_ERR(buf))
211 			return PTR_ERR(buf);
212 
213 		ret = bitmap_check_region(&r);
214 		if (ret)
215 			return ret;
216 
217 		bitmap_set_region(&r, maskp);
218 	}
219 
220 	return 0;
221 }
222 
__bitmap_and(unsigned long * dst,const unsigned long * src1,const unsigned long * src2,unsigned int nbits)223 bool __bitmap_and(unsigned long *dst, const unsigned long *src1,
224 		  const unsigned long *src2, unsigned int nbits)
225 {
226 	unsigned int lim = nbits / BITS_PER_LONG;
227 	unsigned long result = 0;
228 	unsigned int k;
229 
230 	for (k = 0; k < lim; k++)
231 		result |= (dst[k] = src1[k] & src2[k]);
232 
233 	if (nbits % BITS_PER_LONG) {
234 		result |= (dst[k] = src1[k] & src2[k] &
235 			   BITMAP_LAST_WORD_MASK(nbits));
236 	}
237 
238 	return result != 0;
239 }
240 
__bitmap_subset(const unsigned long * bitmap1,const unsigned long * bitmap2,unsigned int nbits)241 bool __bitmap_subset(const unsigned long *bitmap1, const unsigned long *bitmap2,
242 		     unsigned int nbits)
243 {
244 	unsigned int k, lim = nbits / BITS_PER_LONG;
245 
246 	for (k = 0; k < lim; k++)
247 		if (bitmap1[k] & ~bitmap2[k])
248 			return false;
249 
250 	if (nbits % BITS_PER_LONG) {
251 		if ((bitmap1[k] & ~bitmap2[k]) & BITMAP_LAST_WORD_MASK(nbits))
252 			return false;
253 	}
254 
255 	return true;
256 }
257