From 7c0bf4dad6bf44eef4a573985dd053de77688df1 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:13 +0200 Subject: parport: Move magic number "15" to a define Put the size of a parport name behind a define so we can use it in other files. This is a preparation patch to be able to use this size in parport/procfs.c. Signed-off-by: Joel Granados Reviewed-by: Luis Chamberlain Signed-off-by: Luis Chamberlain --- drivers/parport/share.c | 2 +- include/linux/parport.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 62f8407923d4..2d46b1d4fd69 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -467,7 +467,7 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma, atomic_set(&tmp->ref_count, 1); INIT_LIST_HEAD(&tmp->full_list); - name = kmalloc(15, GFP_KERNEL); + name = kmalloc(PARPORT_NAME_MAX_LEN, GFP_KERNEL); if (!name) { kfree(tmp); return NULL; diff --git a/include/linux/parport.h b/include/linux/parport.h index a0bc9e0267b7..243c82d7f852 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -180,6 +180,8 @@ struct ieee1284_info { struct semaphore irq; }; +#define PARPORT_NAME_MAX_LEN 15 + /* A parallel port */ struct parport { unsigned long base; /* base address */ -- cgit From 93810936a6bfbc538771914836fca24570e3bcc5 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:14 +0200 Subject: parport: Remove register_sysctl_table from parport_proc_register This is part of the general push to deprecate register_sysctl_paths and register_sysctl_table. Register dev/parport/PORTNAME and dev/parport/PORTNAME/devices. Temporary allocation for name is freed at the end of the function. Remove all the struct elements that are no longer used in the parport_device_sysctl_template struct. Add parport specific defines that hide the base path sizes. To make sure the resulting directory structure did not change we made sure that `find /proc/sys/dev/ | sha1sum` was the same before and after the change. Signed-off-by: Joel Granados Reported-by: kernel test robot Closes: https://lore.kernel.org/r/202305150948.pHgIh7Ql-lkp@intel.com/ Reported-by: Dan Carpenter Reviewed-by: Luis Chamberlain Signed-off-by: Luis Chamberlain --- drivers/parport/procfs.c | 91 +++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index d740eba3c099..28a37e0ef98c 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -32,6 +32,13 @@ #define PARPORT_MAX_TIMESLICE_VALUE ((unsigned long) HZ) #define PARPORT_MIN_SPINTIME_VALUE 1 #define PARPORT_MAX_SPINTIME_VALUE 1000 +/* + * PARPORT_BASE_* is the size of the known parts of the sysctl path + * in dev/partport/%s/devices/%s. "dev/parport/"(12), "/devices/"(9 + * and null char(1). + */ +#define PARPORT_BASE_PATH_SIZE 13 +#define PARPORT_BASE_DEVICES_PATH_SIZE 22 static int do_active_device(struct ctl_table *table, int write, void *result, size_t *lenp, loff_t *ppos) @@ -260,9 +267,6 @@ struct parport_sysctl_table { struct ctl_table_header *sysctl_header; struct ctl_table vars[12]; struct ctl_table device_dir[2]; - struct ctl_table port_dir[2]; - struct ctl_table parport_dir[2]; - struct ctl_table dev_dir[2]; }; static const struct parport_sysctl_table parport_sysctl_template = { @@ -305,7 +309,6 @@ static const struct parport_sysctl_table parport_sysctl_template = { .mode = 0444, .proc_handler = do_hardware_modes }, - PARPORT_DEVICES_ROOT_DIR, #ifdef CONFIG_PARPORT_1284 { .procname = "autoprobe", @@ -355,18 +358,6 @@ static const struct parport_sysctl_table parport_sysctl_template = { }, {} }, - { - PARPORT_PORT_DIR(NULL), - {} - }, - { - PARPORT_PARPORT_DIR(NULL), - {} - }, - { - PARPORT_DEV_DIR(NULL), - {} - } }; struct parport_device_sysctl_table @@ -473,11 +464,13 @@ parport_default_sysctl_table = { } }; - int parport_proc_register(struct parport *port) { struct parport_sysctl_table *t; - int i; + struct ctl_table_header *devices_h; + char *tmp_dir_path; + size_t tmp_path_len, port_name_len; + int bytes_written, i, err = 0; t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL); if (t == NULL) @@ -485,28 +478,64 @@ int parport_proc_register(struct parport *port) t->device_dir[0].extra1 = port; - for (i = 0; i < 5; i++) + t->vars[0].data = &port->spintime; + for (i = 0; i < 5; i++) { t->vars[i].extra1 = port; + t->vars[5 + i].extra2 = &port->probe_info[i]; + } - t->vars[0].data = &port->spintime; - t->vars[5].child = t->device_dir; - - for (i = 0; i < 5; i++) - t->vars[6 + i].extra2 = &port->probe_info[i]; + port_name_len = strnlen(port->name, PARPORT_NAME_MAX_LEN); + /* + * Allocate a buffer for two paths: dev/parport/PORT and dev/parport/PORT/devices. + * We calculate for the second as that will give us enough for the first. + */ + tmp_path_len = PARPORT_BASE_DEVICES_PATH_SIZE + port_name_len; + tmp_dir_path = kzalloc(tmp_path_len, GFP_KERNEL); + if (!tmp_dir_path) { + err = -ENOMEM; + goto exit_free_t; + } - t->port_dir[0].procname = port->name; + bytes_written = snprintf(tmp_dir_path, tmp_path_len, + "dev/parport/%s/devices", port->name); + if (tmp_path_len <= bytes_written) { + err = -ENOENT; + goto exit_free_tmp_dir_path; + } + devices_h = register_sysctl(tmp_dir_path, t->device_dir); + if (devices_h == NULL) { + err = -ENOENT; + goto exit_free_tmp_dir_path; + } - t->port_dir[0].child = t->vars; - t->parport_dir[0].child = t->port_dir; - t->dev_dir[0].child = t->parport_dir; + tmp_path_len = PARPORT_BASE_PATH_SIZE + port_name_len; + bytes_written = snprintf(tmp_dir_path, tmp_path_len, + "dev/parport/%s", port->name); + if (tmp_path_len <= bytes_written) { + err = -ENOENT; + goto unregister_devices_h; + } - t->sysctl_header = register_sysctl_table(t->dev_dir); + t->sysctl_header = register_sysctl(tmp_dir_path, t->vars); if (t->sysctl_header == NULL) { - kfree(t); - t = NULL; + err = -ENOENT; + goto unregister_devices_h; } + port->sysctl_table = t; + + kfree(tmp_dir_path); return 0; + +unregister_devices_h: + unregister_sysctl_table(devices_h); + +exit_free_tmp_dir_path: + kfree(tmp_dir_path); + +exit_free_t: + kfree(t); + return err; } int parport_proc_unregister(struct parport *port) -- cgit From 4199a64a1c13c58a48917d1eb6492c32b4496bd9 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:15 +0200 Subject: parport: Remove register_sysctl_table from parport_device_proc_register This is part of the general push to deprecate register_sysctl_paths and register_sysctl_table. We use a temp allocation to include both port and device name in proc. Allocated mem is freed at the end. The unused parport_device_sysctl_template struct elements that are not used are removed. Signed-off-by: Joel Granados Reported-by: kernel test robot Closes: https://lore.kernel.org/r/202305150948.pHgIh7Ql-lkp@intel.com/ Reported-by: Dan Carpenter Reviewed-by: Luis Chamberlain Signed-off-by: Luis Chamberlain --- drivers/parport/procfs.c | 56 +++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 28a37e0ef98c..22d211c95168 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -384,6 +384,7 @@ parport_device_sysctl_template = { .extra1 = (void*) &parport_min_timeslice_value, .extra2 = (void*) &parport_max_timeslice_value }, + {} }, { { @@ -394,22 +395,6 @@ parport_device_sysctl_template = { .child = NULL }, {} - }, - { - PARPORT_DEVICES_ROOT_DIR, - {} - }, - { - PARPORT_PORT_DIR(NULL), - {} - }, - { - PARPORT_PARPORT_DIR(NULL), - {} - }, - { - PARPORT_DEV_DIR(NULL), - {} } }; @@ -551,30 +536,53 @@ int parport_proc_unregister(struct parport *port) int parport_device_proc_register(struct pardevice *device) { + int bytes_written, err = 0; struct parport_device_sysctl_table *t; struct parport * port = device->port; + size_t port_name_len, device_name_len, tmp_dir_path_len; + char *tmp_dir_path; t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL); if (t == NULL) return -ENOMEM; - t->dev_dir[0].child = t->parport_dir; - t->parport_dir[0].child = t->port_dir; - t->port_dir[0].procname = port->name; - t->port_dir[0].child = t->devices_root_dir; - t->devices_root_dir[0].child = t->device_dir; + port_name_len = strnlen(port->name, PARPORT_NAME_MAX_LEN); + device_name_len = strnlen(device->name, PATH_MAX); + + /* Allocate a buffer for two paths: dev/parport/PORT/devices/DEVICE. */ + tmp_dir_path_len = PARPORT_BASE_DEVICES_PATH_SIZE + port_name_len + device_name_len; + tmp_dir_path = kzalloc(tmp_dir_path_len, GFP_KERNEL); + if (!tmp_dir_path) { + err = -ENOMEM; + goto exit_free_t; + } + + bytes_written = snprintf(tmp_dir_path, tmp_dir_path_len, "dev/parport/%s/devices/%s", + port->name, device->name); + if (tmp_dir_path_len <= bytes_written) { + err = -ENOENT; + goto exit_free_path; + } - t->device_dir[0].procname = device->name; - t->device_dir[0].child = t->vars; t->vars[0].data = &device->timeslice; - t->sysctl_header = register_sysctl_table(t->dev_dir); + t->sysctl_header = register_sysctl(tmp_dir_path, t->vars); if (t->sysctl_header == NULL) { kfree(t); t = NULL; } device->sysctl_table = t; + + kfree(tmp_dir_path); return 0; + +exit_free_path: + kfree(tmp_dir_path); + +exit_free_t: + kfree(t); + + return err; } int parport_device_proc_unregister(struct pardevice *device) -- cgit From 02ea13480f3ffea36a1f9e549b503402f0b299ca Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:16 +0200 Subject: parport: Remove register_sysctl_table from parport_default_proc_register This is part of the general push to deprecate register_sysctl_paths and register_sysctl_table. Simply change the full path "dev/parport/default" to point to an already existing set of table entries (vars). We also remove the unused elements from parport_default_table. To make sure the resulting directory structure did not change we made sure that `find /proc/sys/dev/ | sha1sum` was the same before and after the change. Signed-off-by: Joel Granados Reviewed-by: Luis Chamberlain Signed-off-by: Luis Chamberlain --- drivers/parport/procfs.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 22d211c95168..1a26918d2cc8 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -430,22 +430,6 @@ parport_default_sysctl_table = { .extra2 = (void*) &parport_max_spintime_value }, {} - }, - { - { - .procname = "default", - .mode = 0555, - .child = parport_default_sysctl_table.vars - }, - {} - }, - { - PARPORT_PARPORT_DIR(parport_default_sysctl_table.default_dir), - {} - }, - { - PARPORT_DEV_DIR(parport_default_sysctl_table.parport_dir), - {} } }; @@ -601,7 +585,7 @@ static int __init parport_default_proc_register(void) int ret; parport_default_sysctl_table.sysctl_header = - register_sysctl_table(parport_default_sysctl_table.dev_dir); + register_sysctl("dev/parport/default", parport_default_sysctl_table.vars); if (!parport_default_sysctl_table.sysctl_header) return -ENOMEM; ret = parport_bus_init(); -- cgit From 9ad0a4e7c2dd101be78e8621c204cec742ee1ce7 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:17 +0200 Subject: parport: Removed sysctl related defines The partport driver used to rely on defines to include different directories in sysctl. Now that we have made the transition to register_sysctl from regsiter_sysctl_table, they are no longer needed. Signed-off-by: Joel Granados Reviewed-by: Luis Chamberlain Signed-off-by: Luis Chamberlain --- drivers/parport/procfs.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 1a26918d2cc8..cbb1fb5127ce 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -243,13 +243,6 @@ do { \ return 0; } -#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD } -#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \ - .mode = 0555, .child = CHILD } -#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD } -#define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \ - .mode = 0555, .child = NULL } - static const unsigned long parport_min_timeslice_value = PARPORT_MIN_TIMESLICE_VALUE; -- cgit From 19c4e618a1bc3d0cad1f04c857be8076cb05bbb2 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:18 +0200 Subject: sysctl: stop exporting register_sysctl_table We make register_sysctl_table static because the only function calling it is in fs/proc/proc_sysctl.c (__register_sysctl_base). We remove it from the sysctl.h header and modify the documentation in both the header and proc_sysctl.c files to mention "register_sysctl" instead of "register_sysctl_table". This plus the commits that remove register_sysctl_table from parport save 217 bytes: ./scripts/bloat-o-meter .bsysctl/vmlinux.old .bsysctl/vmlinux.new add/remove: 0/1 grow/shrink: 5/1 up/down: 458/-675 (-217) Function old new delta __register_sysctl_base 8 286 +278 parport_proc_register 268 379 +111 parport_device_proc_register 195 247 +52 kzalloc.constprop 598 608 +10 parport_default_proc_register 62 69 +7 register_sysctl_table 291 - -291 parport_sysctl_template 1288 904 -384 Total: Before=8603076, After=8602859, chg -0.00% Signed-off-by: Joel Granados Reviewed-by: Luis Chamberlain Signed-off-by: Luis Chamberlain --- fs/proc/proc_sysctl.c | 5 ++--- include/linux/sysctl.h | 8 +------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 8038833ff5b0..f8f19e000d76 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1582,7 +1582,7 @@ out: * array. A completely 0 filled entry terminates the table. * We are slowly deprecating this call so avoid its use. */ -struct ctl_table_header *register_sysctl_table(struct ctl_table *table) +static struct ctl_table_header *register_sysctl_table(struct ctl_table *table) { struct ctl_table *ctl_table_arg = table; int nr_subheaders = count_subheaders(table); @@ -1634,7 +1634,6 @@ err_register_leaves: header = NULL; goto out; } -EXPORT_SYMBOL(register_sysctl_table); int __register_sysctl_base(struct ctl_table *base_table) { @@ -1700,7 +1699,7 @@ static void drop_sysctl_table(struct ctl_table_header *header) /** * unregister_sysctl_table - unregister a sysctl table hierarchy - * @header: the header returned from register_sysctl_table + * @header: the header returned from register_sysctl or __register_sysctl_table * * Unregisters the sysctl table and all children. proc entries may not * actually be removed until they are no longer used by anyone. diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 3d08277959af..218e56a26fb0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -89,7 +89,7 @@ int proc_do_static_key(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); /* - * Register a set of sysctl names by calling register_sysctl_table + * Register a set of sysctl names by calling register_sysctl * with an initialised array of struct ctl_table's. An entry with * NULL procname terminates the table. table->de will be * set up by the registration and need not be initialised in advance. @@ -222,7 +222,6 @@ struct ctl_table_header *__register_sysctl_table( struct ctl_table_set *set, const char *path, struct ctl_table *table); struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table); -struct ctl_table_header *register_sysctl_table(struct ctl_table * table); void unregister_sysctl_table(struct ctl_table_header * table); extern int sysctl_init_bases(void); @@ -257,11 +256,6 @@ static inline int __register_sysctl_base(struct ctl_table *base_table) #define register_sysctl_base(table) __register_sysctl_base(table) -static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table) -{ - return NULL; -} - static inline void register_sysctl_init(const char *path, struct ctl_table *table) { } -- cgit From 2f5edd03ca0d7221a88236b344b84f3fc301b1e3 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:19 +0200 Subject: sysctl: Refactor base paths registrations This is part of the general push to deprecate register_sysctl_paths and register_sysctl_table. The old way of doing this through register_sysctl_base and DECLARE_SYSCTL_BASE macro is replaced with a call to register_sysctl_init. The 5 base paths affected are: "kernel", "vm", "debug", "dev" and "fs". We remove the register_sysctl_base function and the DECLARE_SYSCTL_BASE macro since they are no longer needed. In order to quickly acertain that the paths did not actually change I executed `find /proc/sys/ | sha1sum` and made sure that the sha was the same before and after the commit. We end up saving 563 bytes with this change: ./scripts/bloat-o-meter vmlinux.0.base vmlinux.1.refactor-base-paths add/remove: 0/5 grow/shrink: 2/0 up/down: 77/-640 (-563) Function old new delta sysctl_init_bases 55 111 +56 init_fs_sysctls 12 33 +21 vm_base_table 128 - -128 kernel_base_table 128 - -128 fs_base_table 128 - -128 dev_base_table 128 - -128 debug_base_table 128 - -128 Total: Before=21258215, After=21257652, chg -0.00% [mcgrof: modified to use register_sysctl_init() over register_sysctl() and add bloat-o-meter stats] Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain Tested-by: Stephen Rothwell Acked-by: Christian Brauner --- fs/sysctls.c | 5 ++--- include/linux/sysctl.h | 23 ----------------------- kernel/sysctl.c | 30 +++++++++--------------------- 3 files changed, 11 insertions(+), 47 deletions(-) diff --git a/fs/sysctls.c b/fs/sysctls.c index c701273c9432..76a0aee8c229 100644 --- a/fs/sysctls.c +++ b/fs/sysctls.c @@ -29,11 +29,10 @@ static struct ctl_table fs_shared_sysctls[] = { { } }; -DECLARE_SYSCTL_BASE(fs, fs_shared_sysctls); - static int __init init_fs_sysctls(void) { - return register_sysctl_base(fs); + register_sysctl_init("fs", fs_shared_sysctls); + return 0; } early_initcall(init_fs_sysctls); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 218e56a26fb0..653b66c762b1 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -197,20 +197,6 @@ struct ctl_path { #ifdef CONFIG_SYSCTL -#define DECLARE_SYSCTL_BASE(_name, _table) \ -static struct ctl_table _name##_base_table[] = { \ - { \ - .procname = #_name, \ - .mode = 0555, \ - .child = _table, \ - }, \ - { }, \ -} - -extern int __register_sysctl_base(struct ctl_table *base_table); - -#define register_sysctl_base(_name) __register_sysctl_base(_name##_base_table) - void proc_sys_poll_notify(struct ctl_table_poll *poll); extern void setup_sysctl_set(struct ctl_table_set *p, @@ -247,15 +233,6 @@ extern struct ctl_table sysctl_mount_point[]; #else /* CONFIG_SYSCTL */ -#define DECLARE_SYSCTL_BASE(_name, _table) - -static inline int __register_sysctl_base(struct ctl_table *base_table) -{ - return 0; -} - -#define register_sysctl_base(table) __register_sysctl_base(table) - static inline void register_sysctl_init(const char *path, struct ctl_table *table) { } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index bfe53e835524..73fa9cf7ee11 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1782,11 +1782,6 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = sysctl_max_threads, }, - { - .procname = "usermodehelper", - .mode = 0555, - .child = usermodehelper_table, - }, { .procname = "overflowuid", .data = &overflowuid, @@ -1962,13 +1957,6 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, #endif -#ifdef CONFIG_KEYS - { - .procname = "keys", - .mode = 0555, - .child = key_sysctls, - }, -#endif #ifdef CONFIG_PERF_EVENTS /* * User-space scripts rely on the existence of this file @@ -2348,17 +2336,17 @@ static struct ctl_table dev_table[] = { { } }; -DECLARE_SYSCTL_BASE(kernel, kern_table); -DECLARE_SYSCTL_BASE(vm, vm_table); -DECLARE_SYSCTL_BASE(debug, debug_table); -DECLARE_SYSCTL_BASE(dev, dev_table); - int __init sysctl_init_bases(void) { - register_sysctl_base(kernel); - register_sysctl_base(vm); - register_sysctl_base(debug); - register_sysctl_base(dev); + register_sysctl_init("kernel", kern_table); + register_sysctl_init("kernel/usermodehelper", usermodehelper_table); +#ifdef CONFIG_KEYS + register_sysctl_init("kernel/keys", key_sysctls); +#endif + + register_sysctl_init("vm", vm_table); + register_sysctl_init("debug", debug_table); + register_sysctl_init("dev", dev_table); return 0; } -- cgit From b8cbc0855a22fe386c704ee29fb21282f999b995 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 23 May 2023 14:22:20 +0200 Subject: sysctl: Remove register_sysctl_table This is part of the general push to deprecate register_sysctl_paths and register_sysctl_table. After removing all the calling functions, we remove both the register_sysctl_table function and the documentation check that appeared in check-sysctl-docs awk script. We save 595 bytes with this change: ./scripts/bloat-o-meter vmlinux.1.refactor-base-paths vmlinux.2.remove-sysctl-table add/remove: 2/8 grow/shrink: 1/0 up/down: 1154/-1749 (-595) Function old new delta count_subheaders - 983 +983 unregister_sysctl_table 29 184 +155 __pfx_count_subheaders - 16 +16 __pfx_unregister_sysctl_table.part 16 - -16 __pfx_register_leaf_sysctl_tables.constprop 16 - -16 __pfx_count_subheaders.part 16 - -16 __pfx___register_sysctl_base 16 - -16 unregister_sysctl_table.part 136 - -136 __register_sysctl_base 478 - -478 register_leaf_sysctl_tables.constprop 524 - -524 count_subheaders.part 547 - -547 Total: Before=21257652, After=21257057, chg -0.00% [mcgrof: remove register_leaf_sysctl_tables and append_path too and add bloat-o-meter stats] Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain Acked-by: Christian Brauner --- fs/proc/proc_sysctl.c | 159 ---------------------------------------------- scripts/check-sysctl-docs | 10 --- 2 files changed, 169 deletions(-) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index f8f19e000d76..8873812d22f3 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1466,19 +1466,6 @@ void __init __register_sysctl_init(const char *path, struct ctl_table *table, kmemleak_not_leak(hdr); } -static char *append_path(const char *path, char *pos, const char *name) -{ - int namelen; - namelen = strlen(name); - if (((pos - path) + namelen + 2) >= PATH_MAX) - return NULL; - memcpy(pos, name, namelen); - pos[namelen] = '/'; - pos[namelen + 1] = '\0'; - pos += namelen + 1; - return pos; -} - static int count_subheaders(struct ctl_table *table) { int has_files = 0; @@ -1498,152 +1485,6 @@ static int count_subheaders(struct ctl_table *table) return nr_subheaders + has_files; } -static int register_leaf_sysctl_tables(const char *path, char *pos, - struct ctl_table_header ***subheader, struct ctl_table_set *set, - struct ctl_table *table) -{ - struct ctl_table *ctl_table_arg = NULL; - struct ctl_table *entry, *files; - int nr_files = 0; - int nr_dirs = 0; - int err = -ENOMEM; - - list_for_each_table_entry(entry, table) { - if (entry->child) - nr_dirs++; - else - nr_files++; - } - - files = table; - /* If there are mixed files and directories we need a new table */ - if (nr_dirs && nr_files) { - struct ctl_table *new; - files = kcalloc(nr_files + 1, sizeof(struct ctl_table), - GFP_KERNEL); - if (!files) - goto out; - - ctl_table_arg = files; - new = files; - - list_for_each_table_entry(entry, table) { - if (entry->child) - continue; - *new = *entry; - new++; - } - } - - /* Register everything except a directory full of subdirectories */ - if (nr_files || !nr_dirs) { - struct ctl_table_header *header; - header = __register_sysctl_table(set, path, files); - if (!header) { - kfree(ctl_table_arg); - goto out; - } - - /* Remember if we need to free the file table */ - header->ctl_table_arg = ctl_table_arg; - **subheader = header; - (*subheader)++; - } - - /* Recurse into the subdirectories. */ - list_for_each_table_entry(entry, table) { - char *child_pos; - - if (!entry->child) - continue; - - err = -ENAMETOOLONG; - child_pos = append_path(path, pos, entry->procname); - if (!child_pos) - goto out; - - err = register_leaf_sysctl_tables(path, child_pos, subheader, - set, entry->child); - pos[0] = '\0'; - if (err) - goto out; - } - err = 0; -out: - /* On failure our caller will unregister all registered subheaders */ - return err; -} - -/** - * register_sysctl_table - register a sysctl table hierarchy - * @table: the top-level table structure - * - * Register a sysctl table hierarchy. @table should be a filled in ctl_table - * array. A completely 0 filled entry terminates the table. - * We are slowly deprecating this call so avoid its use. - */ -static struct ctl_table_header *register_sysctl_table(struct ctl_table *table) -{ - struct ctl_table *ctl_table_arg = table; - int nr_subheaders = count_subheaders(table); - struct ctl_table_header *header = NULL, **subheaders, **subheader; - char *new_path, *pos; - - pos = new_path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!new_path) - return NULL; - - pos[0] = '\0'; - while (table->procname && table->child && !table[1].procname) { - pos = append_path(new_path, pos, table->procname); - if (!pos) - goto out; - table = table->child; - } - if (nr_subheaders == 1) { - header = __register_sysctl_table(&sysctl_table_root.default_set, new_path, table); - if (header) - header->ctl_table_arg = ctl_table_arg; - } else { - header = kzalloc(sizeof(*header) + - sizeof(*subheaders)*nr_subheaders, GFP_KERNEL); - if (!header) - goto out; - - subheaders = (struct ctl_table_header **) (header + 1); - subheader = subheaders; - header->ctl_table_arg = ctl_table_arg; - - if (register_leaf_sysctl_tables(new_path, pos, &subheader, - &sysctl_table_root.default_set, table)) - goto err_register_leaves; - } - -out: - kfree(new_path); - return header; - -err_register_leaves: - while (subheader > subheaders) { - struct ctl_table_header *subh = *(--subheader); - struct ctl_table *table = subh->ctl_table_arg; - unregister_sysctl_table(subh); - kfree(table); - } - kfree(header); - header = NULL; - goto out; -} - -int __register_sysctl_base(struct ctl_table *base_table) -{ - struct ctl_table_header *hdr; - - hdr = register_sysctl_table(base_table); - kmemleak_not_leak(hdr); - return 0; -} - static void put_links(struct ctl_table_header *header) { struct ctl_table_set *root_set = &sysctl_table_root.default_set; diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index edc9a629d79e..4f163e0bf6a4 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -146,16 +146,6 @@ curtable && /\.procname[\t ]*=[\t ]*".+"/ { children[curtable][curentry] = child } -/register_sysctl_table\(.*\)/ { - match($0, /register_sysctl_table\(([^)]+)\)/, tables) - if (debug) print "Registering table " tables[1] - if (children[tables[1]][table]) { - for (entry in entries[children[tables[1]][table]]) { - printentry(entry) - } - } -} - END { for (entry in documented) { if (!seen[entry]) { -- cgit From 996ef312f27fa8ee8715c6ec77b6a3cdb748bdca Mon Sep 17 00:00:00 2001 From: Luis Chamberlain Date: Thu, 18 May 2023 13:40:15 -0700 Subject: sysctl: remove empty dev table Now that all the dev sysctls have been moved out we can remove the dev sysctl base directory. We don't need to create base directories, they are created for you as if using 'mkdir -p' with register_syctl() and register_sysctl_init(). For details refer to sysctl_mkdir_p() usage. We save 90 bytes with this changes: ./scripts/bloat-o-meter vmlinux.2.remove-sysctl-table vmlinux.3-remove-dev-table add/remove: 0/1 grow/shrink: 0/1 up/down: 0/-90 (-90) Function old new delta sysctl_init_bases 111 85 -26 dev_table 64 - -64 Total: Before=21257057, After=21256967, chg -0.00% The empty dev table has been in place since the v2.5.0 days because back then ordering was essentialy. But later commit 7ec66d06362d ("sysctl: Stop requiring explicit management of sysctl directories"), merged as of v3.4-rc1, the entire ordering of directories was replaced by allowing sysctl directory autogeneration. This new mechanism introduced on v3.4 allows for sysctl directories to automatically be created for sysctl tables when they are needed and automatically removes them when no sysctl tables use them. That commit also added a dedicated struct ctl_dir as a new type for these autogenerated directories. Reviewed-by: Joel Granados Signed-off-by: Luis Chamberlain --- kernel/sysctl.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 73fa9cf7ee11..9552f221f568 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2332,10 +2332,6 @@ static struct ctl_table debug_table[] = { { } }; -static struct ctl_table dev_table[] = { - { } -}; - int __init sysctl_init_bases(void) { register_sysctl_init("kernel", kern_table); @@ -2346,7 +2342,6 @@ int __init sysctl_init_bases(void) register_sysctl_init("vm", vm_table); register_sysctl_init("debug", debug_table); - register_sysctl_init("dev", dev_table); return 0; } -- cgit From 01e6aac78b1c5c9e7115c15f5dbf942959b8f3ad Mon Sep 17 00:00:00 2001 From: Luis Chamberlain Date: Thu, 18 May 2023 13:37:41 -0700 Subject: signal: move show_unhandled_signals sysctl to its own file The show_unhandled_signals sysctl is the only sysctl for debug left on kernel/sysctl.c. We've been moving the syctls out from kernel/sysctl.c so to help avoid merge conflicts as the shared array gets out of hand. This change incurs simplifies sysctl registration by localizing it where it should go for a penalty in size of increasing the kernel by 23 bytes, we accept this given recent cleanups have actually already saved us 1465 bytes in the prior commits. ./scripts/bloat-o-meter vmlinux.3-remove-dev-table vmlinux.4-remove-debug-table add/remove: 3/1 grow/shrink: 0/1 up/down: 177/-154 (23) Function old new delta signal_debug_table - 128 +128 init_signal_sysctls - 33 +33 __pfx_init_signal_sysctls - 16 +16 sysctl_init_bases 85 59 -26 debug_table 128 - -128 Total: Before=21256967, After=21256990, chg +0.00% Reviewed-by: Joel Granados Signed-off-by: Luis Chamberlain --- kernel/signal.c | 23 +++++++++++++++++++++++ kernel/sysctl.c | 14 -------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 8f6330f0e9ca..5ba4150c01a7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -45,6 +45,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -4771,6 +4772,28 @@ static inline void siginfo_buildtime_checks(void) #endif } +#if defined(CONFIG_SYSCTL) +static struct ctl_table signal_debug_table[] = { +#ifdef CONFIG_SYSCTL_EXCEPTION_TRACE + { + .procname = "exception-trace", + .data = &show_unhandled_signals, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, +#endif + { } +}; + +static int __init init_signal_sysctls(void) +{ + register_sysctl_init("debug", signal_debug_table); + return 0; +} +early_initcall(init_signal_sysctls); +#endif /* CONFIG_SYSCTL */ + void __init signals_init(void) { siginfo_buildtime_checks(); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 9552f221f568..241b817c0240 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2319,19 +2319,6 @@ static struct ctl_table vm_table[] = { { } }; -static struct ctl_table debug_table[] = { -#ifdef CONFIG_SYSCTL_EXCEPTION_TRACE - { - .procname = "exception-trace", - .data = &show_unhandled_signals, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, -#endif - { } -}; - int __init sysctl_init_bases(void) { register_sysctl_init("kernel", kern_table); @@ -2341,7 +2328,6 @@ int __init sysctl_init_bases(void) #endif register_sysctl_init("vm", vm_table); - register_sysctl_init("debug", debug_table); return 0; } -- cgit From 861dc0b46432a7086bc6de526aae775b4d615e28 Mon Sep 17 00:00:00 2001 From: Luis Chamberlain Date: Sun, 28 May 2023 13:43:46 -0700 Subject: sysctl: move umh sysctl registration to its own file Move the umh sysctl registration to its own file, the array is already there. We do this to remove the clutter out of kernel/sysctl.c to avoid merge conflicts. This also lets the sysctls not be built at all now when CONFIG_SYSCTL is not enabled. This has a small penalty of 23 bytes but soon we'll be removing all the empty entries on sysctl arrays so just do this cleanup now: ./scripts/bloat-o-meter vmlinux.base vmlinux.1 add/remove: 2/0 grow/shrink: 0/1 up/down: 49/-26 (23) Function old new delta init_umh_sysctls - 33 +33 __pfx_init_umh_sysctls - 16 +16 sysctl_init_bases 111 85 -26 Total: Before=21256914, After=21256937, chg +0.00% Acked-by: Jarkko Sakkinen Signed-off-by: Luis Chamberlain --- include/linux/umh.h | 2 -- kernel/sysctl.c | 1 - kernel/umh.c | 11 ++++++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/linux/umh.h b/include/linux/umh.h index 5d1f6129b847..daa6a7048c11 100644 --- a/include/linux/umh.h +++ b/include/linux/umh.h @@ -42,8 +42,6 @@ call_usermodehelper_setup(const char *path, char **argv, char **envp, extern int call_usermodehelper_exec(struct subprocess_info *info, int wait); -extern struct ctl_table usermodehelper_table[]; - enum umh_disable_depth { UMH_ENABLED = 0, UMH_FREEZING, diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 241b817c0240..caf4a91522a1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2322,7 +2322,6 @@ static struct ctl_table vm_table[] = { int __init sysctl_init_bases(void) { register_sysctl_init("kernel", kern_table); - register_sysctl_init("kernel/usermodehelper", usermodehelper_table); #ifdef CONFIG_KEYS register_sysctl_init("kernel/keys", key_sysctls); #endif diff --git a/kernel/umh.c b/kernel/umh.c index 60aa9e764a38..41088c5c39fd 100644 --- a/kernel/umh.c +++ b/kernel/umh.c @@ -544,7 +544,8 @@ static int proc_cap_handler(struct ctl_table *table, int write, return 0; } -struct ctl_table usermodehelper_table[] = { +#if defined(CONFIG_SYSCTL) +static struct ctl_table usermodehelper_table[] = { { .procname = "bset", .data = &usermodehelper_bset, @@ -561,3 +562,11 @@ struct ctl_table usermodehelper_table[] = { }, { } }; + +static int __init init_umh_sysctls(void) +{ + register_sysctl_init("kernel/usermodehelper", usermodehelper_table); + return 0; +} +early_initcall(init_umh_sysctls); +#endif /* CONFIG_SYSCTL */ -- cgit From 28898e260a34e840f86ca80bf0c7657d76ad3f80 Mon Sep 17 00:00:00 2001 From: Luis Chamberlain Date: Sun, 28 May 2023 13:54:20 -0700 Subject: sysctl: move security keys sysctl registration to its own file The security keys sysctls are already declared on its own file, just move the sysctl registration to its own file to help avoid merge conflicts on sysctls.c, and help with clearing up sysctl.c further. This creates a small penalty of 23 bytes: ./scripts/bloat-o-meter vmlinux.1 vmlinux.2 add/remove: 2/0 grow/shrink: 0/1 up/down: 49/-26 (23) Function old new delta init_security_keys_sysctls - 33 +33 __pfx_init_security_keys_sysctls - 16 +16 sysctl_init_bases 85 59 -26 Total: Before=21256937, After=21256960, chg +0.00% But soon we'll be saving tons of bytes anyway, as we modify the sysctl registrations to use ARRAY_SIZE and so we get rid of all the empty array elements so let's just clean this up now. Reviewed-by: Paul Moore Acked-by: Jarkko Sakkinen Acked-by: David Howells Signed-off-by: Luis Chamberlain --- include/linux/key.h | 3 --- kernel/sysctl.c | 4 ---- security/keys/sysctl.c | 7 +++++++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/key.h b/include/linux/key.h index 8dc7f7c3088b..938d7ecfb495 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -490,9 +490,6 @@ do { \ rcu_assign_pointer((KEY)->payload.rcu_data0, (PAYLOAD)); \ } while (0) -#ifdef CONFIG_SYSCTL -extern struct ctl_table key_sysctls[]; -#endif /* * the userspace interface */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index caf4a91522a1..48046932d573 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2322,10 +2322,6 @@ static struct ctl_table vm_table[] = { int __init sysctl_init_bases(void) { register_sysctl_init("kernel", kern_table); -#ifdef CONFIG_KEYS - register_sysctl_init("kernel/keys", key_sysctls); -#endif - register_sysctl_init("vm", vm_table); return 0; diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index b46b651b3c4c..b72b82bb20c6 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -68,3 +68,10 @@ struct ctl_table key_sysctls[] = { #endif { } }; + +static int __init init_security_keys_sysctls(void) +{ + register_sysctl_init("kernel/keys", key_sysctls); + return 0; +} +early_initcall(init_security_keys_sysctls); -- cgit From 37e9981e33e4d308c323a56bb908aa54c8f041a8 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:15 +0200 Subject: parport: plug a sysctl register leak parport registers two sysctl directories in the parport_proc_register function but only one of them was getting unregistered in parport_proc_unregister. Keep track of both sysctl table headers and handle them together when (un)registering. Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- drivers/parport/procfs.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index cbb1fb5127ce..0f2d2e1ee28e 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -257,14 +257,16 @@ PARPORT_MAX_SPINTIME_VALUE; struct parport_sysctl_table { - struct ctl_table_header *sysctl_header; + struct ctl_table_header *port_header; + struct ctl_table_header *devices_header; struct ctl_table vars[12]; struct ctl_table device_dir[2]; }; static const struct parport_sysctl_table parport_sysctl_template = { - .sysctl_header = NULL, - { + .port_header = NULL, + .devices_header = NULL, + { { .procname = "spintime", .data = NULL, @@ -429,7 +431,6 @@ parport_default_sysctl_table = { int parport_proc_register(struct parport *port) { struct parport_sysctl_table *t; - struct ctl_table_header *devices_h; char *tmp_dir_path; size_t tmp_path_len, port_name_len; int bytes_written, i, err = 0; @@ -464,8 +465,8 @@ int parport_proc_register(struct parport *port) err = -ENOENT; goto exit_free_tmp_dir_path; } - devices_h = register_sysctl(tmp_dir_path, t->device_dir); - if (devices_h == NULL) { + t->devices_header = register_sysctl(tmp_dir_path, t->device_dir); + if (t->devices_header == NULL) { err = -ENOENT; goto exit_free_tmp_dir_path; } @@ -478,8 +479,8 @@ int parport_proc_register(struct parport *port) goto unregister_devices_h; } - t->sysctl_header = register_sysctl(tmp_dir_path, t->vars); - if (t->sysctl_header == NULL) { + t->port_header = register_sysctl(tmp_dir_path, t->vars); + if (t->port_header == NULL) { err = -ENOENT; goto unregister_devices_h; } @@ -490,7 +491,7 @@ int parport_proc_register(struct parport *port) return 0; unregister_devices_h: - unregister_sysctl_table(devices_h); + unregister_sysctl_table(t->devices_header); exit_free_tmp_dir_path: kfree(tmp_dir_path); @@ -505,7 +506,8 @@ int parport_proc_unregister(struct parport *port) if (port->sysctl_table) { struct parport_sysctl_table *t = port->sysctl_table; port->sysctl_table = NULL; - unregister_sysctl_table(t->sysctl_header); + unregister_sysctl_table(t->devices_header); + unregister_sysctl_table(t->port_header); kfree(t); } return 0; -- cgit From a40b702789a480904b15f5843c4ed79f969ee4d5 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:16 +0200 Subject: test_sysctl: Fix test metadata getters The functions get_test_{count,enabled,target} use awk to get the N'th field in the ALL_TESTS variable. A variable with leading zeros (e.g. 0009) is misinterpreted as an entire line instead of the N'th field. Remove the leading zeros so this does not happen. We can now use the helper in tests 6, 7 and 8. Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- tools/testing/selftests/sysctl/sysctl.sh | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index bfc54b422f25..cb8f83dfe16b 100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -730,7 +730,7 @@ sysctl_test_0005() sysctl_test_0006() { - TARGET="${SYSCTL}/bitmap_0001" + TARGET="${SYSCTL}/$(get_test_target 0006)" reset_vals ORIG="" run_bitmaptest @@ -738,7 +738,7 @@ sysctl_test_0006() sysctl_test_0007() { - TARGET="${SYSCTL}/boot_int" + TARGET="${SYSCTL}/$(get_test_target 0007)" if [ ! -f $TARGET ]; then echo "Skipping test for $TARGET as it is not present ..." return $ksft_skip @@ -778,7 +778,7 @@ sysctl_test_0007() sysctl_test_0008() { - TARGET="${SYSCTL}/match_int" + TARGET="${SYSCTL}/$(get_test_target 0008)" if [ ! -f $TARGET ]; then echo "Skipping test for $TARGET as it is not present ..." return $ksft_skip @@ -857,25 +857,32 @@ function test_num() usage fi } +function remove_leading_zeros() +{ + echo $1 | sed 's/^0*//' +} function get_test_count() { test_num $1 - TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') echo ${TEST_DATA} | awk -F":" '{print $2}' } function get_test_enabled() { test_num $1 - TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') echo ${TEST_DATA} | awk -F":" '{print $3}' } function get_test_target() { test_num $1 - TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') echo ${TEST_DATA} | awk -F":" '{print $4}' } -- cgit From e009bd5efe81c5ccd2c08626a79c37d2a238ff15 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:17 +0200 Subject: test_sysctl: Group node sysctl test under one func Preparation commit to add a new type of test to test_sysctl.c. We want to differentiate between node and (sub)directory tests. Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- lib/test_sysctl.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c index e2a816d85ea2..0cf7c547d61a 100644 --- a/lib/test_sysctl.c +++ b/lib/test_sysctl.c @@ -126,9 +126,7 @@ static struct ctl_table test_table[] = { { } }; -static struct ctl_table_header *test_sysctl_header; - -static int __init test_sysctl_init(void) +static void test_sysctl_calc_match_int_ok(void) { int i; @@ -153,7 +151,13 @@ static int __init test_sysctl_init(void) for (i = 0; i < ARRAY_SIZE(match_int); i++) if (match_int[i].defined != match_int[i].wanted) match_int_ok = 0; +} +static struct ctl_table_header *test_sysctl_header; + +static int test_sysctl_setup_node_tests(void) +{ + test_sysctl_calc_match_int_ok(); test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); if (!test_data.bitmap_0001) return -ENOMEM; @@ -162,8 +166,18 @@ static int __init test_sysctl_init(void) kfree(test_data.bitmap_0001); return -ENOMEM; } + return 0; } + +static int __init test_sysctl_init(void) +{ + int err; + + err = test_sysctl_setup_node_tests(); + + return err; +} module_init(test_sysctl_init); static void __exit test_sysctl_exit(void) -- cgit From 35576438591e8d37c7651e6ff56f2e07c7f9615a Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:18 +0200 Subject: test_sysctl: Add an unregister sysctl test Add a test that checks that the unregistered directory is removed from /proc/sys/debug Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- lib/test_sysctl.c | 30 ++++++++++++++++++++++++++++++ tools/testing/selftests/sysctl/sysctl.sh | 16 ++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c index 0cf7c547d61a..555244687443 100644 --- a/lib/test_sysctl.c +++ b/lib/test_sysctl.c @@ -170,12 +170,42 @@ static int test_sysctl_setup_node_tests(void) return 0; } +/* Used to test that unregister actually removes the directory */ +static struct ctl_table test_table_unregister[] = { + { + .procname = "unregister_error", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + }, + {} +}; + +static int test_sysctl_run_unregister_nested(void) +{ + struct ctl_table_header *unregister; + + unregister = register_sysctl("debug/test_sysctl/unregister_error", + test_table_unregister); + if (!unregister) + return -ENOMEM; + + unregister_sysctl_table(unregister); + return 0; +} + static int __init test_sysctl_init(void) { int err; err = test_sysctl_setup_node_tests(); + if (err) + goto out; + + err = test_sysctl_run_unregister_nested(); +out: return err; } module_init(test_sysctl_init); diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index cb8f83dfe16b..a6d79d7a36e4 100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -31,6 +31,7 @@ ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003" ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001" ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int" ALL_TESTS="$ALL_TESTS 0008:1:1:match_int" +ALL_TESTS="$ALL_TESTS 0009:1:1:unregister_error" function allow_user_defaults() { @@ -797,6 +798,20 @@ sysctl_test_0008() return 0 } +sysctl_test_0009() +{ + TARGET="${SYSCTL}/$(get_test_target 0009)" + echo -n "Testing if $TARGET unregistered correctly ..." + if [ -d $TARGET ]; then + echo "TEST FAILED" + rc=1 + test_rc + fi + + echo "ok" + return 0 +} + list_tests() { echo "Test ID list:" @@ -813,6 +828,7 @@ list_tests() echo "0006 x $(get_test_count 0006) - tests proc_do_large_bitmap()" echo "0007 x $(get_test_count 0007) - tests setting sysctl from kernel boot param" echo "0008 x $(get_test_count 0008) - tests sysctl macro values match" + echo "0009 x $(get_test_count 0009) - tests sysct unregister" } usage() -- cgit From ec866cc6f8a90a65cd085377405c34f0f6d9ba60 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:19 +0200 Subject: test_sysctl: Add an option to prevent test skip Tests were being skipped because the target was not present. Add a flag that controls whether to skip a test based on the presence of the target. Actually skip tests in the test_case function with a "return" instead of a "continue". Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- tools/testing/selftests/sysctl/sysctl.sh | 66 +++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index a6d79d7a36e4..9c0e9711138b 100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -14,24 +14,26 @@ TEST_FILE=$(mktemp) # This represents # -# TEST_ID:TEST_COUNT:ENABLED:TARGET +# TEST_ID:TEST_COUNT:ENABLED:TARGET:SKIP_NO_TARGET # # TEST_ID: is the test id number # TEST_COUNT: number of times we should run the test # ENABLED: 1 if enabled, 0 otherwise # TARGET: test target file required on the test_sysctl module +# SKIP_NO_TARGET: 1 skip if TARGET not there +# 0 run eventhough TARGET not there # # Once these are enabled please leave them as-is. Write your own test, # we have tons of space. -ALL_TESTS="0001:1:1:int_0001" -ALL_TESTS="$ALL_TESTS 0002:1:1:string_0001" -ALL_TESTS="$ALL_TESTS 0003:1:1:int_0002" -ALL_TESTS="$ALL_TESTS 0004:1:1:uint_0001" -ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003" -ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001" -ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int" -ALL_TESTS="$ALL_TESTS 0008:1:1:match_int" -ALL_TESTS="$ALL_TESTS 0009:1:1:unregister_error" +ALL_TESTS="0001:1:1:int_0001:1" +ALL_TESTS="$ALL_TESTS 0002:1:1:string_0001:1" +ALL_TESTS="$ALL_TESTS 0003:1:1:int_0002:1" +ALL_TESTS="$ALL_TESTS 0004:1:1:uint_0001:1" +ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003:1" +ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001:1" +ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int:1" +ALL_TESTS="$ALL_TESTS 0008:1:1:match_int:1" +ALL_TESTS="$ALL_TESTS 0009:1:1:unregister_error:0" function allow_user_defaults() { @@ -614,7 +616,6 @@ target_exists() TEST_ID="$2" if [ ! -f ${TARGET} ] ; then - echo "Target for test $TEST_ID: $TARGET not exist, skipping test ..." return 0 fi return 1 @@ -902,16 +903,36 @@ function get_test_target() echo ${TEST_DATA} | awk -F":" '{print $4}' } +function get_test_skip_no_target() +{ + test_num $1 + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') + echo ${TEST_DATA} | awk -F":" '{print $5}' +} + +function skip_test() +{ + TEST_ID=$1 + TEST_TARGET=$2 + if target_exists $TEST_TARGET $TEST_ID; then + TEST_SKIP=$(get_test_skip_no_target $TEST_ID) + if [[ $TEST_SKIP -eq "1" ]]; then + echo "Target for test $TEST_ID: $TEST_TARGET not exist, skipping test ..." + return 0 + fi + fi + return 1 +} + function run_all_tests() { for i in $ALL_TESTS ; do - TEST_ID=${i%:*:*:*} + TEST_ID=${i%:*:*:*:*} ENABLED=$(get_test_enabled $TEST_ID) TEST_COUNT=$(get_test_count $TEST_ID) TEST_TARGET=$(get_test_target $TEST_ID) - if target_exists $TEST_TARGET $TEST_ID; then - continue - fi + if [[ $ENABLED -eq "1" ]]; then test_case $TEST_ID $TEST_COUNT $TEST_TARGET fi @@ -946,18 +967,19 @@ function watch_case() function test_case() { + TEST_ID=$1 NUM_TESTS=$2 + TARGET=$3 - i=0 - - if target_exists $3 $1; then - continue + if skip_test $TEST_ID $TARGET; then + return fi + i=0 while [ $i -lt $NUM_TESTS ]; do - test_num $1 - watch_log $i ${TEST_NAME}_test_$1 noclear - RUN_TEST=${TEST_NAME}_test_$1 + test_num $TEST_ID + watch_log $i ${TEST_NAME}_test_${TEST_ID} noclear + RUN_TEST=${TEST_NAME}_test_${TEST_ID} $RUN_TEST let i=$i+1 done -- cgit From f2e7a6265e5a5e02ee663eda3d0527dd8230b832 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:20 +0200 Subject: test_sysclt: Test for registering a mount point Test that target gets created by register_sysctl_mount_point and that no additional target can be created "on top" of a permanently empty sysctl table. Create a mount point target (mnt) in the sysctl test driver; try to create another on top of that (mnt_error). Output an error if "mnt_error" is present when we run the sysctl selftests. Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- lib/test_sysctl.c | 45 +++++++++++++++++++++++++++----- tools/testing/selftests/sysctl/sysctl.sh | 16 ++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c index 555244687443..8036aa91a1cb 100644 --- a/lib/test_sysctl.c +++ b/lib/test_sysctl.c @@ -30,6 +30,13 @@ static int i_zero; static int i_one_hundred = 100; static int match_int_ok = 1; + +static struct { + struct ctl_table_header *test_h_setup_node; + struct ctl_table_header *test_h_mnt; + struct ctl_table_header *test_h_mnterror; +} sysctl_test_headers; + struct test_sysctl_data { int int_0001; int int_0002; @@ -153,16 +160,14 @@ static void test_sysctl_calc_match_int_ok(void) match_int_ok = 0; } -static struct ctl_table_header *test_sysctl_header; - static int test_sysctl_setup_node_tests(void) { test_sysctl_calc_match_int_ok(); test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); if (!test_data.bitmap_0001) return -ENOMEM; - test_sysctl_header = register_sysctl("debug/test_sysctl", test_table); - if (!test_sysctl_header) { + sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table); + if (!sysctl_test_headers.test_h_setup_node) { kfree(test_data.bitmap_0001); return -ENOMEM; } @@ -195,6 +200,26 @@ static int test_sysctl_run_unregister_nested(void) return 0; } +static int test_sysctl_run_register_mount_point(void) +{ + sysctl_test_headers.test_h_mnt + = register_sysctl_mount_point("debug/test_sysctl/mnt"); + if (!sysctl_test_headers.test_h_mnt) + return -ENOMEM; + + sysctl_test_headers.test_h_mnterror + = register_sysctl("debug/test_sysctl/mnt/mnt_error", + test_table_unregister); + /* + * Don't check the result.: + * If it fails (expected behavior), return 0. + * If successful (missbehavior of register mount point), we want to see + * mnt_error when we run the sysctl test script + */ + + return 0; +} + static int __init test_sysctl_init(void) { int err; @@ -204,6 +229,10 @@ static int __init test_sysctl_init(void) goto out; err = test_sysctl_run_unregister_nested(); + if (err) + goto out; + + err = test_sysctl_run_register_mount_point(); out: return err; @@ -213,8 +242,12 @@ module_init(test_sysctl_init); static void __exit test_sysctl_exit(void) { kfree(test_data.bitmap_0001); - if (test_sysctl_header) - unregister_sysctl_table(test_sysctl_header); + if (sysctl_test_headers.test_h_setup_node) + unregister_sysctl_table(sysctl_test_headers.test_h_setup_node); + if (sysctl_test_headers.test_h_mnt) + unregister_sysctl_table(sysctl_test_headers.test_h_mnt); + if (sysctl_test_headers.test_h_mnterror) + unregister_sysctl_table(sysctl_test_headers.test_h_mnterror); } module_exit(test_sysctl_exit); diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index 9c0e9711138b..444b2befda82 100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -34,6 +34,7 @@ ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001:1" ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int:1" ALL_TESTS="$ALL_TESTS 0008:1:1:match_int:1" ALL_TESTS="$ALL_TESTS 0009:1:1:unregister_error:0" +ALL_TESTS="$ALL_TESTS 0010:1:1:mnt/mnt_error:0" function allow_user_defaults() { @@ -813,6 +814,20 @@ sysctl_test_0009() return 0 } +sysctl_test_0010() +{ + TARGET="${SYSCTL}/$(get_test_target 0010)" + echo -n "Testing that $TARGET was not created ..." + if [ -d $TARGET ]; then + echo "TEST FAILED" + rc=1 + test_rc + fi + + echo "ok" + return 0 +} + list_tests() { echo "Test ID list:" @@ -830,6 +845,7 @@ list_tests() echo "0007 x $(get_test_count 0007) - tests setting sysctl from kernel boot param" echo "0008 x $(get_test_count 0008) - tests sysctl macro values match" echo "0009 x $(get_test_count 0009) - tests sysct unregister" + echo "0010 x $(get_test_count 0010) - tests sysct mount point" } usage() -- cgit From 94a6490518d80a61c7a8e5aa107547e53636d964 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:21 +0200 Subject: sysctl: Remove debugging dump_stack Remove unneeded dump_stack in __register_sysctl_table Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- fs/proc/proc_sysctl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 8873812d22f3..07804097f997 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1406,7 +1406,6 @@ fail_put_dir_locked: spin_unlock(&sysctl_lock); fail: kfree(header); - dump_stack(); return NULL; } -- cgit From 2f2665c13af4895b26761107c2f637c2f112d8e9 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 16 Jun 2023 10:59:22 +0200 Subject: sysctl: replace child with an enumeration This is part of the effort to remove the empty element at the end of ctl_table structs. "child" was a deprecated elem in this struct and was being used to differentiate between two types of ctl_tables: "normal" and "permanently emtpy". What changed?: * Replace "child" with an enumeration that will have two values: the default (0) and the permanently empty (1). The latter is left at zero so when struct ctl_table is created with kzalloc or in a local context, it will have the zero value by default. We document the new enum with kdoc. * Remove the "empty child" check from sysctl_check_table * Remove count_subheaders function as there is no longer a need to calculate how many headers there are for every child * Remove the recursive call to unregister_sysctl_table as there is no need to traverse down the child tree any longer * Add a new SYSCTL_PERM_EMPTY_DIR binary flag * Remove the last remanence of child from partport/procfs.c Signed-off-by: Joel Granados Signed-off-by: Luis Chamberlain --- drivers/parport/procfs.c | 1 - fs/proc/proc_sysctl.c | 81 +++++++++++------------------------------------- include/linux/sysctl.h | 14 +++++++-- 3 files changed, 30 insertions(+), 66 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 0f2d2e1ee28e..4e5b972c3e26 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -387,7 +387,6 @@ parport_device_sysctl_template = { .data = NULL, .maxlen = 0, .mode = 0555, - .child = NULL }, {} } diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 07804097f997..c4ea804d862b 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -29,9 +29,8 @@ static const struct file_operations proc_sys_dir_file_operations; static const struct inode_operations proc_sys_dir_operations; /* Support for permanently empty directories */ - struct ctl_table sysctl_mount_point[] = { - { } + {.type = SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY } }; /** @@ -48,21 +47,14 @@ struct ctl_table_header *register_sysctl_mount_point(const char *path) } EXPORT_SYMBOL(register_sysctl_mount_point); -static bool is_empty_dir(struct ctl_table_header *head) -{ - return head->ctl_table[0].child == sysctl_mount_point; -} - -static void set_empty_dir(struct ctl_dir *dir) -{ - dir->header.ctl_table[0].child = sysctl_mount_point; -} - -static void clear_empty_dir(struct ctl_dir *dir) - -{ - dir->header.ctl_table[0].child = NULL; -} +#define sysctl_is_perm_empty_ctl_table(tptr) \ + (tptr[0].type == SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY) +#define sysctl_is_perm_empty_ctl_header(hptr) \ + (sysctl_is_perm_empty_ctl_table(hptr->ctl_table)) +#define sysctl_set_perm_empty_ctl_header(hptr) \ + (hptr->ctl_table[0].type = SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY) +#define sysctl_clear_perm_empty_ctl_header(hptr) \ + (hptr->ctl_table[0].type = SYSCTL_TABLE_TYPE_DEFAULT) void proc_sys_poll_notify(struct ctl_table_poll *poll) { @@ -230,20 +222,22 @@ static void erase_header(struct ctl_table_header *head) static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) { struct ctl_table *entry; + struct ctl_table_header *dir_h = &dir->header; int err; + /* Is this a permanently empty directory? */ - if (is_empty_dir(&dir->header)) + if (sysctl_is_perm_empty_ctl_header(dir_h)) return -EROFS; /* Am I creating a permanently empty directory? */ - if (header->ctl_table == sysctl_mount_point) { + if (sysctl_is_perm_empty_ctl_table(header->ctl_table)) { if (!RB_EMPTY_ROOT(&dir->root)) return -EINVAL; - set_empty_dir(dir); + sysctl_set_perm_empty_ctl_header(dir_h); } - dir->header.nreg++; + dir_h->nreg++; header->parent = dir; err = insert_links(header); if (err) @@ -259,9 +253,9 @@ fail: put_links(header); fail_links: if (header->ctl_table == sysctl_mount_point) - clear_empty_dir(dir); + sysctl_clear_perm_empty_ctl_header(dir_h); header->parent = NULL; - drop_sysctl_table(&dir->header); + drop_sysctl_table(dir_h); return err; } @@ -479,7 +473,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, inode->i_mode |= S_IFDIR; inode->i_op = &proc_sys_dir_operations; inode->i_fop = &proc_sys_dir_file_operations; - if (is_empty_dir(head)) + if (sysctl_is_perm_empty_ctl_header(head)) make_empty_dir_inode(inode); } @@ -1136,9 +1130,6 @@ static int sysctl_check_table(const char *path, struct ctl_table *table) struct ctl_table *entry; int err = 0; list_for_each_table_entry(entry, table) { - if (entry->child) - err |= sysctl_err(path, entry, "Not a file"); - if ((entry->proc_handler == proc_dostring) || (entry->proc_handler == proc_dobool) || (entry->proc_handler == proc_dointvec) || @@ -1465,25 +1456,6 @@ void __init __register_sysctl_init(const char *path, struct ctl_table *table, kmemleak_not_leak(hdr); } -static int count_subheaders(struct ctl_table *table) -{ - int has_files = 0; - int nr_subheaders = 0; - struct ctl_table *entry; - - /* special case: no directory and empty directory */ - if (!table || !table->procname) - return 1; - - list_for_each_table_entry(entry, table) { - if (entry->child) - nr_subheaders += count_subheaders(entry->child); - else - has_files = 1; - } - return nr_subheaders + has_files; -} - static void put_links(struct ctl_table_header *header) { struct ctl_table_set *root_set = &sysctl_table_root.default_set; @@ -1546,28 +1518,11 @@ static void drop_sysctl_table(struct ctl_table_header *header) */ void unregister_sysctl_table(struct ctl_table_header * header) { - int nr_subheaders; might_sleep(); if (header == NULL) return; - nr_subheaders = count_subheaders(header->ctl_table_arg); - if (unlikely(nr_subheaders > 1)) { - struct ctl_table_header **subheaders; - int i; - - subheaders = (struct ctl_table_header **)(header + 1); - for (i = nr_subheaders -1; i >= 0; i--) { - struct ctl_table_header *subh = subheaders[i]; - struct ctl_table *table = subh->ctl_table_arg; - unregister_sysctl_table(subh); - kfree(table); - } - kfree(header); - return; - } - spin_lock(&sysctl_lock); drop_sysctl_table(header); spin_unlock(&sysctl_lock); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 653b66c762b1..59d451f455bf 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -137,7 +137,17 @@ struct ctl_table { void *data; int maxlen; umode_t mode; - struct ctl_table *child; /* Deprecated */ + /** + * enum type - Enumeration to differentiate between ctl target types + * @SYSCTL_TABLE_TYPE_DEFAULT: ctl target with no special considerations + * @SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY: Used to identify a permanently + * empty directory target to serve + * as mount point. + */ + enum { + SYSCTL_TABLE_TYPE_DEFAULT, + SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY + } type; proc_handler *proc_handler; /* Callback for text formatting */ struct ctl_table_poll *poll; void *extra1; @@ -229,7 +239,7 @@ extern int unaligned_enabled; extern int unaligned_dump_stack; extern int no_unaligned_warning; -extern struct ctl_table sysctl_mount_point[]; +#define SYSCTL_PERM_EMPTY_DIR (1 << 0) #else /* CONFIG_SYSCTL */ -- cgit