summaryrefslogtreecommitdiffstats
path: root/sys/fs/devfs
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2007-07-03 17:42:37 +0000
committerkib <kib@FreeBSD.org>2007-07-03 17:42:37 +0000
commit0ae42a409556e8dd0fe24abe1db9159f80d1d3a0 (patch)
treecb3718e3a301e214d2cb4e0f64469a55da81c6f6 /sys/fs/devfs
parente711aeee1e917cbb6158fb8c6acd203b13f521df (diff)
downloadFreeBSD-src-0ae42a409556e8dd0fe24abe1db9159f80d1d3a0.zip
FreeBSD-src-0ae42a409556e8dd0fe24abe1db9159f80d1d3a0.tar.gz
Since rev. 1.199 of sys/kern/kern_conf.c, the thread that calls
destroy_dev() from d_close() cdev method would self-deadlock. devfs_close() bump device thread reference counter, and destroy_dev() sleeps, waiting for si_threadcount to reach zero for cdev without d_purge method. destroy_dev_sched() could be used instead from d_close(), to schedule execution of destroy_dev() in another context. The destroy_dev_sched_drain() function can be used to drain the scheduled calls to destroy_dev_sched(). Similarly, drain_dev_clone_events() drains the events clone to make sure no lingering devices are left after dev_clone event handler deregistered. make_dev_credf(MAKEDEV_REF) function should be used from dev_clone event handlers instead of make_dev()/make_dev_cred() to ensure that created device has reference counter bumped before cdev mutex is dropped inside make_dev(). Reviewed by: tegge (early versions), njl (programming interface) Debugging help and testing by: Peter Holm Approved by: re (kensmith)
Diffstat (limited to 'sys/fs/devfs')
-rw-r--r--sys/fs/devfs/devfs_int.h6
-rw-r--r--sys/fs/devfs/devfs_vnops.c13
2 files changed, 19 insertions, 0 deletions
diff --git a/sys/fs/devfs/devfs_int.h b/sys/fs/devfs/devfs_int.h
index 2bd7f99..3848ab4 100644
--- a/sys/fs/devfs/devfs_int.h
+++ b/sys/fs/devfs/devfs_int.h
@@ -47,11 +47,16 @@ struct cdev_priv {
u_int cdp_flags;
#define CDP_ACTIVE (1 << 0)
+#define CDP_SCHED_DTR (1 << 1)
u_int cdp_inuse;
u_int cdp_maxdirent;
struct devfs_dirent **cdp_dirents;
struct devfs_dirent *cdp_dirent0;
+
+ TAILQ_ENTRY(cdev_priv) cdp_dtr_list;
+ void (*cdp_dtr_cb)(void *);
+ void *cdp_dtr_cb_arg;
};
struct cdev *devfs_alloc(void);
@@ -62,6 +67,7 @@ void devfs_destroy(struct cdev *dev);
extern struct unrhdr *devfs_inos;
extern struct mtx devmtx;
extern struct mtx devfs_de_interlock;
+extern struct sx clone_drain_lock;
extern TAILQ_HEAD(cdev_priv_list, cdev_priv) cdevp_list;
#endif /* _KERNEL */
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 0acf99b..fcf3b3a 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -75,6 +75,8 @@ static struct fileops devfs_ops_f;
struct mtx devfs_de_interlock;
MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF);
+struct sx clone_drain_lock;
+SX_SYSINIT(clone_drain_lock, &clone_drain_lock, "clone events drain lock");
static int
devfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp)
@@ -618,8 +620,19 @@ devfs_lookupx(struct vop_lookup_args *ap, int *dm_unlock)
break;
cdev = NULL;
+ DEVFS_DMP_HOLD(dmp);
+ sx_xunlock(&dmp->dm_lock);
+ sx_slock(&clone_drain_lock);
EVENTHANDLER_INVOKE(dev_clone,
td->td_ucred, pname, strlen(pname), &cdev);
+ sx_sunlock(&clone_drain_lock);
+ sx_xlock(&dmp->dm_lock);
+ if (DEVFS_DMP_DROP(dmp)) {
+ *dm_unlock = 0;
+ sx_xunlock(&dmp->dm_lock);
+ devfs_unmount_final(dmp);
+ return (ENOENT);
+ }
if (cdev == NULL)
break;
OpenPOWER on IntegriCloud