diff options
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r-- | kernel/sysctl.c | 60 |
1 files changed, 43 insertions, 17 deletions
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 | ||