From 6fa06591a194a4b88811a5049f88fefcc060fee5 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 22 Mar 2012 21:37:25 -0700 Subject: [PATCH] kernel - Fix mount refs interactions and umount races * It is possible for a umount to race other operations on active mount point, causing one or the other to deadlock. * vfs_busy()/vfs_unbusy() now incr/decr mp->mnt_refs. * cache_findmount() now increments mp->mnt_refs, and add a new API function cache_dropmount() which decrements it. --- sys/kern/vfs_cache.c | 7 +++++++ sys/kern/vfs_mount.c | 10 +++++++++- sys/kern/vfs_nlookup.c | 5 ++++- sys/kern/vfs_syscalls.c | 8 ++++++-- sys/sys/namecache.h | 1 + 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index ea1dec52f8..62b84697c0 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -2498,6 +2498,7 @@ cache_findmount_callback(struct mount *mp, void *data) mp->mnt_ncmounton.ncp == info->nch_ncp ) { info->result = mp; + atomic_add_int(&mp->mnt_refs, 1); return(-1); } return(0); @@ -2516,6 +2517,12 @@ cache_findmount(struct nchandle *nch) return(info.result); } +void +cache_dropmount(struct mount *mp) +{ + atomic_add_int(&mp->mnt_refs, -1); +} + /* * Resolve an unresolved namecache entry, generally by looking it up. * The passed ncp must be locked and refd. diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index 18c83fe052..c5cefa2850 100644 --- a/sys/kern/vfs_mount.c +++ b/sys/kern/vfs_mount.c @@ -251,9 +251,12 @@ vfs_busy(struct mount *mp, int flags) { int lkflags; + atomic_add_int(&mp->mnt_refs, 1); if (mp->mnt_kern_flag & MNTK_UNMOUNT) { - if (flags & LK_NOWAIT) + if (flags & LK_NOWAIT) { + atomic_add_int(&mp->mnt_refs, -1); return (ENOENT); + } /* XXX not MP safe */ mp->mnt_kern_flag |= MNTK_MWAIT; /* @@ -263,6 +266,7 @@ vfs_busy(struct mount *mp, int flags) * exclusive lock at the end of dounmount. */ tsleep((caddr_t)mp, 0, "vfs_busy", 0); + atomic_add_int(&mp->mnt_refs, -1); return (ENOENT); } lkflags = LK_SHARED; @@ -273,10 +277,14 @@ vfs_busy(struct mount *mp, int flags) /* * Free a busy filesystem. + * + * Decrement refs before releasing the lock so e.g. a pending umount + * doesn't give us an unexpected busy error. */ void vfs_unbusy(struct mount *mp) { + atomic_add_int(&mp->mnt_refs, -1); lockmgr(&mp->mnt_lock, LK_RELEASE); } diff --git a/sys/kern/vfs_nlookup.c b/sys/kern/vfs_nlookup.c index ab0a87ead9..e42d06ad3f 100644 --- a/sys/kern/vfs_nlookup.c +++ b/sys/kern/vfs_nlookup.c @@ -727,11 +727,14 @@ nlookup(struct nlookupdata *nd) ; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp); - if (error) + if (error) { + cache_dropmount(mp); break; + } cache_setvp(&nch, tdp); vput(tdp); } + cache_dropmount(mp); } if (error) { cache_put(&nch); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 11c4933bdc..06ce36a490 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -181,10 +181,13 @@ sys_mount(struct mount_args *uap) cache_zero(&nd.nl_nch); nlookup_done(&nd); - if ((nch.ncp->nc_flag & NCF_ISMOUNTPT) && cache_findmount(&nch)) + if ((nch.ncp->nc_flag & NCF_ISMOUNTPT) && + (mp = cache_findmount(&nch)) != NULL) { + cache_dropmount(mp); hasmount = 1; - else + } else { hasmount = 0; + } /* @@ -1544,6 +1547,7 @@ sys_fchdir(struct fchdir_args *uap) cache_drop(&nch); nch = tnch; } + cache_dropmount(mp); } if (error == 0) { ovp = fdp->fd_cdir; diff --git a/sys/sys/namecache.h b/sys/sys/namecache.h index 55ef726409..4078c5ea07 100644 --- a/sys/sys/namecache.h +++ b/sys/sys/namecache.h @@ -192,6 +192,7 @@ struct nchandle cache_nlookup_nonblock(struct nchandle *nch, struct nlcomponent *nlc); void cache_allocroot(struct nchandle *nch, struct mount *mp, struct vnode *vp); struct mount *cache_findmount(struct nchandle *nch); +void cache_dropmount(struct mount *mp); int cache_inval(struct nchandle *nch, int flags); int cache_inval_vp(struct vnode *vp, int flags); int cache_inval_vp_nonblock(struct vnode *vp); -- 2.41.0