aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bitmap.c')
-rw-r--r--lib/bitmap.c255
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}
478EXPORT_SYMBOL(bitmap_print_to_pagebuf); 480EXPORT_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 */
489struct region {
490 unsigned int start;
491 unsigned int off;
492 unsigned int group_len;
493 unsigned int end;
494};
495
496static 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
510static 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
518static 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
533static inline bool end_of_str(char c)
534{
535 return c == '\0' || c == '\n';
536}
537
538static inline bool __end_of_region(char c)
539{
540 return isspace(c) || c == ',';
541}
542
543static 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 */
552static 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
560static 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
591no_end:
592 r->end = r->start;
593no_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 */
505static int __bitmap_parselist(const char *buf, unsigned int buflen, 624int 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
615int 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}
622EXPORT_SYMBOL(bitmap_parselist); 651EXPORT_SYMBOL(bitmap_parselist);
623 652
624 653