author: Al Viro <viro@zeniv.linux.org.uk> 2018-08-09 17:21:17 -0400
committer: Al Viro <viro@zeniv.linux.org.uk> 2018-08-09 17:21:17 -0400
commit: 9ea0a46ca2c318fcc449c1e6b62a7230a17888f1
parent: 90bad5e05bcdb0308cfa3d3a60f5c0b9c8e2efb3
Commit Summary:
Diffstat:
1 file changed, 12 insertions, 2 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 8ddd14806799..d46a951bd541 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1195,12 +1195,22 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput);
static void mntput_no_expire(struct mount *mnt)
{
rcu_read_lock();
- mnt_add_count(mnt, -1);
- if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */
+ if (likely(READ_ONCE(mnt->mnt_ns))) {
+ /*
+ * Since we don't do lock_mount_hash() here,
+ * ->mnt_ns can change under us. However, if it's
+ * non-NULL, then there's a reference that won't
+ * be dropped until after an RCU delay done after
+ * turning ->mnt_ns NULL. So if we observe it
+ * non-NULL under rcu_read_lock(), the reference
+ * we are dropping is not the final one.
+ */
+ mnt_add_count(mnt, -1);
rcu_read_unlock();
return;
}
lock_mount_hash();
+ mnt_add_count(mnt, -1);
if (mnt_get_count(mnt)) {
rcu_read_unlock();
unlock_mount_hash();