diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2007-07-03 17:42:37 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2007-07-03 17:42:37 +0000 |
commit | de10ffa527504dff12f2ba6e4b345dc719f71e6e (patch) | |
tree | cb3718e3a301e214d2cb4e0f64469a55da81c6f6 /sys/fs/devfs | |
parent | 7aee5992a5f546d5610dd7c80c22eef7702a83f9 (diff) | |
download | src-de10ffa527504dff12f2ba6e4b345dc719f71e6e.tar.gz src-de10ffa527504dff12f2ba6e4b345dc719f71e6e.zip |
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)
Notes
Notes:
svn path=/head/; revision=171181
Diffstat (limited to 'sys/fs/devfs')
-rw-r--r-- | sys/fs/devfs/devfs_int.h | 6 | ||||
-rw-r--r-- | sys/fs/devfs/devfs_vnops.c | 13 |
2 files changed, 19 insertions, 0 deletions
diff --git a/sys/fs/devfs/devfs_int.h b/sys/fs/devfs/devfs_int.h index 2bd7f99da73a..3848ab4c1904 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 0acf99bfdcf6..fcf3b3a42f58 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; |