Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'sysctl-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/sysctl/sysctl

Pull sysctl updates from Joel Granados:

- Move jiffies converters out of kernel/sysctl.c

Move the jiffies converters into kernel/time/jiffies.c and replace
the pipe-max-size proc_handler converter with a macro based version.
This is all part of the effort to relocate non-sysctl logic out of
kernel/sysctl.c into more relevant subsystems. No functional changes.

- Generalize proc handler converter creation

Remove duplicated sysctl converter logic by consolidating it in
macros. These are used inside sysctl core as well as in pipe.c and
jiffies.c. Converter kernel and user space pointer args are now
automatically const qualified for the convenience of the caller. No
functional changes.

- Miscellaneous

Fix kernel-doc format warnings, remove unnecessary __user
qualifiers, and move the nmi_watchdog sysctl into .rodata.

- Testing

This series was run through sysctl selftests/kunit test suite in
x86_64. It went into linux-next after rc2, giving it a good 4/5 weeks
of testing.

* tag 'sysctl-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/sysctl/sysctl: (21 commits)
sysctl: Wrap do_proc_douintvec with the public function proc_douintvec_conv
sysctl: Create pipe-max-size converter using sysctl UINT macros
sysctl: Move proc_doulongvec_ms_jiffies_minmax to kernel/time/jiffies.c
sysctl: Move jiffies converters to kernel/time/jiffies.c
sysctl: Move UINT converter macros to sysctl header
sysctl: Move INT converter macros to sysctl header
sysctl: Allow custom converters from outside sysctl
sysctl: remove __user qualifier from stack_erasing_sysctl buffer argument
sysctl: Create macro for user-to-kernel uint converter
sysctl: Add optional range checking to SYSCTL_UINT_CONV_CUSTOM
sysctl: Create unsigned int converter using new macro
sysctl: Add optional range checking to SYSCTL_INT_CONV_CUSTOM
sysctl: Create integer converters with one macro
sysctl: Create converter functions with two new macros
sysctl: Discriminate between kernel and user converter params
sysctl: Indicate the direction of operation with macro names
sysctl: Remove superfluous __do_proc_* indirection
sysctl: Remove superfluous tbl_data param from "dovec" functions
sysctl: Replace void pointer with const pointer to ctl_table
sysctl: fix kernel-doc format warning
...

+436 -546
+7 -21
fs/pipe.c
··· 1481 1481 }; 1482 1482 1483 1483 #ifdef CONFIG_SYSCTL 1484 - static int do_proc_dopipe_max_size_conv(unsigned long *lvalp, 1485 - unsigned int *valp, 1486 - int write, void *data) 1487 - { 1488 - if (write) { 1489 - unsigned int val; 1490 - 1491 - val = round_pipe_size(*lvalp); 1492 - if (val == 0) 1493 - return -EINVAL; 1494 - 1495 - *valp = val; 1496 - } else { 1497 - unsigned int val = *valp; 1498 - *lvalp = (unsigned long) val; 1499 - } 1500 - 1501 - return 0; 1502 - } 1484 + static SYSCTL_USER_TO_KERN_UINT_CONV(_pipe_maxsz, round_pipe_size) 1485 + static SYSCTL_UINT_CONV_CUSTOM(_pipe_maxsz, 1486 + sysctl_user_to_kern_uint_conv_pipe_maxsz, 1487 + sysctl_kern_to_user_uint_conv, true) 1503 1488 1504 1489 static int proc_dopipe_max_size(const struct ctl_table *table, int write, 1505 1490 void *buffer, size_t *lenp, loff_t *ppos) 1506 1491 { 1507 - return do_proc_douintvec(table, write, buffer, lenp, ppos, 1508 - do_proc_dopipe_max_size_conv, NULL); 1492 + return proc_douintvec_conv(table, write, buffer, lenp, ppos, 1493 + do_proc_uint_conv_pipe_maxsz); 1509 1494 } 1510 1495 1511 1496 static const struct ctl_table fs_pipe_sysctls[] = { ··· 1500 1515 .maxlen = sizeof(pipe_max_size), 1501 1516 .mode = 0644, 1502 1517 .proc_handler = proc_dopipe_max_size, 1518 + .extra1 = SYSCTL_ONE, 1503 1519 }, 1504 1520 { 1505 1521 .procname = "pipe-user-pages-hard",
+12
include/linux/jiffies.h
··· 611 611 612 612 #define TIMESTAMP_SIZE 30 613 613 614 + struct ctl_table; 615 + int proc_dointvec_jiffies(const struct ctl_table *table, int dir, void *buffer, 616 + size_t *lenp, loff_t *ppos); 617 + int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, 618 + void *buffer, size_t *lenp, loff_t *ppos); 619 + int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir, 620 + void *buffer, size_t *lenp, loff_t *ppos); 621 + int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir, void *buffer, 622 + size_t *lenp, loff_t *ppos); 623 + int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, 624 + void *buffer, size_t *lenp, loff_t *ppos); 625 + 614 626 #endif
+134 -23
include/linux/sysctl.h
··· 59 59 #define SYSCTL_LONG_ONE ((void *)&sysctl_long_vals[1]) 60 60 #define SYSCTL_LONG_MAX ((void *)&sysctl_long_vals[2]) 61 61 62 + #define SYSCTL_CONV_IDENTITY(val) (val) 63 + /** 64 + * 65 + * "dir" originates from read_iter (dir = 0) or write_iter (dir = 1) 66 + * in the file_operations struct at proc/proc_sysctl.c. Its value means 67 + * one of two things for sysctl: 68 + * 1. SYSCTL_USER_TO_KERN(dir) Writing to an internal kernel variable from user 69 + * space (dir > 0) 70 + * 2. SYSCTL_KERN_TO_USER(dir) Writing to a user space buffer from a kernel 71 + * variable (dir == 0). 72 + */ 73 + #define SYSCTL_USER_TO_KERN(dir) (!!(dir)) 74 + #define SYSCTL_KERN_TO_USER(dir) (!dir) 75 + 76 + #define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \ 77 + int sysctl_user_to_kern_int_conv##name(const bool *negp, \ 78 + const unsigned long *u_ptr,\ 79 + int *k_ptr) \ 80 + { \ 81 + unsigned long u = u_ptr_op(*u_ptr); \ 82 + if (*negp) { \ 83 + if (u > (unsigned long) INT_MAX + 1) \ 84 + return -EINVAL; \ 85 + WRITE_ONCE(*k_ptr, -u); \ 86 + } else { \ 87 + if (u > (unsigned long) INT_MAX) \ 88 + return -EINVAL; \ 89 + WRITE_ONCE(*k_ptr, u); \ 90 + } \ 91 + return 0; \ 92 + } 93 + 94 + #define SYSCTL_KERN_TO_USER_INT_CONV(name, k_ptr_op) \ 95 + int sysctl_kern_to_user_int_conv##name(bool *negp, \ 96 + unsigned long *u_ptr, \ 97 + const int *k_ptr) \ 98 + { \ 99 + int val = READ_ONCE(*k_ptr); \ 100 + if (val < 0) { \ 101 + *negp = true; \ 102 + *u_ptr = -k_ptr_op((unsigned long)val); \ 103 + } else { \ 104 + *negp = false; \ 105 + *u_ptr = k_ptr_op((unsigned long)val); \ 106 + } \ 107 + return 0; \ 108 + } 109 + 110 + /** 111 + * To range check on a converted value, use a temp k_ptr 112 + * When checking range, value should be within (tbl->extra1, tbl->extra2) 113 + */ 114 + #define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ 115 + k_ptr_range_check) \ 116 + int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ 117 + int dir, const struct ctl_table *tbl) \ 118 + { \ 119 + if (SYSCTL_KERN_TO_USER(dir)) \ 120 + return kern_to_user(negp, u_ptr, k_ptr); \ 121 + \ 122 + if (k_ptr_range_check) { \ 123 + int tmp_k, ret; \ 124 + if (!tbl) \ 125 + return -EINVAL; \ 126 + ret = user_to_kern(negp, u_ptr, &tmp_k); \ 127 + if (ret) \ 128 + return ret; \ 129 + if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) || \ 130 + (tbl->extra2 && *(int *)tbl->extra2 < tmp_k)) \ 131 + return -EINVAL; \ 132 + WRITE_ONCE(*k_ptr, tmp_k); \ 133 + } else \ 134 + return user_to_kern(negp, u_ptr, k_ptr); \ 135 + return 0; \ 136 + } 137 + 138 + #define SYSCTL_USER_TO_KERN_UINT_CONV(name, u_ptr_op) \ 139 + int sysctl_user_to_kern_uint_conv##name(const unsigned long *u_ptr,\ 140 + unsigned int *k_ptr) \ 141 + { \ 142 + unsigned long u = u_ptr_op(*u_ptr); \ 143 + if (u > UINT_MAX) \ 144 + return -EINVAL; \ 145 + WRITE_ONCE(*k_ptr, u); \ 146 + return 0; \ 147 + } 148 + 149 + #define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ 150 + k_ptr_range_check) \ 151 + int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ 152 + int dir, const struct ctl_table *tbl) \ 153 + { \ 154 + if (SYSCTL_KERN_TO_USER(dir)) \ 155 + return kern_to_user(u_ptr, k_ptr); \ 156 + \ 157 + if (k_ptr_range_check) { \ 158 + unsigned int tmp_k; \ 159 + int ret; \ 160 + if (!tbl) \ 161 + return -EINVAL; \ 162 + ret = user_to_kern(u_ptr, &tmp_k); \ 163 + if (ret) \ 164 + return ret; \ 165 + if ((tbl->extra1 && \ 166 + *(unsigned int *)tbl->extra1 > tmp_k) || \ 167 + (tbl->extra2 && \ 168 + *(unsigned int *)tbl->extra2 < tmp_k)) \ 169 + return -ERANGE; \ 170 + WRITE_ONCE(*k_ptr, tmp_k); \ 171 + } else \ 172 + return user_to_kern(u_ptr, k_ptr); \ 173 + return 0; \ 174 + } 175 + 176 + 62 177 extern const unsigned long sysctl_long_vals[]; 63 178 64 179 typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer, ··· 183 68 int proc_dobool(const struct ctl_table *table, int write, void *buffer, 184 69 size_t *lenp, loff_t *ppos); 185 70 int proc_dointvec(const struct ctl_table *, int, void *, size_t *, loff_t *); 71 + int proc_dointvec_minmax(const struct ctl_table *table, int dir, void *buffer, 72 + size_t *lenp, loff_t *ppos); 73 + int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer, 74 + size_t *lenp, loff_t *ppos, 75 + int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr, 76 + int dir, const struct ctl_table *table)); 186 77 int proc_douintvec(const struct ctl_table *, int, void *, size_t *, loff_t *); 187 - int proc_dointvec_minmax(const struct ctl_table *, int, void *, size_t *, loff_t *); 188 78 int proc_douintvec_minmax(const struct ctl_table *table, int write, void *buffer, 189 79 size_t *lenp, loff_t *ppos); 80 + int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer, 81 + size_t *lenp, loff_t *ppos, 82 + int (*conv)(unsigned long *lvalp, unsigned int *valp, 83 + int write, const struct ctl_table *table)); 84 + 190 85 int proc_dou8vec_minmax(const struct ctl_table *table, int write, void *buffer, 191 86 size_t *lenp, loff_t *ppos); 192 - int proc_dointvec_jiffies(const struct ctl_table *, int, void *, size_t *, loff_t *); 193 - int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write, 194 - void *buffer, size_t *lenp, loff_t *ppos); 195 - int proc_dointvec_userhz_jiffies(const struct ctl_table *, int, void *, size_t *, 196 - loff_t *); 197 - int proc_dointvec_ms_jiffies(const struct ctl_table *, int, void *, size_t *, 198 - loff_t *); 199 87 int proc_doulongvec_minmax(const struct ctl_table *, int, void *, size_t *, loff_t *); 200 - int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int, void *, 201 - size_t *, loff_t *); 88 + int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir, 89 + void *buffer, size_t *lenp, loff_t *ppos, 90 + unsigned long convmul, unsigned long convdiv); 202 91 int proc_do_large_bitmap(const struct ctl_table *, int, void *, size_t *, loff_t *); 203 92 int proc_do_static_key(const struct ctl_table *table, int write, void *buffer, 204 93 size_t *lenp, loff_t *ppos); 94 + int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr, const unsigned int *k_ptr); 205 95 206 96 /* 207 97 * Register a set of sysctl names by calling register_sysctl ··· 276 156 * @nreg: When nreg drops to 0 the ctl_table_header will be unregistered. 277 157 * @rcu: Delays the freeing of the inode. Introduced with "unfuck proc_sysctl ->d_compare()" 278 158 * 159 + * @type: Enumeration to differentiate between ctl target types 160 + * @type.SYSCTL_TABLE_TYPE_DEFAULT: ctl target with no special considerations 161 + * @type.SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY: Identifies a permanently empty dir 162 + * target to serve as a mount point 279 163 */ 280 164 struct ctl_table_header { 281 165 union { ··· 299 175 struct ctl_dir *parent; 300 176 struct ctl_node *node; 301 177 struct hlist_head inodes; /* head for proc_inode->sysctl_inodes */ 302 - /** 303 - * enum type - Enumeration to differentiate between ctl target types 304 - * @SYSCTL_TABLE_TYPE_DEFAULT: ctl target with no special considerations 305 - * @SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY: Used to identify a permanently 306 - * empty directory target to serve 307 - * as mount point. 308 - */ 309 178 enum { 310 179 SYSCTL_TABLE_TYPE_DEFAULT, 311 180 SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY, ··· 352 235 353 236 void do_sysctl_args(void); 354 237 bool sysctl_is_alias(char *param); 355 - int do_proc_douintvec(const struct ctl_table *table, int write, 356 - void *buffer, size_t *lenp, loff_t *ppos, 357 - int (*conv)(unsigned long *lvalp, 358 - unsigned int *valp, 359 - int write, void *data), 360 - void *data); 361 238 362 239 extern int unaligned_enabled; 363 240 extern int no_unaligned_warning;
+1 -1
kernel/kstack_erase.c
··· 23 23 24 24 #ifdef CONFIG_SYSCTL 25 25 static int stack_erasing_sysctl(const struct ctl_table *table, int write, 26 - void __user *buffer, size_t *lenp, loff_t *ppos) 26 + void *buffer, size_t *lenp, loff_t *ppos) 27 27 { 28 28 int ret = 0; 29 29 int state = !static_branch_unlikely(&stack_erasing_bypass);
+156 -493
kernel/sysctl.c
··· 13 13 #include <linux/highuid.h> 14 14 #include <linux/writeback.h> 15 15 #include <linux/initrd.h> 16 - #include <linux/times.h> 17 16 #include <linux/limits.h> 18 17 #include <linux/syscalls.h> 19 18 #include <linux/capability.h> ··· 54 55 * to the buffer. 55 56 * 56 57 * These write modes control how current file position affects the behavior of 57 - * updating sysctl values through the proc interface on each write. 58 + * updating internal kernel (SYSCTL_USER_TO_KERN) sysctl values through the proc 59 + * interface on each write. 58 60 */ 59 61 enum sysctl_writes_mode { 60 62 SYSCTL_WRITES_LEGACY = -1, ··· 73 73 74 74 #ifdef CONFIG_PROC_SYSCTL 75 75 76 - static int _proc_do_string(char *data, int maxlen, int write, 76 + static int _proc_do_string(char *data, int maxlen, int dir, 77 77 char *buffer, size_t *lenp, loff_t *ppos) 78 78 { 79 79 size_t len; ··· 84 84 return 0; 85 85 } 86 86 87 - if (write) { 87 + if (SYSCTL_USER_TO_KERN(dir)) { 88 88 if (sysctl_writes_strict == SYSCTL_WRITES_STRICT) { 89 89 /* Only continue writes not past the end of buffer. */ 90 90 len = strlen(data); ··· 172 172 /** 173 173 * proc_dostring - read a string sysctl 174 174 * @table: the sysctl table 175 - * @write: %TRUE if this is a write to the sysctl file 175 + * @dir: %TRUE if this is a write to the sysctl file 176 176 * @buffer: the user buffer 177 177 * @lenp: the size of the user buffer 178 178 * @ppos: file position ··· 186 186 * 187 187 * Returns 0 on success. 188 188 */ 189 - int proc_dostring(const struct ctl_table *table, int write, 189 + int proc_dostring(const struct ctl_table *table, int dir, 190 190 void *buffer, size_t *lenp, loff_t *ppos) 191 191 { 192 - if (write) 192 + if (SYSCTL_USER_TO_KERN(dir)) 193 193 proc_first_pos_non_zero_ignore(ppos, table); 194 194 195 - return _proc_do_string(table->data, table->maxlen, write, buffer, lenp, 195 + return _proc_do_string(table->data, table->maxlen, dir, buffer, lenp, 196 196 ppos); 197 197 } 198 198 ··· 354 354 } 355 355 } 356 356 357 - static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, 358 - int *valp, 359 - int write, void *data) 357 + static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY) 358 + static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY) 359 + 360 + static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv, 361 + sysctl_kern_to_user_int_conv, false) 362 + static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv, 363 + sysctl_kern_to_user_int_conv, true) 364 + 365 + 366 + static SYSCTL_USER_TO_KERN_UINT_CONV(, SYSCTL_CONV_IDENTITY) 367 + 368 + int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr, 369 + const unsigned int *k_ptr) 360 370 { 361 - if (write) { 362 - if (*negp) { 363 - if (*lvalp > (unsigned long) INT_MAX + 1) 364 - return -EINVAL; 365 - WRITE_ONCE(*valp, -*lvalp); 366 - } else { 367 - if (*lvalp > (unsigned long) INT_MAX) 368 - return -EINVAL; 369 - WRITE_ONCE(*valp, *lvalp); 370 - } 371 - } else { 372 - int val = READ_ONCE(*valp); 373 - if (val < 0) { 374 - *negp = true; 375 - *lvalp = -(unsigned long)val; 376 - } else { 377 - *negp = false; 378 - *lvalp = (unsigned long)val; 379 - } 380 - } 371 + unsigned int val = READ_ONCE(*k_ptr); 372 + *u_ptr = (unsigned long)val; 381 373 return 0; 382 374 } 383 375 384 - static int do_proc_douintvec_conv(unsigned long *lvalp, 385 - unsigned int *valp, 386 - int write, void *data) 387 - { 388 - if (write) { 389 - if (*lvalp > UINT_MAX) 390 - return -EINVAL; 391 - WRITE_ONCE(*valp, *lvalp); 392 - } else { 393 - unsigned int val = READ_ONCE(*valp); 394 - *lvalp = (unsigned long)val; 395 - } 396 - return 0; 397 - } 376 + static SYSCTL_UINT_CONV_CUSTOM(, sysctl_user_to_kern_uint_conv, 377 + sysctl_kern_to_user_uint_conv, false) 378 + static SYSCTL_UINT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_uint_conv, 379 + sysctl_kern_to_user_uint_conv, true) 398 380 399 381 static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; 400 382 401 - static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table, 402 - int write, void *buffer, 403 - size_t *lenp, loff_t *ppos, 404 - int (*conv)(bool *negp, unsigned long *lvalp, int *valp, 405 - int write, void *data), 406 - void *data) 383 + static int do_proc_dointvec(const struct ctl_table *table, int dir, 384 + void *buffer, size_t *lenp, loff_t *ppos, 385 + int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr, 386 + int dir, const struct ctl_table *table)) 407 387 { 408 388 int *i, vleft, first = 1, err = 0; 409 389 size_t left; 410 390 char *p; 411 391 412 - if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { 392 + if (!table->data || !table->maxlen || !*lenp || 393 + (*ppos && SYSCTL_KERN_TO_USER(dir))) { 413 394 *lenp = 0; 414 395 return 0; 415 396 } 416 397 417 - i = (int *) tbl_data; 398 + i = (int *) table->data; 418 399 vleft = table->maxlen / sizeof(*i); 419 400 left = *lenp; 420 401 421 402 if (!conv) 422 - conv = do_proc_dointvec_conv; 403 + conv = do_proc_int_conv; 423 404 424 - if (write) { 405 + if (SYSCTL_USER_TO_KERN(dir)) { 425 406 if (proc_first_pos_non_zero_ignore(ppos, table)) 426 407 goto out; 427 408 ··· 415 434 unsigned long lval; 416 435 bool neg; 417 436 418 - if (write) { 437 + if (SYSCTL_USER_TO_KERN(dir)) { 419 438 proc_skip_spaces(&p, &left); 420 439 421 440 if (!left) ··· 425 444 sizeof(proc_wspace_sep), NULL); 426 445 if (err) 427 446 break; 428 - if (conv(&neg, &lval, i, 1, data)) { 447 + if (conv(&neg, &lval, i, 1, table)) { 429 448 err = -EINVAL; 430 449 break; 431 450 } 432 451 } else { 433 - if (conv(&neg, &lval, i, 0, data)) { 452 + if (conv(&neg, &lval, i, 0, table)) { 434 453 err = -EINVAL; 435 454 break; 436 455 } ··· 440 459 } 441 460 } 442 461 443 - if (!write && !first && left && !err) 462 + if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err) 444 463 proc_put_char(&buffer, &left, '\n'); 445 - if (write && !err && left) 464 + if (SYSCTL_USER_TO_KERN(dir) && !err && left) 446 465 proc_skip_spaces(&p, &left); 447 - if (write && first) 466 + if (SYSCTL_USER_TO_KERN(dir) && first) 448 467 return err ? : -EINVAL; 449 468 *lenp -= left; 450 469 out: ··· 452 471 return err; 453 472 } 454 473 455 - static int do_proc_dointvec(const struct ctl_table *table, int write, 456 - void *buffer, size_t *lenp, loff_t *ppos, 457 - int (*conv)(bool *negp, unsigned long *lvalp, int *valp, 458 - int write, void *data), 459 - void *data) 460 - { 461 - return __do_proc_dointvec(table->data, table, write, 462 - buffer, lenp, ppos, conv, data); 463 - } 464 - 465 - static int do_proc_douintvec_w(unsigned int *tbl_data, 466 - const struct ctl_table *table, 467 - void *buffer, 474 + static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer, 468 475 size_t *lenp, loff_t *ppos, 469 - int (*conv)(unsigned long *lvalp, 470 - unsigned int *valp, 471 - int write, void *data), 472 - void *data) 476 + int (*conv)(unsigned long *u_ptr, 477 + unsigned int *k_ptr, int dir, 478 + const struct ctl_table *table)) 473 479 { 474 480 unsigned long lval; 475 481 int err = 0; ··· 486 518 goto out_free; 487 519 } 488 520 489 - if (conv(&lval, tbl_data, 1, data)) { 521 + if (conv(&lval, (unsigned int *) table->data, 1, table)) { 490 522 err = -EINVAL; 491 523 goto out_free; 492 524 } ··· 500 532 501 533 return 0; 502 534 503 - /* This is in keeping with old __do_proc_dointvec() */ 504 535 bail_early: 505 536 *ppos += *lenp; 506 537 return err; 507 538 } 508 539 509 - static int do_proc_douintvec_r(unsigned int *tbl_data, void *buffer, 540 + static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer, 510 541 size_t *lenp, loff_t *ppos, 511 - int (*conv)(unsigned long *lvalp, 512 - unsigned int *valp, 513 - int write, void *data), 514 - void *data) 542 + int (*conv)(unsigned long *u_ptr, 543 + unsigned int *k_ptr, int dir, 544 + const struct ctl_table *table)) 515 545 { 516 546 unsigned long lval; 517 547 int err = 0; ··· 517 551 518 552 left = *lenp; 519 553 520 - if (conv(&lval, tbl_data, 0, data)) { 554 + if (conv(&lval, (unsigned int *) table->data, 0, table)) { 521 555 err = -EINVAL; 522 556 goto out; 523 557 } ··· 535 569 return err; 536 570 } 537 571 538 - static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table, 539 - int write, void *buffer, 540 - size_t *lenp, loff_t *ppos, 541 - int (*conv)(unsigned long *lvalp, 542 - unsigned int *valp, 543 - int write, void *data), 544 - void *data) 572 + static int do_proc_douintvec(const struct ctl_table *table, int dir, 573 + void *buffer, size_t *lenp, loff_t *ppos, 574 + int (*conv)(unsigned long *u_ptr, 575 + unsigned int *k_ptr, int dir, 576 + const struct ctl_table *table)) 545 577 { 546 - unsigned int *i, vleft; 578 + unsigned int vleft; 547 579 548 - if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { 580 + if (!table->data || !table->maxlen || !*lenp || 581 + (*ppos && SYSCTL_KERN_TO_USER(dir))) { 549 582 *lenp = 0; 550 583 return 0; 551 584 } 552 585 553 - i = (unsigned int *) tbl_data; 554 - vleft = table->maxlen / sizeof(*i); 586 + vleft = table->maxlen / sizeof(unsigned int); 555 587 556 588 /* 557 589 * Arrays are not supported, keep this simple. *Do not* add ··· 561 597 } 562 598 563 599 if (!conv) 564 - conv = do_proc_douintvec_conv; 600 + conv = do_proc_uint_conv; 565 601 566 - if (write) 567 - return do_proc_douintvec_w(i, table, buffer, lenp, ppos, 568 - conv, data); 569 - return do_proc_douintvec_r(i, buffer, lenp, ppos, conv, data); 602 + if (SYSCTL_USER_TO_KERN(dir)) 603 + return do_proc_douintvec_w(table, buffer, lenp, ppos, conv); 604 + return do_proc_douintvec_r(table, buffer, lenp, ppos, conv); 570 605 } 571 606 572 - int do_proc_douintvec(const struct ctl_table *table, int write, 573 - void *buffer, size_t *lenp, loff_t *ppos, 574 - int (*conv)(unsigned long *lvalp, 575 - unsigned int *valp, 576 - int write, void *data), 577 - void *data) 607 + int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer, 608 + size_t *lenp, loff_t *ppos, 609 + int (*conv)(unsigned long *u_ptr, unsigned int *k_ptr, 610 + int dir, const struct ctl_table *table)) 578 611 { 579 - return __do_proc_douintvec(table->data, table, write, 580 - buffer, lenp, ppos, conv, data); 612 + return do_proc_douintvec(table, dir, buffer, lenp, ppos, conv); 581 613 } 614 + 582 615 583 616 /** 584 617 * proc_dobool - read/write a bool 585 618 * @table: the sysctl table 586 - * @write: %TRUE if this is a write to the sysctl file 619 + * @dir: %TRUE if this is a write to the sysctl file 587 620 * @buffer: the user buffer 588 621 * @lenp: the size of the user buffer 589 622 * @ppos: file position ··· 593 632 * 594 633 * Returns 0 on success. 595 634 */ 596 - int proc_dobool(const struct ctl_table *table, int write, void *buffer, 635 + int proc_dobool(const struct ctl_table *table, int dir, void *buffer, 597 636 size_t *lenp, loff_t *ppos) 598 637 { 599 638 struct ctl_table tmp; ··· 609 648 tmp.data = &val; 610 649 611 650 val = READ_ONCE(*data); 612 - res = proc_dointvec(&tmp, write, buffer, lenp, ppos); 651 + res = proc_dointvec(&tmp, dir, buffer, lenp, ppos); 613 652 if (res) 614 653 return res; 615 - if (write) 654 + if (SYSCTL_USER_TO_KERN(dir)) 616 655 WRITE_ONCE(*data, val); 617 656 return 0; 618 657 } ··· 620 659 /** 621 660 * proc_dointvec - read a vector of integers 622 661 * @table: the sysctl table 623 - * @write: %TRUE if this is a write to the sysctl file 662 + * @dir: %TRUE if this is a write to the sysctl file 624 663 * @buffer: the user buffer 625 664 * @lenp: the size of the user buffer 626 665 * @ppos: file position ··· 630 669 * 631 670 * Returns 0 on success. 632 671 */ 633 - int proc_dointvec(const struct ctl_table *table, int write, void *buffer, 672 + int proc_dointvec(const struct ctl_table *table, int dir, void *buffer, 634 673 size_t *lenp, loff_t *ppos) 635 674 { 636 - return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL); 675 + return do_proc_dointvec(table, dir, buffer, lenp, ppos, NULL); 637 676 } 638 677 639 678 /** 640 679 * proc_douintvec - read a vector of unsigned integers 641 680 * @table: the sysctl table 642 - * @write: %TRUE if this is a write to the sysctl file 681 + * @dir: %TRUE if this is a write to the sysctl file 643 682 * @buffer: the user buffer 644 683 * @lenp: the size of the user buffer 645 684 * @ppos: file position ··· 649 688 * 650 689 * Returns 0 on success. 651 690 */ 652 - int proc_douintvec(const struct ctl_table *table, int write, void *buffer, 691 + int proc_douintvec(const struct ctl_table *table, int dir, void *buffer, 653 692 size_t *lenp, loff_t *ppos) 654 693 { 655 - return do_proc_douintvec(table, write, buffer, lenp, ppos, 656 - do_proc_douintvec_conv, NULL); 657 - } 658 - 659 - /** 660 - * struct do_proc_dointvec_minmax_conv_param - proc_dointvec_minmax() range checking structure 661 - * @min: pointer to minimum allowable value 662 - * @max: pointer to maximum allowable value 663 - * 664 - * The do_proc_dointvec_minmax_conv_param structure provides the 665 - * minimum and maximum values for doing range checking for those sysctl 666 - * parameters that use the proc_dointvec_minmax() handler. 667 - */ 668 - struct do_proc_dointvec_minmax_conv_param { 669 - int *min; 670 - int *max; 671 - }; 672 - 673 - static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp, 674 - int *valp, 675 - int write, void *data) 676 - { 677 - int tmp, ret; 678 - struct do_proc_dointvec_minmax_conv_param *param = data; 679 - /* 680 - * If writing, first do so via a temporary local int so we can 681 - * bounds-check it before touching *valp. 682 - */ 683 - int *ip = write ? &tmp : valp; 684 - 685 - ret = do_proc_dointvec_conv(negp, lvalp, ip, write, data); 686 - if (ret) 687 - return ret; 688 - 689 - if (write) { 690 - if ((param->min && *param->min > tmp) || 691 - (param->max && *param->max < tmp)) 692 - return -EINVAL; 693 - WRITE_ONCE(*valp, tmp); 694 - } 695 - 696 - return 0; 694 + return do_proc_douintvec(table, dir, buffer, lenp, ppos, 695 + do_proc_uint_conv); 697 696 } 698 697 699 698 /** 700 699 * proc_dointvec_minmax - read a vector of integers with min/max values 701 700 * @table: the sysctl table 702 - * @write: %TRUE if this is a write to the sysctl file 701 + * @dir: %TRUE if this is a write to the sysctl file 703 702 * @buffer: the user buffer 704 703 * @lenp: the size of the user buffer 705 704 * @ppos: file position ··· 670 749 * This routine will ensure the values are within the range specified by 671 750 * table->extra1 (min) and table->extra2 (max). 672 751 * 673 - * Returns 0 on success or -EINVAL on write when the range check fails. 752 + * Returns 0 on success or -EINVAL when the range check fails and 753 + * SYSCTL_USER_TO_KERN(dir) == true 674 754 */ 675 - int proc_dointvec_minmax(const struct ctl_table *table, int write, 755 + int proc_dointvec_minmax(const struct ctl_table *table, int dir, 676 756 void *buffer, size_t *lenp, loff_t *ppos) 677 757 { 678 - struct do_proc_dointvec_minmax_conv_param param = { 679 - .min = (int *) table->extra1, 680 - .max = (int *) table->extra2, 681 - }; 682 - return do_proc_dointvec(table, write, buffer, lenp, ppos, 683 - do_proc_dointvec_minmax_conv, &param); 684 - } 685 - 686 - /** 687 - * struct do_proc_douintvec_minmax_conv_param - proc_douintvec_minmax() range checking structure 688 - * @min: pointer to minimum allowable value 689 - * @max: pointer to maximum allowable value 690 - * 691 - * The do_proc_douintvec_minmax_conv_param structure provides the 692 - * minimum and maximum values for doing range checking for those sysctl 693 - * parameters that use the proc_douintvec_minmax() handler. 694 - */ 695 - struct do_proc_douintvec_minmax_conv_param { 696 - unsigned int *min; 697 - unsigned int *max; 698 - }; 699 - 700 - static int do_proc_douintvec_minmax_conv(unsigned long *lvalp, 701 - unsigned int *valp, 702 - int write, void *data) 703 - { 704 - int ret; 705 - unsigned int tmp; 706 - struct do_proc_douintvec_minmax_conv_param *param = data; 707 - /* write via temporary local uint for bounds-checking */ 708 - unsigned int *up = write ? &tmp : valp; 709 - 710 - ret = do_proc_douintvec_conv(lvalp, up, write, data); 711 - if (ret) 712 - return ret; 713 - 714 - if (write) { 715 - if ((param->min && *param->min > tmp) || 716 - (param->max && *param->max < tmp)) 717 - return -ERANGE; 718 - 719 - WRITE_ONCE(*valp, tmp); 720 - } 721 - 722 - return 0; 758 + return do_proc_dointvec(table, dir, buffer, lenp, ppos, 759 + do_proc_int_conv_minmax); 723 760 } 724 761 725 762 /** 726 763 * proc_douintvec_minmax - read a vector of unsigned ints with min/max values 727 764 * @table: the sysctl table 728 - * @write: %TRUE if this is a write to the sysctl file 765 + * @dir: %TRUE if this is a write to the sysctl file 729 766 * @buffer: the user buffer 730 767 * @lenp: the size of the user buffer 731 768 * @ppos: file position ··· 697 818 * check for UINT_MAX to avoid having to support wrap around uses from 698 819 * userspace. 699 820 * 700 - * Returns 0 on success or -ERANGE on write when the range check fails. 821 + * Returns 0 on success or -ERANGE when range check failes and 822 + * SYSCTL_USER_TO_KERN(dir) == true 701 823 */ 702 - int proc_douintvec_minmax(const struct ctl_table *table, int write, 824 + int proc_douintvec_minmax(const struct ctl_table *table, int dir, 703 825 void *buffer, size_t *lenp, loff_t *ppos) 704 826 { 705 - struct do_proc_douintvec_minmax_conv_param param = { 706 - .min = (unsigned int *) table->extra1, 707 - .max = (unsigned int *) table->extra2, 708 - }; 709 - return do_proc_douintvec(table, write, buffer, lenp, ppos, 710 - do_proc_douintvec_minmax_conv, &param); 827 + return do_proc_douintvec(table, dir, buffer, lenp, ppos, 828 + do_proc_uint_conv_minmax); 711 829 } 712 830 713 831 /** 714 832 * proc_dou8vec_minmax - read a vector of unsigned chars with min/max values 715 833 * @table: the sysctl table 716 - * @write: %TRUE if this is a write to the sysctl file 834 + * @dir: %TRUE if this is a write to the sysctl file 717 835 * @buffer: the user buffer 718 836 * @lenp: the size of the user buffer 719 837 * @ppos: file position ··· 722 846 * This routine will ensure the values are within the range specified by 723 847 * table->extra1 (min) and table->extra2 (max). 724 848 * 725 - * Returns 0 on success or an error on write when the range check fails. 849 + * Returns 0 on success or an error on SYSCTL_USER_TO_KERN(dir) == true 850 + * and the range check fails. 726 851 */ 727 - int proc_dou8vec_minmax(const struct ctl_table *table, int write, 852 + int proc_dou8vec_minmax(const struct ctl_table *table, int dir, 728 853 void *buffer, size_t *lenp, loff_t *ppos) 729 854 { 730 855 struct ctl_table tmp; 731 856 unsigned int min = 0, max = 255U, val; 732 857 u8 *data = table->data; 733 - struct do_proc_douintvec_minmax_conv_param param = { 734 - .min = &min, 735 - .max = &max, 736 - }; 737 858 int res; 738 859 739 860 /* Do not support arrays yet. */ 740 861 if (table->maxlen != sizeof(u8)) 741 862 return -EINVAL; 742 863 743 - if (table->extra1) 744 - min = *(unsigned int *) table->extra1; 745 - if (table->extra2) 746 - max = *(unsigned int *) table->extra2; 747 - 748 864 tmp = *table; 749 865 750 866 tmp.maxlen = sizeof(val); 751 867 tmp.data = &val; 868 + if (!tmp.extra1) 869 + tmp.extra1 = (unsigned int *) &min; 870 + if (!tmp.extra2) 871 + tmp.extra2 = (unsigned int *) &max; 872 + 752 873 val = READ_ONCE(*data); 753 - res = do_proc_douintvec(&tmp, write, buffer, lenp, ppos, 754 - do_proc_douintvec_minmax_conv, &param); 874 + res = do_proc_douintvec(&tmp, dir, buffer, lenp, ppos, 875 + do_proc_uint_conv_minmax); 755 876 if (res) 756 877 return res; 757 - if (write) 878 + if (SYSCTL_USER_TO_KERN(dir)) 758 879 WRITE_ONCE(*data, val); 759 880 return 0; 760 881 } 761 882 EXPORT_SYMBOL_GPL(proc_dou8vec_minmax); 762 883 763 - static int __do_proc_doulongvec_minmax(void *data, 764 - const struct ctl_table *table, int write, 765 - void *buffer, size_t *lenp, loff_t *ppos, 766 - unsigned long convmul, unsigned long convdiv) 884 + static int do_proc_doulongvec_minmax(const struct ctl_table *table, int dir, 885 + void *buffer, size_t *lenp, loff_t *ppos, 886 + unsigned long convmul, 887 + unsigned long convdiv) 767 888 { 768 889 unsigned long *i, *min, *max; 769 890 int vleft, first = 1, err = 0; 770 891 size_t left; 771 892 char *p; 772 893 773 - if (!data || !table->maxlen || !*lenp || (*ppos && !write)) { 894 + if (!table->data || !table->maxlen || !*lenp || 895 + (*ppos && SYSCTL_KERN_TO_USER(dir))) { 774 896 *lenp = 0; 775 897 return 0; 776 898 } 777 899 778 - i = data; 900 + i = table->data; 779 901 min = table->extra1; 780 902 max = table->extra2; 781 903 vleft = table->maxlen / sizeof(unsigned long); 782 904 left = *lenp; 783 905 784 - if (write) { 906 + if (SYSCTL_USER_TO_KERN(dir)) { 785 907 if (proc_first_pos_non_zero_ignore(ppos, table)) 786 908 goto out; 787 909 ··· 791 917 for (; left && vleft--; i++, first = 0) { 792 918 unsigned long val; 793 919 794 - if (write) { 920 + if (SYSCTL_USER_TO_KERN(dir)) { 795 921 bool neg; 796 922 797 923 proc_skip_spaces(&p, &left); ··· 820 946 } 821 947 } 822 948 823 - if (!write && !first && left && !err) 949 + if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err) 824 950 proc_put_char(&buffer, &left, '\n'); 825 - if (write && !err) 951 + if (SYSCTL_USER_TO_KERN(dir) && !err) 826 952 proc_skip_spaces(&p, &left); 827 - if (write && first) 953 + if (SYSCTL_USER_TO_KERN(dir) && first) 828 954 return err ? : -EINVAL; 829 955 *lenp -= left; 830 956 out: ··· 832 958 return err; 833 959 } 834 960 835 - static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write, 836 - void *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul, 837 - unsigned long convdiv) 961 + int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir, 962 + void *buffer, size_t *lenp, loff_t *ppos, 963 + unsigned long convmul, unsigned long convdiv) 838 964 { 839 - return __do_proc_doulongvec_minmax(table->data, table, write, 840 - buffer, lenp, ppos, convmul, convdiv); 965 + return do_proc_doulongvec_minmax(table, dir, buffer, lenp, ppos, 966 + convmul, convdiv); 841 967 } 842 968 843 969 /** 844 970 * proc_doulongvec_minmax - read a vector of long integers with min/max values 845 971 * @table: the sysctl table 846 - * @write: %TRUE if this is a write to the sysctl file 972 + * @dir: %TRUE if this is a write to the sysctl file 847 973 * @buffer: the user buffer 848 974 * @lenp: the size of the user buffer 849 975 * @ppos: file position ··· 856 982 * 857 983 * Returns 0 on success. 858 984 */ 859 - int proc_doulongvec_minmax(const struct ctl_table *table, int write, 985 + int proc_doulongvec_minmax(const struct ctl_table *table, int dir, 860 986 void *buffer, size_t *lenp, loff_t *ppos) 861 987 { 862 - return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l); 988 + return proc_doulongvec_minmax_conv(table, dir, buffer, lenp, ppos, 1l, 1l); 863 989 } 864 990 865 - /** 866 - * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values 867 - * @table: the sysctl table 868 - * @write: %TRUE if this is a write to the sysctl file 869 - * @buffer: the user buffer 870 - * @lenp: the size of the user buffer 871 - * @ppos: file position 872 - * 873 - * Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long 874 - * values from/to the user buffer, treated as an ASCII string. The values 875 - * are treated as milliseconds, and converted to jiffies when they are stored. 876 - * 877 - * This routine will ensure the values are within the range specified by 878 - * table->extra1 (min) and table->extra2 (max). 879 - * 880 - * Returns 0 on success. 881 - */ 882 - int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int write, 883 - void *buffer, size_t *lenp, loff_t *ppos) 991 + int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer, 992 + size_t *lenp, loff_t *ppos, 993 + int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr, 994 + int dir, const struct ctl_table *table)) 884 995 { 885 - return do_proc_doulongvec_minmax(table, write, buffer, 886 - lenp, ppos, HZ, 1000l); 887 - } 888 - 889 - 890 - static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp, 891 - int *valp, 892 - int write, void *data) 893 - { 894 - if (write) { 895 - if (*lvalp > INT_MAX / HZ) 896 - return 1; 897 - if (*negp) 898 - WRITE_ONCE(*valp, -*lvalp * HZ); 899 - else 900 - WRITE_ONCE(*valp, *lvalp * HZ); 901 - } else { 902 - int val = READ_ONCE(*valp); 903 - unsigned long lval; 904 - if (val < 0) { 905 - *negp = true; 906 - lval = -(unsigned long)val; 907 - } else { 908 - *negp = false; 909 - lval = (unsigned long)val; 910 - } 911 - *lvalp = lval / HZ; 912 - } 913 - return 0; 914 - } 915 - 916 - static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp, 917 - int *valp, 918 - int write, void *data) 919 - { 920 - if (write) { 921 - if (USER_HZ < HZ && *lvalp > (LONG_MAX / HZ) * USER_HZ) 922 - return 1; 923 - *valp = clock_t_to_jiffies(*negp ? -*lvalp : *lvalp); 924 - } else { 925 - int val = *valp; 926 - unsigned long lval; 927 - if (val < 0) { 928 - *negp = true; 929 - lval = -(unsigned long)val; 930 - } else { 931 - *negp = false; 932 - lval = (unsigned long)val; 933 - } 934 - *lvalp = jiffies_to_clock_t(lval); 935 - } 936 - return 0; 937 - } 938 - 939 - static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp, 940 - int *valp, 941 - int write, void *data) 942 - { 943 - if (write) { 944 - unsigned long jif = msecs_to_jiffies(*negp ? -*lvalp : *lvalp); 945 - 946 - if (jif > INT_MAX) 947 - return 1; 948 - WRITE_ONCE(*valp, (int)jif); 949 - } else { 950 - int val = READ_ONCE(*valp); 951 - unsigned long lval; 952 - if (val < 0) { 953 - *negp = true; 954 - lval = -(unsigned long)val; 955 - } else { 956 - *negp = false; 957 - lval = (unsigned long)val; 958 - } 959 - *lvalp = jiffies_to_msecs(lval); 960 - } 961 - return 0; 962 - } 963 - 964 - static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lvalp, 965 - int *valp, int write, void *data) 966 - { 967 - int tmp, ret; 968 - struct do_proc_dointvec_minmax_conv_param *param = data; 969 - /* 970 - * If writing, first do so via a temporary local int so we can 971 - * bounds-check it before touching *valp. 972 - */ 973 - int *ip = write ? &tmp : valp; 974 - 975 - ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, write, data); 976 - if (ret) 977 - return ret; 978 - 979 - if (write) { 980 - if ((param->min && *param->min > tmp) || 981 - (param->max && *param->max < tmp)) 982 - return -EINVAL; 983 - *valp = tmp; 984 - } 985 - return 0; 986 - } 987 - 988 - /** 989 - * proc_dointvec_jiffies - read a vector of integers as seconds 990 - * @table: the sysctl table 991 - * @write: %TRUE if this is a write to the sysctl file 992 - * @buffer: the user buffer 993 - * @lenp: the size of the user buffer 994 - * @ppos: file position 995 - * 996 - * Reads/writes up to table->maxlen/sizeof(unsigned int) integer 997 - * values from/to the user buffer, treated as an ASCII string. 998 - * The values read are assumed to be in seconds, and are converted into 999 - * jiffies. 1000 - * 1001 - * Returns 0 on success. 1002 - */ 1003 - int proc_dointvec_jiffies(const struct ctl_table *table, int write, 1004 - void *buffer, size_t *lenp, loff_t *ppos) 1005 - { 1006 - return do_proc_dointvec(table,write,buffer,lenp,ppos, 1007 - do_proc_dointvec_jiffies_conv,NULL); 1008 - } 1009 - 1010 - int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write, 1011 - void *buffer, size_t *lenp, loff_t *ppos) 1012 - { 1013 - struct do_proc_dointvec_minmax_conv_param param = { 1014 - .min = (int *) table->extra1, 1015 - .max = (int *) table->extra2, 1016 - }; 1017 - return do_proc_dointvec(table, write, buffer, lenp, ppos, 1018 - do_proc_dointvec_ms_jiffies_minmax_conv, &param); 1019 - } 1020 - 1021 - /** 1022 - * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds 1023 - * @table: the sysctl table 1024 - * @write: %TRUE if this is a write to the sysctl file 1025 - * @buffer: the user buffer 1026 - * @lenp: the size of the user buffer 1027 - * @ppos: pointer to the file position 1028 - * 1029 - * Reads/writes up to table->maxlen/sizeof(unsigned int) integer 1030 - * values from/to the user buffer, treated as an ASCII string. 1031 - * The values read are assumed to be in 1/USER_HZ seconds, and 1032 - * are converted into jiffies. 1033 - * 1034 - * Returns 0 on success. 1035 - */ 1036 - int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int write, 1037 - void *buffer, size_t *lenp, loff_t *ppos) 1038 - { 1039 - return do_proc_dointvec(table, write, buffer, lenp, ppos, 1040 - do_proc_dointvec_userhz_jiffies_conv, NULL); 1041 - } 1042 - 1043 - /** 1044 - * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds 1045 - * @table: the sysctl table 1046 - * @write: %TRUE if this is a write to the sysctl file 1047 - * @buffer: the user buffer 1048 - * @lenp: the size of the user buffer 1049 - * @ppos: the current position in the file 1050 - * 1051 - * Reads/writes up to table->maxlen/sizeof(unsigned int) integer 1052 - * values from/to the user buffer, treated as an ASCII string. 1053 - * The values read are assumed to be in 1/1000 seconds, and 1054 - * are converted into jiffies. 1055 - * 1056 - * Returns 0 on success. 1057 - */ 1058 - int proc_dointvec_ms_jiffies(const struct ctl_table *table, int write, void *buffer, 1059 - size_t *lenp, loff_t *ppos) 1060 - { 1061 - return do_proc_dointvec(table, write, buffer, lenp, ppos, 1062 - do_proc_dointvec_ms_jiffies_conv, NULL); 996 + return do_proc_dointvec(table, dir, buffer, lenp, ppos, conv); 1063 997 } 1064 998 1065 999 /** 1066 1000 * proc_do_large_bitmap - read/write from/to a large bitmap 1067 1001 * @table: the sysctl table 1068 - * @write: %TRUE if this is a write to the sysctl file 1002 + * @dir: %TRUE if this is a write to the sysctl file 1069 1003 * @buffer: the user buffer 1070 1004 * @lenp: the size of the user buffer 1071 1005 * @ppos: file position ··· 887 1205 * 888 1206 * Returns 0 on success. 889 1207 */ 890 - int proc_do_large_bitmap(const struct ctl_table *table, int write, 1208 + int proc_do_large_bitmap(const struct ctl_table *table, int dir, 891 1209 void *buffer, size_t *lenp, loff_t *ppos) 892 1210 { 893 1211 int err = 0; ··· 897 1215 unsigned long *tmp_bitmap = NULL; 898 1216 char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; 899 1217 900 - if (!bitmap || !bitmap_len || !left || (*ppos && !write)) { 1218 + if (!bitmap || !bitmap_len || !left || (*ppos && SYSCTL_KERN_TO_USER(dir))) { 901 1219 *lenp = 0; 902 1220 return 0; 903 1221 } 904 1222 905 - if (write) { 1223 + if (SYSCTL_USER_TO_KERN(dir)) { 906 1224 char *p = buffer; 907 1225 size_t skipped = 0; 908 1226 ··· 1003 1321 } 1004 1322 1005 1323 if (!err) { 1006 - if (write) { 1324 + if (SYSCTL_USER_TO_KERN(dir)) { 1007 1325 if (*ppos) 1008 1326 bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len); 1009 1327 else ··· 1019 1337 1020 1338 #else /* CONFIG_PROC_SYSCTL */ 1021 1339 1022 - int proc_dostring(const struct ctl_table *table, int write, 1340 + int proc_dostring(const struct ctl_table *table, int dir, 1023 1341 void *buffer, size_t *lenp, loff_t *ppos) 1024 1342 { 1025 1343 return -ENOSYS; 1026 1344 } 1027 1345 1028 - int proc_dobool(const struct ctl_table *table, int write, 1346 + int proc_dobool(const struct ctl_table *table, int dir, 1029 1347 void *buffer, size_t *lenp, loff_t *ppos) 1030 1348 { 1031 1349 return -ENOSYS; 1032 1350 } 1033 1351 1034 - int proc_dointvec(const struct ctl_table *table, int write, 1352 + int proc_dointvec(const struct ctl_table *table, int dir, 1035 1353 void *buffer, size_t *lenp, loff_t *ppos) 1036 1354 { 1037 1355 return -ENOSYS; 1038 1356 } 1039 1357 1040 - int proc_douintvec(const struct ctl_table *table, int write, 1358 + int proc_douintvec(const struct ctl_table *table, int dir, 1041 1359 void *buffer, size_t *lenp, loff_t *ppos) 1042 1360 { 1043 1361 return -ENOSYS; 1044 1362 } 1045 1363 1046 - int proc_dointvec_minmax(const struct ctl_table *table, int write, 1364 + int proc_dointvec_minmax(const struct ctl_table *table, int dir, 1047 1365 void *buffer, size_t *lenp, loff_t *ppos) 1048 1366 { 1049 1367 return -ENOSYS; 1050 1368 } 1051 1369 1052 - int proc_douintvec_minmax(const struct ctl_table *table, int write, 1370 + int proc_douintvec_minmax(const struct ctl_table *table, int dir, 1053 1371 void *buffer, size_t *lenp, loff_t *ppos) 1054 1372 { 1055 1373 return -ENOSYS; 1056 1374 } 1057 1375 1058 - int proc_dou8vec_minmax(const struct ctl_table *table, int write, 1376 + int proc_dou8vec_minmax(const struct ctl_table *table, int dir, 1059 1377 void *buffer, size_t *lenp, loff_t *ppos) 1060 1378 { 1061 1379 return -ENOSYS; 1062 1380 } 1063 1381 1064 - int proc_dointvec_jiffies(const struct ctl_table *table, int write, 1382 + int proc_doulongvec_minmax(const struct ctl_table *table, int dir, 1065 1383 void *buffer, size_t *lenp, loff_t *ppos) 1066 1384 { 1067 1385 return -ENOSYS; 1068 1386 } 1069 1387 1070 - int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write, 1071 - void *buffer, size_t *lenp, loff_t *ppos) 1388 + int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir, 1389 + void *buffer, size_t *lenp, loff_t *ppos, 1390 + unsigned long convmul, unsigned long convdiv) 1072 1391 { 1073 1392 return -ENOSYS; 1074 1393 } 1075 1394 1076 - int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int write, 1077 - void *buffer, size_t *lenp, loff_t *ppos) 1395 + int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer, 1396 + size_t *lenp, loff_t *ppos, 1397 + int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr, 1398 + int dir, const struct ctl_table *table)) 1078 1399 { 1079 1400 return -ENOSYS; 1080 1401 } 1081 1402 1082 - int proc_dointvec_ms_jiffies(const struct ctl_table *table, int write, 1083 - void *buffer, size_t *lenp, loff_t *ppos) 1084 - { 1085 - return -ENOSYS; 1086 - } 1087 - 1088 - int proc_doulongvec_minmax(const struct ctl_table *table, int write, 1089 - void *buffer, size_t *lenp, loff_t *ppos) 1090 - { 1091 - return -ENOSYS; 1092 - } 1093 - 1094 - int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int write, 1095 - void *buffer, size_t *lenp, loff_t *ppos) 1096 - { 1097 - return -ENOSYS; 1098 - } 1099 - 1100 - int proc_do_large_bitmap(const struct ctl_table *table, int write, 1403 + int proc_do_large_bitmap(const struct ctl_table *table, int dir, 1101 1404 void *buffer, size_t *lenp, loff_t *ppos) 1102 1405 { 1103 1406 return -ENOSYS; ··· 1091 1424 #endif /* CONFIG_PROC_SYSCTL */ 1092 1425 1093 1426 #if defined(CONFIG_SYSCTL) 1094 - int proc_do_static_key(const struct ctl_table *table, int write, 1427 + int proc_do_static_key(const struct ctl_table *table, int dir, 1095 1428 void *buffer, size_t *lenp, loff_t *ppos) 1096 1429 { 1097 1430 struct static_key *key = (struct static_key *)table->data; ··· 1105 1438 .extra2 = SYSCTL_ONE, 1106 1439 }; 1107 1440 1108 - if (write && !capable(CAP_SYS_ADMIN)) 1441 + if (SYSCTL_USER_TO_KERN(dir) && !capable(CAP_SYS_ADMIN)) 1109 1442 return -EPERM; 1110 1443 1111 1444 mutex_lock(&static_key_mutex); 1112 1445 val = static_key_enabled(key); 1113 - ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 1114 - if (write && !ret) { 1446 + ret = proc_dointvec_minmax(&tmp, dir, buffer, lenp, ppos); 1447 + if (SYSCTL_USER_TO_KERN(dir) && !ret) { 1115 1448 if (val) 1116 1449 static_key_enable(key); 1117 1450 else ··· 1181 1514 EXPORT_SYMBOL(proc_dobool); 1182 1515 EXPORT_SYMBOL(proc_dointvec); 1183 1516 EXPORT_SYMBOL(proc_douintvec); 1184 - EXPORT_SYMBOL(proc_dointvec_jiffies); 1185 1517 EXPORT_SYMBOL(proc_dointvec_minmax); 1186 1518 EXPORT_SYMBOL_GPL(proc_douintvec_minmax); 1187 - EXPORT_SYMBOL(proc_dointvec_userhz_jiffies); 1188 - EXPORT_SYMBOL(proc_dointvec_ms_jiffies); 1189 1519 EXPORT_SYMBOL(proc_dostring); 1190 1520 EXPORT_SYMBOL(proc_doulongvec_minmax); 1191 - EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); 1192 1521 EXPORT_SYMBOL(proc_do_large_bitmap);
+125
kernel/time/jiffies.c
··· 99 99 100 100 __clocksource_register(&refined_jiffies); 101 101 } 102 + 103 + #define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ) 104 + #define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ) 105 + 106 + static SYSCTL_USER_TO_KERN_INT_CONV(_hz, SYSCTL_CONV_MULT_HZ) 107 + static SYSCTL_KERN_TO_USER_INT_CONV(_hz, SYSCTL_CONV_DIV_HZ) 108 + static SYSCTL_USER_TO_KERN_INT_CONV(_userhz, clock_t_to_jiffies) 109 + static SYSCTL_KERN_TO_USER_INT_CONV(_userhz, jiffies_to_clock_t) 110 + static SYSCTL_USER_TO_KERN_INT_CONV(_ms, msecs_to_jiffies) 111 + static SYSCTL_KERN_TO_USER_INT_CONV(_ms, jiffies_to_msecs) 112 + 113 + static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz, 114 + sysctl_kern_to_user_int_conv_hz, false) 115 + static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies, 116 + sysctl_user_to_kern_int_conv_userhz, 117 + sysctl_kern_to_user_int_conv_userhz, false) 118 + static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_user_to_kern_int_conv_ms, 119 + sysctl_kern_to_user_int_conv_ms, false) 120 + static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax, 121 + sysctl_user_to_kern_int_conv_ms, 122 + sysctl_kern_to_user_int_conv_ms, true) 123 + 124 + /** 125 + * proc_dointvec_jiffies - read a vector of integers as seconds 126 + * @table: the sysctl table 127 + * @dir: %TRUE if this is a write to the sysctl file 128 + * @buffer: the user buffer 129 + * @lenp: the size of the user buffer 130 + * @ppos: file position 131 + * 132 + * Reads/writes up to table->maxlen/sizeof(unsigned int) integer 133 + * values from/to the user buffer, treated as an ASCII string. 134 + * The values read are assumed to be in seconds, and are converted into 135 + * jiffies. 136 + * 137 + * Returns 0 on success. 138 + */ 139 + int proc_dointvec_jiffies(const struct ctl_table *table, int dir, 140 + void *buffer, size_t *lenp, loff_t *ppos) 141 + { 142 + return proc_dointvec_conv(table, dir, buffer, lenp, ppos, 143 + do_proc_int_conv_jiffies); 144 + } 145 + EXPORT_SYMBOL(proc_dointvec_jiffies); 146 + 147 + /** 148 + * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds 149 + * @table: the sysctl table 150 + * @dir: %TRUE if this is a write to the sysctl file 151 + * @buffer: the user buffer 152 + * @lenp: the size of the user buffer 153 + * @ppos: pointer to the file position 154 + * 155 + * Reads/writes up to table->maxlen/sizeof(unsigned int) integer 156 + * values from/to the user buffer, treated as an ASCII string. 157 + * The values read are assumed to be in 1/USER_HZ seconds, and 158 + * are converted into jiffies. 159 + * 160 + * Returns 0 on success. 161 + */ 162 + int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir, 163 + void *buffer, size_t *lenp, loff_t *ppos) 164 + { 165 + if (SYSCTL_USER_TO_KERN(dir) && USER_HZ < HZ) 166 + return -EINVAL; 167 + return proc_dointvec_conv(table, dir, buffer, lenp, ppos, 168 + do_proc_int_conv_userhz_jiffies); 169 + } 170 + EXPORT_SYMBOL(proc_dointvec_userhz_jiffies); 171 + 172 + /** 173 + * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds 174 + * @table: the sysctl table 175 + * @dir: %TRUE if this is a write to the sysctl file 176 + * @buffer: the user buffer 177 + * @lenp: the size of the user buffer 178 + * @ppos: the current position in the file 179 + * 180 + * Reads/writes up to table->maxlen/sizeof(unsigned int) integer 181 + * values from/to the user buffer, treated as an ASCII string. 182 + * The values read are assumed to be in 1/1000 seconds, and 183 + * are converted into jiffies. 184 + * 185 + * Returns 0 on success. 186 + */ 187 + int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir, void *buffer, 188 + size_t *lenp, loff_t *ppos) 189 + { 190 + return proc_dointvec_conv(table, dir, buffer, lenp, ppos, 191 + do_proc_int_conv_ms_jiffies); 192 + } 193 + EXPORT_SYMBOL(proc_dointvec_ms_jiffies); 194 + 195 + int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, 196 + void *buffer, size_t *lenp, loff_t *ppos) 197 + { 198 + return proc_dointvec_conv(table, dir, buffer, lenp, ppos, 199 + do_proc_int_conv_ms_jiffies_minmax); 200 + } 201 + 202 + /** 203 + * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values 204 + * @table: the sysctl table 205 + * @dir: %TRUE if this is a write to the sysctl file 206 + * @buffer: the user buffer 207 + * @lenp: the size of the user buffer 208 + * @ppos: file position 209 + * 210 + * Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long 211 + * values from/to the user buffer, treated as an ASCII string. The values 212 + * are treated as milliseconds, and converted to jiffies when they are stored. 213 + * 214 + * This routine will ensure the values are within the range specified by 215 + * table->extra1 (min) and table->extra2 (max). 216 + * 217 + * Returns 0 on success. 218 + */ 219 + int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, 220 + void *buffer, size_t *lenp, loff_t *ppos) 221 + { 222 + return proc_doulongvec_minmax_conv(table, dir, buffer, lenp, ppos, 223 + HZ, 1000l); 224 + } 225 + EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); 226 +
+1 -8
kernel/watchdog.c
··· 1240 1240 }, 1241 1241 #endif /* CONFIG_SMP */ 1242 1242 #endif 1243 - }; 1244 - 1245 - static struct ctl_table watchdog_hardlockup_sysctl[] = { 1246 1243 { 1247 1244 .procname = "nmi_watchdog", 1248 1245 .data = &watchdog_hardlockup_user_enabled, 1249 1246 .maxlen = sizeof(int), 1250 - .mode = 0444, 1247 + .mode = 0644, 1251 1248 .proc_handler = proc_nmi_watchdog, 1252 1249 .extra1 = SYSCTL_ZERO, 1253 1250 .extra2 = SYSCTL_ONE, ··· 1254 1257 static void __init watchdog_sysctl_init(void) 1255 1258 { 1256 1259 register_sysctl_init("kernel", watchdog_sysctls); 1257 - 1258 - if (watchdog_hardlockup_available) 1259 - watchdog_hardlockup_sysctl[0].mode = 0644; 1260 - register_sysctl_init("kernel", watchdog_hardlockup_sysctl); 1261 1260 } 1262 1261 1263 1262 #else