diff options
Diffstat (limited to 'lib/bitmap.c')
-rw-r--r-- | lib/bitmap.c | 255 |
1 files changed, 142 insertions, 113 deletions
diff --git a/lib/bitmap.c b/lib/bitmap.c index c63ddd06a5da..f235434df87b 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c | |||
@@ -20,6 +20,8 @@ | |||
20 | 20 | ||
21 | #include <asm/page.h> | 21 | #include <asm/page.h> |
22 | 22 | ||
23 | #include "kstrtox.h" | ||
24 | |||
23 | /** | 25 | /** |
24 | * DOC: bitmap introduction | 26 | * DOC: bitmap introduction |
25 | * | 27 | * |
@@ -477,12 +479,128 @@ int bitmap_print_to_pagebuf(bool list, char *buf, const unsigned long *maskp, | |||
477 | } | 479 | } |
478 | EXPORT_SYMBOL(bitmap_print_to_pagebuf); | 480 | EXPORT_SYMBOL(bitmap_print_to_pagebuf); |
479 | 481 | ||
482 | /* | ||
483 | * Region 9-38:4/10 describes the following bitmap structure: | ||
484 | * 0 9 12 18 38 | ||
485 | * .........****......****......****...... | ||
486 | * ^ ^ ^ ^ | ||
487 | * start off group_len end | ||
488 | */ | ||
489 | struct region { | ||
490 | unsigned int start; | ||
491 | unsigned int off; | ||
492 | unsigned int group_len; | ||
493 | unsigned int end; | ||
494 | }; | ||
495 | |||
496 | static int bitmap_set_region(const struct region *r, | ||
497 | unsigned long *bitmap, int nbits) | ||
498 | { | ||
499 | unsigned int start; | ||
500 | |||
501 | if (r->end >= nbits) | ||
502 | return -ERANGE; | ||
503 | |||
504 | for (start = r->start; start <= r->end; start += r->group_len) | ||
505 | bitmap_set(bitmap, start, min(r->end - start + 1, r->off)); | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static int bitmap_check_region(const struct region *r) | ||
511 | { | ||
512 | if (r->start > r->end || r->group_len == 0 || r->off > r->group_len) | ||
513 | return -EINVAL; | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static const char *bitmap_getnum(const char *str, unsigned int *num) | ||
519 | { | ||
520 | unsigned long long n; | ||
521 | unsigned int len; | ||
522 | |||
523 | len = _parse_integer(str, 10, &n); | ||
524 | if (!len) | ||
525 | return ERR_PTR(-EINVAL); | ||
526 | if (len & KSTRTOX_OVERFLOW || n != (unsigned int)n) | ||
527 | return ERR_PTR(-EOVERFLOW); | ||
528 | |||
529 | *num = n; | ||
530 | return str + len; | ||
531 | } | ||
532 | |||
533 | static inline bool end_of_str(char c) | ||
534 | { | ||
535 | return c == '\0' || c == '\n'; | ||
536 | } | ||
537 | |||
538 | static inline bool __end_of_region(char c) | ||
539 | { | ||
540 | return isspace(c) || c == ','; | ||
541 | } | ||
542 | |||
543 | static inline bool end_of_region(char c) | ||
544 | { | ||
545 | return __end_of_region(c) || end_of_str(c); | ||
546 | } | ||
547 | |||
548 | /* | ||
549 | * The format allows commas and whitespases at the beginning | ||
550 | * of the region. | ||
551 | */ | ||
552 | static const char *bitmap_find_region(const char *str) | ||
553 | { | ||
554 | while (__end_of_region(*str)) | ||
555 | str++; | ||
556 | |||
557 | return end_of_str(*str) ? NULL : str; | ||
558 | } | ||
559 | |||
560 | static const char *bitmap_parse_region(const char *str, struct region *r) | ||
561 | { | ||
562 | str = bitmap_getnum(str, &r->start); | ||
563 | if (IS_ERR(str)) | ||
564 | return str; | ||
565 | |||
566 | if (end_of_region(*str)) | ||
567 | goto no_end; | ||
568 | |||
569 | if (*str != '-') | ||
570 | return ERR_PTR(-EINVAL); | ||
571 | |||
572 | str = bitmap_getnum(str + 1, &r->end); | ||
573 | if (IS_ERR(str)) | ||
574 | return str; | ||
575 | |||
576 | if (end_of_region(*str)) | ||
577 | goto no_pattern; | ||
578 | |||
579 | if (*str != ':') | ||
580 | return ERR_PTR(-EINVAL); | ||
581 | |||
582 | str = bitmap_getnum(str + 1, &r->off); | ||
583 | if (IS_ERR(str)) | ||
584 | return str; | ||
585 | |||
586 | if (*str != '/') | ||
587 | return ERR_PTR(-EINVAL); | ||
588 | |||
589 | return bitmap_getnum(str + 1, &r->group_len); | ||
590 | |||
591 | no_end: | ||
592 | r->end = r->start; | ||
593 | no_pattern: | ||
594 | r->off = r->end + 1; | ||
595 | r->group_len = r->end + 1; | ||
596 | |||
597 | return end_of_str(*str) ? NULL : str; | ||
598 | } | ||
599 | |||
480 | /** | 600 | /** |
481 | * __bitmap_parselist - convert list format ASCII string to bitmap | 601 | * bitmap_parselist - convert list format ASCII string to bitmap |
482 | * @buf: read nul-terminated user string from this buffer | 602 | * @buf: read user string from this buffer; must be terminated |
483 | * @buflen: buffer size in bytes. If string is smaller than this | 603 | * with a \0 or \n. |
484 | * then it must be terminated with a \0. | ||
485 | * @is_user: location of buffer, 0 indicates kernel space | ||
486 | * @maskp: write resulting mask here | 604 | * @maskp: write resulting mask here |
487 | * @nmaskbits: number of bits in mask to be written | 605 | * @nmaskbits: number of bits in mask to be written |
488 | * | 606 | * |
@@ -498,127 +616,38 @@ EXPORT_SYMBOL(bitmap_print_to_pagebuf); | |||
498 | * | 616 | * |
499 | * Returns: 0 on success, -errno on invalid input strings. Error values: | 617 | * Returns: 0 on success, -errno on invalid input strings. Error values: |
500 | * | 618 | * |
501 | * - ``-EINVAL``: second number in range smaller than first | 619 | * - ``-EINVAL``: wrong region format |
502 | * - ``-EINVAL``: invalid character in string | 620 | * - ``-EINVAL``: invalid character in string |
503 | * - ``-ERANGE``: bit number specified too large for mask | 621 | * - ``-ERANGE``: bit number specified too large for mask |
622 | * - ``-EOVERFLOW``: integer overflow in the input parameters | ||
504 | */ | 623 | */ |
505 | static int __bitmap_parselist(const char *buf, unsigned int buflen, | 624 | int bitmap_parselist(const char *buf, unsigned long *maskp, int nmaskbits) |
506 | int is_user, unsigned long *maskp, | ||
507 | int nmaskbits) | ||
508 | { | 625 | { |
509 | unsigned int a, b, old_a, old_b; | 626 | struct region r; |
510 | unsigned int group_size, used_size, off; | 627 | long ret; |
511 | int c, old_c, totaldigits, ndigits; | ||
512 | const char __user __force *ubuf = (const char __user __force *)buf; | ||
513 | int at_start, in_range, in_partial_range; | ||
514 | 628 | ||
515 | totaldigits = c = 0; | ||
516 | old_a = old_b = 0; | ||
517 | group_size = used_size = 0; | ||
518 | bitmap_zero(maskp, nmaskbits); | 629 | bitmap_zero(maskp, nmaskbits); |
519 | do { | ||
520 | at_start = 1; | ||
521 | in_range = 0; | ||
522 | in_partial_range = 0; | ||
523 | a = b = 0; | ||
524 | ndigits = totaldigits; | ||
525 | |||
526 | /* Get the next cpu# or a range of cpu#'s */ | ||
527 | while (buflen) { | ||
528 | old_c = c; | ||
529 | if (is_user) { | ||
530 | if (__get_user(c, ubuf++)) | ||
531 | return -EFAULT; | ||
532 | } else | ||
533 | c = *buf++; | ||
534 | buflen--; | ||
535 | if (isspace(c)) | ||
536 | continue; | ||
537 | 630 | ||
538 | /* A '\0' or a ',' signal the end of a cpu# or range */ | 631 | while (buf) { |
539 | if (c == '\0' || c == ',') | 632 | buf = bitmap_find_region(buf); |
540 | break; | 633 | if (buf == NULL) |
541 | /* | 634 | return 0; |
542 | * whitespaces between digits are not allowed, | ||
543 | * but it's ok if whitespaces are on head or tail. | ||
544 | * when old_c is whilespace, | ||
545 | * if totaldigits == ndigits, whitespace is on head. | ||
546 | * if whitespace is on tail, it should not run here. | ||
547 | * as c was ',' or '\0', | ||
548 | * the last code line has broken the current loop. | ||
549 | */ | ||
550 | if ((totaldigits != ndigits) && isspace(old_c)) | ||
551 | return -EINVAL; | ||
552 | |||
553 | if (c == '/') { | ||
554 | used_size = a; | ||
555 | at_start = 1; | ||
556 | in_range = 0; | ||
557 | a = b = 0; | ||
558 | continue; | ||
559 | } | ||
560 | 635 | ||
561 | if (c == ':') { | 636 | buf = bitmap_parse_region(buf, &r); |
562 | old_a = a; | 637 | if (IS_ERR(buf)) |
563 | old_b = b; | 638 | return PTR_ERR(buf); |
564 | at_start = 1; | ||
565 | in_range = 0; | ||
566 | in_partial_range = 1; | ||
567 | a = b = 0; | ||
568 | continue; | ||
569 | } | ||
570 | 639 | ||
571 | if (c == '-') { | 640 | ret = bitmap_check_region(&r); |
572 | if (at_start || in_range) | 641 | if (ret) |
573 | return -EINVAL; | 642 | return ret; |
574 | b = 0; | ||
575 | in_range = 1; | ||
576 | at_start = 1; | ||
577 | continue; | ||
578 | } | ||
579 | 643 | ||
580 | if (!isdigit(c)) | 644 | ret = bitmap_set_region(&r, maskp, nmaskbits); |
581 | return -EINVAL; | 645 | if (ret) |
646 | return ret; | ||
647 | } | ||
582 | 648 | ||
583 | b = b * 10 + (c - '0'); | ||
584 | if (!in_range) | ||
585 | a = b; | ||
586 | at_start = 0; | ||
587 | totaldigits++; | ||
588 | } | ||
589 | if (ndigits == totaldigits) | ||
590 | continue; | ||
591 | if (in_partial_range) { | ||
592 | group_size = a; | ||
593 | a = old_a; | ||
594 | b = old_b; | ||
595 | old_a = old_b = 0; | ||
596 | } else { | ||
597 | used_size = group_size = b - a + 1; | ||
598 | } | ||
599 | /* if no digit is after '-', it's wrong*/ | ||
600 | if (at_start && in_range) | ||
601 | return -EINVAL; | ||
602 | if (!(a <= b) || group_size == 0 || !(used_size <= group_size)) | ||
603 | return -EINVAL; | ||
604 | if (b >= nmaskbits) | ||
605 | return -ERANGE; | ||
606 | while (a <= b) { | ||
607 | off = min(b - a + 1, used_size); | ||
608 | bitmap_set(maskp, a, off); | ||
609 | a += group_size; | ||
610 | } | ||
611 | } while (buflen && c == ','); | ||
612 | return 0; | 649 | return 0; |
613 | } | 650 | } |
614 | |||
615 | int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) | ||
616 | { | ||
617 | char *nl = strchrnul(bp, '\n'); | ||
618 | int len = nl - bp; | ||
619 | |||
620 | return __bitmap_parselist(bp, len, 0, maskp, nmaskbits); | ||
621 | } | ||
622 | EXPORT_SYMBOL(bitmap_parselist); | 651 | EXPORT_SYMBOL(bitmap_parselist); |
623 | 652 | ||
624 | 653 | ||