On Fri, Apr 18, 2014 at 03:38:22AM +0100, Al Viro wrote: > Aha, so my guess in another posting was correct. OK... > > So it's not about RCU case at all - if dentry changes under us > in lazy mode, we'll simply fail when we get to checking d_seq. It's > non-lazy case + transition from negative to positive that causes the > trouble. > > Another piece of breakage in there is should_follow_link() directly > afterwards - there the problem is with false negatives. I.e. we > see new dentry->d_inode, but not the new dentry->d_flags. > > I wonder if the right fix would be simply > if (!inode || d_is_negative(dentry)) > bailing out unless both updates are seen. Probably cheaper than any > games with barriers... Crap... No, it's a bit trickier - we start with clearing all flags, so if we see the _intermediate_ d_flags and new d_inode, we'll sail past the check. Which would leave us with correct inode, but might give us a false negative in should_follow_link(). Hmm... How about the following, then? diff --git a/fs/dcache.c b/fs/dcache.c index 40707d8..494a9def 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1647,8 +1647,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) unsigned add_flags = d_flags_for_inode(inode); spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_ENTRY_TYPE; - dentry->d_flags |= add_flags; + __d_set_type(dentry, add_flags); if (inode) hlist_add_head(&dentry->d_alias, &inode->i_dentry); dentry->d_inode = inode; diff --git a/fs/namei.c b/fs/namei.c index c6157c8..7560db7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2994,7 +2994,7 @@ retry_lookup: finish_lookup: /* we _can_ be in RCU mode here */ error = -ENOENT; - if (d_is_negative(path->dentry)) { + if (!inode || d_is_negative(path->dentry)) { path_to_nameidata(path, nd); goto out; } -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html