diff options
author | kib <kib@FreeBSD.org> | 2006-09-18 13:23:08 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2006-09-18 13:23:08 +0000 |
commit | ecf34f450410ca3aac345b52bece9c0fb4ae459c (patch) | |
tree | a22547e48d6aa3545a66b54d100fbcab374bd630 /sys/fs/devfs/devfs_vfsops.c | |
parent | e84508ee5e22076fddd4af5d0ca3092768e7e584 (diff) | |
download | FreeBSD-src-ecf34f450410ca3aac345b52bece9c0fb4ae459c.zip FreeBSD-src-ecf34f450410ca3aac345b52bece9c0fb4ae459c.tar.gz |
Resolve the devfs deadlock caused by LOR between devfs_mount->dm_lock and
vnode lock in devfs_allocv. Do this by temporary dropping dm_lock around
vnode locking.
For safe operation, add hold counters for both devfs_mount and devfs_dirent,
and DE_DOOMED flag for devfs_dirent. The facilities allow to continue after
dropping of the dm_lock, by making sure that referenced memory does not
disappear.
Reviewed by: tegge
Tested by: kris
Approved by: kan (mentor)
PR: kern/102335
Diffstat (limited to 'sys/fs/devfs/devfs_vfsops.c')
-rw-r--r-- | sys/fs/devfs/devfs_vfsops.c | 24 |
1 files changed, 20 insertions, 4 deletions
diff --git a/sys/fs/devfs/devfs_vfsops.c b/sys/fs/devfs/devfs_vfsops.c index 17db07a..8c1069a 100644 --- a/sys/fs/devfs/devfs_vfsops.c +++ b/sys/fs/devfs/devfs_vfsops.c @@ -77,6 +77,7 @@ devfs_mount(struct mount *mp, struct thread *td) fmp = malloc(sizeof *fmp, M_DEVFS, M_WAITOK | M_ZERO); fmp->dm_idx = alloc_unr(devfs_unr); sx_init(&fmp->dm_lock, "devfsmount"); + fmp->dm_holdcnt = 1; mp->mnt_flag |= MNT_LOCAL; mp->mnt_kern_flag |= MNTK_MPSAFE; @@ -104,14 +105,25 @@ devfs_mount(struct mount *mp, struct thread *td) return (0); } +void +devfs_unmount_final(struct devfs_mount *fmp) +{ + sx_destroy(&fmp->dm_lock); + free(fmp, M_DEVFS); +} + static int devfs_unmount(struct mount *mp, int mntflags, struct thread *td) { int error; int flags = 0; struct devfs_mount *fmp; + int hold; + u_int idx; fmp = VFSTODEVFS(mp); + KASSERT(fmp->dm_mount != NULL, + ("devfs_unmount unmounted devfs_mount")); /* There is 1 extra root vnode reference from devfs_mount(). */ error = vflush(mp, 1, flags, td); if (error) @@ -119,11 +131,14 @@ devfs_unmount(struct mount *mp, int mntflags, struct thread *td) sx_xlock(&fmp->dm_lock); devfs_cleanup(fmp); devfs_rules_cleanup(fmp); - sx_xunlock(&fmp->dm_lock); + fmp->dm_mount = NULL; + hold = --fmp->dm_holdcnt; mp->mnt_data = NULL; - sx_destroy(&fmp->dm_lock); - free_unr(devfs_unr, fmp->dm_idx); - free(fmp, M_DEVFS); + idx = fmp->dm_idx; + sx_xunlock(&fmp->dm_lock); + free_unr(devfs_unr, idx); + if (hold == 0) + devfs_unmount_final(fmp); return 0; } @@ -137,6 +152,7 @@ devfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) struct devfs_mount *dmp; dmp = VFSTODEVFS(mp); + sx_xlock(&dmp->dm_lock); error = devfs_allocv(dmp->dm_rootdir, mp, &vp, td); if (error) return (error); |