From fc8ab5cfd6592f9a9df24706a8f5dcc1e41f2b33 Mon Sep 17 00:00:00 2001 From: Andrew Deason Date: Thu, 29 Apr 2010 17:47:15 -0500 Subject: [PATCH] Solaris: prevent AFS umount while busy 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 Tested-by: Derrick Brashear --- src/afs/SOLARIS/osi_machdep.h | 18 +++++++++++++++++- src/afs/SOLARIS/osi_vcache.c | 7 +++++++ src/afs/SOLARIS/osi_vfsops.c | 14 ++++++++++++++ src/afs/SOLARIS/osi_vnodeops.c | 5 +++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/afs/SOLARIS/osi_machdep.h b/src/afs/SOLARIS/osi_machdep.h index 1e17748805..9027a0e7ca 100644 --- a/src/afs/SOLARIS/osi_machdep.h +++ b/src/afs/SOLARIS/osi_machdep.h @@ -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) \ diff --git a/src/afs/SOLARIS/osi_vcache.c b/src/afs/SOLARIS/osi_vcache.c index 14a3828684..7435ed3ec5 100644 --- a/src/afs/SOLARIS/osi_vcache.c +++ b/src/afs/SOLARIS/osi_vcache.c @@ -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 } diff --git a/src/afs/SOLARIS/osi_vfsops.c b/src/afs/SOLARIS/osi_vfsops.c index c474f8aea0..31e9007cf3 100644 --- a/src/afs/SOLARIS/osi_vfsops.c +++ b/src/afs/SOLARIS/osi_vfsops.c @@ -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; diff --git a/src/afs/SOLARIS/osi_vnodeops.c b/src/afs/SOLARIS/osi_vnodeops.c index f02d19f12e..0ec2e09b0b 100644 --- a/src/afs/SOLARIS/osi_vnodeops.c +++ b/src/afs/SOLARIS/osi_vnodeops.c @@ -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; }