diff options
| author | Eric W. Biederman <ebiederm@xmission.com> | 2007-02-14 03:34:11 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-14 11:09:59 -0500 |
| commit | 805b5d5e063e7fde5e2eb724e3f4cb18e47cab19 (patch) | |
| tree | 920f66deb8d6286ee819ac0402d1771543a12a88 | |
| parent | 0b4d414714f0d2f922d39424b0c5c82ad900a381 (diff) | |
[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 <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -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 396b8d984c5d..72ba58b362d8 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
| @@ -924,6 +924,10 @@ enum | |||
| 924 | #ifdef __KERNEL__ | 924 | #ifdef __KERNEL__ |
| 925 | #include <linux/list.h> | 925 | #include <linux/list.h> |
| 926 | 926 | ||
| 927 | /* For the /proc/sys support */ | ||
| 928 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); | ||
| 929 | extern void sysctl_head_finish(struct ctl_table_header *prev); | ||
| 930 | |||
| 927 | extern void sysctl_init(void); | 931 | extern void sysctl_init(void); |
| 928 | 932 | ||
| 929 | typedef struct ctl_table ctl_table; | 933 | typedef struct ctl_table ctl_table; |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6ccb6cc19e28..c3e2ac9cb5fb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -1070,6 +1070,42 @@ static void start_unregistering(struct ctl_table_header *p) | |||
| 1070 | list_del_init(&p->ctl_entry); | 1070 | list_del_init(&p->ctl_entry); |
| 1071 | } | 1071 | } |
| 1072 | 1072 | ||
| 1073 | void sysctl_head_finish(struct ctl_table_header *head) | ||
| 1074 | { | ||
| 1075 | if (!head) | ||
| 1076 | return; | ||
| 1077 | spin_lock(&sysctl_lock); | ||
| 1078 | unuse_table(head); | ||
| 1079 | spin_unlock(&sysctl_lock); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | ||
| 1083 | { | ||
| 1084 | struct ctl_table_header *head; | ||
| 1085 | struct list_head *tmp; | ||
| 1086 | spin_lock(&sysctl_lock); | ||
| 1087 | if (prev) { | ||
| 1088 | tmp = &prev->ctl_entry; | ||
| 1089 | unuse_table(prev); | ||
| 1090 | goto next; | ||
| 1091 | } | ||
| 1092 | tmp = &root_table_header.ctl_entry; | ||
| 1093 | for (;;) { | ||
| 1094 | head = list_entry(tmp, struct ctl_table_header, ctl_entry); | ||
| 1095 | |||
| 1096 | if (!use_table(head)) | ||
| 1097 | goto next; | ||
| 1098 | spin_unlock(&sysctl_lock); | ||
| 1099 | return head; | ||
| 1100 | next: | ||
| 1101 | tmp = tmp->next; | ||
| 1102 | if (tmp == &root_table_header.ctl_entry) | ||
| 1103 | break; | ||
| 1104 | } | ||
| 1105 | spin_unlock(&sysctl_lock); | ||
| 1106 | return NULL; | ||
| 1107 | } | ||
| 1108 | |||
| 1073 | void __init sysctl_init(void) | 1109 | void __init sysctl_init(void) |
| 1074 | { | 1110 | { |
| 1075 | #ifdef CONFIG_PROC_SYSCTL | 1111 | #ifdef CONFIG_PROC_SYSCTL |
| @@ -1081,7 +1117,7 @@ void __init sysctl_init(void) | |||
| 1081 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, | 1117 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, |
| 1082 | void __user *newval, size_t newlen) | 1118 | void __user *newval, size_t newlen) |
| 1083 | { | 1119 | { |
| 1084 | struct list_head *tmp; | 1120 | struct ctl_table_header *head; |
| 1085 | int error = -ENOTDIR; | 1121 | int error = -ENOTDIR; |
| 1086 | 1122 | ||
| 1087 | if (nlen <= 0 || nlen >= CTL_MAXNAME) | 1123 | 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 | |||
| 1091 | if (!oldlenp || get_user(old_len, oldlenp)) | 1127 | if (!oldlenp || get_user(old_len, oldlenp)) |
| 1092 | return -EFAULT; | 1128 | return -EFAULT; |
| 1093 | } | 1129 | } |
| 1094 | spin_lock(&sysctl_lock); | ||
| 1095 | tmp = &root_table_header.ctl_entry; | ||
| 1096 | do { | ||
| 1097 | struct ctl_table_header *head = | ||
| 1098 | list_entry(tmp, struct ctl_table_header, ctl_entry); | ||
| 1099 | |||
| 1100 | if (!use_table(head)) | ||
| 1101 | continue; | ||
| 1102 | |||
| 1103 | spin_unlock(&sysctl_lock); | ||
| 1104 | 1130 | ||
| 1131 | for (head = sysctl_head_next(NULL); head; | ||
| 1132 | head = sysctl_head_next(head)) { | ||
| 1105 | error = parse_table(name, nlen, oldval, oldlenp, | 1133 | error = parse_table(name, nlen, oldval, oldlenp, |
| 1106 | newval, newlen, head->ctl_table); | 1134 | newval, newlen, head->ctl_table); |
| 1107 | 1135 | if (error != -ENOTDIR) { | |
| 1108 | spin_lock(&sysctl_lock); | 1136 | sysctl_head_finish(head); |
| 1109 | unuse_table(head); | ||
| 1110 | if (error != -ENOTDIR) | ||
| 1111 | break; | 1137 | break; |
| 1112 | } while ((tmp = tmp->next) != &root_table_header.ctl_entry); | 1138 | } |
| 1113 | spin_unlock(&sysctl_lock); | 1139 | } |
| 1114 | return error; | 1140 | return error; |
| 1115 | } | 1141 | } |
| 1116 | 1142 | ||
