diff options
| -rw-r--r-- | include/linux/sysctl.h | 4 | ||||
| -rw-r--r-- | 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 <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; 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;  }  |