Commit 83373f70 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs fixes from Al Viro:
 "double iput() on failure exit in lustre, racy removal of spliced
  dentries from ->s_anon in __d_materialise_dentry() plus a bunch of
  assorted RCU pathwalk fixes"

The RCU pathwalk fixes end up fixing a couple of cases where we
incorrectly dropped out of RCU walking, due to incorrect initialization
and testing of the sequence locks in some corner cases.  Since dropping
out of RCU walk mode forces the slow locked accesses, those corner cases
slowed down quite dramatically.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  be careful with nd->inode in path_init() and follow_dotdot_rcu()
  don't bugger nd->seq on set_root_rcu() from follow_dotdot_rcu()
  fix bogus read_seqretry() checks introduced in b37199e6
  move the call of __d_drop(anon) into __d_materialise_unique(dentry, anon)
  [fix] lustre: d_make_root() does iput() on dentry allocation failure
parents 9226b5b4 4023bfc9
...@@ -568,7 +568,7 @@ static int client_common_fill_super(struct super_block *sb, char *md, char *dt, ...@@ -568,7 +568,7 @@ static int client_common_fill_super(struct super_block *sb, char *md, char *dt,
if (sb->s_root == NULL) { if (sb->s_root == NULL) {
CERROR("%s: can't make root dentry\n", CERROR("%s: can't make root dentry\n",
ll_get_fsname(sb, NULL, 0)); ll_get_fsname(sb, NULL, 0));
GOTO(out_root, err = -ENOMEM); GOTO(out_lock_cn_cb, err = -ENOMEM);
} }
sbi->ll_sdev_orig = sb->s_dev; sbi->ll_sdev_orig = sb->s_dev;
......
...@@ -2655,6 +2655,12 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon) ...@@ -2655,6 +2655,12 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
dentry->d_parent = dentry; dentry->d_parent = dentry;
list_del_init(&dentry->d_u.d_child); list_del_init(&dentry->d_u.d_child);
anon->d_parent = dparent; anon->d_parent = dparent;
if (likely(!d_unhashed(anon))) {
hlist_bl_lock(&anon->d_sb->s_anon);
__hlist_bl_del(&anon->d_hash);
anon->d_hash.pprev = NULL;
hlist_bl_unlock(&anon->d_sb->s_anon);
}
list_move(&anon->d_u.d_child, &dparent->d_subdirs); list_move(&anon->d_u.d_child, &dparent->d_subdirs);
write_seqcount_end(&dentry->d_seq); write_seqcount_end(&dentry->d_seq);
...@@ -2713,7 +2719,6 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) ...@@ -2713,7 +2719,6 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
write_seqlock(&rename_lock); write_seqlock(&rename_lock);
__d_materialise_dentry(dentry, new); __d_materialise_dentry(dentry, new);
write_sequnlock(&rename_lock); write_sequnlock(&rename_lock);
__d_drop(new);
_d_rehash(new); _d_rehash(new);
spin_unlock(&new->d_lock); spin_unlock(&new->d_lock);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
...@@ -2777,7 +2782,6 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) ...@@ -2777,7 +2782,6 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
* could splice into our tree? */ * could splice into our tree? */
__d_materialise_dentry(dentry, alias); __d_materialise_dentry(dentry, alias);
write_sequnlock(&rename_lock); write_sequnlock(&rename_lock);
__d_drop(alias);
goto found; goto found;
} else { } else {
/* Nope, but we must(!) avoid directory /* Nope, but we must(!) avoid directory
......
...@@ -644,24 +644,22 @@ static int complete_walk(struct nameidata *nd) ...@@ -644,24 +644,22 @@ static int complete_walk(struct nameidata *nd)
static __always_inline void set_root(struct nameidata *nd) static __always_inline void set_root(struct nameidata *nd)
{ {
if (!nd->root.mnt) get_fs_root(current->fs, &nd->root);
get_fs_root(current->fs, &nd->root);
} }
static int link_path_walk(const char *, struct nameidata *); static int link_path_walk(const char *, struct nameidata *);
static __always_inline void set_root_rcu(struct nameidata *nd) static __always_inline unsigned set_root_rcu(struct nameidata *nd)
{ {
if (!nd->root.mnt) { struct fs_struct *fs = current->fs;
struct fs_struct *fs = current->fs; unsigned seq, res;
unsigned seq;
do { do {
seq = read_seqcount_begin(&fs->seq); seq = read_seqcount_begin(&fs->seq);
nd->root = fs->root; nd->root = fs->root;
nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq); res = __read_seqcount_begin(&nd->root.dentry->d_seq);
} while (read_seqcount_retry(&fs->seq, seq)); } while (read_seqcount_retry(&fs->seq, seq));
} return res;
} }
static void path_put_conditional(struct path *path, struct nameidata *nd) static void path_put_conditional(struct path *path, struct nameidata *nd)
...@@ -861,7 +859,8 @@ follow_link(struct path *link, struct nameidata *nd, void **p) ...@@ -861,7 +859,8 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
return PTR_ERR(s); return PTR_ERR(s);
} }
if (*s == '/') { if (*s == '/') {
set_root(nd); if (!nd->root.mnt)
set_root(nd);
path_put(&nd->path); path_put(&nd->path);
nd->path = nd->root; nd->path = nd->root;
path_get(&nd->root); path_get(&nd->root);
...@@ -1138,13 +1137,15 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, ...@@ -1138,13 +1137,15 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
*/ */
*inode = path->dentry->d_inode; *inode = path->dentry->d_inode;
} }
return read_seqretry(&mount_lock, nd->m_seq) && return !read_seqretry(&mount_lock, nd->m_seq) &&
!(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT); !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
} }
static int follow_dotdot_rcu(struct nameidata *nd) static int follow_dotdot_rcu(struct nameidata *nd)
{ {
set_root_rcu(nd); struct inode *inode = nd->inode;
if (!nd->root.mnt)
set_root_rcu(nd);
while (1) { while (1) {
if (nd->path.dentry == nd->root.dentry && if (nd->path.dentry == nd->root.dentry &&
...@@ -1156,6 +1157,7 @@ static int follow_dotdot_rcu(struct nameidata *nd) ...@@ -1156,6 +1157,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
struct dentry *parent = old->d_parent; struct dentry *parent = old->d_parent;
unsigned seq; unsigned seq;
inode = parent->d_inode;
seq = read_seqcount_begin(&parent->d_seq); seq = read_seqcount_begin(&parent->d_seq);
if (read_seqcount_retry(&old->d_seq, nd->seq)) if (read_seqcount_retry(&old->d_seq, nd->seq))
goto failed; goto failed;
...@@ -1165,6 +1167,7 @@ static int follow_dotdot_rcu(struct nameidata *nd) ...@@ -1165,6 +1167,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
} }
if (!follow_up_rcu(&nd->path)) if (!follow_up_rcu(&nd->path))
break; break;
inode = nd->path.dentry->d_inode;
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
} }
while (d_mountpoint(nd->path.dentry)) { while (d_mountpoint(nd->path.dentry)) {
...@@ -1174,11 +1177,12 @@ static int follow_dotdot_rcu(struct nameidata *nd) ...@@ -1174,11 +1177,12 @@ static int follow_dotdot_rcu(struct nameidata *nd)
break; break;
nd->path.mnt = &mounted->mnt; nd->path.mnt = &mounted->mnt;
nd->path.dentry = mounted->mnt.mnt_root; nd->path.dentry = mounted->mnt.mnt_root;
inode = nd->path.dentry->d_inode;
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
if (!read_seqretry(&mount_lock, nd->m_seq)) if (read_seqretry(&mount_lock, nd->m_seq))
goto failed; goto failed;
} }
nd->inode = nd->path.dentry->d_inode; nd->inode = inode;
return 0; return 0;
failed: failed:
...@@ -1257,7 +1261,8 @@ static void follow_mount(struct path *path) ...@@ -1257,7 +1261,8 @@ static void follow_mount(struct path *path)
static void follow_dotdot(struct nameidata *nd) static void follow_dotdot(struct nameidata *nd)
{ {
set_root(nd); if (!nd->root.mnt)
set_root(nd);
while(1) { while(1) {
struct dentry *old = nd->path.dentry; struct dentry *old = nd->path.dentry;
...@@ -1853,7 +1858,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, ...@@ -1853,7 +1858,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
if (*name=='/') { if (*name=='/') {
if (flags & LOOKUP_RCU) { if (flags & LOOKUP_RCU) {
rcu_read_lock(); rcu_read_lock();
set_root_rcu(nd); nd->seq = set_root_rcu(nd);
} else { } else {
set_root(nd); set_root(nd);
path_get(&nd->root); path_get(&nd->root);
...@@ -1904,7 +1909,14 @@ static int path_init(int dfd, const char *name, unsigned int flags, ...@@ -1904,7 +1909,14 @@ static int path_init(int dfd, const char *name, unsigned int flags,
} }
nd->inode = nd->path.dentry->d_inode; nd->inode = nd->path.dentry->d_inode;
return 0; if (!(flags & LOOKUP_RCU))
return 0;
if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq)))
return 0;
if (!(nd->flags & LOOKUP_ROOT))
nd->root.mnt = NULL;
rcu_read_unlock();
return -ECHILD;
} }
static inline int lookup_last(struct nameidata *nd, struct path *path) static inline int lookup_last(struct nameidata *nd, struct path *path)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment