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 | 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 | 190 | unsigned long *dst, int nbits); |
| 191 | 191 | extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen, |
| 192 | 192 | unsigned long *dst, int nbits); |
| @@ -454,12 +454,6 @@ static inline void bitmap_replace(unsigned long *dst, · 261 unmodified lines | ||
| 454 | 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 | 457 | static inline void bitmap_next_clear_region(unsigned long *bitmap, |
| 464 | 458 | unsigned int *rs, unsigned int *re, |
| 465 | 459 | unsigned int end) |
| @@ -431,97 +431,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off); · 430 unmodified lines | ||
| 431 | 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 | 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 | 451 | if (IS_ERR(buf)) |
| 543 | 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 | 456 | kfree(buf); |
| 548 | 457 | return ret; |
| @@ -653,6 +562,14 @@ static const char *bitmap_find_region(const char *str) · 104 unmodified lines | ||
| 653 | 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 | 573 | static const char *bitmap_parse_region(const char *str, struct region *r) |
| 657 | 574 | { |
| 658 | 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 | 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 | 771 | #ifdef CONFIG_NUMA |
| 781 | 772 | /** |