From 63f86bf72020204ff34fd652a6d738e2ed7fa79e Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 29 Jan 2013 15:04:05 -0800 Subject: [PATCH] kernel - Fix deadlock when umount races an access on the underlying filesystem * The nlookup code temporarily busies the target mount when diving a mount point in cases where the base of the mount is not resolved in the namecache. * Fix a deadlock which can occur between the namecache structural lock and the vfs_busy() on the mount structure by reordering the lock. * Generally only occured if an attempt was made to unmount a filesystem on which programs are still doing active operations (verses just passively holding references to the filesystem). For example, a umount on the target filesystem occuring while a cpdup using an absolute path is running. Reported-by: ftigeot --- sys/kern/vfs_nlookup.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sys/kern/vfs_nlookup.c b/sys/kern/vfs_nlookup.c index 7c1d378364..1bc9d6b718 100644 --- a/sys/kern/vfs_nlookup.c +++ b/sys/kern/vfs_nlookup.c @@ -735,15 +735,29 @@ nlookup(struct nlookupdata *nd) (mp = cache_findmount(&nch)) != NULL ) { struct vnode *tdp; + int vfs_do_busy = 0; + /* + * VFS must be busied before the namecache entry is locked, + * but we don't want to waste time calling vfs_busy() if the + * mount point is already resolved. + */ +again: cache_put(&nch); + if (vfs_do_busy) { + while (vfs_busy(mp, 0)) + ; + } cache_get(&mp->mnt_ncmountpt, &nch); if (nch.ncp->nc_flag & NCF_UNRESOLVED) { - while (vfs_busy(mp, 0)) - ; + if (vfs_do_busy == 0) { + vfs_do_busy = 1; + goto again; + } error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp); + vfs_do_busy = 0; if (error) { cache_dropmount(mp); break; @@ -751,6 +765,8 @@ nlookup(struct nlookupdata *nd) cache_setvp(&nch, tdp); vput(tdp); } + if (vfs_do_busy) + vfs_unbusy(mp); cache_dropmount(mp); } if (error) { -- 2.41.0