Skip to content

Commit

Permalink
[PATCH] sysctl: factor out sysctl_head_next from do_sysctl
Browse files Browse the repository at this point in the history
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: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
ebiederm authored and Linus Torvalds committed Feb 14, 2007
1 parent 0b4d414 commit 805b5d5
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 17 deletions.
4 changes: 4 additions & 0 deletions include/linux/sysctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
60 changes: 43 additions & 17 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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;
}

Expand Down

0 comments on commit 805b5d5

Please sign in to comment.