diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 00:04:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 00:04:44 -0400 |
commit | f8965467f366fd18f01feafb5db10512d7b4422c (patch) | |
tree | 3706a9cd779859271ca61b85c63a1bc3f82d626e /kernel/sysctl.c | |
parent | a26272e5200765691e67d6780e52b32498fdb659 (diff) | |
parent | 2ec8c6bb5d8f3a62a79f463525054bae1e3d4487 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1674 commits)
qlcnic: adding co maintainer
ixgbe: add support for active DA cables
ixgbe: dcb, do not tag tc_prio_control frames
ixgbe: fix ixgbe_tx_is_paused logic
ixgbe: always enable vlan strip/insert when DCB is enabled
ixgbe: remove some redundant code in setting FCoE FIP filter
ixgbe: fix wrong offset to fc_frame_header in ixgbe_fcoe_ddp
ixgbe: fix header len when unsplit packet overflows to data buffer
ipv6: Never schedule DAD timer on dead address
ipv6: Use POSTDAD state
ipv6: Use state_lock to protect ifa state
ipv6: Replace inet6_ifaddr->dead with state
cxgb4: notify upper drivers if the device is already up when they load
cxgb4: keep interrupts available when the ports are brought down
cxgb4: fix initial addition of MAC address
cnic: Return SPQ credit to bnx2x after ring setup and shutdown.
cnic: Convert cnic_local_flags to atomic ops.
can: Fix SJA1000 command register writes on SMP systems
bridge: fix build for CONFIG_SYSFS disabled
ARCNET: Limit com20020 PCI ID matches for SOHARD cards
...
Fix up various conflicts with pcmcia tree drivers/net/
{pcmcia/3c589_cs.c, wireless/orinoco/orinoco_cs.c and
wireless/orinoco/spectrum_cs.c} and feature removal
(Documentation/feature-removal-schedule.txt).
Also fix a non-content conflict due to pm_qos_requirement getting
renamed in the PM tree (now pm_qos_request) in net/mac80211/scan.c
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r-- | kernel/sysctl.c | 551 |
1 files changed, 390 insertions, 161 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 509e6ba5df20..b12583047757 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -2062,8 +2062,132 @@ int proc_dostring(struct ctl_table *table, int write, | |||
2062 | buffer, lenp, ppos); | 2062 | buffer, lenp, ppos); |
2063 | } | 2063 | } |
2064 | 2064 | ||
2065 | static size_t proc_skip_spaces(char **buf) | ||
2066 | { | ||
2067 | size_t ret; | ||
2068 | char *tmp = skip_spaces(*buf); | ||
2069 | ret = tmp - *buf; | ||
2070 | *buf = tmp; | ||
2071 | return ret; | ||
2072 | } | ||
2073 | |||
2074 | static void proc_skip_char(char **buf, size_t *size, const char v) | ||
2075 | { | ||
2076 | while (*size) { | ||
2077 | if (**buf != v) | ||
2078 | break; | ||
2079 | (*size)--; | ||
2080 | (*buf)++; | ||
2081 | } | ||
2082 | } | ||
2083 | |||
2084 | #define TMPBUFLEN 22 | ||
2085 | /** | ||
2086 | * proc_get_long - reads an ASCII formated integer from a user buffer | ||
2087 | * | ||
2088 | * @buf - a kernel buffer | ||
2089 | * @size - size of the kernel buffer | ||
2090 | * @val - this is where the number will be stored | ||
2091 | * @neg - set to %TRUE if number is negative | ||
2092 | * @perm_tr - a vector which contains the allowed trailers | ||
2093 | * @perm_tr_len - size of the perm_tr vector | ||
2094 | * @tr - pointer to store the trailer character | ||
2095 | * | ||
2096 | * In case of success 0 is returned and buf and size are updated with | ||
2097 | * the amount of bytes read. If tr is non NULL and a trailing | ||
2098 | * character exist (size is non zero after returning from this | ||
2099 | * function) tr is updated with the trailing character. | ||
2100 | */ | ||
2101 | static int proc_get_long(char **buf, size_t *size, | ||
2102 | unsigned long *val, bool *neg, | ||
2103 | const char *perm_tr, unsigned perm_tr_len, char *tr) | ||
2104 | { | ||
2105 | int len; | ||
2106 | char *p, tmp[TMPBUFLEN]; | ||
2107 | |||
2108 | if (!*size) | ||
2109 | return -EINVAL; | ||
2110 | |||
2111 | len = *size; | ||
2112 | if (len > TMPBUFLEN - 1) | ||
2113 | len = TMPBUFLEN - 1; | ||
2114 | |||
2115 | memcpy(tmp, *buf, len); | ||
2116 | |||
2117 | tmp[len] = 0; | ||
2118 | p = tmp; | ||
2119 | if (*p == '-' && *size > 1) { | ||
2120 | *neg = true; | ||
2121 | p++; | ||
2122 | } else | ||
2123 | *neg = false; | ||
2124 | if (!isdigit(*p)) | ||
2125 | return -EINVAL; | ||
2126 | |||
2127 | *val = simple_strtoul(p, &p, 0); | ||
2128 | |||
2129 | len = p - tmp; | ||
2130 | |||
2131 | /* We don't know if the next char is whitespace thus we may accept | ||
2132 | * invalid integers (e.g. 1234...a) or two integers instead of one | ||
2133 | * (e.g. 123...1). So lets not allow such large numbers. */ | ||
2134 | if (len == TMPBUFLEN - 1) | ||
2135 | return -EINVAL; | ||
2136 | |||
2137 | if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len)) | ||
2138 | return -EINVAL; | ||
2139 | |||
2140 | if (tr && (len < *size)) | ||
2141 | *tr = *p; | ||
2142 | |||
2143 | *buf += len; | ||
2144 | *size -= len; | ||
2145 | |||
2146 | return 0; | ||
2147 | } | ||
2148 | |||
2149 | /** | ||
2150 | * proc_put_long - coverts an integer to a decimal ASCII formated string | ||
2151 | * | ||
2152 | * @buf - the user buffer | ||
2153 | * @size - the size of the user buffer | ||
2154 | * @val - the integer to be converted | ||
2155 | * @neg - sign of the number, %TRUE for negative | ||
2156 | * | ||
2157 | * In case of success 0 is returned and buf and size are updated with | ||
2158 | * the amount of bytes read. | ||
2159 | */ | ||
2160 | static int proc_put_long(void __user **buf, size_t *size, unsigned long val, | ||
2161 | bool neg) | ||
2162 | { | ||
2163 | int len; | ||
2164 | char tmp[TMPBUFLEN], *p = tmp; | ||
2165 | |||
2166 | sprintf(p, "%s%lu", neg ? "-" : "", val); | ||
2167 | len = strlen(tmp); | ||
2168 | if (len > *size) | ||
2169 | len = *size; | ||
2170 | if (copy_to_user(*buf, tmp, len)) | ||
2171 | return -EFAULT; | ||
2172 | *size -= len; | ||
2173 | *buf += len; | ||
2174 | return 0; | ||
2175 | } | ||
2176 | #undef TMPBUFLEN | ||
2177 | |||
2178 | static int proc_put_char(void __user **buf, size_t *size, char c) | ||
2179 | { | ||
2180 | if (*size) { | ||
2181 | char __user **buffer = (char __user **)buf; | ||
2182 | if (put_user(c, *buffer)) | ||
2183 | return -EFAULT; | ||
2184 | (*size)--, (*buffer)++; | ||
2185 | *buf = *buffer; | ||
2186 | } | ||
2187 | return 0; | ||
2188 | } | ||
2065 | 2189 | ||
2066 | static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | 2190 | static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, |
2067 | int *valp, | 2191 | int *valp, |
2068 | int write, void *data) | 2192 | int write, void *data) |
2069 | { | 2193 | { |
@@ -2072,33 +2196,31 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | |||
2072 | } else { | 2196 | } else { |
2073 | int val = *valp; | 2197 | int val = *valp; |
2074 | if (val < 0) { | 2198 | if (val < 0) { |
2075 | *negp = -1; | 2199 | *negp = true; |
2076 | *lvalp = (unsigned long)-val; | 2200 | *lvalp = (unsigned long)-val; |
2077 | } else { | 2201 | } else { |
2078 | *negp = 0; | 2202 | *negp = false; |
2079 | *lvalp = (unsigned long)val; | 2203 | *lvalp = (unsigned long)val; |
2080 | } | 2204 | } |
2081 | } | 2205 | } |
2082 | return 0; | 2206 | return 0; |
2083 | } | 2207 | } |
2084 | 2208 | ||
2209 | static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; | ||
2210 | |||
2085 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | 2211 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, |
2086 | int write, void __user *buffer, | 2212 | int write, void __user *buffer, |
2087 | size_t *lenp, loff_t *ppos, | 2213 | size_t *lenp, loff_t *ppos, |
2088 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2214 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
2089 | int write, void *data), | 2215 | int write, void *data), |
2090 | void *data) | 2216 | void *data) |
2091 | { | 2217 | { |
2092 | #define TMPBUFLEN 21 | 2218 | int *i, vleft, first = 1, err = 0; |
2093 | int *i, vleft, first = 1, neg; | 2219 | unsigned long page = 0; |
2094 | unsigned long lval; | 2220 | size_t left; |
2095 | size_t left, len; | 2221 | char *kbuf; |
2096 | 2222 | ||
2097 | char buf[TMPBUFLEN], *p; | 2223 | if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { |
2098 | char __user *s = buffer; | ||
2099 | |||
2100 | if (!tbl_data || !table->maxlen || !*lenp || | ||
2101 | (*ppos && !write)) { | ||
2102 | *lenp = 0; | 2224 | *lenp = 0; |
2103 | return 0; | 2225 | return 0; |
2104 | } | 2226 | } |
@@ -2110,89 +2232,69 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | |||
2110 | if (!conv) | 2232 | if (!conv) |
2111 | conv = do_proc_dointvec_conv; | 2233 | conv = do_proc_dointvec_conv; |
2112 | 2234 | ||
2235 | if (write) { | ||
2236 | if (left > PAGE_SIZE - 1) | ||
2237 | left = PAGE_SIZE - 1; | ||
2238 | page = __get_free_page(GFP_TEMPORARY); | ||
2239 | kbuf = (char *) page; | ||
2240 | if (!kbuf) | ||
2241 | return -ENOMEM; | ||
2242 | if (copy_from_user(kbuf, buffer, left)) { | ||
2243 | err = -EFAULT; | ||
2244 | goto free; | ||
2245 | } | ||
2246 | kbuf[left] = 0; | ||
2247 | } | ||
2248 | |||
2113 | for (; left && vleft--; i++, first=0) { | 2249 | for (; left && vleft--; i++, first=0) { |
2114 | if (write) { | 2250 | unsigned long lval; |
2115 | while (left) { | 2251 | bool neg; |
2116 | char c; | ||
2117 | if (get_user(c, s)) | ||
2118 | return -EFAULT; | ||
2119 | if (!isspace(c)) | ||
2120 | break; | ||
2121 | left--; | ||
2122 | s++; | ||
2123 | } | ||
2124 | if (!left) | ||
2125 | break; | ||
2126 | neg = 0; | ||
2127 | len = left; | ||
2128 | if (len > sizeof(buf) - 1) | ||
2129 | len = sizeof(buf) - 1; | ||
2130 | if (copy_from_user(buf, s, len)) | ||
2131 | return -EFAULT; | ||
2132 | buf[len] = 0; | ||
2133 | p = buf; | ||
2134 | if (*p == '-' && left > 1) { | ||
2135 | neg = 1; | ||
2136 | p++; | ||
2137 | } | ||
2138 | if (*p < '0' || *p > '9') | ||
2139 | break; | ||
2140 | 2252 | ||
2141 | lval = simple_strtoul(p, &p, 0); | 2253 | if (write) { |
2254 | left -= proc_skip_spaces(&kbuf); | ||
2142 | 2255 | ||
2143 | len = p-buf; | 2256 | err = proc_get_long(&kbuf, &left, &lval, &neg, |
2144 | if ((len < left) && *p && !isspace(*p)) | 2257 | proc_wspace_sep, |
2258 | sizeof(proc_wspace_sep), NULL); | ||
2259 | if (err) | ||
2145 | break; | 2260 | break; |
2146 | s += len; | 2261 | if (conv(&neg, &lval, i, 1, data)) { |
2147 | left -= len; | 2262 | err = -EINVAL; |
2148 | |||
2149 | if (conv(&neg, &lval, i, 1, data)) | ||
2150 | break; | 2263 | break; |
2264 | } | ||
2151 | } else { | 2265 | } else { |
2152 | p = buf; | 2266 | if (conv(&neg, &lval, i, 0, data)) { |
2267 | err = -EINVAL; | ||
2268 | break; | ||
2269 | } | ||
2153 | if (!first) | 2270 | if (!first) |
2154 | *p++ = '\t'; | 2271 | err = proc_put_char(&buffer, &left, '\t'); |
2155 | 2272 | if (err) | |
2156 | if (conv(&neg, &lval, i, 0, data)) | 2273 | break; |
2274 | err = proc_put_long(&buffer, &left, lval, neg); | ||
2275 | if (err) | ||
2157 | break; | 2276 | break; |
2158 | |||
2159 | sprintf(p, "%s%lu", neg ? "-" : "", lval); | ||
2160 | len = strlen(buf); | ||
2161 | if (len > left) | ||
2162 | len = left; | ||
2163 | if(copy_to_user(s, buf, len)) | ||
2164 | return -EFAULT; | ||
2165 | left -= len; | ||
2166 | s += len; | ||
2167 | } | 2277 | } |
2168 | } | 2278 | } |
2169 | 2279 | ||
2170 | if (!write && !first && left) { | 2280 | if (!write && !first && left && !err) |
2171 | if(put_user('\n', s)) | 2281 | err = proc_put_char(&buffer, &left, '\n'); |
2172 | return -EFAULT; | 2282 | if (write && !err) |
2173 | left--, s++; | 2283 | left -= proc_skip_spaces(&kbuf); |
2174 | } | 2284 | free: |
2175 | if (write) { | 2285 | if (write) { |
2176 | while (left) { | 2286 | free_page(page); |
2177 | char c; | 2287 | if (first) |
2178 | if (get_user(c, s++)) | 2288 | return err ? : -EINVAL; |
2179 | return -EFAULT; | ||
2180 | if (!isspace(c)) | ||
2181 | break; | ||
2182 | left--; | ||
2183 | } | ||
2184 | } | 2289 | } |
2185 | if (write && first) | ||
2186 | return -EINVAL; | ||
2187 | *lenp -= left; | 2290 | *lenp -= left; |
2188 | *ppos += *lenp; | 2291 | *ppos += *lenp; |
2189 | return 0; | 2292 | return err; |
2190 | #undef TMPBUFLEN | ||
2191 | } | 2293 | } |
2192 | 2294 | ||
2193 | static int do_proc_dointvec(struct ctl_table *table, int write, | 2295 | static int do_proc_dointvec(struct ctl_table *table, int write, |
2194 | void __user *buffer, size_t *lenp, loff_t *ppos, | 2296 | void __user *buffer, size_t *lenp, loff_t *ppos, |
2195 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2297 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
2196 | int write, void *data), | 2298 | int write, void *data), |
2197 | void *data) | 2299 | void *data) |
2198 | { | 2300 | { |
@@ -2260,8 +2362,8 @@ struct do_proc_dointvec_minmax_conv_param { | |||
2260 | int *max; | 2362 | int *max; |
2261 | }; | 2363 | }; |
2262 | 2364 | ||
2263 | static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, | 2365 | static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp, |
2264 | int *valp, | 2366 | int *valp, |
2265 | int write, void *data) | 2367 | int write, void *data) |
2266 | { | 2368 | { |
2267 | struct do_proc_dointvec_minmax_conv_param *param = data; | 2369 | struct do_proc_dointvec_minmax_conv_param *param = data; |
@@ -2274,10 +2376,10 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, | |||
2274 | } else { | 2376 | } else { |
2275 | int val = *valp; | 2377 | int val = *valp; |
2276 | if (val < 0) { | 2378 | if (val < 0) { |
2277 | *negp = -1; | 2379 | *negp = true; |
2278 | *lvalp = (unsigned long)-val; | 2380 | *lvalp = (unsigned long)-val; |
2279 | } else { | 2381 | } else { |
2280 | *negp = 0; | 2382 | *negp = false; |
2281 | *lvalp = (unsigned long)val; | 2383 | *lvalp = (unsigned long)val; |
2282 | } | 2384 | } |
2283 | } | 2385 | } |
@@ -2317,102 +2419,78 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int | |||
2317 | unsigned long convmul, | 2419 | unsigned long convmul, |
2318 | unsigned long convdiv) | 2420 | unsigned long convdiv) |
2319 | { | 2421 | { |
2320 | #define TMPBUFLEN 21 | 2422 | unsigned long *i, *min, *max; |
2321 | unsigned long *i, *min, *max, val; | 2423 | int vleft, first = 1, err = 0; |
2322 | int vleft, first=1, neg; | 2424 | unsigned long page = 0; |
2323 | size_t len, left; | 2425 | size_t left; |
2324 | char buf[TMPBUFLEN], *p; | 2426 | char *kbuf; |
2325 | char __user *s = buffer; | 2427 | |
2326 | 2428 | if (!data || !table->maxlen || !*lenp || (*ppos && !write)) { | |
2327 | if (!data || !table->maxlen || !*lenp || | ||
2328 | (*ppos && !write)) { | ||
2329 | *lenp = 0; | 2429 | *lenp = 0; |
2330 | return 0; | 2430 | return 0; |
2331 | } | 2431 | } |
2332 | 2432 | ||
2333 | i = (unsigned long *) data; | 2433 | i = (unsigned long *) data; |
2334 | min = (unsigned long *) table->extra1; | 2434 | min = (unsigned long *) table->extra1; |
2335 | max = (unsigned long *) table->extra2; | 2435 | max = (unsigned long *) table->extra2; |
2336 | vleft = table->maxlen / sizeof(unsigned long); | 2436 | vleft = table->maxlen / sizeof(unsigned long); |
2337 | left = *lenp; | 2437 | left = *lenp; |
2338 | 2438 | ||
2439 | if (write) { | ||
2440 | if (left > PAGE_SIZE - 1) | ||
2441 | left = PAGE_SIZE - 1; | ||
2442 | page = __get_free_page(GFP_TEMPORARY); | ||
2443 | kbuf = (char *) page; | ||
2444 | if (!kbuf) | ||
2445 | return -ENOMEM; | ||
2446 | if (copy_from_user(kbuf, buffer, left)) { | ||
2447 | err = -EFAULT; | ||
2448 | goto free; | ||
2449 | } | ||
2450 | kbuf[left] = 0; | ||
2451 | } | ||
2452 | |||
2339 | for (; left && vleft--; i++, min++, max++, first=0) { | 2453 | for (; left && vleft--; i++, min++, max++, first=0) { |
2454 | unsigned long val; | ||
2455 | |||
2340 | if (write) { | 2456 | if (write) { |
2341 | while (left) { | 2457 | bool neg; |
2342 | char c; | 2458 | |
2343 | if (get_user(c, s)) | 2459 | left -= proc_skip_spaces(&kbuf); |
2344 | return -EFAULT; | 2460 | |
2345 | if (!isspace(c)) | 2461 | err = proc_get_long(&kbuf, &left, &val, &neg, |
2346 | break; | 2462 | proc_wspace_sep, |
2347 | left--; | 2463 | sizeof(proc_wspace_sep), NULL); |
2348 | s++; | 2464 | if (err) |
2349 | } | ||
2350 | if (!left) | ||
2351 | break; | ||
2352 | neg = 0; | ||
2353 | len = left; | ||
2354 | if (len > TMPBUFLEN-1) | ||
2355 | len = TMPBUFLEN-1; | ||
2356 | if (copy_from_user(buf, s, len)) | ||
2357 | return -EFAULT; | ||
2358 | buf[len] = 0; | ||
2359 | p = buf; | ||
2360 | if (*p == '-' && left > 1) { | ||
2361 | neg = 1; | ||
2362 | p++; | ||
2363 | } | ||
2364 | if (*p < '0' || *p > '9') | ||
2365 | break; | ||
2366 | val = simple_strtoul(p, &p, 0) * convmul / convdiv ; | ||
2367 | len = p-buf; | ||
2368 | if ((len < left) && *p && !isspace(*p)) | ||
2369 | break; | 2465 | break; |
2370 | if (neg) | 2466 | if (neg) |
2371 | val = -val; | ||
2372 | s += len; | ||
2373 | left -= len; | ||
2374 | |||
2375 | if(neg) | ||
2376 | continue; | 2467 | continue; |
2377 | if ((min && val < *min) || (max && val > *max)) | 2468 | if ((min && val < *min) || (max && val > *max)) |
2378 | continue; | 2469 | continue; |
2379 | *i = val; | 2470 | *i = val; |
2380 | } else { | 2471 | } else { |
2381 | p = buf; | 2472 | val = convdiv * (*i) / convmul; |
2382 | if (!first) | 2473 | if (!first) |
2383 | *p++ = '\t'; | 2474 | err = proc_put_char(&buffer, &left, '\t'); |
2384 | sprintf(p, "%lu", convdiv * (*i) / convmul); | 2475 | err = proc_put_long(&buffer, &left, val, false); |
2385 | len = strlen(buf); | 2476 | if (err) |
2386 | if (len > left) | 2477 | break; |
2387 | len = left; | ||
2388 | if(copy_to_user(s, buf, len)) | ||
2389 | return -EFAULT; | ||
2390 | left -= len; | ||
2391 | s += len; | ||
2392 | } | 2478 | } |
2393 | } | 2479 | } |
2394 | 2480 | ||
2395 | if (!write && !first && left) { | 2481 | if (!write && !first && left && !err) |
2396 | if(put_user('\n', s)) | 2482 | err = proc_put_char(&buffer, &left, '\n'); |
2397 | return -EFAULT; | 2483 | if (write && !err) |
2398 | left--, s++; | 2484 | left -= proc_skip_spaces(&kbuf); |
2399 | } | 2485 | free: |
2400 | if (write) { | 2486 | if (write) { |
2401 | while (left) { | 2487 | free_page(page); |
2402 | char c; | 2488 | if (first) |
2403 | if (get_user(c, s++)) | 2489 | return err ? : -EINVAL; |
2404 | return -EFAULT; | ||
2405 | if (!isspace(c)) | ||
2406 | break; | ||
2407 | left--; | ||
2408 | } | ||
2409 | } | 2490 | } |
2410 | if (write && first) | ||
2411 | return -EINVAL; | ||
2412 | *lenp -= left; | 2491 | *lenp -= left; |
2413 | *ppos += *lenp; | 2492 | *ppos += *lenp; |
2414 | return 0; | 2493 | return err; |
2415 | #undef TMPBUFLEN | ||
2416 | } | 2494 | } |
2417 | 2495 | ||
2418 | static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, | 2496 | static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, |
@@ -2473,7 +2551,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write, | |||
2473 | } | 2551 | } |
2474 | 2552 | ||
2475 | 2553 | ||
2476 | static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | 2554 | static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp, |
2477 | int *valp, | 2555 | int *valp, |
2478 | int write, void *data) | 2556 | int write, void *data) |
2479 | { | 2557 | { |
@@ -2485,10 +2563,10 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | |||
2485 | int val = *valp; | 2563 | int val = *valp; |
2486 | unsigned long lval; | 2564 | unsigned long lval; |
2487 | if (val < 0) { | 2565 | if (val < 0) { |
2488 | *negp = -1; | 2566 | *negp = true; |
2489 | lval = (unsigned long)-val; | 2567 | lval = (unsigned long)-val; |
2490 | } else { | 2568 | } else { |
2491 | *negp = 0; | 2569 | *negp = false; |
2492 | lval = (unsigned long)val; | 2570 | lval = (unsigned long)val; |
2493 | } | 2571 | } |
2494 | *lvalp = lval / HZ; | 2572 | *lvalp = lval / HZ; |
@@ -2496,7 +2574,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | |||
2496 | return 0; | 2574 | return 0; |
2497 | } | 2575 | } |
2498 | 2576 | ||
2499 | static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | 2577 | static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp, |
2500 | int *valp, | 2578 | int *valp, |
2501 | int write, void *data) | 2579 | int write, void *data) |
2502 | { | 2580 | { |
@@ -2508,10 +2586,10 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | |||
2508 | int val = *valp; | 2586 | int val = *valp; |
2509 | unsigned long lval; | 2587 | unsigned long lval; |
2510 | if (val < 0) { | 2588 | if (val < 0) { |
2511 | *negp = -1; | 2589 | *negp = true; |
2512 | lval = (unsigned long)-val; | 2590 | lval = (unsigned long)-val; |
2513 | } else { | 2591 | } else { |
2514 | *negp = 0; | 2592 | *negp = false; |
2515 | lval = (unsigned long)val; | 2593 | lval = (unsigned long)val; |
2516 | } | 2594 | } |
2517 | *lvalp = jiffies_to_clock_t(lval); | 2595 | *lvalp = jiffies_to_clock_t(lval); |
@@ -2519,7 +2597,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | |||
2519 | return 0; | 2597 | return 0; |
2520 | } | 2598 | } |
2521 | 2599 | ||
2522 | static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, | 2600 | static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp, |
2523 | int *valp, | 2601 | int *valp, |
2524 | int write, void *data) | 2602 | int write, void *data) |
2525 | { | 2603 | { |
@@ -2529,10 +2607,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, | |||
2529 | int val = *valp; | 2607 | int val = *valp; |
2530 | unsigned long lval; | 2608 | unsigned long lval; |
2531 | if (val < 0) { | 2609 | if (val < 0) { |
2532 | *negp = -1; | 2610 | *negp = true; |
2533 | lval = (unsigned long)-val; | 2611 | lval = (unsigned long)-val; |
2534 | } else { | 2612 | } else { |
2535 | *negp = 0; | 2613 | *negp = false; |
2536 | lval = (unsigned long)val; | 2614 | lval = (unsigned long)val; |
2537 | } | 2615 | } |
2538 | *lvalp = jiffies_to_msecs(lval); | 2616 | *lvalp = jiffies_to_msecs(lval); |
@@ -2629,6 +2707,157 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, | |||
2629 | return 0; | 2707 | return 0; |
2630 | } | 2708 | } |
2631 | 2709 | ||
2710 | /** | ||
2711 | * proc_do_large_bitmap - read/write from/to a large bitmap | ||
2712 | * @table: the sysctl table | ||
2713 | * @write: %TRUE if this is a write to the sysctl file | ||
2714 | * @buffer: the user buffer | ||
2715 | * @lenp: the size of the user buffer | ||
2716 | * @ppos: file position | ||
2717 | * | ||
2718 | * The bitmap is stored at table->data and the bitmap length (in bits) | ||
2719 | * in table->maxlen. | ||
2720 | * | ||
2721 | * We use a range comma separated format (e.g. 1,3-4,10-10) so that | ||
2722 | * large bitmaps may be represented in a compact manner. Writing into | ||
2723 | * the file will clear the bitmap then update it with the given input. | ||
2724 | * | ||
2725 | * Returns 0 on success. | ||
2726 | */ | ||
2727 | int proc_do_large_bitmap(struct ctl_table *table, int write, | ||
2728 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
2729 | { | ||
2730 | int err = 0; | ||
2731 | bool first = 1; | ||
2732 | size_t left = *lenp; | ||
2733 | unsigned long bitmap_len = table->maxlen; | ||
2734 | unsigned long *bitmap = (unsigned long *) table->data; | ||
2735 | unsigned long *tmp_bitmap = NULL; | ||
2736 | char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; | ||
2737 | |||
2738 | if (!bitmap_len || !left || (*ppos && !write)) { | ||
2739 | *lenp = 0; | ||
2740 | return 0; | ||
2741 | } | ||
2742 | |||
2743 | if (write) { | ||
2744 | unsigned long page = 0; | ||
2745 | char *kbuf; | ||
2746 | |||
2747 | if (left > PAGE_SIZE - 1) | ||
2748 | left = PAGE_SIZE - 1; | ||
2749 | |||
2750 | page = __get_free_page(GFP_TEMPORARY); | ||
2751 | kbuf = (char *) page; | ||
2752 | if (!kbuf) | ||
2753 | return -ENOMEM; | ||
2754 | if (copy_from_user(kbuf, buffer, left)) { | ||
2755 | free_page(page); | ||
2756 | return -EFAULT; | ||
2757 | } | ||
2758 | kbuf[left] = 0; | ||
2759 | |||
2760 | tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long), | ||
2761 | GFP_KERNEL); | ||
2762 | if (!tmp_bitmap) { | ||
2763 | free_page(page); | ||
2764 | return -ENOMEM; | ||
2765 | } | ||
2766 | proc_skip_char(&kbuf, &left, '\n'); | ||
2767 | while (!err && left) { | ||
2768 | unsigned long val_a, val_b; | ||
2769 | bool neg; | ||
2770 | |||
2771 | err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a, | ||
2772 | sizeof(tr_a), &c); | ||
2773 | if (err) | ||
2774 | break; | ||
2775 | if (val_a >= bitmap_len || neg) { | ||
2776 | err = -EINVAL; | ||
2777 | break; | ||
2778 | } | ||
2779 | |||
2780 | val_b = val_a; | ||
2781 | if (left) { | ||
2782 | kbuf++; | ||
2783 | left--; | ||
2784 | } | ||
2785 | |||
2786 | if (c == '-') { | ||
2787 | err = proc_get_long(&kbuf, &left, &val_b, | ||
2788 | &neg, tr_b, sizeof(tr_b), | ||
2789 | &c); | ||
2790 | if (err) | ||
2791 | break; | ||
2792 | if (val_b >= bitmap_len || neg || | ||
2793 | val_a > val_b) { | ||
2794 | err = -EINVAL; | ||
2795 | break; | ||
2796 | } | ||
2797 | if (left) { | ||
2798 | kbuf++; | ||
2799 | left--; | ||
2800 | } | ||
2801 | } | ||
2802 | |||
2803 | while (val_a <= val_b) | ||
2804 | set_bit(val_a++, tmp_bitmap); | ||
2805 | |||
2806 | first = 0; | ||
2807 | proc_skip_char(&kbuf, &left, '\n'); | ||
2808 | } | ||
2809 | free_page(page); | ||
2810 | } else { | ||
2811 | unsigned long bit_a, bit_b = 0; | ||
2812 | |||
2813 | while (left) { | ||
2814 | bit_a = find_next_bit(bitmap, bitmap_len, bit_b); | ||
2815 | if (bit_a >= bitmap_len) | ||
2816 | break; | ||
2817 | bit_b = find_next_zero_bit(bitmap, bitmap_len, | ||
2818 | bit_a + 1) - 1; | ||
2819 | |||
2820 | if (!first) { | ||
2821 | err = proc_put_char(&buffer, &left, ','); | ||
2822 | if (err) | ||
2823 | break; | ||
2824 | } | ||
2825 | err = proc_put_long(&buffer, &left, bit_a, false); | ||
2826 | if (err) | ||
2827 | break; | ||
2828 | if (bit_a != bit_b) { | ||
2829 | err = proc_put_char(&buffer, &left, '-'); | ||
2830 | if (err) | ||
2831 | break; | ||
2832 | err = proc_put_long(&buffer, &left, bit_b, false); | ||
2833 | if (err) | ||
2834 | break; | ||
2835 | } | ||
2836 | |||
2837 | first = 0; bit_b++; | ||
2838 | } | ||
2839 | if (!err) | ||
2840 | err = proc_put_char(&buffer, &left, '\n'); | ||
2841 | } | ||
2842 | |||
2843 | if (!err) { | ||
2844 | if (write) { | ||
2845 | if (*ppos) | ||
2846 | bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len); | ||
2847 | else | ||
2848 | memcpy(bitmap, tmp_bitmap, | ||
2849 | BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long)); | ||
2850 | } | ||
2851 | kfree(tmp_bitmap); | ||
2852 | *lenp -= left; | ||
2853 | *ppos += *lenp; | ||
2854 | return 0; | ||
2855 | } else { | ||
2856 | kfree(tmp_bitmap); | ||
2857 | return err; | ||
2858 | } | ||
2859 | } | ||
2860 | |||
2632 | #else /* CONFIG_PROC_FS */ | 2861 | #else /* CONFIG_PROC_FS */ |
2633 | 2862 | ||
2634 | int proc_dostring(struct ctl_table *table, int write, | 2863 | int proc_dostring(struct ctl_table *table, int write, |