aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-06-10 11:32:47 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-06-10 11:32:47 -0400
commitba65dc5ef16f82fba77869cecf7a7d515f61446b (patch)
tree58368a848f6b396be2ffcc2a43a4bee44fb354ef /fs
parent1607f09c226d1378439c411baaaa020042750338 (diff)
much milder d_walk() race
d_walk() relies upon the tree not getting rearranged under it without rename_lock being touched. And we do grab rename_lock around the places that change the tree topology. Unfortunately, branch reordering is just as bad from d_walk() POV and we have two places that do it without touching rename_lock - one in handling of cursors (for ramfs-style directories) and another in autofs. autofs one is a separate story; this commit deals with the cursors. * mark cursor dentries explicitly at allocation time * make __dentry_kill() leave ->d_child.next pointing to the next non-cursor sibling, making sure that it won't be moved around unnoticed before the parent is relocked on ascend-to-parent path in d_walk(). * make d_walk() skip cursors explicitly; strictly speaking it's not necessary (all callbacks we pass to d_walk() are no-ops on cursors), but it makes analysis easier. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c58
-rw-r--r--fs/internal.h1
-rw-r--r--fs/libfs.c4
3 files changed, 54 insertions, 9 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 817c243c1ff1..b7eddfd35aa5 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -507,6 +507,44 @@ void d_drop(struct dentry *dentry)
507} 507}
508EXPORT_SYMBOL(d_drop); 508EXPORT_SYMBOL(d_drop);
509 509
510static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
511{
512 struct dentry *next;
513 /*
514 * Inform d_walk() and shrink_dentry_list() that we are no longer
515 * attached to the dentry tree
516 */
517 dentry->d_flags |= DCACHE_DENTRY_KILLED;
518 if (unlikely(list_empty(&dentry->d_child)))
519 return;
520 __list_del_entry(&dentry->d_child);
521 /*
522 * Cursors can move around the list of children. While we'd been
523 * a normal list member, it didn't matter - ->d_child.next would've
524 * been updated. However, from now on it won't be and for the
525 * things like d_walk() it might end up with a nasty surprise.
526 * Normally d_walk() doesn't care about cursors moving around -
527 * ->d_lock on parent prevents that and since a cursor has no children
528 * of its own, we get through it without ever unlocking the parent.
529 * There is one exception, though - if we ascend from a child that
530 * gets killed as soon as we unlock it, the next sibling is found
531 * using the value left in its ->d_child.next. And if _that_
532 * pointed to a cursor, and cursor got moved (e.g. by lseek())
533 * before d_walk() regains parent->d_lock, we'll end up skipping
534 * everything the cursor had been moved past.
535 *
536 * Solution: make sure that the pointer left behind in ->d_child.next
537 * points to something that won't be moving around. I.e. skip the
538 * cursors.
539 */
540 while (dentry->d_child.next != &parent->d_subdirs) {
541 next = list_entry(dentry->d_child.next, struct dentry, d_child);
542 if (likely(!(next->d_flags & DCACHE_DENTRY_CURSOR)))
543 break;
544 dentry->d_child.next = next->d_child.next;
545 }
546}
547
510static void __dentry_kill(struct dentry *dentry) 548static void __dentry_kill(struct dentry *dentry)
511{ 549{
512 struct dentry *parent = NULL; 550 struct dentry *parent = NULL;
@@ -532,12 +570,7 @@ static void __dentry_kill(struct dentry *dentry)
532 } 570 }
533 /* if it was on the hash then remove it */ 571 /* if it was on the hash then remove it */
534 __d_drop(dentry); 572 __d_drop(dentry);
535 __list_del_entry(&dentry->d_child); 573 dentry_unlist(dentry, parent);
536 /*
537 * Inform d_walk() that we are no longer attached to the
538 * dentry tree
539 */
540 dentry->d_flags |= DCACHE_DENTRY_KILLED;
541 if (parent) 574 if (parent)
542 spin_unlock(&parent->d_lock); 575 spin_unlock(&parent->d_lock);
543 dentry_iput(dentry); 576 dentry_iput(dentry);
@@ -1203,6 +1236,9 @@ resume:
1203 struct dentry *dentry = list_entry(tmp, struct dentry, d_child); 1236 struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
1204 next = tmp->next; 1237 next = tmp->next;
1205 1238
1239 if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
1240 continue;
1241
1206 spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); 1242 spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
1207 1243
1208 ret = enter(data, dentry); 1244 ret = enter(data, dentry);
@@ -1651,6 +1687,16 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
1651} 1687}
1652EXPORT_SYMBOL(d_alloc); 1688EXPORT_SYMBOL(d_alloc);
1653 1689
1690struct dentry *d_alloc_cursor(struct dentry * parent)
1691{
1692 struct dentry *dentry = __d_alloc(parent->d_sb, NULL);
1693 if (dentry) {
1694 dentry->d_flags |= DCACHE_RCUACCESS | DCACHE_DENTRY_CURSOR;
1695 dentry->d_parent = dget(parent);
1696 }
1697 return dentry;
1698}
1699
1654/** 1700/**
1655 * d_alloc_pseudo - allocate a dentry (for lookup-less filesystems) 1701 * d_alloc_pseudo - allocate a dentry (for lookup-less filesystems)
1656 * @sb: the superblock 1702 * @sb: the superblock
diff --git a/fs/internal.h b/fs/internal.h
index b71deeecea17..f57ced528cde 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -130,6 +130,7 @@ extern int invalidate_inodes(struct super_block *, bool);
130extern struct dentry *__d_alloc(struct super_block *, const struct qstr *); 130extern struct dentry *__d_alloc(struct super_block *, const struct qstr *);
131extern int d_set_mounted(struct dentry *dentry); 131extern int d_set_mounted(struct dentry *dentry);
132extern long prune_dcache_sb(struct super_block *sb, struct shrink_control *sc); 132extern long prune_dcache_sb(struct super_block *sb, struct shrink_control *sc);
133extern struct dentry *d_alloc_cursor(struct dentry *);
133 134
134/* 135/*
135 * read_write.c 136 * read_write.c
diff --git a/fs/libfs.c b/fs/libfs.c
index 3db2721144c2..cedeacbae303 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -71,9 +71,7 @@ EXPORT_SYMBOL(simple_lookup);
71 71
72int dcache_dir_open(struct inode *inode, struct file *file) 72int dcache_dir_open(struct inode *inode, struct file *file)
73{ 73{
74 static struct qstr cursor_name = QSTR_INIT(".", 1); 74 file->private_data = d_alloc_cursor(file->f_path.dentry);
75
76 file->private_data = d_alloc(file->f_path.dentry, &cursor_name);
77 75
78 return file->private_data ? 0 : -ENOMEM; 76 return file->private_data ? 0 : -ENOMEM;
79} 77}