History

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.

Yury Norov committed 6 years ago

Commit 2d626151 parent e66eda02 files · +84 −99Browse files

Unified · Split

2 files
  1. include/linux/bitmap.h +1 −7
  2. lib/bitmap.c +83 −92
include/linux/bitmap.h+1−7
@@ -186,7 +186,7 @@ bitmap_find_next_zero_area(unsigned long *map, · 185 unmodified lines
186186 align_mask, 0);
187187 }
188188
189−extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,
189+extern int bitmap_parse(const char *buf, unsigned int buflen,
190190 unsigned long *dst, int nbits);
191191 extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
192192 unsigned long *dst, int nbits);
@@ -454,12 +454,6 @@ static inline void bitmap_replace(unsigned long *dst, · 261 unmodified lines
454454 __bitmap_replace(dst, old, new, mask, nbits);
455455 }
456456
457−static inline int bitmap_parse(const char *buf, unsigned int buflen,
458− unsigned long *maskp, int nmaskbits)
459−{
460− return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits);
461−}
462−
463457 static inline void bitmap_next_clear_region(unsigned long *bitmap,
464458 unsigned int *rs, unsigned int *re,
465459 unsigned int end)
lib/bitmap.c+83−92
@@ -431,97 +431,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off); · 430 unmodified lines
431431 * second version by Paul Jackson, third by Joe Korty.
432432 */
433433
434−#define CHUNKSZ 32
435−#define nbits_to_hold_value(val) fls(val)
436−#define BASEDEC 10 /* fancier cpuset lists input in decimal */
437−
438−/**
439− * __bitmap_parse - convert an ASCII hex string into a bitmap.
440− * @buf: pointer to buffer containing string.
441− * @buflen: buffer size in bytes. If string is smaller than this
442− * then it must be terminated with a \0.
443− * @is_user: location of buffer, 0 indicates kernel space
444− * @maskp: pointer to bitmap array that will contain result.
445− * @nmaskbits: size of bitmap, in bits.
446− *
447− * Commas group hex digits into chunks. Each chunk defines exactly 32
448− * bits of the resultant bitmask. No chunk may specify a value larger
449− * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
450− * then leading 0-bits are prepended. %-EINVAL is returned for illegal
451− * characters and for grouping errors such as "1,,5", ",44", "," and "".
452− * Leading and trailing whitespace accepted, but not embedded whitespace.
453− */
454−int __bitmap_parse(const char *buf, unsigned int buflen,
455− int is_user, unsigned long *maskp,
456− int nmaskbits)
457−{
458− int c, old_c, totaldigits, ndigits, nchunks, nbits;
459− u32 chunk;
460− const char __user __force *ubuf = (const char __user __force *)buf;
461−
462− bitmap_zero(maskp, nmaskbits);
463−
464− nchunks = nbits = totaldigits = c = 0;
465− do {
466− chunk = 0;
467− ndigits = totaldigits;
468−
469− /* Get the next chunk of the bitmap */
470− while (buflen) {
471− old_c = c;
472− if (is_user) {
473− if (__get_user(c, ubuf++))
474− return -EFAULT;
475− }
476− else
477− c = *buf++;
478− buflen--;
479− if (isspace(c))
480− continue;
481−
482− /*
483− * If the last character was a space and the current
484− * character isn't '\0', we've got embedded whitespace.
485− * This is a no-no, so throw an error.
486− */
487− if (totaldigits && c && isspace(old_c))
488− return -EINVAL;
489−
490− /* A '\0' or a ',' signal the end of the chunk */
491− if (c == '\0' || c == ',')
492− break;
493−
494− if (!isxdigit(c))
495− return -EINVAL;
496−
497− /*
498− * Make sure there are at least 4 free bits in 'chunk'.
499− * If not, this hexdigit will overflow 'chunk', so
500− * throw an error.
501− */
502− if (chunk & ~((1UL << (CHUNKSZ - 4)) - 1))
503− return -EOVERFLOW;
504−
505− chunk = (chunk << 4) | hex_to_bin(c);
506− totaldigits++;
507− }
508− if (ndigits == totaldigits)
509− return -EINVAL;
510− if (nchunks == 0 && chunk == 0)
511− continue;
512−
513− __bitmap_shift_left(maskp, maskp, CHUNKSZ, nmaskbits);
514− *maskp |= chunk;
515− nchunks++;
516− nbits += (nchunks == 1) ? nbits_to_hold_value(chunk) : CHUNKSZ;
517− if (nbits > nmaskbits)
518− return -EOVERFLOW;
519− } while (buflen && c == ',');
520−
521− return 0;
522−}
523−EXPORT_SYMBOL(__bitmap_parse);
524−
525434 /**
526435 * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap
527436 *
@@ -542,7 +451,7 @@ int bitmap_parse_user(const char __user *ubuf, · 14 unmodified lines
542451 if (IS_ERR(buf))
543452 return PTR_ERR(buf);
544453
545− ret = bitmap_parse(buf, ulen, maskp, nmaskbits);
454+ ret = bitmap_parse(buf, UINT_MAX, maskp, nmaskbits);
546455
547456 kfree(buf);
548457 return ret;
@@ -653,6 +562,14 @@ static const char *bitmap_find_region(const char *str) · 104 unmodified lines
653562 return end_of_str(*str) ? NULL : str;
654563 }
655564
565+static const char *bitmap_find_region_reverse(const char *start, const char *end)
566+{
567+ while (start <= end && __end_of_region(*end))
568+ end--;
569+
570+ return end;
571+}
572+
656573 static const char *bitmap_parse_region(const char *str, struct region *r)
657574 {
658575 str = bitmap_getnum(str, &r->start);
@@ -776,6 +693,80 @@ int bitmap_parselist_user(const char __user *ubuf, · 117 unmodified lines
776693 }
777694 EXPORT_SYMBOL(bitmap_parselist_user);
778695
696+static const char *bitmap_get_x32_reverse(const char *start,
697+ const char *end, u32 *num)
698+{
699+ u32 ret = 0;
700+ int c, i;
701+
702+ for (i = 0; i < 32; i += 4) {
703+ c = hex_to_bin(*end--);
704+ if (c < 0)
705+ return ERR_PTR(-EINVAL);
706+
707+ ret |= c << i;
708+
709+ if (start > end || __end_of_region(*end))
710+ goto out;
711+ }
712+
713+ if (hex_to_bin(*end--) >= 0)
714+ return ERR_PTR(-EOVERFLOW);
715+out:
716+ *num = ret;
717+ return end;
718+}
719+
720+/**
721+ * bitmap_parse - convert an ASCII hex string into a bitmap.
722+ * @start: pointer to buffer containing string.
723+ * @buflen: buffer size in bytes. If string is smaller than this
724+ * then it must be terminated with a \0 or \n. In that case,
725+ * UINT_MAX may be provided instead of string length.
726+ * @maskp: pointer to bitmap array that will contain result.
727+ * @nmaskbits: size of bitmap, in bits.
728+ *
729+ * Commas group hex digits into chunks. Each chunk defines exactly 32
730+ * bits of the resultant bitmask. No chunk may specify a value larger
731+ * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
732+ * then leading 0-bits are prepended. %-EINVAL is returned for illegal
733+ * characters. Grouping such as "1,,5", ",44", "," or "" is allowed.
734+ * Leading, embedded and trailing whitespace accepted.
735+ */
736+int bitmap_parse(const char *start, unsigned int buflen,
737+ unsigned long *maskp, int nmaskbits)
738+{
739+ const char *end = strnchrnul(start, buflen, '\n') - 1;
740+ int chunks = BITS_TO_U32(nmaskbits);
741+ u32 *bitmap = (u32 *)maskp;
742+ int unset_bit;
743+
744+ while (1) {
745+ end = bitmap_find_region_reverse(start, end);
746+ if (start > end)
747+ break;
748+
749+ if (!chunks--)
750+ return -EOVERFLOW;
751+
752+ end = bitmap_get_x32_reverse(start, end, bitmap++);
753+ if (IS_ERR(end))
754+ return PTR_ERR(end);
755+ }
756+
757+ unset_bit = (BITS_TO_U32(nmaskbits) - chunks) * 32;
758+ if (unset_bit < nmaskbits) {
759+ bitmap_clear(maskp, unset_bit, nmaskbits - unset_bit);
760+ return 0;
761+ }
762+
763+ if (find_next_bit(maskp, unset_bit, nmaskbits) != unset_bit)
764+ return -EOVERFLOW;
765+
766+ return 0;
767+}
768+EXPORT_SYMBOL(bitmap_parse);
769+
779770
780771 #ifdef CONFIG_NUMA
781772 /**

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t