Skip to content

Commit e840093

Browse files
author
Al Viro
committed
fix dget_parent() fastpath race
We are overoptimistic about taking the fast path there; seeing the same value in ->d_parent after having grabbed a reference to that parent does *not* mean that it has remained our parent all along. That wouldn't be a big deal (in the end it is our parent and we have grabbed the reference we are about to return), but... the situation with barriers is messed up. We might have hit the following sequence: d is a dentry of /tmp/a/b CPU1: CPU2: parent = d->d_parent (i.e. dentry of /tmp/a) rename /tmp/a/b to /tmp/b rmdir /tmp/a, making its dentry negative grab reference to parent, end up with cached parent->d_inode (NULL) mkdir /tmp/a, rename /tmp/b to /tmp/a/b recheck d->d_parent, which is back to original decide that everything's fine and return the reference we'd got. The trouble is, caller (on CPU1) will observe dget_parent() returning an apparently negative dentry. It actually is positive, but CPU1 has stale ->d_inode cached. Use d->d_seq to see if it has been moved instead of rechecking ->d_parent. NOTE: we are *NOT* going to retry on any kind of ->d_seq mismatch; we just go into the slow path in such case. We don't wait for ->d_seq to become even either - again, if we are racing with renames, we can bloody well go to slow path anyway. Signed-off-by: Al Viro <[email protected]>
1 parent 6c2d479 commit e840093

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

fs/dcache.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -903,17 +903,19 @@ struct dentry *dget_parent(struct dentry *dentry)
903903
{
904904
int gotref;
905905
struct dentry *ret;
906+
unsigned seq;
906907

907908
/*
908909
* Do optimistic parent lookup without any
909910
* locking.
910911
*/
911912
rcu_read_lock();
913+
seq = raw_seqcount_begin(&dentry->d_seq);
912914
ret = READ_ONCE(dentry->d_parent);
913915
gotref = lockref_get_not_zero(&ret->d_lockref);
914916
rcu_read_unlock();
915917
if (likely(gotref)) {
916-
if (likely(ret == READ_ONCE(dentry->d_parent)))
918+
if (!read_seqcount_retry(&dentry->d_seq, seq))
917919
return ret;
918920
dput(ret);
919921
}

0 commit comments

Comments
 (0)