diff options
Diffstat (limited to 'sys/fs/devfs/devfs_vnops.c')
-rw-r--r-- | sys/fs/devfs/devfs_vnops.c | 74 |
1 files changed, 65 insertions, 9 deletions
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index b711573..4b348ec 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -71,7 +71,7 @@ static struct fileops devfs_ops_f; #include <fs/devfs/devfs.h> #include <fs/devfs/devfs_int.h> -static struct mtx devfs_de_interlock; +struct mtx devfs_de_interlock; MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF); static int @@ -286,7 +286,7 @@ devfs_close(struct vop_close_args *ap) struct thread *td = ap->a_td; struct cdev *dev = vp->v_rdev; struct cdevsw *dsw; - int error; + int vp_locked, error; /* * Hack: a tty device that is a controlling terminal @@ -334,7 +334,10 @@ devfs_close(struct vop_close_args *ap) dev_relthread(dev); return (0); } + vholdl(vp); VI_UNLOCK(vp); + vp_locked = VOP_ISLOCKED(vp, td); + VOP_UNLOCK(vp, 0, td); KASSERT(dev->si_refcount > 0, ("devfs_close() on un-referenced struct cdev *(%s)", devtoname(dev))); if (!(dsw->d_flags & D_NEEDGIANT)) { @@ -345,6 +348,8 @@ devfs_close(struct vop_close_args *ap) error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); } dev_relthread(dev); + vn_lock(vp, vp_locked | LK_RETRY, td); + vdrop(vp); return (error); } @@ -568,7 +573,14 @@ devfs_lookupx(struct vop_lookup_args *ap, int *dm_unlock) return (error); } + DEVFS_DMP_HOLD(dmp); devfs_populate(dmp); + if (DEVFS_DMP_DROP(dmp)) { + *dm_unlock = 0; + sx_xunlock(&dmp->dm_lock); + devfs_unmount_final(dmp); + return (ENOENT); + } dd = dvp->v_data; de = devfs_find(dd, cnp->cn_nameptr, cnp->cn_namelen); while (de == NULL) { /* While(...) so we can use break */ @@ -590,7 +602,14 @@ devfs_lookupx(struct vop_lookup_args *ap, int *dm_unlock) if (cdev == NULL) break; + DEVFS_DMP_HOLD(dmp); devfs_populate(dmp); + if (DEVFS_DMP_DROP(dmp)) { + *dm_unlock = 0; + sx_xunlock(&dmp->dm_lock); + devfs_unmount_final(dmp); + return (ENOENT); + } dev_lock(); dde = &cdev->si_priv->cdp_dirents[dmp->dm_idx]; @@ -875,7 +894,15 @@ devfs_readdir(struct vop_readdir_args *ap) dmp = VFSTODEVFS(ap->a_vp->v_mount); sx_xlock(&dmp->dm_lock); + DEVFS_DMP_HOLD(dmp); devfs_populate(dmp); + if (DEVFS_DMP_DROP(dmp)) { + sx_xunlock(&dmp->dm_lock); + devfs_unmount_final(dmp); + if (tmp_ncookies != NULL) + ap->a_ncookies = tmp_ncookies; + return (EIO); + } error = 0; de = ap->a_vp->v_data; off = 0; @@ -927,7 +954,7 @@ devfs_reclaim(struct vop_reclaim_args *ap) struct vnode *vp = ap->a_vp; struct devfs_dirent *de; struct cdev *dev; - + mtx_lock(&devfs_de_interlock); de = vp->v_data; if (de != NULL) { @@ -964,7 +991,7 @@ devfs_remove(struct vop_remove_args *ap) de = vp->v_data; if (de->de_cdp == NULL) { TAILQ_REMOVE(&dd->de_dlist, de, de_list); - devfs_delete(dmp, de); + devfs_delete(dmp, de, 1); } else { de->de_flags |= DE_WHITEOUT; } @@ -991,6 +1018,17 @@ devfs_revoke(struct vop_revoke_args *ap) dev = vp->v_rdev; cdp = dev->si_priv; + + dev_lock(); + cdp->cdp_inuse++; + dev_unlock(); + + vhold(vp); + vgone(vp); + vdrop(vp); + + VOP_UNLOCK(vp,0,curthread); + loop: for (;;) { mtx_lock(&devfs_de_interlock); dev_lock(); @@ -1000,18 +1038,20 @@ devfs_revoke(struct vop_revoke_args *ap) if (de == NULL) continue; - vp2 = de->de_vnode; + vp2 = de->de_vnode; if (vp2 != NULL) { - de->de_vnode = NULL; dev_unlock(); VI_LOCK(vp2); mtx_unlock(&devfs_de_interlock); - vholdl(vp2); - VI_UNLOCK(vp2); + if (vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK, + curthread)) + goto loop; + vhold(vp2); vgone(vp2); vdrop(vp2); + vput(vp2); break; - } + } } if (vp2 != NULL) { continue; @@ -1020,6 +1060,16 @@ devfs_revoke(struct vop_revoke_args *ap) mtx_unlock(&devfs_de_interlock); break; } + dev_lock(); + cdp->cdp_inuse--; + if (!(cdp->cdp_flags & CDP_ACTIVE) && cdp->cdp_inuse == 0) { + TAILQ_REMOVE(&cdevp_list, cdp, cdp_list); + dev_unlock(); + dev_rel(&cdp->cdp_c); + } else + dev_unlock(); + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread); return (0); } @@ -1031,7 +1081,13 @@ devfs_rioctl(struct vop_ioctl_args *ap) dmp = VFSTODEVFS(ap->a_vp->v_mount); sx_xlock(&dmp->dm_lock); + DEVFS_DMP_HOLD(dmp); devfs_populate(dmp); + if (DEVFS_DMP_DROP(dmp)) { + sx_xunlock(&dmp->dm_lock); + devfs_unmount_final(dmp); + return (ENOENT); + } error = devfs_rules_ioctl(dmp, ap->a_command, ap->a_data, ap->a_td); sx_xunlock(&dmp->dm_lock); return (error); |