aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/sysctl.c
diff options
context:
space:
mode:
authorOctavian Purdila <opurdila@ixiacom.com>2010-05-04 20:26:55 -0400
committerDavid S. Miller <davem@davemloft.net>2010-05-16 02:28:39 -0400
commit9f977fb7ae9ddf565b4800854212fb9a1ed6c2ea (patch)
tree87c88ca344134f5935ec9efc0a17ee66eddb7e51 /kernel/sysctl.c
parent00b7c3395aec3df43de5bd02a3c5a099ca51169f (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.c161
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
2052static 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 */
2705int 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
2680int proc_dostring(struct ctl_table *table, int write, 2841int proc_dostring(struct ctl_table *table, int write,