summaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-09-29 22:06:07 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-11-09 00:16:19 -0500
commit48a066e72d970a3e225a9c18690d570c736fc455 (patch)
tree9a1861c1be4309cf69964742d238eadd5d0b1832 /fs/dcache.c
parent42c326082d8a2c91506f951ace638deae1faf083 (diff)
RCU'd vfsmounts
* RCU-delayed freeing of vfsmounts * vfsmount_lock replaced with a seqlock (mount_lock) * sequence number from mount_lock is stored in nameidata->m_seq and used when we exit RCU mode * new vfsmount flag - MNT_SYNC_UMOUNT. Set by umount_tree() when its caller knows that vfsmount will have no surviving references. * synchronize_rcu() done between unlocking namespace_sem in namespace_unlock() and doing pending mntput(). * new helper: legitimize_mnt(mnt, seq). Checks the mount_lock sequence number against seq, then grabs reference to mnt. Then it rechecks mount_lock again to close the race and either returns success or drops the reference it has acquired. The subtle point is that in case of MNT_SYNC_UMOUNT we can simply decrement the refcount and sod off - aforementioned synchronize_rcu() makes sure that final mntput() won't come until we leave RCU mode. We need that, since we don't want to end up with some lazy pathwalk racing with umount() and stealing the final mntput() from it - caller of umount() may expect it to return only once the fs is shut down and we don't want to break that. In other cases (i.e. with MNT_SYNC_UMOUNT absent) we have to do full-blown mntput() in case of mount_lock sequence number mismatch happening just as we'd grabbed the reference, but in those cases we won't be stealing the final mntput() from anything that would care. * mntput_no_expire() doesn't lock anything on the fast path now. Incidentally, SMP and UP cases are handled the same way - no ifdefs there. * normal pathname resolution does *not* do any writes to mount_lock. It does, of course, bump the refcounts of vfsmount and dentry in the very end, but that's it. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r--fs/dcache.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index eb0978da1bd4..aafa2a146434 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2887,24 +2887,28 @@ static int prepend_path(const struct path *path,
2887 struct vfsmount *vfsmnt = path->mnt; 2887 struct vfsmount *vfsmnt = path->mnt;
2888 struct mount *mnt = real_mount(vfsmnt); 2888 struct mount *mnt = real_mount(vfsmnt);
2889 int error = 0; 2889 int error = 0;
2890 unsigned seq = 0; 2890 unsigned seq, m_seq = 0;
2891 char *bptr; 2891 char *bptr;
2892 int blen; 2892 int blen;
2893 2893
2894 br_read_lock(&vfsmount_lock);
2895 rcu_read_lock(); 2894 rcu_read_lock();
2895restart_mnt:
2896 read_seqbegin_or_lock(&mount_lock, &m_seq);
2897 seq = 0;
2896restart: 2898restart:
2897 bptr = *buffer; 2899 bptr = *buffer;
2898 blen = *buflen; 2900 blen = *buflen;
2901 error = 0;
2899 read_seqbegin_or_lock(&rename_lock, &seq); 2902 read_seqbegin_or_lock(&rename_lock, &seq);
2900 while (dentry != root->dentry || vfsmnt != root->mnt) { 2903 while (dentry != root->dentry || vfsmnt != root->mnt) {
2901 struct dentry * parent; 2904 struct dentry * parent;
2902 2905
2903 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { 2906 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
2907 struct mount *parent = ACCESS_ONCE(mnt->mnt_parent);
2904 /* Global root? */ 2908 /* Global root? */
2905 if (mnt_has_parent(mnt)) { 2909 if (mnt != parent) {
2906 dentry = mnt->mnt_mountpoint; 2910 dentry = ACCESS_ONCE(mnt->mnt_mountpoint);
2907 mnt = mnt->mnt_parent; 2911 mnt = parent;
2908 vfsmnt = &mnt->mnt; 2912 vfsmnt = &mnt->mnt;
2909 continue; 2913 continue;
2910 } 2914 }
@@ -2938,7 +2942,11 @@ restart:
2938 goto restart; 2942 goto restart;
2939 } 2943 }
2940 done_seqretry(&rename_lock, seq); 2944 done_seqretry(&rename_lock, seq);
2941 br_read_unlock(&vfsmount_lock); 2945 if (need_seqretry(&mount_lock, m_seq)) {
2946 m_seq = 1;
2947 goto restart_mnt;
2948 }
2949 done_seqretry(&mount_lock, m_seq);
2942 2950
2943 if (error >= 0 && bptr == *buffer) { 2951 if (error >= 0 && bptr == *buffer) {
2944 if (--blen < 0) 2952 if (--blen < 0)