diff options
Diffstat (limited to 'kernel/sysctl.c')
| -rw-r--r-- | kernel/sysctl.c | 662 |
1 files changed, 475 insertions, 187 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8a68b2448468..d24f761f4876 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/swap.h> | 23 | #include <linux/swap.h> |
| 24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
| 25 | #include <linux/sysctl.h> | 25 | #include <linux/sysctl.h> |
| 26 | #include <linux/signal.h> | ||
| 26 | #include <linux/proc_fs.h> | 27 | #include <linux/proc_fs.h> |
| 27 | #include <linux/security.h> | 28 | #include <linux/security.h> |
| 28 | #include <linux/ctype.h> | 29 | #include <linux/ctype.h> |
| @@ -36,6 +37,7 @@ | |||
| 36 | #include <linux/highuid.h> | 37 | #include <linux/highuid.h> |
| 37 | #include <linux/writeback.h> | 38 | #include <linux/writeback.h> |
| 38 | #include <linux/ratelimit.h> | 39 | #include <linux/ratelimit.h> |
| 40 | #include <linux/compaction.h> | ||
| 39 | #include <linux/hugetlb.h> | 41 | #include <linux/hugetlb.h> |
| 40 | #include <linux/initrd.h> | 42 | #include <linux/initrd.h> |
| 41 | #include <linux/key.h> | 43 | #include <linux/key.h> |
| @@ -50,6 +52,8 @@ | |||
| 50 | #include <linux/ftrace.h> | 52 | #include <linux/ftrace.h> |
| 51 | #include <linux/slow-work.h> | 53 | #include <linux/slow-work.h> |
| 52 | #include <linux/perf_event.h> | 54 | #include <linux/perf_event.h> |
| 55 | #include <linux/kprobes.h> | ||
| 56 | #include <linux/pipe_fs_i.h> | ||
| 53 | 57 | ||
| 54 | #include <asm/uaccess.h> | 58 | #include <asm/uaccess.h> |
| 55 | #include <asm/processor.h> | 59 | #include <asm/processor.h> |
| @@ -59,13 +63,23 @@ | |||
| 59 | #include <asm/stacktrace.h> | 63 | #include <asm/stacktrace.h> |
| 60 | #include <asm/io.h> | 64 | #include <asm/io.h> |
| 61 | #endif | 65 | #endif |
| 66 | #ifdef CONFIG_BSD_PROCESS_ACCT | ||
| 67 | #include <linux/acct.h> | ||
| 68 | #endif | ||
| 69 | #ifdef CONFIG_RT_MUTEXES | ||
| 70 | #include <linux/rtmutex.h> | ||
| 71 | #endif | ||
| 72 | #if defined(CONFIG_PROVE_LOCKING) || defined(CONFIG_LOCK_STAT) | ||
| 73 | #include <linux/lockdep.h> | ||
| 74 | #endif | ||
| 75 | #ifdef CONFIG_CHR_DEV_SG | ||
| 76 | #include <scsi/sg.h> | ||
| 77 | #endif | ||
| 62 | 78 | ||
| 63 | 79 | ||
| 64 | #if defined(CONFIG_SYSCTL) | 80 | #if defined(CONFIG_SYSCTL) |
| 65 | 81 | ||
| 66 | /* External variables not in a header file. */ | 82 | /* External variables not in a header file. */ |
| 67 | extern int C_A_D; | ||
| 68 | extern int print_fatal_signals; | ||
| 69 | extern int sysctl_overcommit_memory; | 83 | extern int sysctl_overcommit_memory; |
| 70 | extern int sysctl_overcommit_ratio; | 84 | extern int sysctl_overcommit_ratio; |
| 71 | extern int sysctl_panic_on_oom; | 85 | extern int sysctl_panic_on_oom; |
| @@ -87,9 +101,6 @@ extern int sysctl_nr_open_min, sysctl_nr_open_max; | |||
| 87 | #ifndef CONFIG_MMU | 101 | #ifndef CONFIG_MMU |
| 88 | extern int sysctl_nr_trim_pages; | 102 | extern int sysctl_nr_trim_pages; |
| 89 | #endif | 103 | #endif |
| 90 | #ifdef CONFIG_RCU_TORTURE_TEST | ||
| 91 | extern int rcutorture_runnable; | ||
| 92 | #endif /* #ifdef CONFIG_RCU_TORTURE_TEST */ | ||
| 93 | #ifdef CONFIG_BLOCK | 104 | #ifdef CONFIG_BLOCK |
| 94 | extern int blk_iopoll_enabled; | 105 | extern int blk_iopoll_enabled; |
| 95 | #endif | 106 | #endif |
| @@ -119,14 +130,6 @@ static int min_percpu_pagelist_fract = 8; | |||
| 119 | 130 | ||
| 120 | static int ngroups_max = NGROUPS_MAX; | 131 | static int ngroups_max = NGROUPS_MAX; |
| 121 | 132 | ||
| 122 | #ifdef CONFIG_MODULES | ||
| 123 | extern char modprobe_path[]; | ||
| 124 | extern int modules_disabled; | ||
| 125 | #endif | ||
| 126 | #ifdef CONFIG_CHR_DEV_SG | ||
| 127 | extern int sg_big_buff; | ||
| 128 | #endif | ||
| 129 | |||
| 130 | #ifdef CONFIG_SPARC | 133 | #ifdef CONFIG_SPARC |
| 131 | #include <asm/system.h> | 134 | #include <asm/system.h> |
| 132 | #endif | 135 | #endif |
| @@ -148,10 +151,6 @@ extern int sysctl_userprocess_debug; | |||
| 148 | extern int spin_retry; | 151 | extern int spin_retry; |
| 149 | #endif | 152 | #endif |
| 150 | 153 | ||
| 151 | #ifdef CONFIG_BSD_PROCESS_ACCT | ||
| 152 | extern int acct_parm[]; | ||
| 153 | #endif | ||
| 154 | |||
| 155 | #ifdef CONFIG_IA64 | 154 | #ifdef CONFIG_IA64 |
| 156 | extern int no_unaligned_warning; | 155 | extern int no_unaligned_warning; |
| 157 | extern int unaligned_dump_stack; | 156 | extern int unaligned_dump_stack; |
| @@ -159,10 +158,6 @@ extern int unaligned_dump_stack; | |||
| 159 | 158 | ||
| 160 | extern struct ratelimit_state printk_ratelimit_state; | 159 | extern struct ratelimit_state printk_ratelimit_state; |
| 161 | 160 | ||
| 162 | #ifdef CONFIG_RT_MUTEXES | ||
| 163 | extern int max_lock_depth; | ||
| 164 | #endif | ||
| 165 | |||
| 166 | #ifdef CONFIG_PROC_SYSCTL | 161 | #ifdef CONFIG_PROC_SYSCTL |
| 167 | static int proc_do_cad_pid(struct ctl_table *table, int write, | 162 | static int proc_do_cad_pid(struct ctl_table *table, int write, |
| 168 | void __user *buffer, size_t *lenp, loff_t *ppos); | 163 | void __user *buffer, size_t *lenp, loff_t *ppos); |
| @@ -170,6 +165,27 @@ static int proc_taint(struct ctl_table *table, int write, | |||
| 170 | void __user *buffer, size_t *lenp, loff_t *ppos); | 165 | void __user *buffer, size_t *lenp, loff_t *ppos); |
| 171 | #endif | 166 | #endif |
| 172 | 167 | ||
| 168 | #ifdef CONFIG_MAGIC_SYSRQ | ||
| 169 | static int __sysrq_enabled; /* Note: sysrq code ises it's own private copy */ | ||
| 170 | |||
| 171 | static int sysrq_sysctl_handler(ctl_table *table, int write, | ||
| 172 | void __user *buffer, size_t *lenp, | ||
| 173 | loff_t *ppos) | ||
| 174 | { | ||
| 175 | int error; | ||
| 176 | |||
| 177 | error = proc_dointvec(table, write, buffer, lenp, ppos); | ||
| 178 | if (error) | ||
| 179 | return error; | ||
| 180 | |||
| 181 | if (write) | ||
| 182 | sysrq_toggle_support(__sysrq_enabled); | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | #endif | ||
| 188 | |||
| 173 | static struct ctl_table root_table[]; | 189 | static struct ctl_table root_table[]; |
| 174 | static struct ctl_table_root sysctl_table_root; | 190 | static struct ctl_table_root sysctl_table_root; |
| 175 | static struct ctl_table_header root_table_header = { | 191 | static struct ctl_table_header root_table_header = { |
| @@ -201,9 +217,6 @@ extern struct ctl_table epoll_table[]; | |||
| 201 | int sysctl_legacy_va_layout; | 217 | int sysctl_legacy_va_layout; |
| 202 | #endif | 218 | #endif |
| 203 | 219 | ||
| 204 | extern int prove_locking; | ||
| 205 | extern int lock_stat; | ||
| 206 | |||
| 207 | /* The default sysctl tables: */ | 220 | /* The default sysctl tables: */ |
| 208 | 221 | ||
| 209 | static struct ctl_table root_table[] = { | 222 | static struct ctl_table root_table[] = { |
| @@ -250,6 +263,11 @@ static int min_sched_shares_ratelimit = 100000; /* 100 usec */ | |||
| 250 | static int max_sched_shares_ratelimit = NSEC_PER_SEC; /* 1 second */ | 263 | static int max_sched_shares_ratelimit = NSEC_PER_SEC; /* 1 second */ |
| 251 | #endif | 264 | #endif |
| 252 | 265 | ||
| 266 | #ifdef CONFIG_COMPACTION | ||
| 267 | static int min_extfrag_threshold; | ||
| 268 | static int max_extfrag_threshold = 1000; | ||
| 269 | #endif | ||
| 270 | |||
| 253 | static struct ctl_table kern_table[] = { | 271 | static struct ctl_table kern_table[] = { |
| 254 | { | 272 | { |
| 255 | .procname = "sched_child_runs_first", | 273 | .procname = "sched_child_runs_first", |
| @@ -577,7 +595,7 @@ static struct ctl_table kern_table[] = { | |||
| 577 | .data = &__sysrq_enabled, | 595 | .data = &__sysrq_enabled, |
| 578 | .maxlen = sizeof (int), | 596 | .maxlen = sizeof (int), |
| 579 | .mode = 0644, | 597 | .mode = 0644, |
| 580 | .proc_handler = proc_dointvec, | 598 | .proc_handler = sysrq_sysctl_handler, |
| 581 | }, | 599 | }, |
| 582 | #endif | 600 | #endif |
| 583 | #ifdef CONFIG_PROC_SYSCTL | 601 | #ifdef CONFIG_PROC_SYSCTL |
| @@ -631,7 +649,7 @@ static struct ctl_table kern_table[] = { | |||
| 631 | #endif | 649 | #endif |
| 632 | { | 650 | { |
| 633 | .procname = "userprocess_debug", | 651 | .procname = "userprocess_debug", |
| 634 | .data = &sysctl_userprocess_debug, | 652 | .data = &show_unhandled_signals, |
| 635 | .maxlen = sizeof(int), | 653 | .maxlen = sizeof(int), |
| 636 | .mode = 0644, | 654 | .mode = 0644, |
| 637 | .proc_handler = proc_dointvec, | 655 | .proc_handler = proc_dointvec, |
| @@ -1109,6 +1127,25 @@ static struct ctl_table vm_table[] = { | |||
| 1109 | .mode = 0644, | 1127 | .mode = 0644, |
| 1110 | .proc_handler = drop_caches_sysctl_handler, | 1128 | .proc_handler = drop_caches_sysctl_handler, |
| 1111 | }, | 1129 | }, |
| 1130 | #ifdef CONFIG_COMPACTION | ||
| 1131 | { | ||
| 1132 | .procname = "compact_memory", | ||
| 1133 | .data = &sysctl_compact_memory, | ||
| 1134 | .maxlen = sizeof(int), | ||
| 1135 | .mode = 0200, | ||
| 1136 | .proc_handler = sysctl_compaction_handler, | ||
| 1137 | }, | ||
| 1138 | { | ||
| 1139 | .procname = "extfrag_threshold", | ||
| 1140 | .data = &sysctl_extfrag_threshold, | ||
| 1141 | .maxlen = sizeof(int), | ||
| 1142 | .mode = 0644, | ||
| 1143 | .proc_handler = sysctl_extfrag_handler, | ||
| 1144 | .extra1 = &min_extfrag_threshold, | ||
| 1145 | .extra2 = &max_extfrag_threshold, | ||
| 1146 | }, | ||
| 1147 | |||
| 1148 | #endif /* CONFIG_COMPACTION */ | ||
| 1112 | { | 1149 | { |
| 1113 | .procname = "min_free_kbytes", | 1150 | .procname = "min_free_kbytes", |
| 1114 | .data = &min_free_kbytes, | 1151 | .data = &min_free_kbytes, |
| @@ -1433,6 +1470,14 @@ static struct ctl_table fs_table[] = { | |||
| 1433 | .child = binfmt_misc_table, | 1470 | .child = binfmt_misc_table, |
| 1434 | }, | 1471 | }, |
| 1435 | #endif | 1472 | #endif |
| 1473 | { | ||
| 1474 | .procname = "pipe-max-size", | ||
| 1475 | .data = &pipe_max_size, | ||
| 1476 | .maxlen = sizeof(int), | ||
| 1477 | .mode = 0644, | ||
| 1478 | .proc_handler = &pipe_proc_fn, | ||
| 1479 | .extra1 = &pipe_min_size, | ||
| 1480 | }, | ||
| 1436 | /* | 1481 | /* |
| 1437 | * NOTE: do not add new entries to this table unless you have read | 1482 | * NOTE: do not add new entries to this table unless you have read |
| 1438 | * Documentation/sysctl/ctl_unnumbered.txt | 1483 | * Documentation/sysctl/ctl_unnumbered.txt |
| @@ -1441,7 +1486,8 @@ static struct ctl_table fs_table[] = { | |||
| 1441 | }; | 1486 | }; |
| 1442 | 1487 | ||
| 1443 | static struct ctl_table debug_table[] = { | 1488 | static struct ctl_table debug_table[] = { |
| 1444 | #if defined(CONFIG_X86) || defined(CONFIG_PPC) | 1489 | #if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \ |
| 1490 | defined(CONFIG_S390) | ||
| 1445 | { | 1491 | { |
| 1446 | .procname = "exception-trace", | 1492 | .procname = "exception-trace", |
| 1447 | .data = &show_unhandled_signals, | 1493 | .data = &show_unhandled_signals, |
| @@ -1450,6 +1496,17 @@ static struct ctl_table debug_table[] = { | |||
| 1450 | .proc_handler = proc_dointvec | 1496 | .proc_handler = proc_dointvec |
| 1451 | }, | 1497 | }, |
| 1452 | #endif | 1498 | #endif |
| 1499 | #if defined(CONFIG_OPTPROBES) | ||
| 1500 | { | ||
| 1501 | .procname = "kprobes-optimization", | ||
| 1502 | .data = &sysctl_kprobes_optimization, | ||
| 1503 | .maxlen = sizeof(int), | ||
| 1504 | .mode = 0644, | ||
| 1505 | .proc_handler = proc_kprobes_optimization_handler, | ||
| 1506 | .extra1 = &zero, | ||
| 1507 | .extra2 = &one, | ||
| 1508 | }, | ||
| 1509 | #endif | ||
| 1453 | { } | 1510 | { } |
| 1454 | }; | 1511 | }; |
| 1455 | 1512 | ||
| @@ -2039,8 +2096,132 @@ int proc_dostring(struct ctl_table *table, int write, | |||
| 2039 | buffer, lenp, ppos); | 2096 | buffer, lenp, ppos); |
| 2040 | } | 2097 | } |
| 2041 | 2098 | ||
| 2099 | static size_t proc_skip_spaces(char **buf) | ||
| 2100 | { | ||
| 2101 | size_t ret; | ||
| 2102 | char *tmp = skip_spaces(*buf); | ||
| 2103 | ret = tmp - *buf; | ||
| 2104 | *buf = tmp; | ||
| 2105 | return ret; | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | static void proc_skip_char(char **buf, size_t *size, const char v) | ||
| 2109 | { | ||
| 2110 | while (*size) { | ||
| 2111 | if (**buf != v) | ||
| 2112 | break; | ||
| 2113 | (*size)--; | ||
| 2114 | (*buf)++; | ||
| 2115 | } | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | #define TMPBUFLEN 22 | ||
| 2119 | /** | ||
| 2120 | * proc_get_long - reads an ASCII formatted integer from a user buffer | ||
| 2121 | * | ||
| 2122 | * @buf: a kernel buffer | ||
| 2123 | * @size: size of the kernel buffer | ||
| 2124 | * @val: this is where the number will be stored | ||
| 2125 | * @neg: set to %TRUE if number is negative | ||
| 2126 | * @perm_tr: a vector which contains the allowed trailers | ||
| 2127 | * @perm_tr_len: size of the perm_tr vector | ||
| 2128 | * @tr: pointer to store the trailer character | ||
| 2129 | * | ||
| 2130 | * In case of success %0 is returned and @buf and @size are updated with | ||
| 2131 | * the amount of bytes read. If @tr is non-NULL and a trailing | ||
| 2132 | * character exists (size is non-zero after returning from this | ||
| 2133 | * function), @tr is updated with the trailing character. | ||
| 2134 | */ | ||
| 2135 | static int proc_get_long(char **buf, size_t *size, | ||
| 2136 | unsigned long *val, bool *neg, | ||
| 2137 | const char *perm_tr, unsigned perm_tr_len, char *tr) | ||
| 2138 | { | ||
| 2139 | int len; | ||
| 2140 | char *p, tmp[TMPBUFLEN]; | ||
| 2141 | |||
| 2142 | if (!*size) | ||
| 2143 | return -EINVAL; | ||
| 2144 | |||
| 2145 | len = *size; | ||
| 2146 | if (len > TMPBUFLEN - 1) | ||
| 2147 | len = TMPBUFLEN - 1; | ||
| 2148 | |||
| 2149 | memcpy(tmp, *buf, len); | ||
| 2150 | |||
| 2151 | tmp[len] = 0; | ||
| 2152 | p = tmp; | ||
| 2153 | if (*p == '-' && *size > 1) { | ||
| 2154 | *neg = true; | ||
| 2155 | p++; | ||
| 2156 | } else | ||
| 2157 | *neg = false; | ||
| 2158 | if (!isdigit(*p)) | ||
| 2159 | return -EINVAL; | ||
| 2160 | |||
| 2161 | *val = simple_strtoul(p, &p, 0); | ||
| 2162 | |||
| 2163 | len = p - tmp; | ||
| 2164 | |||
| 2165 | /* We don't know if the next char is whitespace thus we may accept | ||
| 2166 | * invalid integers (e.g. 1234...a) or two integers instead of one | ||
| 2167 | * (e.g. 123...1). So lets not allow such large numbers. */ | ||
| 2168 | if (len == TMPBUFLEN - 1) | ||
| 2169 | return -EINVAL; | ||
| 2170 | |||
| 2171 | if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len)) | ||
| 2172 | return -EINVAL; | ||
| 2173 | |||
| 2174 | if (tr && (len < *size)) | ||
| 2175 | *tr = *p; | ||
| 2176 | |||
| 2177 | *buf += len; | ||
| 2178 | *size -= len; | ||
| 2179 | |||
| 2180 | return 0; | ||
| 2181 | } | ||
| 2182 | |||
| 2183 | /** | ||
| 2184 | * proc_put_long - converts an integer to a decimal ASCII formatted string | ||
| 2185 | * | ||
| 2186 | * @buf: the user buffer | ||
| 2187 | * @size: the size of the user buffer | ||
| 2188 | * @val: the integer to be converted | ||
| 2189 | * @neg: sign of the number, %TRUE for negative | ||
| 2190 | * | ||
| 2191 | * In case of success %0 is returned and @buf and @size are updated with | ||
| 2192 | * the amount of bytes written. | ||
| 2193 | */ | ||
| 2194 | static int proc_put_long(void __user **buf, size_t *size, unsigned long val, | ||
| 2195 | bool neg) | ||
| 2196 | { | ||
| 2197 | int len; | ||
| 2198 | char tmp[TMPBUFLEN], *p = tmp; | ||
| 2199 | |||
| 2200 | sprintf(p, "%s%lu", neg ? "-" : "", val); | ||
| 2201 | len = strlen(tmp); | ||
| 2202 | if (len > *size) | ||
| 2203 | len = *size; | ||
| 2204 | if (copy_to_user(*buf, tmp, len)) | ||
| 2205 | return -EFAULT; | ||
| 2206 | *size -= len; | ||
| 2207 | *buf += len; | ||
| 2208 | return 0; | ||
| 2209 | } | ||
| 2210 | #undef TMPBUFLEN | ||
| 2211 | |||
| 2212 | static int proc_put_char(void __user **buf, size_t *size, char c) | ||
| 2213 | { | ||
| 2214 | if (*size) { | ||
| 2215 | char __user **buffer = (char __user **)buf; | ||
| 2216 | if (put_user(c, *buffer)) | ||
| 2217 | return -EFAULT; | ||
| 2218 | (*size)--, (*buffer)++; | ||
| 2219 | *buf = *buffer; | ||
| 2220 | } | ||
| 2221 | return 0; | ||
| 2222 | } | ||
| 2042 | 2223 | ||
| 2043 | static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | 2224 | static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, |
| 2044 | int *valp, | 2225 | int *valp, |
| 2045 | int write, void *data) | 2226 | int write, void *data) |
| 2046 | { | 2227 | { |
| @@ -2049,33 +2230,31 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | |||
| 2049 | } else { | 2230 | } else { |
| 2050 | int val = *valp; | 2231 | int val = *valp; |
| 2051 | if (val < 0) { | 2232 | if (val < 0) { |
| 2052 | *negp = -1; | 2233 | *negp = true; |
| 2053 | *lvalp = (unsigned long)-val; | 2234 | *lvalp = (unsigned long)-val; |
| 2054 | } else { | 2235 | } else { |
| 2055 | *negp = 0; | 2236 | *negp = false; |
| 2056 | *lvalp = (unsigned long)val; | 2237 | *lvalp = (unsigned long)val; |
| 2057 | } | 2238 | } |
| 2058 | } | 2239 | } |
| 2059 | return 0; | 2240 | return 0; |
| 2060 | } | 2241 | } |
| 2061 | 2242 | ||
| 2243 | static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; | ||
| 2244 | |||
| 2062 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | 2245 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, |
| 2063 | int write, void __user *buffer, | 2246 | int write, void __user *buffer, |
| 2064 | size_t *lenp, loff_t *ppos, | 2247 | size_t *lenp, loff_t *ppos, |
| 2065 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2248 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
| 2066 | int write, void *data), | 2249 | int write, void *data), |
| 2067 | void *data) | 2250 | void *data) |
| 2068 | { | 2251 | { |
| 2069 | #define TMPBUFLEN 21 | 2252 | int *i, vleft, first = 1, err = 0; |
| 2070 | int *i, vleft, first = 1, neg; | 2253 | unsigned long page = 0; |
| 2071 | unsigned long lval; | 2254 | size_t left; |
| 2072 | size_t left, len; | 2255 | char *kbuf; |
| 2073 | |||
| 2074 | char buf[TMPBUFLEN], *p; | ||
| 2075 | char __user *s = buffer; | ||
| 2076 | 2256 | ||
| 2077 | if (!tbl_data || !table->maxlen || !*lenp || | 2257 | if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { |
| 2078 | (*ppos && !write)) { | ||
| 2079 | *lenp = 0; | 2258 | *lenp = 0; |
| 2080 | return 0; | 2259 | return 0; |
| 2081 | } | 2260 | } |
| @@ -2087,89 +2266,71 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | |||
| 2087 | if (!conv) | 2266 | if (!conv) |
| 2088 | conv = do_proc_dointvec_conv; | 2267 | conv = do_proc_dointvec_conv; |
| 2089 | 2268 | ||
| 2269 | if (write) { | ||
| 2270 | if (left > PAGE_SIZE - 1) | ||
| 2271 | left = PAGE_SIZE - 1; | ||
| 2272 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2273 | kbuf = (char *) page; | ||
| 2274 | if (!kbuf) | ||
| 2275 | return -ENOMEM; | ||
| 2276 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2277 | err = -EFAULT; | ||
| 2278 | goto free; | ||
| 2279 | } | ||
| 2280 | kbuf[left] = 0; | ||
| 2281 | } | ||
| 2282 | |||
| 2090 | for (; left && vleft--; i++, first=0) { | 2283 | for (; left && vleft--; i++, first=0) { |
| 2284 | unsigned long lval; | ||
| 2285 | bool neg; | ||
| 2286 | |||
| 2091 | if (write) { | 2287 | if (write) { |
| 2092 | while (left) { | 2288 | left -= proc_skip_spaces(&kbuf); |
| 2093 | char c; | 2289 | |
| 2094 | if (get_user(c, s)) | ||
| 2095 | return -EFAULT; | ||
| 2096 | if (!isspace(c)) | ||
| 2097 | break; | ||
| 2098 | left--; | ||
| 2099 | s++; | ||
| 2100 | } | ||
| 2101 | if (!left) | 2290 | if (!left) |
| 2102 | break; | 2291 | break; |
| 2103 | neg = 0; | 2292 | err = proc_get_long(&kbuf, &left, &lval, &neg, |
| 2104 | len = left; | 2293 | proc_wspace_sep, |
| 2105 | if (len > sizeof(buf) - 1) | 2294 | sizeof(proc_wspace_sep), NULL); |
| 2106 | len = sizeof(buf) - 1; | 2295 | if (err) |
| 2107 | if (copy_from_user(buf, s, len)) | ||
| 2108 | return -EFAULT; | ||
| 2109 | buf[len] = 0; | ||
| 2110 | p = buf; | ||
| 2111 | if (*p == '-' && left > 1) { | ||
| 2112 | neg = 1; | ||
| 2113 | p++; | ||
| 2114 | } | ||
| 2115 | if (*p < '0' || *p > '9') | ||
| 2116 | break; | ||
| 2117 | |||
| 2118 | lval = simple_strtoul(p, &p, 0); | ||
| 2119 | |||
| 2120 | len = p-buf; | ||
| 2121 | if ((len < left) && *p && !isspace(*p)) | ||
| 2122 | break; | 2296 | break; |
| 2123 | s += len; | 2297 | if (conv(&neg, &lval, i, 1, data)) { |
| 2124 | left -= len; | 2298 | err = -EINVAL; |
| 2125 | |||
| 2126 | if (conv(&neg, &lval, i, 1, data)) | ||
| 2127 | break; | 2299 | break; |
| 2300 | } | ||
| 2128 | } else { | 2301 | } else { |
| 2129 | p = buf; | 2302 | if (conv(&neg, &lval, i, 0, data)) { |
| 2303 | err = -EINVAL; | ||
| 2304 | break; | ||
| 2305 | } | ||
| 2130 | if (!first) | 2306 | if (!first) |
| 2131 | *p++ = '\t'; | 2307 | err = proc_put_char(&buffer, &left, '\t'); |
| 2132 | 2308 | if (err) | |
| 2133 | if (conv(&neg, &lval, i, 0, data)) | 2309 | break; |
| 2310 | err = proc_put_long(&buffer, &left, lval, neg); | ||
| 2311 | if (err) | ||
| 2134 | break; | 2312 | break; |
| 2135 | |||
| 2136 | sprintf(p, "%s%lu", neg ? "-" : "", lval); | ||
| 2137 | len = strlen(buf); | ||
| 2138 | if (len > left) | ||
| 2139 | len = left; | ||
| 2140 | if(copy_to_user(s, buf, len)) | ||
| 2141 | return -EFAULT; | ||
| 2142 | left -= len; | ||
| 2143 | s += len; | ||
| 2144 | } | 2313 | } |
| 2145 | } | 2314 | } |
| 2146 | 2315 | ||
| 2147 | if (!write && !first && left) { | 2316 | if (!write && !first && left && !err) |
| 2148 | if(put_user('\n', s)) | 2317 | err = proc_put_char(&buffer, &left, '\n'); |
| 2149 | return -EFAULT; | 2318 | if (write && !err && left) |
| 2150 | left--, s++; | 2319 | left -= proc_skip_spaces(&kbuf); |
| 2151 | } | 2320 | free: |
| 2152 | if (write) { | 2321 | if (write) { |
| 2153 | while (left) { | 2322 | free_page(page); |
| 2154 | char c; | 2323 | if (first) |
| 2155 | if (get_user(c, s++)) | 2324 | return err ? : -EINVAL; |
| 2156 | return -EFAULT; | ||
| 2157 | if (!isspace(c)) | ||
| 2158 | break; | ||
| 2159 | left--; | ||
| 2160 | } | ||
| 2161 | } | 2325 | } |
| 2162 | if (write && first) | ||
| 2163 | return -EINVAL; | ||
| 2164 | *lenp -= left; | 2326 | *lenp -= left; |
| 2165 | *ppos += *lenp; | 2327 | *ppos += *lenp; |
| 2166 | return 0; | 2328 | return err; |
| 2167 | #undef TMPBUFLEN | ||
| 2168 | } | 2329 | } |
| 2169 | 2330 | ||
| 2170 | static int do_proc_dointvec(struct ctl_table *table, int write, | 2331 | static int do_proc_dointvec(struct ctl_table *table, int write, |
| 2171 | void __user *buffer, size_t *lenp, loff_t *ppos, | 2332 | void __user *buffer, size_t *lenp, loff_t *ppos, |
| 2172 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2333 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
| 2173 | int write, void *data), | 2334 | int write, void *data), |
| 2174 | void *data) | 2335 | void *data) |
| 2175 | { | 2336 | { |
| @@ -2237,8 +2398,8 @@ struct do_proc_dointvec_minmax_conv_param { | |||
| 2237 | int *max; | 2398 | int *max; |
| 2238 | }; | 2399 | }; |
| 2239 | 2400 | ||
| 2240 | static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, | 2401 | static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp, |
| 2241 | int *valp, | 2402 | int *valp, |
| 2242 | int write, void *data) | 2403 | int write, void *data) |
| 2243 | { | 2404 | { |
| 2244 | struct do_proc_dointvec_minmax_conv_param *param = data; | 2405 | struct do_proc_dointvec_minmax_conv_param *param = data; |
| @@ -2251,10 +2412,10 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, | |||
| 2251 | } else { | 2412 | } else { |
| 2252 | int val = *valp; | 2413 | int val = *valp; |
| 2253 | if (val < 0) { | 2414 | if (val < 0) { |
| 2254 | *negp = -1; | 2415 | *negp = true; |
| 2255 | *lvalp = (unsigned long)-val; | 2416 | *lvalp = (unsigned long)-val; |
| 2256 | } else { | 2417 | } else { |
| 2257 | *negp = 0; | 2418 | *negp = false; |
| 2258 | *lvalp = (unsigned long)val; | 2419 | *lvalp = (unsigned long)val; |
| 2259 | } | 2420 | } |
| 2260 | } | 2421 | } |
| @@ -2294,102 +2455,78 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int | |||
| 2294 | unsigned long convmul, | 2455 | unsigned long convmul, |
| 2295 | unsigned long convdiv) | 2456 | unsigned long convdiv) |
| 2296 | { | 2457 | { |
| 2297 | #define TMPBUFLEN 21 | 2458 | unsigned long *i, *min, *max; |
| 2298 | unsigned long *i, *min, *max, val; | 2459 | int vleft, first = 1, err = 0; |
| 2299 | int vleft, first=1, neg; | 2460 | unsigned long page = 0; |
| 2300 | size_t len, left; | 2461 | size_t left; |
| 2301 | char buf[TMPBUFLEN], *p; | 2462 | char *kbuf; |
| 2302 | char __user *s = buffer; | 2463 | |
| 2303 | 2464 | if (!data || !table->maxlen || !*lenp || (*ppos && !write)) { | |
| 2304 | if (!data || !table->maxlen || !*lenp || | ||
| 2305 | (*ppos && !write)) { | ||
| 2306 | *lenp = 0; | 2465 | *lenp = 0; |
| 2307 | return 0; | 2466 | return 0; |
| 2308 | } | 2467 | } |
| 2309 | 2468 | ||
| 2310 | i = (unsigned long *) data; | 2469 | i = (unsigned long *) data; |
| 2311 | min = (unsigned long *) table->extra1; | 2470 | min = (unsigned long *) table->extra1; |
| 2312 | max = (unsigned long *) table->extra2; | 2471 | max = (unsigned long *) table->extra2; |
| 2313 | vleft = table->maxlen / sizeof(unsigned long); | 2472 | vleft = table->maxlen / sizeof(unsigned long); |
| 2314 | left = *lenp; | 2473 | left = *lenp; |
| 2315 | 2474 | ||
| 2475 | if (write) { | ||
| 2476 | if (left > PAGE_SIZE - 1) | ||
| 2477 | left = PAGE_SIZE - 1; | ||
| 2478 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2479 | kbuf = (char *) page; | ||
| 2480 | if (!kbuf) | ||
| 2481 | return -ENOMEM; | ||
| 2482 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2483 | err = -EFAULT; | ||
| 2484 | goto free; | ||
| 2485 | } | ||
| 2486 | kbuf[left] = 0; | ||
| 2487 | } | ||
| 2488 | |||
| 2316 | for (; left && vleft--; i++, min++, max++, first=0) { | 2489 | for (; left && vleft--; i++, min++, max++, first=0) { |
| 2490 | unsigned long val; | ||
| 2491 | |||
| 2317 | if (write) { | 2492 | if (write) { |
| 2318 | while (left) { | 2493 | bool neg; |
| 2319 | char c; | 2494 | |
| 2320 | if (get_user(c, s)) | 2495 | left -= proc_skip_spaces(&kbuf); |
| 2321 | return -EFAULT; | 2496 | |
| 2322 | if (!isspace(c)) | 2497 | err = proc_get_long(&kbuf, &left, &val, &neg, |
| 2323 | break; | 2498 | proc_wspace_sep, |
| 2324 | left--; | 2499 | sizeof(proc_wspace_sep), NULL); |
| 2325 | s++; | 2500 | if (err) |
| 2326 | } | ||
| 2327 | if (!left) | ||
| 2328 | break; | ||
| 2329 | neg = 0; | ||
| 2330 | len = left; | ||
| 2331 | if (len > TMPBUFLEN-1) | ||
| 2332 | len = TMPBUFLEN-1; | ||
| 2333 | if (copy_from_user(buf, s, len)) | ||
| 2334 | return -EFAULT; | ||
| 2335 | buf[len] = 0; | ||
| 2336 | p = buf; | ||
| 2337 | if (*p == '-' && left > 1) { | ||
| 2338 | neg = 1; | ||
| 2339 | p++; | ||
| 2340 | } | ||
| 2341 | if (*p < '0' || *p > '9') | ||
| 2342 | break; | ||
| 2343 | val = simple_strtoul(p, &p, 0) * convmul / convdiv ; | ||
| 2344 | len = p-buf; | ||
| 2345 | if ((len < left) && *p && !isspace(*p)) | ||
| 2346 | break; | 2501 | break; |
| 2347 | if (neg) | 2502 | if (neg) |
| 2348 | val = -val; | ||
| 2349 | s += len; | ||
| 2350 | left -= len; | ||
| 2351 | |||
| 2352 | if(neg) | ||
| 2353 | continue; | 2503 | continue; |
| 2354 | if ((min && val < *min) || (max && val > *max)) | 2504 | if ((min && val < *min) || (max && val > *max)) |
| 2355 | continue; | 2505 | continue; |
| 2356 | *i = val; | 2506 | *i = val; |
| 2357 | } else { | 2507 | } else { |
| 2358 | p = buf; | 2508 | val = convdiv * (*i) / convmul; |
| 2359 | if (!first) | 2509 | if (!first) |
| 2360 | *p++ = '\t'; | 2510 | err = proc_put_char(&buffer, &left, '\t'); |
| 2361 | sprintf(p, "%lu", convdiv * (*i) / convmul); | 2511 | err = proc_put_long(&buffer, &left, val, false); |
| 2362 | len = strlen(buf); | 2512 | if (err) |
| 2363 | if (len > left) | 2513 | break; |
| 2364 | len = left; | ||
| 2365 | if(copy_to_user(s, buf, len)) | ||
| 2366 | return -EFAULT; | ||
| 2367 | left -= len; | ||
| 2368 | s += len; | ||
| 2369 | } | 2514 | } |
| 2370 | } | 2515 | } |
| 2371 | 2516 | ||
| 2372 | if (!write && !first && left) { | 2517 | if (!write && !first && left && !err) |
| 2373 | if(put_user('\n', s)) | 2518 | err = proc_put_char(&buffer, &left, '\n'); |
| 2374 | return -EFAULT; | 2519 | if (write && !err) |
| 2375 | left--, s++; | 2520 | left -= proc_skip_spaces(&kbuf); |
| 2376 | } | 2521 | free: |
| 2377 | if (write) { | 2522 | if (write) { |
| 2378 | while (left) { | 2523 | free_page(page); |
| 2379 | char c; | 2524 | if (first) |
| 2380 | if (get_user(c, s++)) | 2525 | return err ? : -EINVAL; |
| 2381 | return -EFAULT; | ||
| 2382 | if (!isspace(c)) | ||
| 2383 | break; | ||
| 2384 | left--; | ||
| 2385 | } | ||
| 2386 | } | 2526 | } |
| 2387 | if (write && first) | ||
| 2388 | return -EINVAL; | ||
| 2389 | *lenp -= left; | 2527 | *lenp -= left; |
| 2390 | *ppos += *lenp; | 2528 | *ppos += *lenp; |
| 2391 | return 0; | 2529 | return err; |
| 2392 | #undef TMPBUFLEN | ||
| 2393 | } | 2530 | } |
| 2394 | 2531 | ||
| 2395 | static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, | 2532 | static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, |
| @@ -2450,7 +2587,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write, | |||
| 2450 | } | 2587 | } |
| 2451 | 2588 | ||
| 2452 | 2589 | ||
| 2453 | static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | 2590 | static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp, |
| 2454 | int *valp, | 2591 | int *valp, |
| 2455 | int write, void *data) | 2592 | int write, void *data) |
| 2456 | { | 2593 | { |
| @@ -2462,10 +2599,10 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2462 | int val = *valp; | 2599 | int val = *valp; |
| 2463 | unsigned long lval; | 2600 | unsigned long lval; |
| 2464 | if (val < 0) { | 2601 | if (val < 0) { |
| 2465 | *negp = -1; | 2602 | *negp = true; |
| 2466 | lval = (unsigned long)-val; | 2603 | lval = (unsigned long)-val; |
| 2467 | } else { | 2604 | } else { |
| 2468 | *negp = 0; | 2605 | *negp = false; |
| 2469 | lval = (unsigned long)val; | 2606 | lval = (unsigned long)val; |
| 2470 | } | 2607 | } |
| 2471 | *lvalp = lval / HZ; | 2608 | *lvalp = lval / HZ; |
| @@ -2473,7 +2610,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2473 | return 0; | 2610 | return 0; |
| 2474 | } | 2611 | } |
| 2475 | 2612 | ||
| 2476 | static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | 2613 | static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp, |
| 2477 | int *valp, | 2614 | int *valp, |
| 2478 | int write, void *data) | 2615 | int write, void *data) |
| 2479 | { | 2616 | { |
| @@ -2485,10 +2622,10 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2485 | int val = *valp; | 2622 | int val = *valp; |
| 2486 | unsigned long lval; | 2623 | unsigned long lval; |
| 2487 | if (val < 0) { | 2624 | if (val < 0) { |
| 2488 | *negp = -1; | 2625 | *negp = true; |
| 2489 | lval = (unsigned long)-val; | 2626 | lval = (unsigned long)-val; |
| 2490 | } else { | 2627 | } else { |
| 2491 | *negp = 0; | 2628 | *negp = false; |
| 2492 | lval = (unsigned long)val; | 2629 | lval = (unsigned long)val; |
| 2493 | } | 2630 | } |
| 2494 | *lvalp = jiffies_to_clock_t(lval); | 2631 | *lvalp = jiffies_to_clock_t(lval); |
| @@ -2496,7 +2633,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2496 | return 0; | 2633 | return 0; |
| 2497 | } | 2634 | } |
| 2498 | 2635 | ||
| 2499 | static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, | 2636 | static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp, |
| 2500 | int *valp, | 2637 | int *valp, |
| 2501 | int write, void *data) | 2638 | int write, void *data) |
| 2502 | { | 2639 | { |
| @@ -2506,10 +2643,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2506 | int val = *valp; | 2643 | int val = *valp; |
| 2507 | unsigned long lval; | 2644 | unsigned long lval; |
| 2508 | if (val < 0) { | 2645 | if (val < 0) { |
| 2509 | *negp = -1; | 2646 | *negp = true; |
| 2510 | lval = (unsigned long)-val; | 2647 | lval = (unsigned long)-val; |
| 2511 | } else { | 2648 | } else { |
| 2512 | *negp = 0; | 2649 | *negp = false; |
| 2513 | lval = (unsigned long)val; | 2650 | lval = (unsigned long)val; |
| 2514 | } | 2651 | } |
| 2515 | *lvalp = jiffies_to_msecs(lval); | 2652 | *lvalp = jiffies_to_msecs(lval); |
| @@ -2606,6 +2743,157 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, | |||
| 2606 | return 0; | 2743 | return 0; |
| 2607 | } | 2744 | } |
| 2608 | 2745 | ||
| 2746 | /** | ||
| 2747 | * proc_do_large_bitmap - read/write from/to a large bitmap | ||
| 2748 | * @table: the sysctl table | ||
| 2749 | * @write: %TRUE if this is a write to the sysctl file | ||
| 2750 | * @buffer: the user buffer | ||
| 2751 | * @lenp: the size of the user buffer | ||
| 2752 | * @ppos: file position | ||
| 2753 | * | ||
| 2754 | * The bitmap is stored at table->data and the bitmap length (in bits) | ||
| 2755 | * in table->maxlen. | ||
| 2756 | * | ||
| 2757 | * We use a range comma separated format (e.g. 1,3-4,10-10) so that | ||
| 2758 | * large bitmaps may be represented in a compact manner. Writing into | ||
| 2759 | * the file will clear the bitmap then update it with the given input. | ||
| 2760 | * | ||
| 2761 | * Returns 0 on success. | ||
| 2762 | */ | ||
| 2763 | int proc_do_large_bitmap(struct ctl_table *table, int write, | ||
| 2764 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
| 2765 | { | ||
| 2766 | int err = 0; | ||
| 2767 | bool first = 1; | ||
| 2768 | size_t left = *lenp; | ||
| 2769 | unsigned long bitmap_len = table->maxlen; | ||
| 2770 | unsigned long *bitmap = (unsigned long *) table->data; | ||
| 2771 | unsigned long *tmp_bitmap = NULL; | ||
| 2772 | char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; | ||
| 2773 | |||
| 2774 | if (!bitmap_len || !left || (*ppos && !write)) { | ||
| 2775 | *lenp = 0; | ||
| 2776 | return 0; | ||
| 2777 | } | ||
| 2778 | |||
| 2779 | if (write) { | ||
| 2780 | unsigned long page = 0; | ||
| 2781 | char *kbuf; | ||
| 2782 | |||
| 2783 | if (left > PAGE_SIZE - 1) | ||
| 2784 | left = PAGE_SIZE - 1; | ||
| 2785 | |||
| 2786 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2787 | kbuf = (char *) page; | ||
| 2788 | if (!kbuf) | ||
| 2789 | return -ENOMEM; | ||
| 2790 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2791 | free_page(page); | ||
| 2792 | return -EFAULT; | ||
| 2793 | } | ||
| 2794 | kbuf[left] = 0; | ||
| 2795 | |||
| 2796 | tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long), | ||
| 2797 | GFP_KERNEL); | ||
| 2798 | if (!tmp_bitmap) { | ||
| 2799 | free_page(page); | ||
| 2800 | return -ENOMEM; | ||
| 2801 | } | ||
| 2802 | proc_skip_char(&kbuf, &left, '\n'); | ||
| 2803 | while (!err && left) { | ||
| 2804 | unsigned long val_a, val_b; | ||
| 2805 | bool neg; | ||
| 2806 | |||
| 2807 | err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a, | ||
| 2808 | sizeof(tr_a), &c); | ||
| 2809 | if (err) | ||
| 2810 | break; | ||
| 2811 | if (val_a >= bitmap_len || neg) { | ||
| 2812 | err = -EINVAL; | ||
| 2813 | break; | ||
| 2814 | } | ||
| 2815 | |||
| 2816 | val_b = val_a; | ||
| 2817 | if (left) { | ||
| 2818 | kbuf++; | ||
| 2819 | left--; | ||
| 2820 | } | ||
| 2821 | |||
| 2822 | if (c == '-') { | ||
| 2823 | err = proc_get_long(&kbuf, &left, &val_b, | ||
| 2824 | &neg, tr_b, sizeof(tr_b), | ||
| 2825 | &c); | ||
| 2826 | if (err) | ||
| 2827 | break; | ||
| 2828 | if (val_b >= bitmap_len || neg || | ||
| 2829 | val_a > val_b) { | ||
| 2830 | err = -EINVAL; | ||
| 2831 | break; | ||
| 2832 | } | ||
| 2833 | if (left) { | ||
| 2834 | kbuf++; | ||
| 2835 | left--; | ||
| 2836 | } | ||
| 2837 | } | ||
| 2838 | |||
| 2839 | while (val_a <= val_b) | ||
| 2840 | set_bit(val_a++, tmp_bitmap); | ||
| 2841 | |||
| 2842 | first = 0; | ||
| 2843 | proc_skip_char(&kbuf, &left, '\n'); | ||
| 2844 | } | ||
| 2845 | free_page(page); | ||
| 2846 | } else { | ||
| 2847 | unsigned long bit_a, bit_b = 0; | ||
| 2848 | |||
| 2849 | while (left) { | ||
| 2850 | bit_a = find_next_bit(bitmap, bitmap_len, bit_b); | ||
| 2851 | if (bit_a >= bitmap_len) | ||
| 2852 | break; | ||
| 2853 | bit_b = find_next_zero_bit(bitmap, bitmap_len, | ||
| 2854 | bit_a + 1) - 1; | ||
| 2855 | |||
| 2856 | if (!first) { | ||
| 2857 | err = proc_put_char(&buffer, &left, ','); | ||
| 2858 | if (err) | ||
| 2859 | break; | ||
| 2860 | } | ||
| 2861 | err = proc_put_long(&buffer, &left, bit_a, false); | ||
| 2862 | if (err) | ||
| 2863 | break; | ||
| 2864 | if (bit_a != bit_b) { | ||
| 2865 | err = proc_put_char(&buffer, &left, '-'); | ||
| 2866 | if (err) | ||
| 2867 | break; | ||
| 2868 | err = proc_put_long(&buffer, &left, bit_b, false); | ||
| 2869 | if (err) | ||
| 2870 | break; | ||
| 2871 | } | ||
| 2872 | |||
| 2873 | first = 0; bit_b++; | ||
| 2874 | } | ||
| 2875 | if (!err) | ||
| 2876 | err = proc_put_char(&buffer, &left, '\n'); | ||
| 2877 | } | ||
| 2878 | |||
| 2879 | if (!err) { | ||
| 2880 | if (write) { | ||
| 2881 | if (*ppos) | ||
| 2882 | bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len); | ||
| 2883 | else | ||
| 2884 | memcpy(bitmap, tmp_bitmap, | ||
| 2885 | BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long)); | ||
| 2886 | } | ||
| 2887 | kfree(tmp_bitmap); | ||
| 2888 | *lenp -= left; | ||
| 2889 | *ppos += *lenp; | ||
| 2890 | return 0; | ||
| 2891 | } else { | ||
| 2892 | kfree(tmp_bitmap); | ||
| 2893 | return err; | ||
| 2894 | } | ||
| 2895 | } | ||
| 2896 | |||
| 2609 | #else /* CONFIG_PROC_FS */ | 2897 | #else /* CONFIG_PROC_FS */ |
| 2610 | 2898 | ||
| 2611 | int proc_dostring(struct ctl_table *table, int write, | 2899 | int proc_dostring(struct ctl_table *table, int write, |
