Index: fs/devfs/devfs_int.h =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/fs/devfs/devfs_int.h,v retrieving revision 1.3 diff -u -r1.3 devfs_int.h --- fs/devfs/devfs_int.h 18 Oct 2006 11:17:14 -0000 1.3 +++ fs/devfs/devfs_int.h 17 Apr 2007 10:34:42 -0000 @@ -47,11 +47,14 @@ 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; }; struct cdev *devfs_alloc(void); @@ -62,6 +65,7 @@ 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 */ Index: fs/devfs/devfs_vnops.c =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/fs/devfs/devfs_vnops.c,v retrieving revision 1.143 diff -u -r1.143 devfs_vnops.c --- fs/devfs/devfs_vnops.c 4 Apr 2007 09:11:32 -0000 1.143 +++ fs/devfs/devfs_vnops.c 17 Apr 2007 10:34:42 -0000 @@ -75,6 +75,8 @@ 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 @@ 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; Index: kern/kern_conf.c =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/kern/kern_conf.c,v retrieving revision 1.201 diff -u -r1.201 kern_conf.c --- kern/kern_conf.c 2 Feb 2007 22:27:45 -0000 1.201 +++ kern/kern_conf.c 17 Apr 2007 10:34:42 -0000 @@ -39,9 +39,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -50,7 +52,10 @@ struct mtx devmtx; static void destroy_devl(struct cdev *dev); -static struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr, + +#define MAKEDEV_REF 0x1 +static struct cdev *make_dev_credv(int flags, + struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, va_list ap); @@ -527,7 +532,8 @@ } static struct cdev * -make_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, +make_dev_credv(int flags, struct cdevsw *devsw, int minornr, + struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, va_list ap) { struct cdev *dev; @@ -541,6 +547,8 @@ dev = devfs_alloc(); dev_lock(); dev = newdev(devsw, minornr, dev); + if (flags & MAKEDEV_REF) + dev_refl(dev); if (dev->si_flags & SI_CHEAPCLONE && dev->si_flags & SI_NAMED) { /* @@ -583,7 +591,7 @@ va_list ap; va_start(ap, fmt); - dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap); + dev = make_dev_credv(0, devsw, minornr, NULL, uid, gid, mode, fmt, ap); va_end(ap); return (dev); } @@ -596,7 +604,36 @@ va_list ap; va_start(ap, fmt); - dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap); + dev = make_dev_credv(0, devsw, minornr, cr, uid, gid, mode, fmt, ap); + va_end(ap); + + return (dev); +} + +struct cdev * +make_dev_ref(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode, + const char *fmt, ...) +{ + struct cdev *dev; + va_list ap; + + va_start(ap, fmt); + dev = make_dev_credv(MAKEDEV_REF, devsw, minornr, NULL, uid, gid, mode, + fmt, ap); + va_end(ap); + return (dev); +} + +struct cdev * +make_dev_cred_ref(struct cdevsw *devsw, int minornr, struct ucred *cr, + uid_t uid, gid_t gid, int mode, const char *fmt, ...) +{ + struct cdev *dev; + va_list ap; + + va_start(ap, fmt); + dev = make_dev_credv(MAKEDEV_REF, devsw, minornr, cr, uid, gid, mode, + fmt, ap); va_end(ap); return (dev); @@ -700,8 +737,10 @@ LIST_REMOVE(dev, si_list); /* If cdevsw has no more struct cdev *'s, clean it */ - if (LIST_EMPTY(&csw->d_devs)) + if (LIST_EMPTY(&csw->d_devs)) { fini_cdevsw(csw); + wakeup(&csw->d_devs); + } } dev->si_flags &= ~SI_ALIAS; dev->si_refcount--; /* Avoid race with dev_rel() */ @@ -887,21 +926,98 @@ void clone_cleanup(struct clonedevs **cdp) { - struct cdev *dev, *tdev; + struct cdev *dev; + struct cdev_priv *cp; struct clonedevs *cd; cd = *cdp; if (cd == NULL) return; dev_lock(); - LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) { + while (!LIST_EMPTY(&cd->head)) { + dev = LIST_FIRST(&cd->head); + LIST_REMOVE(dev, si_clone); KASSERT(dev->si_flags & SI_CLONELIST, ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); - KASSERT(dev->si_flags & SI_NAMED, - ("Driver has goofed in cloning underways udev %x", dev->si_drv0)); - destroy_devl(dev); + dev->si_flags &= ~SI_CLONELIST; + cp = dev->si_priv; + if (!(cp->cdp_flags & CDP_SCHED_DTR)) { + cp->cdp_flags |= CDP_SCHED_DTR; + KASSERT(dev->si_flags & SI_NAMED, + ("Driver has goofed in cloning underways udev %x", dev->si_drv0)); + destroy_devl(dev); + } } dev_unlock(); free(cd, M_DEVBUF); *cdp = NULL; } + +static TAILQ_HEAD(, cdev_priv) dev_ddtr = + TAILQ_HEAD_INITIALIZER(dev_ddtr); +static struct task dev_dtr_task; + +static void +destroy_dev_tq(void *ctx, int pending) +{ + struct cdev_priv *cp; + struct cdev *dev; + + dev_lock(); + while (!TAILQ_EMPTY(&dev_ddtr)) { + cp = TAILQ_FIRST(&dev_ddtr); + dev = &cp->cdp_c; + KASSERT(cp->cdp_flags & CDP_SCHED_DTR, + ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); + TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); + destroy_devl(dev); + dev_unlock(); + dev_rel(dev); + dev_lock(); + } + dev_unlock(); +} + +int +destroy_dev_sched(struct cdev *dev) +{ + struct cdev_priv *cp; + + cp = dev->si_priv; + dev_lock(); + if (cp->cdp_flags & CDP_SCHED_DTR) { + dev_unlock(); + return (0); + } + dev_refl(dev); + cp->cdp_flags |= CDP_SCHED_DTR; + TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); + dev_unlock(); + taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task); + return (1); +} + +void +destroy_dev_sched_drain(struct cdevsw *csw) +{ + dev_lock(); + while (!LIST_EMPTY(&csw->d_devs)) { + msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); + } + dev_unlock(); +} + +void +drain_dev_clone_events(void) +{ + sx_xlock(&clone_drain_lock); + sx_xunlock(&clone_drain_lock); +} + +static void +devdtr_init(void *dummy __unused) +{ + TASK_INIT(&dev_dtr_task, 0, destroy_dev_tq, NULL); +} + +SYSINIT(devdtr, SI_SUB_DEVFS, SI_ORDER_SECOND, devdtr_init, NULL); Index: kern/tty_pts.c =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/kern/tty_pts.c,v retrieving revision 1.14 diff -u -r1.14 tty_pts.c --- kern/tty_pts.c 8 Jan 2007 17:49:59 -0000 1.14 +++ kern/tty_pts.c 17 Apr 2007 10:34:42 -0000 @@ -881,11 +881,10 @@ * opened, or some way to tell devfs that "this had better be for * an open() or we won't create a device". */ - pt->pt_devc = devc = make_dev_cred(&ptc_cdevsw, + pt->pt_devc = devc = make_dev_cred_ref(&ptc_cdevsw, NUM_TO_MINOR(pt->pt_num), cred, UID_ROOT, GID_WHEEL, 0666, "pty/%d", pt->pt_num); - dev_ref(devc); devc->si_drv1 = pt; devc->si_tty = pt->pt_tty; *dev = devc; Index: kern/tty_pty.c =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/kern/tty_pty.c,v retrieving revision 1.151 diff -u -r1.151 tty_pty.c --- kern/tty_pty.c 6 Nov 2006 13:42:01 -0000 1.151 +++ kern/tty_pty.c 17 Apr 2007 10:34:42 -0000 @@ -785,9 +785,8 @@ u += name[4] - 'a' + 10; else return; - *dev = make_dev_cred(&ptc_cdevsw, u, cr, + *dev = make_dev_cred_ref(&ptc_cdevsw, u, cr, UID_ROOT, GID_WHEEL, 0666, "pty%c%r", names[u / 32], u % 32); - dev_ref(*dev); (*dev)->si_flags |= SI_CHEAPCLONE; return; } Index: kern/tty_tty.c =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/kern/tty_tty.c,v retrieving revision 1.59 diff -u -r1.59 tty_tty.c --- kern/tty_tty.c 27 Sep 2006 16:41:15 -0000 1.59 +++ kern/tty_tty.c 17 Apr 2007 10:34:42 -0000 @@ -31,8 +31,12 @@ #include #include #include +#include #include +#include +#include + static d_open_t cttyopen; static struct cdevsw ctty_cdevsw = { @@ -60,6 +64,11 @@ return; if (strcmp(name, "tty")) return; + sx_sunlock(&clone_drain_lock); + mtx_lock(&Giant); + sx_slock(&proctree_lock); + sx_slock(&clone_drain_lock); + dev_lock(); if (!(curthread->td_proc->p_flag & P_CONTROLT)) *dev = ctty; else if (curthread->td_proc->p_session->s_ttyvp == NULL) @@ -70,7 +79,10 @@ *dev = ctty; } else *dev = curthread->td_proc->p_session->s_ttyvp->v_rdev; - dev_ref(*dev); + dev_refl(*dev); + dev_unlock(); + sx_sunlock(&proctree_lock); + mtx_unlock(&Giant); } static void Index: sys/conf.h =================================================================== RCS file: /usr/local/arch/ncvs/src/sys/sys/conf.h,v retrieving revision 1.231 diff -u -r1.231 conf.h --- sys/conf.h 2 Feb 2007 22:27:45 -0000 1.231 +++ sys/conf.h 17 Apr 2007 10:34:42 -0000 @@ -244,6 +244,9 @@ int count_dev(struct cdev *_dev); void destroy_dev(struct cdev *_dev); +int destroy_dev_sched(struct cdev *dev); +void destroy_dev_sched_drain(struct cdevsw *csw); +void drain_dev_clone_events(void); struct cdevsw *dev_refthread(struct cdev *_dev); struct cdevsw *devvn_refthread(struct vnode *vp, struct cdev **devp); void dev_relthread(struct cdev *_dev); @@ -254,9 +257,15 @@ void dev_strategy(struct cdev *dev, struct buf *bp); struct cdev *make_dev(struct cdevsw *_devsw, int _minor, uid_t _uid, gid_t _gid, int _perms, const char *_fmt, ...) __printflike(6, 7); +struct cdev *make_dev_ref(struct cdevsw *_devsw, int _minor, + uid_t _uid, gid_t _gid, + int _perms, const char *_fmt, ...) __printflike(6, 7); struct cdev *make_dev_cred(struct cdevsw *_devsw, int _minor, struct ucred *_cr, uid_t _uid, gid_t _gid, int _perms, const char *_fmt, ...) __printflike(7, 8); +struct cdev *make_dev_cred_ref(struct cdevsw *_devsw, int _minor, + struct ucred *_cr, uid_t _uid, gid_t _gid, int _perms, + const char *_fmt, ...) __printflike(7, 8); struct cdev *make_dev_alias(struct cdev *_pdev, const char *_fmt, ...) __printflike(2, 3); int dev2unit(struct cdev *_dev); void dev_lock(void);