Commit 6a8cbd92 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 4e3c09e9 2f2665c1
Loading
Loading
Loading
Loading
+100 −85
Original line number Diff line number Diff line
@@ -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,16 +257,15 @@ 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",
@@ -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[i].extra1 = port;

	t->vars[0].data = &port->spintime;
	t->vars[5].child = t->device_dir;
	for (i = 0; i < 5; i++) {
		t->vars[i].extra1 = port;
		t->vars[5 + i].extra2 = &port->probe_info[i];
	}

	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();
+1 −1
Original line number Diff line number Diff line
@@ -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;
+19 −225
Original line number Diff line number Diff line
@@ -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 @@ static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
	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 @@ struct ctl_table_header *__register_sysctl_table(
	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);
+2 −3
Original line number Diff line number Diff line
@@ -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);
+0 −3
Original line number Diff line number Diff line
@@ -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
 */
Loading