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.
Unified · Split
| @@ -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 | ||
| 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, |
| 190 | unsigned long *dst, int nbits); | 190 | unsigned long *dst, int nbits); |
| 191 | extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen, | 191 | extern 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 | ||
| 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 | |||
| 463 | static inline void bitmap_next_clear_region(unsigned long *bitmap, | 457 | static 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) |
| @@ -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 | */ | ||
| 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 | |||
| 525 | /** | 434 | /** |
| 526 | * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap | 435 | * 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 | ||
| 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 | |||
| 656 | static const char *bitmap_parse_region(const char *str, struct region *r) | 573 | static 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 | } |
| 777 | EXPORT_SYMBOL(bitmap_parselist_user); | 694 | EXPORT_SYMBOL(bitmap_parselist_user); |
| 778 | 695 | ||
| 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 | |||
| 779 | 770 | ||
| 780 | #ifdef CONFIG_NUMA | 771 | #ifdef CONFIG_NUMA |
| 781 | /** | 772 | /** |