aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-23 21:08:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-23 21:08:58 -0400
commitf1d38e423a697b7aa06e12d3ca4753bcc1aa3531 (patch)
tree1cbfd86070f724d5ffe53146d4c67edf14cccf98
parentdae430c6f6e5d0b98c238c340a41a39e221e8940 (diff)
parent4e474a00d7ff746ed177ddae14fa8b2d4bad7a00 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/sysctl
Pull sysctl updates from Eric Biederman: - Rewrite of sysctl for speed and clarity. Insert/remove/Lookup in sysctl are all now O(NlogN) operations, and are no longer bottlenecks in the process of adding and removing network devices. sysctl is now focused on being a filesystem instead of system call and the code can all be found in fs/proc/proc_sysctl.c. Hopefully this means the code is now approachable. Much thanks is owed to Lucian Grinjincu for keeping at this until something was found that was usable. - The recent proc_sys_poll oops found by the fuzzer during hibernation is fixed. * git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/sysctl: (36 commits) sysctl: protect poll() in entries that may go away sysctl: Don't call sysctl_follow_link unless we are a link. sysctl: Comments to make the code clearer. sysctl: Correct error return from get_subdir sysctl: An easier to read version of find_subdir sysctl: fix memset parameters in setup_sysctl_set() sysctl: remove an unused variable sysctl: Add register_sysctl for normal sysctl users sysctl: Index sysctl directories with rbtrees. sysctl: Make the header lists per directory. sysctl: Move sysctl_check_dups into insert_header sysctl: Modify __register_sysctl_paths to take a set instead of a root and an nsproxy sysctl: Replace root_list with links between sysctl_table_sets. sysctl: Add sysctl_print_dir and use it in get_subdir sysctl: Stop requiring explicit management of sysctl directories sysctl: Add a root pointer to ctl_table_set sysctl: Rewrite proc_sys_readdir in terms of first_entry and next_entry sysctl: Rewrite proc_sys_lookup introducing find_entry and lookup_entry. sysctl: Normalize the root_table data structure. sysctl: Factor out insert_header and erase_header ...
-rw-r--r--fs/proc/internal.h3
-rw-r--r--fs/proc/proc_sysctl.c1274
-rw-r--r--include/linux/sysctl.h106
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/sysctl.c501
-rw-r--r--kernel/sysctl_check.c160
-rw-r--r--lib/Kconfig.debug8
-rw-r--r--net/sysctl_net.c24
8 files changed, 1280 insertions, 797 deletions
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index c44efe19798f..5f79bb8b4c60 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -10,12 +10,15 @@
10 */ 10 */
11 11
12#include <linux/proc_fs.h> 12#include <linux/proc_fs.h>
13struct ctl_table_header;
13 14
14extern struct proc_dir_entry proc_root; 15extern struct proc_dir_entry proc_root;
15#ifdef CONFIG_PROC_SYSCTL 16#ifdef CONFIG_PROC_SYSCTL
16extern int proc_sys_init(void); 17extern int proc_sys_init(void);
18extern void sysctl_head_put(struct ctl_table_header *head);
17#else 19#else
18static inline void proc_sys_init(void) { } 20static inline void proc_sys_init(void) { }
21static inline void sysctl_head_put(struct ctl_table_header *head) { }
19#endif 22#endif
20#ifdef CONFIG_NET 23#ifdef CONFIG_NET
21extern int proc_net_init(void); 24extern int proc_net_init(void);
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 67bbf6e4e197..21d836f40292 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -9,6 +9,7 @@
9#include <linux/sched.h> 9#include <linux/sched.h>
10#include <linux/namei.h> 10#include <linux/namei.h>
11#include <linux/mm.h> 11#include <linux/mm.h>
12#include <linux/module.h>
12#include "internal.h" 13#include "internal.h"
13 14
14static const struct dentry_operations proc_sys_dentry_operations; 15static const struct dentry_operations proc_sys_dentry_operations;
@@ -26,6 +27,371 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll)
26 wake_up_interruptible(&poll->wait); 27 wake_up_interruptible(&poll->wait);
27} 28}
28 29
30static struct ctl_table root_table[] = {
31 {
32 .procname = "",
33 .mode = S_IFDIR|S_IRUGO|S_IXUGO,
34 },
35 { }
36};
37static struct ctl_table_root sysctl_table_root = {
38 .default_set.dir.header = {
39 {{.count = 1,
40 .nreg = 1,
41 .ctl_table = root_table }},
42 .ctl_table_arg = root_table,
43 .root = &sysctl_table_root,
44 .set = &sysctl_table_root.default_set,
45 },
46};
47
48static DEFINE_SPINLOCK(sysctl_lock);
49
50static void drop_sysctl_table(struct ctl_table_header *header);
51static int sysctl_follow_link(struct ctl_table_header **phead,
52 struct ctl_table **pentry, struct nsproxy *namespaces);
53static int insert_links(struct ctl_table_header *head);
54static void put_links(struct ctl_table_header *header);
55
56static void sysctl_print_dir(struct ctl_dir *dir)
57{
58 if (dir->header.parent)
59 sysctl_print_dir(dir->header.parent);
60 printk(KERN_CONT "%s/", dir->header.ctl_table[0].procname);
61}
62
63static int namecmp(const char *name1, int len1, const char *name2, int len2)
64{
65 int minlen;
66 int cmp;
67
68 minlen = len1;
69 if (minlen > len2)
70 minlen = len2;
71
72 cmp = memcmp(name1, name2, minlen);
73 if (cmp == 0)
74 cmp = len1 - len2;
75 return cmp;
76}
77
78/* Called under sysctl_lock */
79static struct ctl_table *find_entry(struct ctl_table_header **phead,
80 struct ctl_dir *dir, const char *name, int namelen)
81{
82 struct ctl_table_header *head;
83 struct ctl_table *entry;
84 struct rb_node *node = dir->root.rb_node;
85
86 while (node)
87 {
88 struct ctl_node *ctl_node;
89 const char *procname;
90 int cmp;
91
92 ctl_node = rb_entry(node, struct ctl_node, node);
93 head = ctl_node->header;
94 entry = &head->ctl_table[ctl_node - head->node];
95 procname = entry->procname;
96
97 cmp = namecmp(name, namelen, procname, strlen(procname));
98 if (cmp < 0)
99 node = node->rb_left;
100 else if (cmp > 0)
101 node = node->rb_right;
102 else {
103 *phead = head;
104 return entry;
105 }
106 }
107 return NULL;
108}
109
110static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry)
111{
112 struct rb_node *node = &head->node[entry - head->ctl_table].node;
113 struct rb_node **p = &head->parent->root.rb_node;
114 struct rb_node *parent = NULL;
115 const char *name = entry->procname;
116 int namelen = strlen(name);
117
118 while (*p) {
119 struct ctl_table_header *parent_head;
120 struct ctl_table *parent_entry;
121 struct ctl_node *parent_node;
122 const char *parent_name;
123 int cmp;
124
125 parent = *p;
126 parent_node = rb_entry(parent, struct ctl_node, node);
127 parent_head = parent_node->header;
128 parent_entry = &parent_head->ctl_table[parent_node - parent_head->node];
129 parent_name = parent_entry->procname;
130
131 cmp = namecmp(name, namelen, parent_name, strlen(parent_name));
132 if (cmp < 0)
133 p = &(*p)->rb_left;
134 else if (cmp > 0)
135 p = &(*p)->rb_right;
136 else {
137 printk(KERN_ERR "sysctl duplicate entry: ");
138 sysctl_print_dir(head->parent);
139 printk(KERN_CONT "/%s\n", entry->procname);
140 return -EEXIST;
141 }
142 }
143
144 rb_link_node(node, parent, p);
145 return 0;
146}
147
148static void erase_entry(struct ctl_table_header *head, struct ctl_table *entry)
149{
150 struct rb_node *node = &head->node[entry - head->ctl_table].node;
151
152 rb_erase(node, &head->parent->root);
153}
154
155static void init_header(struct ctl_table_header *head,
156 struct ctl_table_root *root, struct ctl_table_set *set,
157 struct ctl_node *node, struct ctl_table *table)
158{
159 head->ctl_table = table;
160 head->ctl_table_arg = table;
161 head->used = 0;
162 head->count = 1;
163 head->nreg = 1;
164 head->unregistering = NULL;
165 head->root = root;
166 head->set = set;
167 head->parent = NULL;
168 head->node = node;
169 if (node) {
170 struct ctl_table *entry;
171 for (entry = table; entry->procname; entry++, node++) {
172 rb_init_node(&node->node);
173 node->header = head;