diff options
author | Nicolas Dichtel <nicolas.dichtel@6wind.com> | 2014-12-10 18:45:01 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-10 20:41:09 -0500 |
commit | 710585d4922fd315f2cada8fbe550ae8ed23e994 (patch) | |
tree | dd783ed159fcf3be1463c41637b12345d60cd1de /fs/proc/generic.c | |
parent | 9edad6ea0f1416415f6fe31cc9d1dbc3817803ed (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.c | 164 |
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 | ||
32 | static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) | 32 | static 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 | |||
42 | static 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 | |||
52 | static 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 | |||
62 | static 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 | |||
84 | static 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 | ||
39 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) | 111 | static 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 | ||
287 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) | 353 | static 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 | */ |
486 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | 545 | void 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 | } |
524 | EXPORT_SYMBOL(remove_proc_entry); | 577 | EXPORT_SYMBOL(remove_proc_entry); |
525 | 578 | ||
526 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | 579 | int 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 | } |