summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2006-10-20 07:59:50 +0000
committerkib <kib@FreeBSD.org>2006-10-20 07:59:50 +0000
commit5f5bf9dadc00a7cc8dc6b37e8abf10a23ced17f4 (patch)
tree0c8a260910037a642d1dd98c671e553817da8b56
parent5b6bf7eb1c1469a528a6e28e72f035a6aed74314 (diff)
downloadFreeBSD-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)
-rw-r--r--sys/fs/devfs/devfs_vnops.c14
-rw-r--r--sys/kern/kern_conf.c18
-rw-r--r--sys/sys/conf.h2
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);
OpenPOWER on IntegriCloud