diff options
author | kib <kib@FreeBSD.org> | 2011-07-13 21:07:41 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2011-07-13 21:07:41 +0000 |
commit | e3d33b853d78d9c9665b164f2343f53be9338b44 (patch) | |
tree | 450324ad4ac0e9626ed44ee9323a092155663468 | |
parent | 6620a4474b278d01edd0ca6fc446c01f9fa8b619 (diff) | |
download | FreeBSD-src-e3d33b853d78d9c9665b164f2343f53be9338b44.zip FreeBSD-src-e3d33b853d78d9c9665b164f2343f53be9338b44.tar.gz |
While fixing the looping of a thread while devfs vnode is reclaimed,
r179247 introduced a possibility of devfs_allocv() returning spurious
ENOENT. If the vnode is selected by vnlru daemon for reclamation, then
devfs_allocv() can get ENOENT from vget() due to devfs_close() dropping
vnode lock around the call to cdevsw d_close method.
Use LK_RETRY in the vget() call, and do some part of the devfs_reclaim()
work in devfs_allocv(), clearing vp->v_data and de->de_vnode. Retry the
allocation of the vnode, now with de->de_vnode == NULL.
The check vp->v_data == NULL at the start of devfs_close() cannot be
affected by the change, since vnode lock must be held while VI_DOOMED
is set, and only dropped after the check.
Reported and tested by: Kohji Okuno <okuno.kohji jp panasonic com>
Reviewed by: attilio
MFC after: 3 weeks
-rw-r--r-- | sys/fs/devfs/devfs_vnops.c | 18 |
1 files changed, 12 insertions, 6 deletions
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index bf6dab8..955bd8b 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -397,6 +397,7 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode, sx_xunlock(&dmp->dm_lock); return (ENOENT); } +loop: DEVFS_DE_HOLD(de); DEVFS_DMP_HOLD(dmp); mtx_lock(&devfs_de_interlock); @@ -405,16 +406,21 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode, VI_LOCK(vp); mtx_unlock(&devfs_de_interlock); sx_xunlock(&dmp->dm_lock); - error = vget(vp, lockmode | LK_INTERLOCK, curthread); + vget(vp, lockmode | LK_INTERLOCK | LK_RETRY, curthread); sx_xlock(&dmp->dm_lock); if (devfs_allocv_drop_refs(0, dmp, de)) { - if (error == 0) - vput(vp); + vput(vp); return (ENOENT); } - else if (error) { - sx_xunlock(&dmp->dm_lock); - return (error); + else if ((vp->v_iflag & VI_DOOMED) != 0) { + mtx_lock(&devfs_de_interlock); + if (de->de_vnode == vp) { + de->de_vnode = NULL; + vp->v_data = NULL; + } + mtx_unlock(&devfs_de_interlock); + vput(vp); + goto loop; } sx_xunlock(&dmp->dm_lock); *vpp = vp; |