mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	lib: rework bitmap_parse()
bitmap_parse() is ineffective and full of opaque variables and opencoded parts. It leads to hard understanding and usage of it. This rework includes: - remove bitmap_shift_left() call from the cycle. Now it makes the complexity of the algorithm as O(nbits^2). In the suggested approach the input string is parsed in reverse direction, so no shifts needed; - relax requirement on a single comma and no white spaces between chunks. It is considered useful in scripting, and it aligns with bitmap_parselist(); - split bitmap_parse() to small readable helpers; - make an explicit calculation of the end of input line at the beginning, so users of the bitmap_parse() won't bother doing this. Link: http://lkml.kernel.org/r/20200102043031.30357-6-yury.norov@gmail.com Signed-off-by: Yury Norov <yury.norov@gmail.com> Cc: Amritha Nambiar <amritha.nambiar@intel.com> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Kees Cook <keescook@chromium.org> Cc: Matthew Wilcox <willy@infradead.org> Cc: Miklos Szeredi <mszeredi@redhat.com> Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk> Cc: Steffen Klassert <steffen.klassert@secunet.com> Cc: "Tobin C . Harding" <tobin@kernel.org> Cc: Vineet Gupta <vineet.gupta1@synopsys.com> Cc: Will Deacon <will.deacon@arm.com> Cc: Willem de Bruijn <willemb@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									e66eda0615
								
							
						
					
					
						commit
						2d6261583b
					
				
					 2 changed files with 84 additions and 99 deletions
				
			
		| 
						 | 
					@ -186,7 +186,7 @@ bitmap_find_next_zero_area(unsigned long *map,
 | 
				
			||||||
					      align_mask, 0);
 | 
										      align_mask, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,
 | 
					extern int bitmap_parse(const char *buf, unsigned int buflen,
 | 
				
			||||||
			unsigned long *dst, int nbits);
 | 
								unsigned long *dst, int nbits);
 | 
				
			||||||
extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
 | 
					extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
 | 
				
			||||||
			unsigned long *dst, int nbits);
 | 
								unsigned long *dst, int nbits);
 | 
				
			||||||
| 
						 | 
					@ -454,12 +454,6 @@ static inline void bitmap_replace(unsigned long *dst,
 | 
				
			||||||
		__bitmap_replace(dst, old, new, mask, nbits);
 | 
							__bitmap_replace(dst, old, new, mask, nbits);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int bitmap_parse(const char *buf, unsigned int buflen,
 | 
					 | 
				
			||||||
			unsigned long *maskp, int nmaskbits)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline void bitmap_next_clear_region(unsigned long *bitmap,
 | 
					static inline void bitmap_next_clear_region(unsigned long *bitmap,
 | 
				
			||||||
					    unsigned int *rs, unsigned int *re,
 | 
										    unsigned int *rs, unsigned int *re,
 | 
				
			||||||
					    unsigned int end)
 | 
										    unsigned int end)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										175
									
								
								lib/bitmap.c
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								lib/bitmap.c
									
									
									
									
									
								
							| 
						 | 
					@ -431,97 +431,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off);
 | 
				
			||||||
 * second version by Paul Jackson, third by Joe Korty.
 | 
					 * second version by Paul Jackson, third by Joe Korty.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CHUNKSZ				32
 | 
					 | 
				
			||||||
#define nbits_to_hold_value(val)	fls(val)
 | 
					 | 
				
			||||||
#define BASEDEC 10		/* fancier cpuset lists input in decimal */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * __bitmap_parse - convert an ASCII hex string into a bitmap.
 | 
					 | 
				
			||||||
 * @buf: pointer to buffer containing string.
 | 
					 | 
				
			||||||
 * @buflen: buffer size in bytes.  If string is smaller than this
 | 
					 | 
				
			||||||
 *    then it must be terminated with a \0.
 | 
					 | 
				
			||||||
 * @is_user: location of buffer, 0 indicates kernel space
 | 
					 | 
				
			||||||
 * @maskp: pointer to bitmap array that will contain result.
 | 
					 | 
				
			||||||
 * @nmaskbits: size of bitmap, in bits.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Commas group hex digits into chunks.  Each chunk defines exactly 32
 | 
					 | 
				
			||||||
 * bits of the resultant bitmask.  No chunk may specify a value larger
 | 
					 | 
				
			||||||
 * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
 | 
					 | 
				
			||||||
 * then leading 0-bits are prepended.  %-EINVAL is returned for illegal
 | 
					 | 
				
			||||||
 * characters and for grouping errors such as "1,,5", ",44", "," and "".
 | 
					 | 
				
			||||||
 * Leading and trailing whitespace accepted, but not embedded whitespace.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
int __bitmap_parse(const char *buf, unsigned int buflen,
 | 
					 | 
				
			||||||
		int is_user, unsigned long *maskp,
 | 
					 | 
				
			||||||
		int nmaskbits)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int c, old_c, totaldigits, ndigits, nchunks, nbits;
 | 
					 | 
				
			||||||
	u32 chunk;
 | 
					 | 
				
			||||||
	const char __user __force *ubuf = (const char __user __force *)buf;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bitmap_zero(maskp, nmaskbits);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	nchunks = nbits = totaldigits = c = 0;
 | 
					 | 
				
			||||||
	do {
 | 
					 | 
				
			||||||
		chunk = 0;
 | 
					 | 
				
			||||||
		ndigits = totaldigits;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Get the next chunk of the bitmap */
 | 
					 | 
				
			||||||
		while (buflen) {
 | 
					 | 
				
			||||||
			old_c = c;
 | 
					 | 
				
			||||||
			if (is_user) {
 | 
					 | 
				
			||||||
				if (__get_user(c, ubuf++))
 | 
					 | 
				
			||||||
					return -EFAULT;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				c = *buf++;
 | 
					 | 
				
			||||||
			buflen--;
 | 
					 | 
				
			||||||
			if (isspace(c))
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/*
 | 
					 | 
				
			||||||
			 * If the last character was a space and the current
 | 
					 | 
				
			||||||
			 * character isn't '\0', we've got embedded whitespace.
 | 
					 | 
				
			||||||
			 * This is a no-no, so throw an error.
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			if (totaldigits && c && isspace(old_c))
 | 
					 | 
				
			||||||
				return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* A '\0' or a ',' signal the end of the chunk */
 | 
					 | 
				
			||||||
			if (c == '\0' || c == ',')
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!isxdigit(c))
 | 
					 | 
				
			||||||
				return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/*
 | 
					 | 
				
			||||||
			 * Make sure there are at least 4 free bits in 'chunk'.
 | 
					 | 
				
			||||||
			 * If not, this hexdigit will overflow 'chunk', so
 | 
					 | 
				
			||||||
			 * throw an error.
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			if (chunk & ~((1UL << (CHUNKSZ - 4)) - 1))
 | 
					 | 
				
			||||||
				return -EOVERFLOW;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			chunk = (chunk << 4) | hex_to_bin(c);
 | 
					 | 
				
			||||||
			totaldigits++;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (ndigits == totaldigits)
 | 
					 | 
				
			||||||
			return -EINVAL;
 | 
					 | 
				
			||||||
		if (nchunks == 0 && chunk == 0)
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		__bitmap_shift_left(maskp, maskp, CHUNKSZ, nmaskbits);
 | 
					 | 
				
			||||||
		*maskp |= chunk;
 | 
					 | 
				
			||||||
		nchunks++;
 | 
					 | 
				
			||||||
		nbits += (nchunks == 1) ? nbits_to_hold_value(chunk) : CHUNKSZ;
 | 
					 | 
				
			||||||
		if (nbits > nmaskbits)
 | 
					 | 
				
			||||||
			return -EOVERFLOW;
 | 
					 | 
				
			||||||
	} while (buflen && c == ',');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
EXPORT_SYMBOL(__bitmap_parse);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap
 | 
					 * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -542,7 +451,7 @@ int bitmap_parse_user(const char __user *ubuf,
 | 
				
			||||||
	if (IS_ERR(buf))
 | 
						if (IS_ERR(buf))
 | 
				
			||||||
		return PTR_ERR(buf);
 | 
							return PTR_ERR(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = bitmap_parse(buf, ulen, maskp, nmaskbits);
 | 
						ret = bitmap_parse(buf, UINT_MAX, maskp, nmaskbits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(buf);
 | 
						kfree(buf);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					@ -653,6 +562,14 @@ static const char *bitmap_find_region(const char *str)
 | 
				
			||||||
	return end_of_str(*str) ? NULL : str;
 | 
						return end_of_str(*str) ? NULL : str;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *bitmap_find_region_reverse(const char *start, const char *end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (start <= end && __end_of_region(*end))
 | 
				
			||||||
 | 
							end--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *bitmap_parse_region(const char *str, struct region *r)
 | 
					static const char *bitmap_parse_region(const char *str, struct region *r)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	str = bitmap_getnum(str, &r->start);
 | 
						str = bitmap_getnum(str, &r->start);
 | 
				
			||||||
| 
						 | 
					@ -776,6 +693,80 @@ int bitmap_parselist_user(const char __user *ubuf,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(bitmap_parselist_user);
 | 
					EXPORT_SYMBOL(bitmap_parselist_user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *bitmap_get_x32_reverse(const char *start,
 | 
				
			||||||
 | 
										const char *end, u32 *num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 ret = 0;
 | 
				
			||||||
 | 
						int c, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < 32; i += 4) {
 | 
				
			||||||
 | 
							c = hex_to_bin(*end--);
 | 
				
			||||||
 | 
							if (c < 0)
 | 
				
			||||||
 | 
								return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret |= c << i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (start > end || __end_of_region(*end))
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (hex_to_bin(*end--) >= 0)
 | 
				
			||||||
 | 
							return ERR_PTR(-EOVERFLOW);
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						*num = ret;
 | 
				
			||||||
 | 
						return end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * bitmap_parse - convert an ASCII hex string into a bitmap.
 | 
				
			||||||
 | 
					 * @start: pointer to buffer containing string.
 | 
				
			||||||
 | 
					 * @buflen: buffer size in bytes.  If string is smaller than this
 | 
				
			||||||
 | 
					 *    then it must be terminated with a \0 or \n. In that case,
 | 
				
			||||||
 | 
					 *    UINT_MAX may be provided instead of string length.
 | 
				
			||||||
 | 
					 * @maskp: pointer to bitmap array that will contain result.
 | 
				
			||||||
 | 
					 * @nmaskbits: size of bitmap, in bits.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Commas group hex digits into chunks.  Each chunk defines exactly 32
 | 
				
			||||||
 | 
					 * bits of the resultant bitmask.  No chunk may specify a value larger
 | 
				
			||||||
 | 
					 * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
 | 
				
			||||||
 | 
					 * then leading 0-bits are prepended.  %-EINVAL is returned for illegal
 | 
				
			||||||
 | 
					 * characters. Grouping such as "1,,5", ",44", "," or "" is allowed.
 | 
				
			||||||
 | 
					 * Leading, embedded and trailing whitespace accepted.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int bitmap_parse(const char *start, unsigned int buflen,
 | 
				
			||||||
 | 
							unsigned long *maskp, int nmaskbits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *end = strnchrnul(start, buflen, '\n') - 1;
 | 
				
			||||||
 | 
						int chunks = BITS_TO_U32(nmaskbits);
 | 
				
			||||||
 | 
						u32 *bitmap = (u32 *)maskp;
 | 
				
			||||||
 | 
						int unset_bit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (1) {
 | 
				
			||||||
 | 
							end = bitmap_find_region_reverse(start, end);
 | 
				
			||||||
 | 
							if (start > end)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!chunks--)
 | 
				
			||||||
 | 
								return -EOVERFLOW;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							end = bitmap_get_x32_reverse(start, end, bitmap++);
 | 
				
			||||||
 | 
							if (IS_ERR(end))
 | 
				
			||||||
 | 
								return PTR_ERR(end);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unset_bit = (BITS_TO_U32(nmaskbits) - chunks) * 32;
 | 
				
			||||||
 | 
						if (unset_bit < nmaskbits) {
 | 
				
			||||||
 | 
							bitmap_clear(maskp, unset_bit, nmaskbits - unset_bit);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (find_next_bit(maskp, unset_bit, nmaskbits) != unset_bit)
 | 
				
			||||||
 | 
							return -EOVERFLOW;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(bitmap_parse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_NUMA
 | 
					#ifdef CONFIG_NUMA
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue