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/sysctl.c | |
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/sysctl.c')
-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, |