Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 805b5d5e authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Linus Torvalds
Browse files

[PATCH] sysctl: factor out sysctl_head_next from do_sysctl



The current logic to walk through the list of sysctl table headers is slightly
painful and implement in a way it cannot be used by code outside sysctl.c

I am in the process of implementing a version of the sysctl proc support that
instead of using the proc generic non-caching monster, just uses the existing
sysctl data structure as backing store for building the dcache entries and for
doing directory reads.  To use the existing data structures however I need a
way to get at them.

[akpm@osdl.org: warning fix]
Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 0b4d4147
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -924,6 +924,10 @@ enum
#ifdef __KERNEL__
#include <linux/list.h>

/* For the /proc/sys support */
extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev);
extern void sysctl_head_finish(struct ctl_table_header *prev);

extern void sysctl_init(void);

typedef struct ctl_table ctl_table;
+43 −17
Original line number Diff line number Diff line
@@ -1070,6 +1070,42 @@ static void start_unregistering(struct ctl_table_header *p)
	list_del_init(&p->ctl_entry);
}

void sysctl_head_finish(struct ctl_table_header *head)
{
	if (!head)
		return;
	spin_lock(&sysctl_lock);
	unuse_table(head);
	spin_unlock(&sysctl_lock);
}

struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev)
{
	struct ctl_table_header *head;
	struct list_head *tmp;
	spin_lock(&sysctl_lock);
	if (prev) {
		tmp = &prev->ctl_entry;
		unuse_table(prev);
		goto next;
	}
	tmp = &root_table_header.ctl_entry;
	for (;;) {
		head = list_entry(tmp, struct ctl_table_header, ctl_entry);

		if (!use_table(head))
			goto next;
		spin_unlock(&sysctl_lock);
		return head;
	next:
		tmp = tmp->next;
		if (tmp == &root_table_header.ctl_entry)
			break;
	}
	spin_unlock(&sysctl_lock);
	return NULL;
}

void __init sysctl_init(void)
{
#ifdef CONFIG_PROC_SYSCTL
@@ -1081,7 +1117,7 @@ void __init sysctl_init(void)
int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp,
	       void __user *newval, size_t newlen)
{
	struct list_head *tmp;
	struct ctl_table_header *head;
	int error = -ENOTDIR;

	if (nlen <= 0 || nlen >= CTL_MAXNAME)
@@ -1091,26 +1127,16 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol
		if (!oldlenp || get_user(old_len, oldlenp))
			return -EFAULT;
	}
	spin_lock(&sysctl_lock);
	tmp = &root_table_header.ctl_entry;
	do {
		struct ctl_table_header *head =
			list_entry(tmp, struct ctl_table_header, ctl_entry);

		if (!use_table(head))
			continue;

		spin_unlock(&sysctl_lock);

	for (head = sysctl_head_next(NULL); head;
			head = sysctl_head_next(head)) {
		error = parse_table(name, nlen, oldval, oldlenp, 
					newval, newlen, head->ctl_table);

		spin_lock(&sysctl_lock);
		unuse_table(head);
		if (error != -ENOTDIR)
		if (error != -ENOTDIR) {
			sysctl_head_finish(head);
			break;
	} while ((tmp = tmp->next) != &root_table_header.ctl_entry);
	spin_unlock(&sysctl_lock);
		}
	}
	return error;
}