diff options
author | kib <kib@FreeBSD.org> | 2006-10-20 07:59:50 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2006-10-20 07:59:50 +0000 |
commit | 5f5bf9dadc00a7cc8dc6b37e8abf10a23ced17f4 (patch) | |
tree | 0c8a260910037a642d1dd98c671e553817da8b56 /sys | |
parent | 5b6bf7eb1c1469a528a6e28e72f035a6aed74314 (diff) | |
download | FreeBSD-src-5f5bf9dadc00a7cc8dc6b37e8abf10a23ced17f4.zip FreeBSD-src-5f5bf9dadc00a7cc8dc6b37e8abf10a23ced17f4.tar.gz |
Fix the race between devfs_fp_check and devfs_reclaim. Derefence the
vnode' v_rdev and increment the dev threadcount , as well as clear it
(in devfs_reclaim) under the dev_lock().
Reviewed by: tegge
Approved by: pjd (mentor)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/fs/devfs/devfs_vnops.c | 14 | ||||
-rw-r--r-- | sys/kern/kern_conf.c | 18 | ||||
-rw-r--r-- | sys/sys/conf.h | 2 |
3 files changed, 29 insertions, 5 deletions
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 4b348ec..55eaeb6 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -78,12 +78,14 @@ static int devfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp) { - *devp = fp->f_vnode->v_rdev; - if (*devp != fp->f_data) + *dswp = devvn_refthread(fp->f_vnode, devp); + if (*devp != fp->f_data) { + if (*dswp != NULL) + dev_relthread(*devp); return (ENXIO); + } KASSERT((*devp)->si_refcount > 0, ("devfs: un-referenced struct cdev *(%s)", devtoname(*devp))); - *dswp = dev_refthread(*devp); if (*dswp == NULL) return (ENXIO); return (0); @@ -965,13 +967,15 @@ devfs_reclaim(struct vop_reclaim_args *ap) vnode_destroy_vobject(vp); + dev_lock(); dev = vp->v_rdev; vp->v_rdev = NULL; - if (dev == NULL) + if (dev == NULL) { + dev_unlock(); return (0); + } - dev_lock(); dev->si_usecount -= vp->v_usecount; dev_unlock(); dev_rel(dev); diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index d331d53..5520da8 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -125,6 +125,24 @@ dev_refthread(struct cdev *dev) return (csw); } +struct cdevsw * +devvn_refthread(struct vnode *vp, struct cdev **devp) +{ + struct cdevsw *csw; + + mtx_assert(&devmtx, MA_NOTOWNED); + csw = NULL; + dev_lock(); + *devp = vp->v_rdev; + if (*devp != NULL) { + csw = (*devp)->si_devsw; + if (csw != NULL) + (*devp)->si_threadcount++; + } + dev_unlock(); + return (csw); +} + void dev_relthread(struct cdev *dev) { diff --git a/sys/sys/conf.h b/sys/sys/conf.h index f79f428..4f679fb 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -105,6 +105,7 @@ struct thread; struct uio; struct knote; struct clonedevs; +struct vnode; /* * Note: d_thread_t is provided as a transition aid for those drivers @@ -244,6 +245,7 @@ int clone_create(struct clonedevs **, struct cdevsw *, int *unit, struct cdev ** int count_dev(struct cdev *_dev); void destroy_dev(struct cdev *_dev); struct cdevsw *dev_refthread(struct cdev *_dev); +struct cdevsw *devvn_refthread(struct vnode *vp, struct cdev **devp); void dev_relthread(struct cdev *_dev); void dev_depends(struct cdev *_pdev, struct cdev *_cdev); void dev_ref(struct cdev *dev); |