summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorWaiman Long <Waiman.Long@hp.com>2015-09-09 18:35:57 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-10 16:29:01 -0400
commitecf1a3dfff22bd165532a3af64f32697ae97c1ef (patch)
tree3128a5831f780d3d9ec016daeaa87aeb1344c673 /fs
parentbdb4d100afe9818aebd1d98ced575c5ef143456c (diff)
proc: change proc_subdir_lock to a rwlock
The proc_subdir_lock spinlock is used to allow only one task to make change to the proc directory structure as well as looking up information in it. However, the information lookup part can actually be entered by more than one task as the pde_get() and pde_put() reference count update calls in the critical sections are atomic increment and decrement respectively and so are safe with concurrent updates. The x86 architecture has already used qrwlock which is fair and other architectures like ARM are in the process of switching to qrwlock. So unfairness shouldn't be a concern in that conversion. This patch changed the proc_subdir_lock to a rwlock in order to enable concurrent lookup. The following functions were modified to take a write lock: - proc_register() - remove_proc_entry() - remove_proc_subtree() The following functions were modified to take a read lock: - xlate_proc_name() - proc_lookup_de() - proc_readdir_de() A parallel /proc filesystem search with the "find" command (1000 threads) was run on a 4-socket Haswell-EX box (144 threads). Before the patch, the parallel search took about 39s. After the patch, the parallel find took only 25s, a saving of about 14s. The micro-benchmark that I used was artificial, but it was used to reproduce an exit hanging problem that I saw in real application. In fact, only allow one task to do a lookup seems too limiting to me. Signed-off-by: Waiman Long <Waiman.Long@hp.com> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Nicolas Dichtel <nicolas.dichtel@6wind.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Scott J Norton <scott.norton@hp.com> Cc: Douglas Hatch <doug.hatch@hp.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/proc/generic.c44
1 files changed, 22 insertions, 22 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index e5dee5c3188e..ff3ffc76a937 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -26,7 +26,7 @@
26 26
27#include "internal.h" 27#include "internal.h"
28 28
29static DEFINE_SPINLOCK(proc_subdir_lock); 29static DEFINE_RWLOCK(proc_subdir_lock);
30 30
31static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) 31static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de)
32{ 32{
@@ -172,9 +172,9 @@ static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
172{ 172{
173 int rv; 173 int rv;
174 174
175 spin_lock(&proc_subdir_lock); 175 read_lock(&proc_subdir_lock);
176 rv = __xlate_proc_name(name, ret, residual); 176 rv = __xlate_proc_name(name, ret, residual);
177 spin_unlock(&proc_subdir_lock); 177 read_unlock(&proc_subdir_lock);
178 return rv; 178 return rv;
179} 179}
180 180
@@ -231,11 +231,11 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
231{ 231{
232 struct inode *inode; 232 struct inode *inode;
233 233
234 spin_lock(&proc_subdir_lock); 234 read_lock(&proc_subdir_lock);
235 de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len); 235 de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len);
236 if (de) { 236 if (de) {
237 pde_get(de); 237 pde_get(de);
238 spin_unlock(&proc_subdir_lock); 238 read_unlock(&proc_subdir_lock);
239 inode = proc_get_inode(dir->i_sb, de); 239 inode = proc_get_inode(dir->i_sb, de);
240 if (!inode) 240 if (!inode)
241 return ERR_PTR(-ENOMEM); 241 return ERR_PTR(-ENOMEM);
@@ -243,7 +243,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
243 d_add(dentry, inode); 243 d_add(dentry, inode);
244 return NULL; 244 return NULL;
245 } 245 }
246 spin_unlock(&proc_subdir_lock); 246 read_unlock(&proc_subdir_lock);
247 return ERR_PTR(-ENOENT); 247 return ERR_PTR(-ENOENT);
248} 248}
249 249
@@ -270,12 +270,12 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
270 if (!dir_emit_dots(file, ctx)) 270 if (!dir_emit_dots(file, ctx))
271 return 0; 271 return 0;
272 272
273 spin_lock(&proc_subdir_lock); 273 read_lock(&proc_subdir_lock);
274 de = pde_subdir_first(de); 274 de = pde_subdir_first(de);
275 i = ctx->pos - 2; 275 i = ctx->pos - 2;
276 for (;;) { 276 for (;;) {
277 if (!de) { 277 if (!de) {
278 spin_unlock(&proc_subdir_lock); 278 read_unlock(&proc_subdir_lock);
279 return 0; 279 return 0;
280 } 280 }
281 if (!i) 281 if (!i)
@@ -287,19 +287,19 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
287 do { 287 do {
288 struct proc_dir_entry *next; 288 struct proc_dir_entry *next;
289 pde_get(de); 289 pde_get(de);
290 spin_unlock(&proc_subdir_lock); 290 read_unlock(&proc_subdir_lock);
291 if (!dir_emit(ctx, de->name, de->namelen, 291 if (!dir_emit(ctx, de->name, de->namelen,
292 de->low_ino, de->mode >> 12)) { 292 de->low_ino, de->mode >> 12)) {
293 pde_put(de); 293 pde_put(de);
294 return 0; 294 return 0;
295 } 295 }
296 spin_lock(&proc_subdir_lock); 296 read_lock(&proc_subdir_lock);
297 ctx->pos++; 297 ctx->pos++;
298 next = pde_subdir_next(de); 298 next = pde_subdir_next(de);
299 pde_put(de); 299 pde_put(de);
300 de = next; 300 de = next;
301 } while (de); 301 } while (de);
302 spin_unlock(&proc_subdir_lock); 302 read_unlock(&proc_subdir_lock);
303 return 1; 303 return 1;
304} 304}
305 305
@@ -338,16 +338,16 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
338 if (ret) 338 if (ret)
339 return ret; 339 return ret;
340 340
341 spin_lock(&proc_subdir_lock); 341 write_lock(&proc_subdir_lock);
342 dp->parent = dir; 342 dp->parent = dir;
343 if (pde_subdir_insert(dir, dp) == false) { 343 if (pde_subdir_insert(dir, dp) == false) {
344 WARN(1, "proc_dir_entry '%s/%s' already registered\n", 344 WARN(1, "proc_dir_entry '%s/%s' already registered\n",
345 dir->name, dp->name); 345 dir->name, dp->name);
346 spin_unlock(&proc_subdir_lock); 346 write_unlock(&proc_subdir_lock);
347 proc_free_inum(dp->low_ino); 347 proc_free_inum(dp->low_ino);
348 return -EEXIST; 348 return -EEXIST;
349 } 349 }
350 spin_unlock(&proc_subdir_lock); 350 write_unlock(&proc_subdir_lock);
351 351
352 return 0; 352 return 0;
353} 353}
@@ -549,9 +549,9 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
549 const char *fn = name; 549 const char *fn = name;
550 unsigned int len; 550 unsigned int len;
551 551
552 spin_lock(&proc_subdir_lock); 552 write_lock(&proc_subdir_lock);
553 if (__xlate_proc_name(name, &parent, &fn) != 0) { 553 if (__xlate_proc_name(name, &parent, &fn) != 0) {
554 spin_unlock(&proc_subdir_lock); 554 write_unlock(&proc_subdir_lock);
555 return; 555 return;
556 } 556 }
557 len = strlen(fn); 557 len = strlen(fn);
@@ -559,7 +559,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
559 de = pde_subdir_find(parent, fn, len); 559 de = pde_subdir_find(parent, fn, len);
560 if (de) 560 if (de)
561 rb_erase(&de->subdir_node, &parent->subdir); 561 rb_erase(&de->subdir_node, &parent->subdir);
562 spin_unlock(&proc_subdir_lock); 562 write_unlock(&proc_subdir_lock);
563 if (!de) { 563 if (!de) {
564 WARN(1, "name '%s'\n", name); 564 WARN(1, "name '%s'\n", name);
565 return; 565 return;
@@ -583,16 +583,16 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
583 const char *fn = name; 583 const char *fn = name;
584 unsigned int len; 584 unsigned int len;
585 585
586 spin_lock(&proc_subdir_lock); 586 write_lock(&proc_subdir_lock);
587 if (__xlate_proc_name(name, &parent, &fn) != 0) { 587 if (__xlate_proc_name(name, &parent, &fn) != 0) {
588 spin_unlock(&proc_subdir_lock); 588 write_unlock(&proc_subdir_lock);
589 return -ENOENT; 589 return -ENOENT;
590 } 590 }
591 len = strlen(fn); 591 len = strlen(fn);
592 592
593 root = pde_subdir_find(parent, fn, len); 593 root = pde_subdir_find(parent, fn, len);
594 if (!root) { 594 if (!root) {
595 spin_unlock(&proc_subdir_lock); 595 write_unlock(&proc_subdir_lock);
596 return -ENOENT; 596 return -ENOENT;
597 } 597 }
598 rb_erase(&root->subdir_node, &parent->subdir); 598 rb_erase(&root->subdir_node, &parent->subdir);
@@ -605,7 +605,7 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
605 de = next; 605 de = next;
606 continue; 606 continue;
607 } 607 }
608 spin_unlock(&proc_subdir_lock); 608 write_unlock(&proc_subdir_lock);
609 609
610 proc_entry_rundown(de); 610 proc_entry_rundown(de);
611 next = de->parent; 611 next = de->parent;
@@ -616,7 +616,7 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
616 break; 616 break;
617 pde_put(de); 617 pde_put(de);
618 618
619 spin_lock(&proc_subdir_lock); 619 write_lock(&proc_subdir_lock);
620 de = next; 620 de = next;
621 } 621 }
622 pde_put(root); 622 pde_put(root);