aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2012-01-08 02:24:30 -0500
committerEric W. Biederman <ebiederm@xmission.com>2012-01-24 19:40:29 -0500
commit0e47c99d7fe25e0f3907d9f3401079169d904891 (patch)
treeb8d8e5b1d7d7ba00431d3e476f72644b374b9010
parent6980128fe1b834c92a85e556ca8198030f0d8d01 (diff)
sysctl: Replace root_list with links between sysctl_table_sets.
Piecing together directories by looking first in one directory tree, than in another directory tree and finally in a third directory tree makes it hard to verify that some directory entries are not multiply defined and makes it hard to create efficient implementations the sysctl filesystem. Replace the sysctl wide list of roots with autogenerated links from the core sysctl directory tree to the other sysctl directory trees. This simplifies sysctl directory reading and lookups as now only entries in a single sysctl directory tree need to be considered. Benchmark before: make-dummies 0 999 -> 0.44s rmmod dummy -> 0.065s make-dummies 0 9999 -> 1m36s rmmod dummy -> 0.4s Benchmark after: make-dummies 0 999 -> 0.63s rmmod dummy -> 0.12s make-dummies 0 9999 -> 2m35s rmmod dummy -> 18s The slowdown is caused by the lookups used in insert_headers and put_links to see if we need to add links or remove links. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r--fs/proc/proc_sysctl.c397
-rw-r--r--include/linux/sysctl.h3
2 files changed, 296 insertions, 104 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index a78556514a87..ec54a57c4690 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -32,26 +32,26 @@ static struct ctl_table root_table[] = {
32 }, 32 },
33 { } 33 { }
34}; 34};
35static struct ctl_table_root sysctl_table_root; 35static struct ctl_table_root sysctl_table_root = {
36static struct ctl_dir sysctl_root_dir = { 36 .default_set.list = LIST_HEAD_INIT(sysctl_table_root.default_set.dir.header.ctl_entry),
37 .header = { 37 .default_set.dir.header = {
38 {{.count = 1, 38 {{.count = 1,
39 .nreg = 1, 39 .nreg = 1,
40 .ctl_table = root_table, 40 .ctl_table = root_table,
41 .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, 41 .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}},
42 .ctl_table_arg = root_table,
42 .root = &sysctl_table_root, 43 .root = &sysctl_table_root,
43 .set = &sysctl_table_root.default_set, 44 .set = &sysctl_table_root.default_set,
44 }, 45 },
45}; 46};
46static struct ctl_table_root sysctl_table_root = {
47 .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list),
48 .default_set.list = LIST_HEAD_INIT(sysctl_root_dir.header.ctl_entry),
49 .default_set.root = &sysctl_table_root,
50};
51 47
52static DEFINE_SPINLOCK(sysctl_lock); 48static DEFINE_SPINLOCK(sysctl_lock);
53 49
54static void drop_sysctl_table(struct ctl_table_header *header); 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 55
56static void sysctl_print_dir(struct ctl_dir *dir) 56static void sysctl_print_dir(struct ctl_dir *dir)
57{ 57{
@@ -76,9 +76,9 @@ static int namecmp(const char *name1, int len1, const char *name2, int len2)
76} 76}
77 77
78static struct ctl_table *find_entry(struct ctl_table_header **phead, 78static struct ctl_table *find_entry(struct ctl_table_header **phead,
79 struct ctl_table_set *set, struct ctl_dir *dir, 79 struct ctl_dir *dir, const char *name, int namelen)
80 const char *name, int namelen)
81{ 80{
81 struct ctl_table_set *set = dir->header.set;
82 struct ctl_table_header *head; 82 struct ctl_table_header *head;
83 struct ctl_table *entry; 83 struct ctl_table *entry;
84 84
@@ -119,11 +119,21 @@ static void erase_header(struct ctl_table_header *head)
119 list_del_init(&head->ctl_entry); 119 list_del_init(&head->ctl_entry);
120} 120}
121 121
122static void insert_header(struct ctl_dir *dir, struct ctl_table_header *header) 122static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
123{ 123{
124 int err;
125
126 dir->header.nreg++;
124 header->parent = dir; 127 header->parent = dir;
125 header->parent->header.nreg++; 128 err = insert_links(header);
129 if (err)
130 goto fail_links;
126 list_add_tail(&header->ctl_entry, &header->set->list); 131 list_add_tail(&header->ctl_entry, &header->set->list);
132 return 0;
133fail_links:
134 header->parent = NULL;
135 drop_sysctl_table(&dir->header);
136 return err;
127} 137}
128 138
129/* called under sysctl_lock */ 139/* called under sysctl_lock */
@@ -212,72 +222,39 @@ lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces)
212 return set; 222 return set;
213} 223}
214 224
215static struct list_head *
216lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces)
217{
218 struct ctl_table_set *set = lookup_header_set(root, namespaces);
219 return &set->list;
220}
221
222static struct ctl_table *lookup_entry(struct ctl_table_header **phead, 225static struct ctl_table *lookup_entry(struct ctl_table_header **phead,
223 struct ctl_dir *dir, 226 struct ctl_dir *dir,
224 const char *name, int namelen) 227 const char *name, int namelen)
225{ 228{
226 struct ctl_table_header *head; 229 struct ctl_table_header *head;
227 struct ctl_table *entry; 230 struct ctl_table *entry;
228 struct ctl_table_root *root;
229 struct ctl_table_set *set;
230 231
231 spin_lock(&sysctl_lock); 232 spin_lock(&sysctl_lock);
232 root = &sysctl_table_root; 233 entry = find_entry(&head, dir, name, namelen);
233 do { 234 if (entry && use_table(head))
234 set = lookup_header_set(root, current->nsproxy); 235 *phead = head;
235 entry = find_entry(&head, set, dir, name, namelen); 236 else
236 if (entry && use_table(head)) 237 entry = NULL;
237 *phead = head;
238 else
239 entry = NULL;
240 root = list_entry(root->root_list.next,
241 struct ctl_table_root, root_list);
242 } while (!entry && root != &sysctl_table_root);
243 spin_unlock(&sysctl_lock); 238 spin_unlock(&sysctl_lock);
244 return entry; 239 return entry;
245} 240}
246 241
247static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir, 242static struct ctl_table_header *next_usable_entry(struct ctl_dir *dir,
248 struct ctl_table_root *root, struct list_head *tmp) 243 struct list_head *tmp)
249{ 244{
250 struct nsproxy *namespaces = current->nsproxy; 245 struct ctl_table_set *set = dir->header.set;
251 struct list_head *header_list;
252 struct ctl_table_header *head; 246 struct ctl_table_header *head;
253 247
254 goto next; 248 for (tmp = tmp->next; tmp != &set->list; tmp = tmp->next) {
255 for (;;) {
256 head = list_entry(tmp, struct ctl_table_header, ctl_entry); 249 head = list_entry(tmp, struct ctl_table_header, ctl_entry);
257 root = head->root;
258 250
259 if (head->parent != dir || 251 if (head->parent != dir ||
260 !head->ctl_table->procname || 252 !head->ctl_table->procname ||
261 !use_table(head)) 253 !use_table(head))
262 goto next;
263
264 return head;
265 next:
266 tmp = tmp->next;
267 header_list = lookup_header_list(root, namespaces);
268 if (tmp != header_list)
269 continue; 254 continue;
270 255
271 do { 256 return head;
272 root = list_entry(root->root_list.next,
273 struct ctl_table_root, root_list);
274 if (root == &sysctl_table_root)
275 goto out;
276 header_list = lookup_header_list(root, namespaces);
277 } while (list_empty(header_list));
278 tmp = header_list->next;
279 } 257 }
280out:
281 return NULL; 258 return NULL;
282} 259}
283 260
@@ -288,8 +265,7 @@ static void first_entry(struct ctl_dir *dir,
288 struct ctl_table *entry = NULL; 265 struct ctl_table *entry = NULL;
289 266
290 spin_lock(&sysctl_lock); 267 spin_lock(&sysctl_lock);
291 head = next_usable_entry(dir, &sysctl_table_root, 268 head = next_usable_entry(dir, &dir->header.set->list);
292 &sysctl_table_root.default_set.list);
293 spin_unlock(&sysctl_lock); 269 spin_unlock(&sysctl_lock);
294 if (head) 270 if (head)
295 entry = head->ctl_table; 271 entry = head->ctl_table;
@@ -306,7 +282,7 @@ static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentr
306 if (!entry->procname) { 282 if (!entry->procname) {
307 spin_lock(&sysctl_lock); 283 spin_lock(&sysctl_lock);
308 unuse_table(head); 284 unuse_table(head);
309 head = next_usable_entry(head->parent, head->root, &head->ctl_entry); 285 head = next_usable_entry(head->parent, &head->ctl_entry);
310 spin_unlock(&sysctl_lock); 286 spin_unlock(&sysctl_lock);
311 if (head) 287 if (head)
312 entry = head->ctl_table; 288 entry = head->ctl_table;
@@ -317,9 +293,6 @@ static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentr
317 293
318void register_sysctl_root(struct ctl_table_root *root) 294void register_sysctl_root(struct ctl_table_root *root)
319{ 295{
320 spin_lock(&sysctl_lock);
321 list_add_tail(&root->root_list, &sysctl_table_root.root_list);
322 spin_unlock(&sysctl_lock);
323} 296}
324 297
325/* 298/*
@@ -386,7 +359,7 @@ static struct ctl_table_header *grab_header(struct inode *inode)
386{ 359{
387 struct ctl_table_header *head = PROC_I(inode)->sysctl; 360 struct ctl_table_header *head = PROC_I(inode)->sysctl;
388 if (!head) 361 if (!head)
389 head = &sysctl_root_dir.header; 362 head = &sysctl_table_root.default_set.dir.header;
390 return sysctl_head_grab(head); 363 return sysctl_head_grab(head);
391} 364}
392 365
@@ -400,6 +373,7 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
400 struct inode *inode; 373 struct inode *inode;
401 struct dentry *err = ERR_PTR(-ENOENT); 374 struct dentry *err = ERR_PTR(-ENOENT);
402 struct ctl_dir *ctl_dir; 375 struct ctl_dir *ctl_dir;
376 int ret;
403 377
404 if (IS_ERR(head)) 378 if (IS_ERR(head))
405 return ERR_CAST(head); 379 return ERR_CAST(head);
@@ -410,6 +384,11 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
410 if (!p) 384 if (!p)
411 goto out; 385 goto out;
412 386
387 ret = sysctl_follow_link(&h, &p, current->nsproxy);
388 err = ERR_PTR(ret);
389 if (ret)
390 goto out;
391
413 err = ERR_PTR(-ENOMEM); 392 err = ERR_PTR(-ENOMEM);
414 inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p); 393 inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
415 if (h) 394 if (h)
@@ -547,6 +526,25 @@ static int proc_sys_fill_cache(struct file *filp, void *dirent,
547 return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); 526 return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
548} 527}
549 528
529static int proc_sys_link_fill_cache(struct file *filp, void *dirent,
530 filldir_t filldir,
531 struct ctl_table_header *head,
532 struct ctl_table *table)
533{
534 int err, ret = 0;
535 head = sysctl_head_grab(head);
536
537 /* It is not an error if we can not follow the link ignore it */
538 err = sysctl_follow_link(&head, &table, current->nsproxy);
539 if (err)
540 goto out;
541
542 ret = proc_sys_fill_cache(filp, dirent, filldir, head, table);
543out:
544 sysctl_head_finish(head);
545 return ret;
546}
547
550static int scan(struct ctl_table_header *head, ctl_table *table, 548static int scan(struct ctl_table_header *head, ctl_table *table,
551 unsigned long *pos, struct file *file, 549 unsigned long *pos, struct file *file,
552 void *dirent, filldir_t filldir) 550 void *dirent, filldir_t filldir)
@@ -556,7 +554,10 @@ static int scan(struct ctl_table_header *head, ctl_table *table,
556 if ((*pos)++ < file->f_pos) 554 if ((*pos)++ < file->f_pos)
557 return 0; 555 return 0;
558 556
559 res = proc_sys_fill_cache(file, dirent, filldir, head, table); 557 if (unlikely(S_ISLNK(table->mode)))
558 res = proc_sys_link_fill_cache(file, dirent, filldir, head, table);
559 else
560 res = proc_sys_fill_cache(file, dirent, filldir, head, table);
560 561
561 if (res == 0) 562 if (res == 0)
562 file->f_pos = *pos; 563 file->f_pos = *pos;
@@ -757,13 +758,13 @@ static const struct dentry_operations proc_sys_dentry_operations = {
757 .d_compare = proc_sys_compare, 758 .d_compare = proc_sys_compare,
758}; 759};
759 760
760static struct ctl_dir *find_subdir(struct ctl_table_set *set, struct ctl_dir *dir, 761static struct ctl_dir *find_subdir(struct ctl_dir *dir,
761 const char *name, int namelen) 762 const char *name, int namelen)
762{ 763{
763 struct ctl_table_header *head; 764 struct ctl_table_header *head;
764 struct ctl_table *entry; 765 struct ctl_table *entry;
765 766
766 entry = find_entry(&head, set, dir, name, namelen); 767 entry = find_entry(&head, dir, name, namelen);
767 if (!entry) 768 if (!entry)
768 return ERR_PTR(-ENOENT); 769 return ERR_PTR(-ENOENT);
769 if (S_ISDIR(entry->mode)) 770 if (S_ISDIR(entry->mode))
@@ -772,7 +773,7 @@ static struct ctl_dir *find_subdir(struct ctl_table_set *set, struct ctl_dir *di
772} 773}
773 774
774static struct ctl_dir *new_dir(struct ctl_table_set *set, 775static struct ctl_dir *new_dir(struct ctl_table_set *set,
775 const char *name, int namelen) 776 const char *name, int namelen)
776{ 777{
777 struct ctl_table *table; 778 struct ctl_table *table;
778 struct ctl_dir *new; 779 struct ctl_dir *new;
@@ -789,22 +790,19 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set,
789 new_name[namelen] = '\0'; 790 new_name[namelen] = '\0';
790 table[0].procname = new_name; 791 table[0].procname = new_name;
791 table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; 792 table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO;
792 init_header(&new->header, set->root, set, table); 793 init_header(&new->header, set->dir.header.root, set, table);
793 794
794 return new; 795 return new;
795} 796}
796 797
797static struct ctl_dir *get_subdir(struct ctl_table_set *set, 798static struct ctl_dir *get_subdir(struct ctl_dir *dir,
798 struct ctl_dir *dir, const char *name, int namelen) 799 const char *name, int namelen)
799{ 800{
801 struct ctl_table_set *set = dir->header.set;
800 struct ctl_dir *subdir, *new = NULL; 802 struct ctl_dir *subdir, *new = NULL;
801 803
802 spin_lock(&sysctl_lock); 804 spin_lock(&sysctl_lock);
803 subdir = find_subdir(dir->header.set, dir, name, namelen); 805 subdir = find_subdir(dir, name, namelen);
804 if (!IS_ERR(subdir))
805 goto found;
806 if ((PTR_ERR(subdir) == -ENOENT) && set != dir->header.set)
807 subdir = find_subdir(set, dir, name, namelen);
808 if (!IS_ERR(subdir)) 806 if (!IS_ERR(subdir))
809 goto found; 807 goto found;
810 if (PTR_ERR(subdir) != -ENOENT) 808 if (PTR_ERR(subdir) != -ENOENT)
@@ -817,13 +815,14 @@ static struct ctl_dir *get_subdir(struct ctl_table_set *set,
817 if (!new) 815 if (!new)
818 goto failed; 816 goto failed;
819 817
820 subdir = find_subdir(set, dir, name, namelen); 818 subdir = find_subdir(dir, name, namelen);
821 if (!IS_ERR(subdir)) 819 if (!IS_ERR(subdir))
822 goto found; 820 goto found;
823 if (PTR_ERR(subdir) != -ENOENT) 821 if (PTR_ERR(subdir) != -ENOENT)
824 goto failed; 822 goto failed;
825 823
826 insert_header(dir, &new->header); 824 if (insert_header(dir, &new->header))
825 goto failed;
827 subdir = new; 826 subdir = new;
828found: 827found:
829 subdir->header.nreg++; 828 subdir->header.nreg++;
@@ -841,6 +840,57 @@ failed:
841 return subdir; 840 return subdir;
842} 841}
843 842
843static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir)
844{
845 struct ctl_dir *parent;
846 const char *procname;
847 if (!dir->header.parent)
848 return &set->dir;
849 parent = xlate_dir(set, dir->header.parent);
850 if (IS_ERR(parent))
851 return parent;
852 procname = dir->header.ctl_table[0].procname;
853 return find_subdir(parent, procname, strlen(procname));
854}
855
856static int sysctl_follow_link(struct ctl_table_header **phead,
857 struct ctl_table **pentry, struct nsproxy *namespaces)
858{
859 struct ctl_table_header *head;
860 struct ctl_table_root *root;
861 struct ctl_table_set *set;
862 struct ctl_table *entry;
863 struct ctl_dir *dir;
864 int ret;
865
866 /* Get out quickly if not a link */
867 if (!S_ISLNK((*pentry)->mode))
868 return 0;
869
870 ret = 0;
871 spin_lock(&sysctl_lock);
872 root = (*pentry)->data;
873 set = lookup_header_set(root, namespaces);
874 dir = xlate_dir(set, (*phead)->parent);
875 if (IS_ERR(dir))
876 ret = PTR_ERR(dir);
877 else {
878 const char *procname = (*pentry)->procname;
879 head = NULL;
880 entry = find_entry(&head, dir, procname, strlen(procname));
881 ret = -ENOENT;
882 if (entry && use_table(head)) {
883 unuse_table(*phead);
884 *phead = head;
885 *pentry = entry;
886 ret = 0;
887 }
888 }
889
890 spin_unlock(&sysctl_lock);
891 return ret;
892}
893
844static int sysctl_check_table_dups(const char *path, struct ctl_table *old, 894static int sysctl_check_table_dups(const char *path, struct ctl_table *old,
845 struct ctl_table *table) 895 struct ctl_table *table)
846{ 896{
@@ -859,30 +909,21 @@ static int sysctl_check_table_dups(const char *path, struct ctl_table *old,
859 return error; 909 return error;
860} 910}
861 911
862static int sysctl_check_dups(struct nsproxy *namespaces, 912static int sysctl_check_dups(struct ctl_dir *dir,
863 struct ctl_dir *dir,
864 const char *path, struct ctl_table *table) 913 const char *path, struct ctl_table *table)
865{ 914{
866 struct ctl_table_root *root;
867 struct ctl_table_set *set; 915 struct ctl_table_set *set;
868 struct ctl_table_header *head; 916 struct ctl_table_header *head;
869 int error = 0; 917 int error = 0;
870 918
871 root = &sysctl_table_root; 919 set = dir->header.set;
872 do { 920 list_for_each_entry(head, &set->list, ctl_entry) {
873 set = lookup_header_set(root, namespaces); 921 if (head->unregistering)
874 922 continue;
875 list_for_each_entry(head, &set->list, ctl_entry) { 923 if (head->parent != dir)
876 if (head->unregistering) 924 continue;
877 continue; 925 error = sysctl_check_table_dups(path, head->ctl_table, table);
878 if (head->parent != dir) 926 }
879 continue;
880 error = sysctl_check_table_dups(path, head->ctl_table,
881 table);
882 }
883 root = list_entry(root->root_list.next,
884 struct ctl_table_root, root_list);
885 } while (root != &sysctl_table_root);
886 return error; 927 return error;
887} 928}
888 929
@@ -932,6 +973,115 @@ static int sysctl_check_table(const char *path, struct ctl_table *table)
932 return err; 973 return err;
933} 974}
934 975
976static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table *table,
977 struct ctl_table_root *link_root)
978{
979 struct ctl_table *link_table, *entry, *link;
980 struct ctl_table_header *links;
981 char *link_name;
982 int nr_entries, name_bytes;
983
984 name_bytes = 0;
985 nr_entries = 0;
986 for (entry = table; entry->procname; entry++) {
987 nr_entries++;
988 name_bytes += strlen(entry->procname) + 1;
989 }
990
991 links = kzalloc(sizeof(struct ctl_table_header) +
992 sizeof(struct ctl_table)*(nr_entries + 1) +
993 name_bytes,
994 GFP_KERNEL);
995
996 if (!links)
997 return NULL;
998
999 link_table = (struct ctl_table *)(links + 1);
1000 link_name = (char *)&link_table[nr_entries + 1];
1001
1002 for (link = link_table, entry = table; entry->procname; link++, entry++) {
1003 int len = strlen(entry->procname) + 1;
1004 memcpy(link_name, entry->procname, len);
1005 link->procname = link_name;
1006 link->mode = S_IFLNK|S_IRWXUGO;
1007 link->data = link_root;
1008 link_name += len;
1009 }
1010 init_header(links, dir->header.root, dir->header.set, link_table);
1011 links->nreg = nr_entries;
1012
1013 return links;
1014}
1015
1016static bool get_links(struct ctl_dir *dir,
1017 struct ctl_table *table, struct ctl_table_root *link_root)
1018{
1019 struct ctl_table_header *head;
1020 struct ctl_table *entry, *link;
1021
1022 /* Are there links available for every entry in table? */
1023 for (entry = table; entry->procname; entry++) {
1024 const char *procname = entry->procname;
1025 link = find_entry(&head, dir, procname, strlen(procname));
1026 if (!link)
1027 return false;
1028 if (S_ISDIR(link->mode) && S_ISDIR(entry->mode))
1029 continue;
1030 if (S_ISLNK(link->mode) && (link->data == link_root))
1031 continue;
1032 return false;
1033 }
1034
1035 /* The checks passed. Increase the registration count on the links */
1036 for (entry = table; entry->procname; entry++) {
1037 const char *procname = entry->procname;
1038 link = find_entry(&head, dir, procname, strlen(procname));
1039 head->nreg++;
1040 }
1041 return true;
1042}
1043
1044static int insert_links(struct ctl_table_header *head)
1045{
1046 struct ctl_table_set *root_set = &sysctl_table_root.default_set;
1047 struct ctl_dir *core_parent = NULL;
1048 struct ctl_table_header *links;
1049 int err;
1050
1051 if (head->set == root_set)
1052 return 0;
1053
1054 core_parent = xlate_dir(root_set, head->parent);
1055 if (IS_ERR(core_parent))
1056 return 0;
1057
1058 if (get_links(core_parent, head->ctl_table, head->root))
1059 return 0;
1060
1061 core_parent->header.nreg++;
1062 spin_unlock(&sysctl_lock);
1063
1064 links = new_links(core_parent, head->ctl_table, head->root);
1065
1066 spin_lock(&sysctl_lock);
1067 err = -ENOMEM;
1068 if (!links)
1069 goto out;
1070
1071 err = 0;
1072 if (get_links(core_parent, head->ctl_table, head->root)) {
1073 kfree(links);
1074 goto out;
1075 }
1076
1077 err = insert_header(core_parent, links);
1078 if (err)
1079 kfree(links);
1080out:
1081 drop_sysctl_table(&core_parent->header);
1082 return err;
1083}
1084
935/** 1085/**
936 * __register_sysctl_table - register a leaf sysctl table 1086 * __register_sysctl_table - register a leaf sysctl table
937 * @root: List of sysctl headers to register on 1087 * @root: List of sysctl headers to register on
@@ -980,6 +1130,7 @@ struct ctl_table_header *__register_sysctl_table(
980 struct nsproxy *namespaces, 1130 struct nsproxy *namespaces,
981 const char *path, struct ctl_table *table) 1131 const char *path, struct ctl_table *table)
982{ 1132{
1133 struct ctl_table_header *links = NULL;
983 struct ctl_table_header *header; 1134 struct ctl_table_header *header;
984 const char *name, *nextname; 1135 const char *name, *nextname;
985 struct ctl_table_set *set; 1136 struct ctl_table_set *set;
@@ -995,7 +1146,7 @@ struct ctl_table_header *__register_sysctl_table(
995 1146
996 spin_lock(&sysctl_lock); 1147 spin_lock(&sysctl_lock);
997 header->set = set = lookup_header_set(root, namespaces); 1148 header->set = set = lookup_header_set(root, namespaces);
998 dir = &sysctl_root_dir; 1149 dir = &set->dir;
999 dir->header.nreg++; 1150 dir->header.nreg++;
1000 spin_unlock(&sysctl_lock); 1151 spin_unlock(&sysctl_lock);
1001 1152
@@ -1012,22 +1163,28 @@ struct ctl_table_header *__register_sysctl_table(
1012 if (namelen == 0) 1163 if (namelen == 0)
1013 continue; 1164 continue;
1014 1165
1015 dir = get_subdir(set, dir, name, namelen); 1166 dir = get_subdir(dir, name, namelen);
1016 if (IS_ERR(dir)) 1167 if (IS_ERR(dir))
1017 goto fail; 1168 goto fail;
1018 } 1169 }
1170
1019 spin_lock(&sysctl_lock); 1171 spin_lock(&sysctl_lock);
1020 if (sysctl_check_dups(namespaces, dir, path, table)) 1172 if (sysctl_check_dups(dir, path, table))
1173 goto fail_put_dir_locked;
1174
1175 if (insert_header(dir, header))
1021 goto fail_put_dir_locked; 1176 goto fail_put_dir_locked;
1022 insert_header(dir, header); 1177
1023 drop_sysctl_table(&dir->header); 1178 drop_sysctl_table(&dir->header);
1024 spin_unlock(&sysctl_lock); 1179 spin_unlock(&sysctl_lock);
1025 1180
1026 return header; 1181 return header;
1182
1027fail_put_dir_locked: 1183fail_put_dir_locked:
1028 drop_sysctl_table(&dir->header); 1184 drop_sysctl_table(&dir->header);
1029 spin_unlock(&sysctl_lock); 1185 spin_unlock(&sysctl_lock);
1030fail: 1186fail:
1187 kfree(links);
1031 kfree(header); 1188 kfree(header);
1032 dump_stack(); 1189 dump_stack();
1033 return NULL; 1190 return NULL;
@@ -1249,6 +1406,40 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table *table)
1249} 1406}
1250EXPORT_SYMBOL(register_sysctl_table); 1407EXPORT_SYMBOL(register_sysctl_table);
1251 1408
1409static void put_links(struct ctl_table_header *header)
1410{
1411 struct ctl_table_set *root_set = &sysctl_table_root.default_set;
1412 struct ctl_table_root *root = header->root;
1413 struct ctl_dir *parent = header->parent;
1414 struct ctl_dir *core_parent;
1415 struct ctl_table *entry;
1416
1417 if (header->set == root_set)
1418 return;
1419
1420 core_parent = xlate_dir(root_set, parent);
1421 if (IS_ERR(core_parent))
1422 return;
1423
1424 for (entry = header->ctl_table; entry->procname; entry++) {
1425 struct ctl_table_header *link_head;
1426 struct ctl_table *link;
1427 const char *name = entry->procname;
1428
1429 link = find_entry(&link_head, core_parent, name, strlen(name));
1430 if (link &&
1431 ((S_ISDIR(link->mode) && S_ISDIR(entry->mode)) ||
1432 (S_ISLNK(link->mode) && (link->data == root)))) {
1433 drop_sysctl_table(link_head);
1434 }
1435 else {
1436 printk(KERN_ERR "sysctl link missing during unregister: ");
1437 sysctl_print_dir(parent);
1438 printk(KERN_CONT "/%s\n", name);
1439 }
1440 }
1441}
1442
1252static void drop_sysctl_table(struct ctl_table_header *header) 1443static void drop_sysctl_table(struct ctl_table_header *header)
1253{ 1444{
1254 struct ctl_dir *parent = header->parent; 1445 struct ctl_dir *parent = header->parent;
@@ -1256,6 +1447,7 @@ static void drop_sysctl_table(struct ctl_table_header *header)
1256 if (--header->nreg) 1447 if (--header->nreg)
1257 return; 1448 return;
1258 1449
1450 put_links(header);
1259 start_unregistering(header); 1451 start_unregistering(header);
1260 if (!--header->count) 1452 if (!--header->count)
1261 kfree_rcu(header, rcu); 1453 kfree_rcu(header, rcu);
@@ -1301,13 +1493,14 @@ void unregister_sysctl_table(struct ctl_table_header * header)
1301} 1493}
1302EXPORT_SYMBOL(unregister_sysctl_table); 1494EXPORT_SYMBOL(unregister_sysctl_table);
1303 1495
1304void setup_sysctl_set(struct ctl_table_set *p, 1496void setup_sysctl_set(struct ctl_table_set *set,
1305 struct ctl_table_root *root, 1497 struct ctl_table_root *root,
1306 int (*is_seen)(struct ctl_table_set *)) 1498 int (*is_seen)(struct ctl_table_set *))
1307{ 1499{
1308 INIT_LIST_HEAD(&p->list); 1500 memset(set, sizeof(*set), 0);
1309 p->root = root; 1501 INIT_LIST_HEAD(&set->list);
1310 p->is_seen = is_seen; 1502 set->is_seen = is_seen;
1503 init_header(&set->dir.header, root, set, root_table);
1311} 1504}
1312 1505
1313void retire_sysctl_set(struct ctl_table_set *set) 1506void retire_sysctl_set(struct ctl_table_set *set)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 3084b624868c..2a1446a96094 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -1051,12 +1051,11 @@ struct ctl_dir {
1051 1051
1052struct ctl_table_set { 1052struct ctl_table_set {
1053 struct list_head list; 1053 struct list_head list;
1054 struct ctl_table_root *root;
1055 int (*is_seen)(struct ctl_table_set *); 1054 int (*is_seen)(struct ctl_table_set *);
1055 struct ctl_dir dir;
1056}; 1056};
1057 1057
1058struct ctl_table_root { 1058struct ctl_table_root {
1059 struct list_head root_list;
1060 struct ctl_table_set default_set; 1059 struct ctl_table_set default_set;
1061 struct ctl_table_set *(*lookup)(struct ctl_table_root *root, 1060 struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
1062 struct nsproxy *namespaces); 1061 struct nsproxy *namespaces);