diff options
author | attilio <attilio@FreeBSD.org> | 2008-12-16 23:16:10 +0000 |
---|---|---|
committer | attilio <attilio@FreeBSD.org> | 2008-12-16 23:16:10 +0000 |
commit | 697e2a94e4778901b53481a53119fcb1da110429 (patch) | |
tree | 37ed10fcd9d48b4f45318c6b268fd7ee0d69aa00 /sys/kern/vfs_mount.c | |
parent | 447f3863c6c6567480d2cfde0634e3ad5b851f3a (diff) | |
download | FreeBSD-src-697e2a94e4778901b53481a53119fcb1da110429.zip FreeBSD-src-697e2a94e4778901b53481a53119fcb1da110429.tar.gz |
1) Fix a deadlock in the VFS:
- threadA runs vfs_rel(mp1)
- threadB does unmount the mp1 fs, sets MNTK_UNMOUNT and drop MNT_ILOCK()
- threadA runs vfs_busy(mp1) and, as long as, MNTK_UNMOUNT is set, sleeps
waiting for threadB to complete the unmount
- threadB, in vfs_mount_destroy(), finds mnt_lock > 0 and sleeps waiting
for the refcount to expire.
Fix the deadlock by adding a flag called MNTK_REFEXPIRE which signals the
unmounter is waiting for mnt_ref to expire.
The vfs_busy contenders got awake, fails, and if they retry the
MNTK_REFEXPIRE won't allow them to sleep again.
2) Simplify significantly the code of vfs_mount_destroy() trimming
unnecessary codes:
- as long as any reference exited, it is no-more possible to have
write-op (primarty and secondary) in progress.
- it is no needed to drop and reacquire the mount lock.
- filling the structures with dummy values is unuseful as long as
it is going to be freed.
Tested by: pho, Andrea Barberio <insomniac at slackware dot it>
Discussed with: kib
Diffstat (limited to 'sys/kern/vfs_mount.c')
-rw-r--r-- | sys/kern/vfs_mount.c | 45 |
1 files changed, 14 insertions, 31 deletions
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index 12368c9..2b870f0 100644 --- a/sys/kern/vfs_mount.c +++ b/sys/kern/vfs_mount.c @@ -507,29 +507,20 @@ vfs_mount_destroy(struct mount *mp) { MNT_ILOCK(mp); + mp->mnt_kern_flag |= MNTK_REFEXPIRE; + if (mp->mnt_kern_flag & MNTK_MWAIT) { + mp->mnt_kern_flag &= ~MNTK_MWAIT; + wakeup(mp); + } while (mp->mnt_ref) msleep(mp, MNT_MTX(mp), PVFS, "mntref", 0); - if (mp->mnt_writeopcount > 0) { - printf("Waiting for mount point write ops\n"); - while (mp->mnt_writeopcount > 0) { - mp->mnt_kern_flag |= MNTK_SUSPEND; - msleep(&mp->mnt_writeopcount, - MNT_MTX(mp), - PZERO, "mntdestroy2", 0); - } - printf("mount point write ops completed\n"); - } - if (mp->mnt_secondary_writes > 0) { - printf("Waiting for mount point secondary write ops\n"); - while (mp->mnt_secondary_writes > 0) { - mp->mnt_kern_flag |= MNTK_SUSPEND; - msleep(&mp->mnt_secondary_writes, - MNT_MTX(mp), - PZERO, "mntdestroy3", 0); - } - printf("mount point secondary write ops completed\n"); - } - MNT_IUNLOCK(mp); + KASSERT(mp->mnt_ref == 0, + ("%s: invalid refcount in the drain path @ %s:%d", __func__, + __FILE__, __LINE__)); + if (mp->mnt_writeopcount != 0) + panic("vfs_mount_destroy: nonzero writeopcount"); + if (mp->mnt_secondary_writes != 0) + panic("vfs_mount_destroy: nonzero secondary_writes"); mp->mnt_vfc->vfc_refcount--; if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) { struct vnode *vp; @@ -538,18 +529,10 @@ vfs_mount_destroy(struct mount *mp) vprint("", vp); panic("unmount: dangling vnode"); } - MNT_ILOCK(mp); - if (mp->mnt_kern_flag & MNTK_MWAIT) - wakeup(mp); - if (mp->mnt_writeopcount != 0) - panic("vfs_mount_destroy: nonzero writeopcount"); - if (mp->mnt_secondary_writes != 0) - panic("vfs_mount_destroy: nonzero secondary_writes"); if (mp->mnt_nvnodelistsize != 0) panic("vfs_mount_destroy: nonzero nvnodelistsize"); - mp->mnt_writeopcount = -1000; - mp->mnt_nvnodelistsize = -1000; - mp->mnt_secondary_writes = -1000; + if (mp->mnt_lockref != 0) + panic("vfs_mount_destroy: nonzero lock refcount"); MNT_IUNLOCK(mp); #ifdef MAC mac_mount_destroy(mp); |