fs/namei.c | 21 +++++++++++++++++++-- 1 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 5c867dd1c0b3..b9cd558a00be 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -938,11 +938,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, { for (;;) { struct vfsmount *mounted; + unsigned int seq; + /* * Don't forget we might have a non-mountpoint managed dentry * that wants to block transit. */ - *inode = path->dentry->d_inode; if (unlikely(managed_dentry_might_block(path->dentry))) return false; @@ -952,9 +953,25 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, mounted = __lookup_mnt(path->mnt, path->dentry, 1); if (!mounted) break; + seq = read_seqcount_begin(&mounted->mnt_root->d_seq); + + /* + * The memory barrier in read_seqcount_begin() is sufficient, + * so we can use __read_seqcount_retry() to check the prev + * sequence numbers. + */ + if (!__read_seqcount_retry(&path->dentry->d_seq, nd->seq)) + return false; path->mnt = mounted; path->dentry = mounted->mnt_root; - nd->seq = read_seqcount_begin(&path->dentry->d_seq); + nd->seq = seq; + + /* + * Update the inode too. We don't need to re-check the + * dentry sequence number here after this d_inode read, + * because a mount-point is always pinned. + */ + *inode = path->dentry->d_inode; } return true; }