summaryrefslogtreecommitdiffstats
path: root/fs/proc/generic.c
diff options
context:
space:
mode:
authorNicolas Dichtel <nicolas.dichtel@6wind.com>2014-12-10 18:45:01 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-10 20:41:09 -0500
commit710585d4922fd315f2cada8fbe550ae8ed23e994 (patch)
treedd783ed159fcf3be1463c41637b12345d60cd1de /fs/proc/generic.c
parent9edad6ea0f1416415f6fe31cc9d1dbc3817803ed (diff)
fs/proc: use a rb tree for the directory entries
When a lot of netdevices are created, one of the bottleneck is the creation of proc entries. This serie aims to accelerate this part. The current implementation for the directories in /proc is using a single linked list. This is slow when handling directories with large numbers of entries (eg netdevice-related entries when lots of tunnels are opened). This patch replaces this linked list by a red-black tree. Here are some numbers: dummy30000.batch contains 30 000 times 'link add type dummy'. Before the patch: $ time ip -b dummy30000.batch real 2m31.950s user 0m0.440s sys 2m21.440s $ time rmmod dummy real 1m35.764s user 0m0.000s sys 1m24.088s After the patch: $ time ip -b dummy30000.batch real 2m0.874s user 0m0.448s sys 1m49.720s $ time rmmod dummy real 1m13.988s user 0m0.000s sys 1m1.008s The idea of improving this part was suggested by Thierry Herbelot. [akpm@linux-foundation.org: initialise proc_root.subdir at compile time] Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Acked-by: David S. Miller <davem@davemloft.net> Cc: Thierry Herbelot <thierry.herbelot@6wind.com>. Acked-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>
Diffstat (limited to 'fs/proc/generic.c')
-rw-r--r--fs/proc/generic.c164
1 files changed, 105 insertions, 59 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 317b72641ebf..9f8fa1e5e8aa 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -31,9 +31,81 @@ static DEFINE_SPINLOCK(proc_subdir_lock);
31 31
32static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) 32static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de)
33{ 33{
34 if (de->namelen != len) 34 if (len < de->namelen)
35 return 0; 35 return -1;
36 return !memcmp(name, de->name, len); 36 if (len > de->namelen)
37 return 1;
38
39 return memcmp(name, de->name, len);
40}
41
42static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir)
43{
44 struct rb_node *node = rb_first(&dir->subdir);
45
46 if (node == NULL)
47 return NULL;
48
49 return rb_entry(node, struct proc_dir_entry, subdir_node);
50}
51
52static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir)
53{
54 struct rb_node *node = rb_next(&dir->subdir_node);
55
56 if (node == NULL)
57 return NULL;
58
59 return rb_entry(node, struct proc_dir_entry, subdir_node);
60}
61
62static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
63 const char *name,
64 unsigned int len)
65{
66 struct rb_node *node = dir->subdir.rb_node;
67
68 while (node) {
69 struct proc_dir_entry *de = container_of(node,
70 struct proc_dir_entry,
71 subdir_node);
72 int result = proc_match(len, name, de);
73
74 if (result < 0)
75 node = node->rb_left;
76 else if (result > 0)
77 node = node->rb_right;
78 else
79 return de;
80 }
81 return NULL;
82}
83
84static bool pde_subdir_insert(struct proc_dir_entry *dir,
85 struct proc_dir_entry *de)
86{
87 struct rb_root *root = &dir->subdir;
88 struct rb_node **new = &root->rb_node, *parent = NULL;
89
90 /* Figure out where to put new node */
91 while (*new) {
92 struct proc_dir_entry *this =
93 container_of(*new, struct proc_dir_entry, subdir_node);
94 int result = proc_match(de->namelen, de->name, this);
95
96 parent = *new;
97 if (result < 0)
98 new = &(*new)->rb_left;
99 else if (result > 0)
100 new = &(*new)->rb_right;
101 else
102 return false;
103 }
104
105 /* Add new node and rebalance tree. */
106 rb_link_node(&de->subdir_node, parent, new);
107 rb_insert_color(&de->subdir_node, root);
108 return true;
37} 109}
38 110
39static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) 111static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
@@ -92,10 +164,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
92 break; 164 break;
93 165
94 len = next - cp; 166 len = next - cp;
95 for (de = de->subdir; de ; de = de->next) { 167 de = pde_subdir_find(de, cp, len);
96 if (proc_match(len, cp, de))
97 break;
98 }
99 if (!de) { 168 if (!de) {
100 WARN(1, "name '%s'\n", name); 169 WARN(1, "name '%s'\n", name);
101 return -ENOENT; 170 return -ENOENT;
@@ -183,19 +252,16 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
183 struct inode *inode; 252 struct inode *inode;
184 253
185 spin_lock(&proc_subdir_lock); 254 spin_lock(&proc_subdir_lock);
186 for (de = de->subdir; de ; de = de->next) { 255 de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len);
187 if (de->namelen != dentry->d_name.len) 256 if (de) {
188 continue; 257 pde_get(de);
189 if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { 258 spin_unlock(&proc_subdir_lock);
190 pde_get(de); 259 inode = proc_get_inode(dir->i_sb, de);
191 spin_unlock(&proc_subdir_lock); 260 if (!inode)
192 inode = proc_get_inode(dir->i_sb, de); 261 return ERR_PTR(-ENOMEM);
193 if (!inode) 262 d_set_d_op(dentry, &simple_dentry_operations);
194 return ERR_PTR(-ENOMEM); 263 d_add(dentry, inode);
195 d_set_d_op(dentry, &simple_dentry_operations); 264 return NULL;
196 d_add(dentry, inode);
197 return NULL;
198 }
199 } 265 }
200 spin_unlock(&proc_subdir_lock); 266 spin_unlock(&proc_subdir_lock);
201 return ERR_PTR(-ENOENT); 267 return ERR_PTR(-ENOENT);
@@ -225,7 +291,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
225 return 0; 291 return 0;
226 292
227 spin_lock(&proc_subdir_lock); 293 spin_lock(&proc_subdir_lock);
228 de = de->subdir; 294 de = pde_subdir_first(de);
229 i = ctx->pos - 2; 295 i = ctx->pos - 2;
230 for (;;) { 296 for (;;) {
231 if (!de) { 297 if (!de) {
@@ -234,7 +300,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
234 } 300 }
235 if (!i) 301 if (!i)
236 break; 302 break;
237 de = de->next; 303 de = pde_subdir_next(de);
238 i--; 304 i--;
239 } 305 }
240 306
@@ -249,7 +315,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
249 } 315 }
250 spin_lock(&proc_subdir_lock); 316 spin_lock(&proc_subdir_lock);
251 ctx->pos++; 317 ctx->pos++;
252 next = de->next; 318 next = pde_subdir_next(de);
253 pde_put(de); 319 pde_put(de);
254 de = next; 320 de = next;
255 } while (de); 321 } while (de);
@@ -286,9 +352,8 @@ static const struct inode_operations proc_dir_inode_operations = {
286 352
287static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) 353static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
288{ 354{
289 struct proc_dir_entry *tmp;
290 int ret; 355 int ret;
291 356
292 ret = proc_alloc_inum(&dp->low_ino); 357 ret = proc_alloc_inum(&dp->low_ino);
293 if (ret) 358 if (ret)
294 return ret; 359 return ret;
@@ -308,17 +373,10 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
308 } 373 }
309 374
310 spin_lock(&proc_subdir_lock); 375 spin_lock(&proc_subdir_lock);
311
312 for (tmp = dir->subdir; tmp; tmp = tmp->next)
313 if (strcmp(tmp->name, dp->name) == 0) {
314 WARN(1, "proc_dir_entry '%s/%s' already registered\n",
315 dir->name, dp->name);
316 break;
317 }
318
319 dp->next = dir->subdir;
320 dp->parent = dir; 376 dp->parent = dir;
321 dir->subdir = dp; 377 if (pde_subdir_insert(dir, dp) == false)
378 WARN(1, "proc_dir_entry '%s/%s' already registered\n",
379 dir->name, dp->name);
322 spin_unlock(&proc_subdir_lock); 380 spin_unlock(&proc_subdir_lock);
323 381
324 return 0; 382 return 0;
@@ -354,6 +412,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
354 ent->namelen = qstr.len; 412 ent->namelen = qstr.len;
355 ent->mode = mode; 413 ent->mode = mode;
356 ent->nlink = nlink; 414 ent->nlink = nlink;
415 ent->subdir = RB_ROOT;
357 atomic_set(&ent->count, 1); 416 atomic_set(&ent->count, 1);
358 spin_lock_init(&ent->pde_unload_lock); 417 spin_lock_init(&ent->pde_unload_lock);
359 INIT_LIST_HEAD(&ent->pde_openers); 418 INIT_LIST_HEAD(&ent->pde_openers);
@@ -485,7 +544,6 @@ void pde_put(struct proc_dir_entry *pde)
485 */ 544 */
486void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 545void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
487{ 546{
488 struct proc_dir_entry **p;
489 struct proc_dir_entry *de = NULL; 547 struct proc_dir_entry *de = NULL;
490 const char *fn = name; 548 const char *fn = name;
491 unsigned int len; 549 unsigned int len;
@@ -497,14 +555,9 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
497 } 555 }
498 len = strlen(fn); 556 len = strlen(fn);
499 557
500 for (p = &parent->subdir; *p; p=&(*p)->next ) { 558 de = pde_subdir_find(parent, fn, len);
501 if (proc_match(len, fn, *p)) { 559 if (de)
502 de = *p; 560 rb_erase(&de->subdir_node, &parent->subdir);
503 *p = de->next;
504 de->next = NULL;
505 break;
506 }
507 }
508 spin_unlock(&proc_subdir_lock); 561 spin_unlock(&proc_subdir_lock);
509 if (!de) { 562 if (!de) {
510 WARN(1, "name '%s'\n", name); 563 WARN(1, "name '%s'\n", name);
@@ -516,16 +569,15 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
516 if (S_ISDIR(de->mode)) 569 if (S_ISDIR(de->mode))
517 parent->nlink--; 570 parent->nlink--;
518 de->nlink = 0; 571 de->nlink = 0;
519 WARN(de->subdir, "%s: removing non-empty directory " 572 WARN(pde_subdir_first(de),
520 "'%s/%s', leaking at least '%s'\n", __func__, 573 "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
521 de->parent->name, de->name, de->subdir->name); 574 __func__, de->parent->name, de->name, pde_subdir_first(de)->name);
522 pde_put(de); 575 pde_put(de);
523} 576}
524EXPORT_SYMBOL(remove_proc_entry); 577EXPORT_SYMBOL(remove_proc_entry);
525 578
526int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) 579int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
527{ 580{
528 struct proc_dir_entry **p;
529 struct proc_dir_entry *root = NULL, *de, *next; 581 struct proc_dir_entry *root = NULL, *de, *next;
530 const char *fn = name; 582 const char *fn = name;
531 unsigned int len; 583 unsigned int len;
@@ -537,24 +589,18 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
537 } 589 }
538 len = strlen(fn); 590 len = strlen(fn);
539 591
540 for (p = &parent->subdir; *p; p=&(*p)->next ) { 592 root = pde_subdir_find(parent, fn, len);
541 if (proc_match(len, fn, *p)) {
542 root = *p;
543 *p = root->next;
544 root->next = NULL;
545 break;
546 }
547 }
548 if (!root) { 593 if (!root) {
549 spin_unlock(&proc_subdir_lock); 594 spin_unlock(&proc_subdir_lock);
550 return -ENOENT; 595 return -ENOENT;
551 } 596 }
597 rb_erase(&root->subdir_node, &parent->subdir);
598
552 de = root; 599 de = root;
553 while (1) { 600 while (1) {
554 next = de->subdir; 601 next = pde_subdir_first(de);
555 if (next) { 602 if (next) {
556 de->subdir = next->next; 603 rb_erase(&next->subdir_node, &de->subdir);
557 next->next = NULL;
558 de = next; 604 de = next;
559 continue; 605 continue;
560 } 606 }