diff options
Diffstat (limited to 'lib/bitmap.c')
| -rw-r--r-- | lib/bitmap.c | 52 |
1 files changed, 47 insertions, 5 deletions
diff --git a/lib/bitmap.c b/lib/bitmap.c index c66da508cbf7..0b66f0e5eb6b 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c | |||
| @@ -14,9 +14,9 @@ | |||
| 14 | #include <linux/bug.h> | 14 | #include <linux/bug.h> |
| 15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
| 16 | #include <linux/string.h> | 16 | #include <linux/string.h> |
| 17 | #include <linux/uaccess.h> | ||
| 17 | 18 | ||
| 18 | #include <asm/page.h> | 19 | #include <asm/page.h> |
| 19 | #include <asm/uaccess.h> | ||
| 20 | 20 | ||
| 21 | /* | 21 | /* |
| 22 | * bitmaps provide an array of bits, implemented using an an | 22 | * bitmaps provide an array of bits, implemented using an an |
| @@ -496,6 +496,11 @@ EXPORT_SYMBOL(bitmap_print_to_pagebuf); | |||
| 496 | * ranges. Consecutively set bits are shown as two hyphen-separated | 496 | * ranges. Consecutively set bits are shown as two hyphen-separated |
| 497 | * decimal numbers, the smallest and largest bit numbers set in | 497 | * decimal numbers, the smallest and largest bit numbers set in |
| 498 | * the range. | 498 | * the range. |
| 499 | * Optionally each range can be postfixed to denote that only parts of it | ||
| 500 | * should be set. The range will divided to groups of specific size. | ||
| 501 | * From each group will be used only defined amount of bits. | ||
| 502 | * Syntax: range:used_size/group_size | ||
| 503 | * Example: 0-1023:2/256 ==> 0,1,256,257,512,513,768,769 | ||
| 499 | * | 504 | * |
| 500 | * Returns 0 on success, -errno on invalid input strings. | 505 | * Returns 0 on success, -errno on invalid input strings. |
| 501 | * Error values: | 506 | * Error values: |
| @@ -507,16 +512,20 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen, | |||
| 507 | int is_user, unsigned long *maskp, | 512 | int is_user, unsigned long *maskp, |
| 508 | int nmaskbits) | 513 | int nmaskbits) |
| 509 | { | 514 | { |
| 510 | unsigned a, b; | 515 | unsigned int a, b, old_a, old_b; |
| 516 | unsigned int group_size, used_size; | ||
| 511 | int c, old_c, totaldigits, ndigits; | 517 | int c, old_c, totaldigits, ndigits; |
| 512 | const char __user __force *ubuf = (const char __user __force *)buf; | 518 | const char __user __force *ubuf = (const char __user __force *)buf; |
| 513 | int at_start, in_range; | 519 | int at_start, in_range, in_partial_range; |
| 514 | 520 | ||
| 515 | totaldigits = c = 0; | 521 | totaldigits = c = 0; |
| 522 | old_a = old_b = 0; | ||
| 523 | group_size = used_size = 0; | ||
| 516 | bitmap_zero(maskp, nmaskbits); | 524 | bitmap_zero(maskp, nmaskbits); |
| 517 | do { | 525 | do { |
| 518 | at_start = 1; | 526 | at_start = 1; |
| 519 | in_range = 0; | 527 | in_range = 0; |
| 528 | in_partial_range = 0; | ||
| 520 | a = b = 0; | 529 | a = b = 0; |
| 521 | ndigits = totaldigits; | 530 | ndigits = totaldigits; |
| 522 | 531 | ||
| @@ -547,6 +556,24 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen, | |||
| 547 | if ((totaldigits != ndigits) && isspace(old_c)) | 556 | if ((totaldigits != ndigits) && isspace(old_c)) |
| 548 | return -EINVAL; | 557 | return -EINVAL; |
| 549 | 558 | ||
| 559 | if (c == '/') { | ||
| 560 | used_size = a; | ||
| 561 | at_start = 1; | ||
| 562 | in_range = 0; | ||
| 563 | a = b = 0; | ||
| 564 | continue; | ||
| 565 | } | ||
| 566 | |||
| 567 | if (c == ':') { | ||
| 568 | old_a = a; | ||
| 569 | old_b = b; | ||
| 570 | at_start = 1; | ||
| 571 | in_range = 0; | ||
| 572 | in_partial_range = 1; | ||
| 573 | a = b = 0; | ||
| 574 | continue; | ||
| 575 | } | ||
| 576 | |||
| 550 | if (c == '-') { | 577 | if (c == '-') { |
| 551 | if (at_start || in_range) | 578 | if (at_start || in_range) |
| 552 | return -EINVAL; | 579 | return -EINVAL; |
| @@ -567,15 +594,30 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen, | |||
| 567 | } | 594 | } |
| 568 | if (ndigits == totaldigits) | 595 | if (ndigits == totaldigits) |
| 569 | continue; | 596 | continue; |
| 597 | if (in_partial_range) { | ||
| 598 | group_size = a; | ||
| 599 | a = old_a; | ||
| 600 | b = old_b; | ||
| 601 | old_a = old_b = 0; | ||
| 602 | } | ||
| 570 | /* if no digit is after '-', it's wrong*/ | 603 | /* if no digit is after '-', it's wrong*/ |
| 571 | if (at_start && in_range) | 604 | if (at_start && in_range) |
| 572 | return -EINVAL; | 605 | return -EINVAL; |
| 573 | if (!(a <= b)) | 606 | if (!(a <= b) || !(used_size <= group_size)) |
| 574 | return -EINVAL; | 607 | return -EINVAL; |
| 575 | if (b >= nmaskbits) | 608 | if (b >= nmaskbits) |
| 576 | return -ERANGE; | 609 | return -ERANGE; |
| 577 | while (a <= b) { | 610 | while (a <= b) { |
| 578 | set_bit(a, maskp); | 611 | if (in_partial_range) { |
| 612 | static int pos_in_group = 1; | ||
| 613 | |||
| 614 | if (pos_in_group <= used_size) | ||
| 615 | set_bit(a, maskp); | ||
| 616 | |||
| 617 | if (a == b || ++pos_in_group > group_size) | ||
| 618 | pos_in_group = 1; | ||
| 619 | } else | ||
| 620 | set_bit(a, maskp); | ||
| 579 | a++; | 621 | a++; |
| 580 | } | 622 | } |
| 581 | } while (buflen && c == ','); | 623 | } while (buflen && c == ','); |
