diff options
| author | Octavian Purdila <opurdila@ixiacom.com> | 2010-05-04 20:26:55 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-05-16 02:28:39 -0400 |
| commit | 9f977fb7ae9ddf565b4800854212fb9a1ed6c2ea (patch) | |
| tree | 87c88ca344134f5935ec9efc0a17ee66eddb7e51 /kernel | |
| parent | 00b7c3395aec3df43de5bd02a3c5a099ca51169f (diff) | |
sysctl: add proc_do_large_bitmap
The new function can be used to read/write large bitmaps via /proc. A
comma separated range format is used for compact output and input
(e.g. 1,3-4,10-10).
Writing into the file will first reset the bitmap then update it
based on the given input.
Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
Signed-off-by: WANG Cong <amwang@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/sysctl.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4a976208de29..bcfb79e94ec7 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -2049,6 +2049,16 @@ static size_t proc_skip_spaces(char **buf) | |||
| 2049 | return ret; | 2049 | return ret; |
| 2050 | } | 2050 | } |
| 2051 | 2051 | ||
| 2052 | static void proc_skip_char(char **buf, size_t *size, const char v) | ||
| 2053 | { | ||
| 2054 | while (*size) { | ||
| 2055 | if (**buf != v) | ||
| 2056 | break; | ||
| 2057 | (*size)--; | ||
| 2058 | (*buf)++; | ||
| 2059 | } | ||
| 2060 | } | ||
| 2061 | |||
| 2052 | #define TMPBUFLEN 22 | 2062 | #define TMPBUFLEN 22 |
| 2053 | /** | 2063 | /** |
| 2054 | * proc_get_long - reads an ASCII formated integer from a user buffer | 2064 | * proc_get_long - reads an ASCII formated integer from a user buffer |
| @@ -2675,6 +2685,157 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, | |||
| 2675 | return 0; | 2685 | return 0; |
| 2676 | } | 2686 | } |
| 2677 | 2687 | ||
| 2688 | /** | ||
| 2689 | * proc_do_large_bitmap - read/write from/to a large bitmap | ||
| 2690 | * @table: the sysctl table | ||
| 2691 | * @write: %TRUE if this is a write to the sysctl file | ||
| 2692 | * @buffer: the user buffer | ||
| 2693 | * @lenp: the size of the user buffer | ||
| 2694 | * @ppos: file position | ||
| 2695 | * | ||
| 2696 | * The bitmap is stored at table->data and the bitmap length (in bits) | ||
| 2697 | * in table->maxlen. | ||
| 2698 | * | ||
| 2699 | * We use a range comma separated format (e.g. 1,3-4,10-10) so that | ||
| 2700 | * large bitmaps may be represented in a compact manner. Writing into | ||
| 2701 | * the file will clear the bitmap then update it with the given input. | ||
| 2702 | * | ||
| 2703 | * Returns 0 on success. | ||
| 2704 | */ | ||
| 2705 | int proc_do_large_bitmap(struct ctl_table *table, int write, | ||
| 2706 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
| 2707 | { | ||
| 2708 | int err = 0; | ||
| 2709 | bool first = 1; | ||
| 2710 | size_t left = *lenp; | ||
| 2711 | unsigned long bitmap_len = table->maxlen; | ||
| 2712 | unsigned long *bitmap = (unsigned long *) table->data; | ||
| 2713 | unsigned long *tmp_bitmap = NULL; | ||
| 2714 | char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; | ||
| 2715 | |||
| 2716 | if (!bitmap_len || !left || (*ppos && !write)) { | ||
| 2717 | *lenp = 0; | ||
| 2718 | return 0; | ||
| 2719 | } | ||
| 2720 | |||
| 2721 | if (write) { | ||
| 2722 | unsigned long page = 0; | ||
| 2723 | char *kbuf; | ||
| 2724 | |||
| 2725 | if (left > PAGE_SIZE - 1) | ||
| 2726 | left = PAGE_SIZE - 1; | ||
| 2727 | |||
| 2728 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2729 | kbuf = (char *) page; | ||
| 2730 | if (!kbuf) | ||
| 2731 | return -ENOMEM; | ||
| 2732 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2733 | free_page(page); | ||
| 2734 | return -EFAULT; | ||
| 2735 | } | ||
| 2736 | kbuf[left] = 0; | ||
| 2737 | |||
| 2738 | tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long), | ||
| 2739 | GFP_KERNEL); | ||
| 2740 | if (!tmp_bitmap) { | ||
| 2741 | free_page(page); | ||
| 2742 | return -ENOMEM; | ||
| 2743 | } | ||
| 2744 | proc_skip_char(&kbuf, &left, '\n'); | ||
| 2745 | while (!err && left) { | ||
| 2746 | unsigned long val_a, val_b; | ||
| 2747 | bool neg; | ||
| 2748 | |||
| 2749 | err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a, | ||
| 2750 | sizeof(tr_a), &c); | ||
| 2751 | if (err) | ||
| 2752 | break; | ||
| 2753 | if (val_a >= bitmap_len || neg) { | ||
| 2754 | err = -EINVAL; | ||
| 2755 | break; | ||
| 2756 | } | ||
| 2757 | |||
| 2758 | val_b = val_a; | ||
| 2759 | if (left) { | ||
| 2760 | kbuf++; | ||
| 2761 | left--; | ||
| 2762 | } | ||
| 2763 | |||
| 2764 | if (c == '-') { | ||
| 2765 | err = proc_get_long(&kbuf, &left, &val_b, | ||
| 2766 | &neg, tr_b, sizeof(tr_b), | ||
| 2767 | &c); | ||
| 2768 | if (err) | ||
| 2769 | break; | ||
| 2770 | if (val_b >= bitmap_len || neg || | ||
| 2771 | val_a > val_b) { | ||
| 2772 | err = -EINVAL; | ||
| 2773 | break; | ||
| 2774 | } | ||
| 2775 | if (left) { | ||
| 2776 | kbuf++; | ||
| 2777 | left--; | ||
| 2778 | } | ||
| 2779 | } | ||
| 2780 | |||
| 2781 | while (val_a <= val_b) | ||
| 2782 | set_bit(val_a++, tmp_bitmap); | ||
| 2783 | |||
| 2784 | first = 0; | ||
| 2785 | proc_skip_char(&kbuf, &left, '\n'); | ||
| 2786 | } | ||
| 2787 | free_page(page); | ||
| 2788 | } else { | ||
| 2789 | unsigned long bit_a, bit_b = 0; | ||
| 2790 | |||
| 2791 | while (left) { | ||
| 2792 | bit_a = find_next_bit(bitmap, bitmap_len, bit_b); | ||
| 2793 | if (bit_a >= bitmap_len) | ||
| 2794 | break; | ||
| 2795 | bit_b = find_next_zero_bit(bitmap, bitmap_len, | ||
| 2796 | bit_a + 1) - 1; | ||
| 2797 | |||
| 2798 | if (!first) { | ||
| 2799 | err = proc_put_char(&buffer, &left, ','); | ||
| 2800 | if (err) | ||
| 2801 | break; | ||
| 2802 | } | ||
| 2803 | err = proc_put_long(&buffer, &left, bit_a, false); | ||
| 2804 | if (err) | ||
| 2805 | break; | ||
| 2806 | if (bit_a != bit_b) { | ||
| 2807 | err = proc_put_char(&buffer, &left, '-'); | ||
| 2808 | if (err) | ||
| 2809 | break; | ||
| 2810 | err = proc_put_long(&buffer, &left, bit_b, false); | ||
| 2811 | if (err) | ||
| 2812 | break; | ||
| 2813 | } | ||
| 2814 | |||
| 2815 | first = 0; bit_b++; | ||
| 2816 | } | ||
| 2817 | if (!err) | ||
| 2818 | err = proc_put_char(&buffer, &left, '\n'); | ||
| 2819 | } | ||
| 2820 | |||
| 2821 | if (!err) { | ||
| 2822 | if (write) { | ||
| 2823 | if (*ppos) | ||
| 2824 | bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len); | ||
| 2825 | else | ||
| 2826 | memcpy(bitmap, tmp_bitmap, | ||
| 2827 | BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long)); | ||
| 2828 | } | ||
| 2829 | kfree(tmp_bitmap); | ||
| 2830 | *lenp -= left; | ||
| 2831 | *ppos += *lenp; | ||
| 2832 | return 0; | ||
| 2833 | } else { | ||
| 2834 | kfree(tmp_bitmap); | ||
| 2835 | return err; | ||
| 2836 | } | ||
| 2837 | } | ||
| 2838 | |||
| 2678 | #else /* CONFIG_PROC_FS */ | 2839 | #else /* CONFIG_PROC_FS */ |
| 2679 | 2840 | ||
| 2680 | int proc_dostring(struct ctl_table *table, int write, | 2841 | int proc_dostring(struct ctl_table *table, int write, |
