diff options
author | Davidlohr Bueso <dave@stgolabs.net> | 2017-09-08 19:15:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-08 21:26:49 -0400 |
commit | 410bd5ecb276593e7ec1552014083215d4a43c3a (patch) | |
tree | 3d507f3ba123be043d8a4e7582c8a48a0f71cb07 | |
parent | f2686bb48618718c7141f117e2d607594e959bfc (diff) |
procfs: use faster rb_first_cached()
... such that we can avoid the tree walks to get the node with the
smallest key. Semantically the same, as the previously used rb_first(),
but O(1). The main overhead is the extra footprint for the cached rb_node
pointer, which should not matter for procfs.
Link: http://lkml.kernel.org/r/20170719014603.19029-14-dave@stgolabs.net
Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/proc/generic.c | 26 | ||||
-rw-r--r-- | fs/proc/internal.h | 2 | ||||
-rw-r--r-- | fs/proc/proc_net.c | 2 | ||||
-rw-r--r-- | fs/proc/root.c | 2 |
4 files changed, 17 insertions, 15 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 85566abe6f83..793a67574668 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -40,8 +40,8 @@ static int proc_match(unsigned int len, const char *name, struct proc_dir_entry | |||
40 | 40 | ||
41 | static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir) | 41 | static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir) |
42 | { | 42 | { |
43 | return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry, | 43 | return rb_entry_safe(rb_first_cached(&dir->subdir), |
44 | subdir_node); | 44 | struct proc_dir_entry, subdir_node); |
45 | } | 45 | } |
46 | 46 | ||
47 | static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) | 47 | static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) |
@@ -54,7 +54,7 @@ static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, | |||
54 | const char *name, | 54 | const char *name, |
55 | unsigned int len) | 55 | unsigned int len) |
56 | { | 56 | { |
57 | struct rb_node *node = dir->subdir.rb_node; | 57 | struct rb_node *node = dir->subdir.rb_root.rb_node; |
58 | 58 | ||
59 | while (node) { | 59 | while (node) { |
60 | struct proc_dir_entry *de = rb_entry(node, | 60 | struct proc_dir_entry *de = rb_entry(node, |
@@ -75,8 +75,9 @@ static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, | |||
75 | static bool pde_subdir_insert(struct proc_dir_entry *dir, | 75 | static bool pde_subdir_insert(struct proc_dir_entry *dir, |
76 | struct proc_dir_entry *de) | 76 | struct proc_dir_entry *de) |
77 | { | 77 | { |
78 | struct rb_root *root = &dir->subdir; | 78 | struct rb_root_cached *root = &dir->subdir; |
79 | struct rb_node **new = &root->rb_node, *parent = NULL; | 79 | struct rb_node **new = &root->rb_root.rb_node, *parent = NULL; |
80 | bool leftmost = true; | ||
80 | 81 | ||
81 | /* Figure out where to put new node */ | 82 | /* Figure out where to put new node */ |
82 | while (*new) { | 83 | while (*new) { |
@@ -88,15 +89,16 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir, | |||
88 | parent = *new; | 89 | parent = *new; |
89 | if (result < 0) | 90 | if (result < 0) |
90 | new = &(*new)->rb_left; | 91 | new = &(*new)->rb_left; |
91 | else if (result > 0) | 92 | else if (result > 0) { |
92 | new = &(*new)->rb_right; | 93 | new = &(*new)->rb_right; |
93 | else | 94 | leftmost = false; |
95 | } else | ||
94 | return false; | 96 | return false; |
95 | } | 97 | } |
96 | 98 | ||
97 | /* Add new node and rebalance tree. */ | 99 | /* Add new node and rebalance tree. */ |
98 | rb_link_node(&de->subdir_node, parent, new); | 100 | rb_link_node(&de->subdir_node, parent, new); |
99 | rb_insert_color(&de->subdir_node, root); | 101 | rb_insert_color_cached(&de->subdir_node, root, leftmost); |
100 | return true; | 102 | return true; |
101 | } | 103 | } |
102 | 104 | ||
@@ -369,7 +371,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, | |||
369 | ent->namelen = qstr.len; | 371 | ent->namelen = qstr.len; |
370 | ent->mode = mode; | 372 | ent->mode = mode; |
371 | ent->nlink = nlink; | 373 | ent->nlink = nlink; |
372 | ent->subdir = RB_ROOT; | 374 | ent->subdir = RB_ROOT_CACHED; |
373 | atomic_set(&ent->count, 1); | 375 | atomic_set(&ent->count, 1); |
374 | spin_lock_init(&ent->pde_unload_lock); | 376 | spin_lock_init(&ent->pde_unload_lock); |
375 | INIT_LIST_HEAD(&ent->pde_openers); | 377 | INIT_LIST_HEAD(&ent->pde_openers); |
@@ -553,7 +555,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
553 | 555 | ||
554 | de = pde_subdir_find(parent, fn, len); | 556 | de = pde_subdir_find(parent, fn, len); |
555 | if (de) | 557 | if (de) |
556 | rb_erase(&de->subdir_node, &parent->subdir); | 558 | rb_erase_cached(&de->subdir_node, &parent->subdir); |
557 | write_unlock(&proc_subdir_lock); | 559 | write_unlock(&proc_subdir_lock); |
558 | if (!de) { | 560 | if (!de) { |
559 | WARN(1, "name '%s'\n", name); | 561 | WARN(1, "name '%s'\n", name); |
@@ -590,13 +592,13 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | |||
590 | write_unlock(&proc_subdir_lock); | 592 | write_unlock(&proc_subdir_lock); |
591 | return -ENOENT; | 593 | return -ENOENT; |
592 | } | 594 | } |
593 | rb_erase(&root->subdir_node, &parent->subdir); | 595 | rb_erase_cached(&root->subdir_node, &parent->subdir); |
594 | 596 | ||
595 | de = root; | 597 | de = root; |
596 | while (1) { | 598 | while (1) { |
597 | next = pde_subdir_first(de); | 599 | next = pde_subdir_first(de); |
598 | if (next) { | 600 | if (next) { |
599 | rb_erase(&next->subdir_node, &de->subdir); | 601 | rb_erase_cached(&next->subdir_node, &de->subdir); |
600 | de = next; | 602 | de = next; |
601 | continue; | 603 | continue; |
602 | } | 604 | } |
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 2cbfcd32e884..a34195e92b20 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -40,7 +40,7 @@ struct proc_dir_entry { | |||
40 | const struct inode_operations *proc_iops; | 40 | const struct inode_operations *proc_iops; |
41 | const struct file_operations *proc_fops; | 41 | const struct file_operations *proc_fops; |
42 | struct proc_dir_entry *parent; | 42 | struct proc_dir_entry *parent; |
43 | struct rb_root subdir; | 43 | struct rb_root_cached subdir; |
44 | struct rb_node subdir_node; | 44 | struct rb_node subdir_node; |
45 | void *data; | 45 | void *data; |
46 | atomic_t count; /* use count */ | 46 | atomic_t count; /* use count */ |
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index d72fc40241d9..a2bf369c923d 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c | |||
@@ -196,7 +196,7 @@ static __net_init int proc_net_ns_init(struct net *net) | |||
196 | if (!netd) | 196 | if (!netd) |
197 | goto out; | 197 | goto out; |
198 | 198 | ||
199 | netd->subdir = RB_ROOT; | 199 | netd->subdir = RB_ROOT_CACHED; |
200 | netd->data = net; | 200 | netd->data = net; |
201 | netd->nlink = 2; | 201 | netd->nlink = 2; |
202 | netd->namelen = 3; | 202 | netd->namelen = 3; |
diff --git a/fs/proc/root.c b/fs/proc/root.c index deecb397daa3..926fb27f4ca2 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -210,7 +210,7 @@ struct proc_dir_entry proc_root = { | |||
210 | .proc_iops = &proc_root_inode_operations, | 210 | .proc_iops = &proc_root_inode_operations, |
211 | .proc_fops = &proc_root_operations, | 211 | .proc_fops = &proc_root_operations, |
212 | .parent = &proc_root, | 212 | .parent = &proc_root, |
213 | .subdir = RB_ROOT, | 213 | .subdir = RB_ROOT_CACHED, |
214 | .name = "/proc", | 214 | .name = "/proc", |
215 | }; | 215 | }; |
216 | 216 | ||