From 805b5d5e063e7fde5e2eb724e3f4cb18e47cab19 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 14 Feb 2007 00:34:11 -0800 Subject: [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: Eric W. Biederman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sysctl.h | 4 ++++ kernel/sysctl.c | 60 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 396b8d984c5..72ba58b362d 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -924,6 +924,10 @@ enum #ifdef __KERNEL__ #include +/* 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; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6ccb6cc19e2..c3e2ac9cb5f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -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; } -- cgit v1.2.3-70-g09d2