Solaris: prevent AFS umount while busy
authorAndrew Deason <adeason@sinenomine.net>
Thu, 29 Apr 2010 22:47:15 +0000 (17:47 -0500)
committerDerrick Brashear <shadow@dementia.org>
Tue, 4 May 2010 00:11:44 +0000 (17:11 -0700)
Return EBUSY from unmount if someone still references stuff in AFS.
This prevents kernel panics that can occur on shutdown if we umount
while there is a file in AFS open. Normally a process can hold a file
in AFS open, AFS is unmounted, and the file is closed, triggering our
code which explodes if called after we're unmounted.

This adds VFS_HOLD/VFS_RELE calls whenever we 'create' a vcache, or
retire an old one, to keep track if anyone has an open reference to
us.

Change-Id: I95d8cf7e7e4d32a05bee97e06832a530b40af217
Reviewed-on: http://gerrit.openafs.org/1880
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: Derrick Brashear <shadow@dementia.org>

src/afs/SOLARIS/osi_machdep.h
src/afs/SOLARIS/osi_vcache.c
src/afs/SOLARIS/osi_vfsops.c
src/afs/SOLARIS/osi_vnodeops.c

index 1e17748..9027a0e 100644 (file)
@@ -56,7 +56,23 @@ local_osi_Time()
 #undef afs_osi_Alloc_NoSleep
 extern void *afs_osi_Alloc_NoSleep(size_t size);
 
-#define osi_vnhold(avc, r)  do { VN_HOLD(AFSTOV(avc)); } while(0)
+#ifdef AFS_SUN58_ENV
+# define osi_vnhold(avc, r)  do {    \
+    struct vnode *vp = AFSTOV(avc); \
+    uint_t prevcount;               \
+                                    \
+    mutex_enter(&vp->v_lock);       \
+    prevcount = vp->v_count++;      \
+    mutex_exit(&vp->v_lock);        \
+                                    \
+    if (prevcount == 0) {           \
+       VFS_HOLD(afs_globalVFS);    \
+    }                               \
+} while(0)
+#else /* !AFS_SUN58_ENV */
+# define osi_vnhold(avc, r)  do { VN_HOLD(AFSTOV(avc)); } while(0)
+#endif /* !AFS_SUN58_ENV */
+
 #define gop_rdwr(rw,gp,base,len,offset,segflg,ioflag,ulimit,cr,aresid) \
   vn_rdwr((rw),(gp),(base),(len),(offset),(segflg),(ioflag),(ulimit),(cr),(aresid))
 #define gop_lookupname(fnamep,segflg,followlink,compvpp) \
index 14a3828..7435ed3 100644 (file)
@@ -67,4 +67,11 @@ osi_PostPopulateVCache(struct vcache *avc) {
     AFSTOV(avc)->v_op = afs_ops;
     AFSTOV(avc)->v_vfsp = afs_globalVFS;
     vSetType(avc, VREG);
+
+#ifdef AFS_SUN58_ENV
+    /* Normally we do this in osi_vnhold when we notice the ref count went from
+     * 0 -> 1. But if we just setup or reused a vcache, we set the refcount to
+     * 1 directly. So, we must explicitly VFS_HOLD here. */
+    VFS_HOLD(afs_globalVFS);
+#endif
 }
index c474f8a..31e9007 100644 (file)
@@ -96,6 +96,20 @@ afs_unmount(struct vfs *afsp, afs_ucred_t *credp)
        AFS_GUNLOCK();
        return ENOTSUP;
     }
+
+    /* We should have one reference from the caller, and one reference for the
+     * root vnode; any more and someone is still referencing something */
+    if (afsp->vfs_count > 2) {
+       AFS_GUNLOCK();
+       return EBUSY;
+    }
+
+    /* The root vnode should have one ref for the mount; any more, and someone
+     * else is using the root vnode */
+    if (afs_globalVp && VREFCOUNT_GT(afs_globalVp, 1)) {
+       AFS_GUNLOCK();
+       return EBUSY;
+    }
 #endif /* AFS_SUN58_ENV */
 
     afs_globalVFS = 0;
index f02d19f..0ec2e09 100644 (file)
@@ -1815,6 +1815,11 @@ afs_inactive(struct vcache *avc, afs_ucred_t *acred)
        avc->opens = avc->execsOrWriters = 0;
 
     afs_InactiveVCache(avc, acred);
+
+#ifdef AFS_SUN58_ENV
+    VFS_RELE(afs_globalVFS);
+#endif
+
     return 0;
 }