summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-06-28 16:05:21 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-06-28 16:05:21 -0700
commit6a8cbd9253abc1bd0df4d60c4c24fa555190376d (patch)
tree76f3ef8bde90d9aa4c2f1a17835641b58637ed7b
parent4e3c09e95499e83dafc93860d56070a76d20e830 (diff)
parent2f2665c13af4895b26761107c2f637c2f112d8e9 (diff)
Merge tag 'v6.5-rc1-sysctl-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux
Pull sysctl updates from Luis Chamberlain: "The changes for sysctl are in line with prior efforts to stop usage of deprecated routines which incur recursion and also make it hard to remove the empty array element in each sysctl array declaration. The most difficult user to modify was parport which required a bit of re-thinking of how to declare shared sysctls there, Joel Granados has stepped up to the plate to do most of this work and eventual removal of register_sysctl_table(). That work ended up saving us about 1465 bytes according to bloat-o-meter. Since we gained a few bloat-o-meter karma points I moved two rather small sysctl arrays from kernel/sysctl.c leaving us only two more sysctl arrays to move left. Most changes have been tested on linux-next for about a month. The last straggler patches are a minor parport fix, changes to the sysctl kernel selftest so to verify correctness and prevent regressions for the future change he made to provide an alternative solution for the special sysctl mount point target which was using the now deprecated sysctl child element. This is all prep work to now finally be able to remove the empty array element in all sysctl declarations / registrations which is expected to save us a bit of bytes all over the kernel. That work will be tested early after v6.5-rc1 is out" * tag 'v6.5-rc1-sysctl-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux: sysctl: replace child with an enumeration sysctl: Remove debugging dump_stack test_sysclt: Test for registering a mount point test_sysctl: Add an option to prevent test skip test_sysctl: Add an unregister sysctl test test_sysctl: Group node sysctl test under one func test_sysctl: Fix test metadata getters parport: plug a sysctl register leak sysctl: move security keys sysctl registration to its own file sysctl: move umh sysctl registration to its own file signal: move show_unhandled_signals sysctl to its own file sysctl: remove empty dev table sysctl: Remove register_sysctl_table sysctl: Refactor base paths registrations sysctl: stop exporting register_sysctl_table parport: Removed sysctl related defines parport: Remove register_sysctl_table from parport_default_proc_register parport: Remove register_sysctl_table from parport_device_proc_register parport: Remove register_sysctl_table from parport_proc_register parport: Move magic number "15" to a define
-rw-r--r--drivers/parport/procfs.c185
-rw-r--r--drivers/parport/share.c2
-rw-r--r--fs/proc/proc_sysctl.c244
-rw-r--r--fs/sysctls.c5
-rw-r--r--include/linux/key.h3
-rw-r--r--include/linux/parport.h2
-rw-r--r--include/linux/sysctl.h45
-rw-r--r--include/linux/umh.h2
-rw-r--r--kernel/signal.c23
-rw-r--r--kernel/sysctl.c40
-rw-r--r--kernel/umh.c11
-rw-r--r--lib/test_sysctl.c91
-rwxr-xr-xscripts/check-sysctl-docs10
-rw-r--r--security/keys/sysctl.c7
-rwxr-xr-xtools/testing/selftests/sysctl/sysctl.sh115
15 files changed, 351 insertions, 434 deletions
diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c
index d740eba3c099..4e5b972c3e26 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)
@@ -236,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;
@@ -257,17 +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];
- 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 = {
- .sysctl_header = NULL,
- {
+ .port_header = NULL,
+ .devices_header = NULL,
+ {
{
.procname = "spintime",
.data = NULL,
@@ -305,7 +304,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 +353,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
@@ -393,6 +379,7 @@ parport_device_sysctl_template = {
.extra1 = (void*) &parport_min_timeslice_value,
.extra2 = (void*) &parport_max_timeslice_value
},
+ {}
},
{
{
@@ -400,25 +387,8 @@ parport_device_sysctl_template = {
.data = NULL,
.maxlen = 0,
.mode = 0555,
- .child = NULL
},
{}
- },
- {
- PARPORT_DEVICES_ROOT_DIR,
- {}
- },
- {
- PARPORT_PORT_DIR(NULL),
- {}
- },
- {
- PARPORT_PARPORT_DIR(NULL),
- {}
- },
- {
- PARPORT_DEV_DIR(NULL),
- {}
}
};
@@ -454,30 +424,15 @@ 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),
- {}
}
};
-
int parport_proc_register(struct parport *port)
{
struct parport_sysctl_table *t;
- int i;
+ 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 +440,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;
+ }
+ t->devices_header = register_sysctl(tmp_dir_path, t->device_dir);
+ if (t->devices_header == 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);
- if (t->sysctl_header == NULL) {
- kfree(t);
- t = NULL;
+ t->port_header = register_sysctl(tmp_dir_path, t->vars);
+ if (t->port_header == NULL) {
+ err = -ENOENT;
+ goto unregister_devices_h;
}
+
port->sysctl_table = t;
+
+ kfree(tmp_dir_path);
return 0;
+
+unregister_devices_h:
+ unregister_sysctl_table(t->devices_header);
+
+exit_free_tmp_dir_path:
+ kfree(tmp_dir_path);
+
+exit_free_t:
+ kfree(t);
+ return err;
}
int parport_proc_unregister(struct parport *port)
@@ -514,7 +505,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;
@@ -522,30 +514,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)
@@ -564,7 +579,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();
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/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index ae832e982003..4e5488975415 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) ||
@@ -1406,7 +1397,6 @@ fail_put_dir_locked:
spin_unlock(&sysctl_lock);
fail:
kfree(header);
- dump_stack();
return NULL;
}
@@ -1466,185 +1456,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;
- 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 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.
- */
-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;
-}
-EXPORT_SYMBOL(register_sysctl_table);
-
-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;
@@ -1700,35 +1511,18 @@ 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.
*/
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/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/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/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 */
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 3d08277959af..59d451f455bf 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.
@@ -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;
@@ -197,20 +207,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,
@@ -222,7 +218,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);
@@ -244,24 +239,10 @@ 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 */
-#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 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)
{
}
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/signal.c b/kernel/signal.c
index 2547fa73bde5..b5370fe5c198 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -45,6 +45,7 @@
#include <linux/posix-timers.h>
#include <linux/cgroup.h>
#include <linux/audit.h>
+#include <linux/sysctl.h>
#define CREATE_TRACE_POINTS
#include <trace/events/signal.h>
@@ -4773,6 +4774,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 a57de67f032f..354a2d294f52 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1783,11 +1783,6 @@ static struct ctl_table kern_table[] = {
.proc_handler = sysctl_max_threads,
},
{
- .procname = "usermodehelper",
- .mode = 0555,
- .child = usermodehelper_table,
- },
- {
.procname = "overflowuid",
.data = &overflowuid,
.maxlen = sizeof(int),
@@ -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
@@ -2264,34 +2252,10 @@ 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
- { }
-};
-
-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("vm", vm_table);
return 0;
}
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 */
diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c
index e2a816d85ea2..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;
@@ -126,9 +133,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,24 +158,96 @@ 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 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;
}
+
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 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;
+
+ err = test_sysctl_setup_node_tests();
+ if (err)
+ goto out;
+
+ err = test_sysctl_run_unregister_nested();
+ if (err)
+ goto out;
+
+ err = test_sysctl_run_register_mount_point();
+
+out:
+ return err;
+}
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/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]) {
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);
diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh
index bfc54b422f25..444b2befda82 100755
--- a/tools/testing/selftests/sysctl/sysctl.sh
+++ b/tools/testing/selftests/sysctl/sysctl.sh
@@ -14,23 +14,27 @@ 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="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"
+ALL_TESTS="$ALL_TESTS 0010:1:1:mnt/mnt_error:0"
function allow_user_defaults()
{
@@ -613,7 +617,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
@@ -730,7 +733,7 @@ sysctl_test_0005()
sysctl_test_0006()
{
- TARGET="${SYSCTL}/bitmap_0001"
+ TARGET="${SYSCTL}/$(get_test_target 0006)"
reset_vals
ORIG=""
run_bitmaptest
@@ -738,7 +741,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 +781,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
@@ -797,6 +800,34 @@ 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
+}
+
+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:"
@@ -813,6 +844,8 @@ 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"
+ echo "0010 x $(get_test_count 0010) - tests sysct mount point"
}
usage()
@@ -857,38 +890,65 @@ 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}'
}
+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
@@ -923,18 +983,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