Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/devfs/devfs_vnops.c
Show First 20 Lines • Show All 475 Lines • ▼ Show 20 Lines | if (de->de_dirent->d_type == DT_CHR) { | ||||
vp->v_type = VCHR; | vp->v_type = VCHR; | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
dev_lock(); | dev_lock(); | ||||
dev_refl(dev); | dev_refl(dev); | ||||
/* XXX: v_rdev should be protect by vnode lock */ | /* XXX: v_rdev should be protect by vnode lock */ | ||||
vp->v_rdev = dev; | vp->v_rdev = dev; | ||||
KASSERT(vp->v_usecount == 1, | KASSERT(vp->v_usecount == 1, | ||||
("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount)); | ("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount)); | ||||
dev->si_usecount += vp->v_usecount; | dev->si_usecount++; | ||||
/* Special casing of ttys for deadfs. Probably redundant. */ | /* Special casing of ttys for deadfs. Probably redundant. */ | ||||
dsw = dev->si_devsw; | dsw = dev->si_devsw; | ||||
if (dsw != NULL && (dsw->d_flags & D_TTY) != 0) | if (dsw != NULL && (dsw->d_flags & D_TTY) != 0) | ||||
vp->v_vflag |= VV_ISTTY; | vp->v_vflag |= VV_ISTTY; | ||||
dev_unlock(); | dev_unlock(); | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
if ((dev->si_flags & SI_ETERNAL) != 0) | if ((dev->si_flags & SI_ETERNAL) != 0) | ||||
vp->v_vflag |= VV_ETERNALDEV; | vp->v_vflag |= VV_ETERNALDEV; | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | devfs_close(struct vop_close_args *ap) | ||||
* Hack: a tty device that is a controlling terminal | * Hack: a tty device that is a controlling terminal | ||||
* has a reference from the session structure. | * has a reference from the session structure. | ||||
* We cannot easily tell that a character device is | * We cannot easily tell that a character device is | ||||
* a controlling terminal, unless it is the closing | * a controlling terminal, unless it is the closing | ||||
* process' controlling terminal. In that case, | * process' controlling terminal. In that case, | ||||
* if the reference count is 2 (this last descriptor | * if the reference count is 2 (this last descriptor | ||||
* plus the session), release the reference from the session. | * plus the session), release the reference from the session. | ||||
*/ | */ | ||||
if (td != NULL) { | if (vp->v_usecount == 2 && td != NULL) { | ||||
p = td->td_proc; | p = td->td_proc; | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
if (vp == p->p_session->s_ttyvp) { | if (vp == p->p_session->s_ttyvp) { | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
oldvp = NULL; | oldvp = NULL; | ||||
sx_xlock(&proctree_lock); | sx_xlock(&proctree_lock); | ||||
if (vp == p->p_session->s_ttyvp) { | if (vp == p->p_session->s_ttyvp) { | ||||
SESS_LOCK(p->p_session); | SESS_LOCK(p->p_session); | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
if (count_dev(dev) == 2 && | if (vp->v_usecount == 2 && vcount(vp) == 1 && | ||||
kib: These two checks for v_usecount == 2 are optimizations and can be committed separately.
OTOH I… | |||||
mjgAuthorUnsubmitted Done Inline ActionsI think the code was racy even beforehand. Moreover, I suspect there is a different bug here as well: if the target device gets a ref from another devfs mount point, this code will decline clearing it which probably was not intended. At least dragonfly only checks the current vnode and changing this would be a noop for typical use cases. That said, I consider the patch to maintain to be bug compatible. mjg: I think the code was racy even beforehand. Moreover, I suspect there is a different bug here as… | |||||
(vp->v_iflag & VI_DOOMED) == 0) { | (vp->v_iflag & VI_DOOMED) == 0) { | ||||
p->p_session->s_ttyvp = NULL; | p->p_session->s_ttyvp = NULL; | ||||
p->p_session->s_ttydp = NULL; | p->p_session->s_ttydp = NULL; | ||||
oldvp = vp; | oldvp = vp; | ||||
} | } | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
SESS_UNLOCK(p->p_session); | SESS_UNLOCK(p->p_session); | ||||
} | } | ||||
Show All 12 Lines | devfs_close(struct vop_close_args *ap) | ||||
* sum of the reference counts on all the aliased | * sum of the reference counts on all the aliased | ||||
* vnodes descends to one, we are on last close. | * vnodes descends to one, we are on last close. | ||||
*/ | */ | ||||
dsw = dev_refthread(dev, &ref); | dsw = dev_refthread(dev, &ref); | ||||
if (dsw == NULL) | if (dsw == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
dflags = 0; | dflags = 0; | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
if (vp->v_usecount == 1 && vcount(vp) == 1) | |||||
dflags |= FLASTCLOSE; | |||||
if (vp->v_iflag & VI_DOOMED) { | if (vp->v_iflag & VI_DOOMED) { | ||||
/* Forced close. */ | /* Forced close. */ | ||||
dflags |= FREVOKE | FNONBLOCK; | dflags |= FREVOKE | FNONBLOCK; | ||||
} else if (dsw->d_flags & D_TRACKCLOSE) { | } else if (dsw->d_flags & D_TRACKCLOSE) { | ||||
/* Keep device updated on status. */ | /* Keep device updated on status. */ | ||||
} else if (count_dev(dev) > 1) { | } else if ((dflags & FLASTCLOSE) == 0) { | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
dev_relthread(dev, ref); | dev_relthread(dev, ref); | ||||
return (0); | return (0); | ||||
} | } | ||||
if (count_dev(dev) == 1) | vholdnz(vp); | ||||
dflags |= FLASTCLOSE; | |||||
vholdl(vp); | |||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
vp_locked = VOP_ISLOCKED(vp); | vp_locked = VOP_ISLOCKED(vp); | ||||
VOP_UNLOCK(vp, 0); | VOP_UNLOCK(vp, 0); | ||||
KASSERT(dev->si_refcount > 0, | KASSERT(dev->si_refcount > 0, | ||||
("devfs_close() on un-referenced struct cdev *(%s)", devtoname(dev))); | ("devfs_close() on un-referenced struct cdev *(%s)", devtoname(dev))); | ||||
error = dsw->d_close(dev, ap->a_fflag | dflags, S_IFCHR, td); | error = dsw->d_close(dev, ap->a_fflag | dflags, S_IFCHR, td); | ||||
dev_relthread(dev, ref); | dev_relthread(dev, ref); | ||||
vn_lock(vp, vp_locked | LK_RETRY); | vn_lock(vp, vp_locked | LK_RETRY); | ||||
▲ Show 20 Lines • Show All 776 Lines • ▼ Show 20 Lines | devfs_reclaim_vchr(struct vop_reclaim_args *ap) | ||||
devfs_reclaim(ap); | devfs_reclaim(ap); | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
dev_lock(); | dev_lock(); | ||||
dev = vp->v_rdev; | dev = vp->v_rdev; | ||||
vp->v_rdev = NULL; | vp->v_rdev = NULL; | ||||
if (dev != NULL) | if (dev != NULL) | ||||
dev->si_usecount -= vp->v_usecount; | dev->si_usecount -= (vp->v_usecount > 0); | ||||
dev_unlock(); | dev_unlock(); | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
if (dev != NULL) | if (dev != NULL) | ||||
dev_rel(dev); | dev_rel(dev); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 537 Lines • Show Last 20 Lines |
These two checks for v_usecount == 2 are optimizations and can be committed separately.
OTOH I should note that changing v_usecount updates to atomic make these checks racy.