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
186 align_mask, 0);186 align_mask, 0);
187}187}
188 188
189extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,189extern int bitmap_parse(const char *buf, unsigned int buflen,
190 unsigned long *dst, int nbits);190 unsigned long *dst, int nbits);
191extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,191extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
192 unsigned long *dst, int nbits);192 unsigned long *dst, int nbits);
@@ -454,12 +454,6 @@ static inline void bitmap_replace(unsigned long *dst, · 261 unmodified lines
454 __bitmap_replace(dst, old, new, mask, nbits);454 __bitmap_replace(dst, old, new, mask, nbits);
455}455}
456 456
457static 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
463static inline void bitmap_next_clear_region(unsigned long *bitmap,457static inline void bitmap_next_clear_region(unsigned long *bitmap,
464 unsigned int *rs, unsigned int *re,458 unsigned int *rs, unsigned int *re,
465 unsigned int end)459 unsigned int end)
lib/bitmap.c+83−92
@@ -431,97 +431,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off); · 430 unmodified lines
431 * second version by Paul Jackson, third by Joe Korty.431 * second version by Paul Jackson, third by Joe Korty.
432 */432 */
433 433
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 */
454int __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}
523EXPORT_SYMBOL(__bitmap_parse);
524
525/**434/**
526 * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap435 * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap
527 *436 *
@@ -542,7 +451,7 @@ int bitmap_parse_user(const char __user *ubuf, · 14 unmodified lines
542 if (IS_ERR(buf))451 if (IS_ERR(buf))
543 return PTR_ERR(buf);452 return PTR_ERR(buf);
544 453
545 ret = bitmap_parse(buf, ulen, maskp, nmaskbits);454 ret = bitmap_parse(buf, UINT_MAX, maskp, nmaskbits);
546 455
547 kfree(buf);456 kfree(buf);
548 return ret;457 return ret;
@@ -653,6 +562,14 @@ static const char *bitmap_find_region(const char *str) · 104 unmodified lines
653 return end_of_str(*str) ? NULL : str;562 return end_of_str(*str) ? NULL : str;
654}563}
655 564
565static 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
656static const char *bitmap_parse_region(const char *str, struct region *r)573static const char *bitmap_parse_region(const char *str, struct region *r)
657{574{
658 str = bitmap_getnum(str, &r->start);575 str = bitmap_getnum(str, &r->start);
@@ -776,6 +693,80 @@ int bitmap_parselist_user(const char __user *ubuf, · 117 unmodified lines
776}693}
777EXPORT_SYMBOL(bitmap_parselist_user);694EXPORT_SYMBOL(bitmap_parselist_user);
778 695
696static 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);
715out:
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 */
736int 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}
768EXPORT_SYMBOL(bitmap_parse);
769
779 770
780#ifdef CONFIG_NUMA771#ifdef CONFIG_NUMA
781/**772/**

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