Index: sys/kern/kern_conf.c =================================================================== --- sys/kern/kern_conf.c +++ sys/kern/kern_conf.c @@ -54,7 +54,7 @@ static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); struct mtx devmtx; -static void destroy_devl(struct cdev *dev); +static void destroy_devl(struct cdev *dev, bool failure); static int destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg); static void destroy_dev_tq(void *ctx, int pending); @@ -1092,14 +1092,16 @@ } static void -destroy_devl(struct cdev *dev) +destroy_devl(struct cdev *dev, bool failure) { struct cdevsw *csw; struct cdev_privdata *p; struct cdev_priv *cdp; + bool incomplete; + incomplete = (dev->si_flags & SI_NAMED) == 0; mtx_assert(&devmtx, MA_OWNED); - KASSERT(dev->si_flags & SI_NAMED, + KASSERT(failure || !incomplete, ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); KASSERT((dev->si_flags & SI_ETERNAL) == 0, ("WARNING: Driver mistake: destroy_dev on eternal %d\n", @@ -1128,8 +1130,10 @@ } /* Kill our children */ + KASSERT(!incomplete || LIST_EMPTY(&dev->si_children), + ("Children on partially constructed device %d\n", dev2unit(dev))); while (!LIST_EMPTY(&dev->si_children)) - destroy_devl(LIST_FIRST(&dev->si_children)); + destroy_devl(LIST_FIRST(&dev->si_children), false); /* Remove from clone list */ if (dev->si_flags & SI_CLONELIST) { @@ -1239,7 +1243,7 @@ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); dev_lock(); - destroy_devl(dev); + destroy_devl(dev, false); dev_unlock_and_free(); } @@ -1389,6 +1393,79 @@ return (1); } +#ifdef INVARIANTS +static bool +clone_on_list(struct clonedevs **cdp, struct cdev *dev) +{ + struct clonedevs *cd; + struct cdev *dp; + + cd = *cdp; + mtx_assert(&devmtx, MA_OWNED); + LIST_FOREACH(dp, &cd->head, si_clone) { + if (dp == dev) + return (true); + } + return (false); +} +#endif /* INVARIANTS */ + +static void +clone_destroy(struct clonedevs **cdp, struct cdev *dev, bool failure) +{ + struct cdev_priv *cp; + + KASSERT((dev->si_flags & SI_CLONELIST) != 0, + ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); + KASSERT(clone_on_list(cdp, dev), + ("Dev %p(%s) not on passed in clonelist", dev, dev->si_name)); + + cp = cdev2priv(dev); + if (!(cp->cdp_flags & CDP_SCHED_DTR)) { + cp->cdp_flags |= CDP_SCHED_DTR; + KASSERT(failure || (dev->si_flags & SI_NAMED) != 0, + ("Driver has goofed in cloning underways udev %jx unit %x", + (uintmax_t)dev2udev(dev), dev2unit(dev))); + destroy_devl(dev, failure); + } else { + /* + * Remove from clone list, which would otherwise be done above + * in destroy_devl(). + */ + LIST_REMOVE(dev, si_clone); + dev->si_flags &= ~SI_CLONELIST; + } +} + +/* + * clone_failed must only be called on a device that hasn't been fully + * constructed with the make_dev* family of functions. This is used in the + * exceptional case that something has gone wrong between clone_create() and + * make_dev*(), leading the driver to not be able to finish the process. + */ +void +clone_failed(struct clonedevs **cdp, struct cdevsw *csw, int unit) +{ + struct cdev *dev, *si; + + dev_lock(); + KASSERT(csw->d_flags & D_NEEDMINOR, + ("clone_failed() on cdevsw without minor numbers")); + dev = NULL; + LIST_FOREACH(si, &csw->d_devs, si_list) { + if (dev2unit(si) == unit) { + dev = si; + break; + } + } + + KASSERT(dev != NULL, + ("Attempt to mark an absent unit %d as failed clone in driver \"%s\"", + unit, csw->d_name)); + clone_destroy(cdp, dev, true); + dev_unlock_and_free(); +} + /* * Kill everything still on the list. The driver should already have * disposed of any softc hung of the struct cdev *'s at this time. @@ -1397,27 +1474,15 @@ clone_cleanup(struct clonedevs **cdp) { struct cdev *dev; - struct cdev_priv *cp; struct clonedevs *cd; - + cd = *cdp; if (cd == NULL) return; dev_lock(); 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)); - dev->si_flags &= ~SI_CLONELIST; - cp = cdev2priv(dev); - 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 %jx unit %x", - (uintmax_t)dev2udev(dev), dev2unit(dev))); - destroy_devl(dev); - } + clone_destroy(cdp, dev, false); } dev_unlock_and_free(); free(cd, M_DEVBUF); @@ -1445,7 +1510,7 @@ TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); cb = cp->cdp_dtr_cb; cb_arg = cp->cdp_dtr_cb_arg; - destroy_devl(dev); + destroy_devl(dev, false); dev_unlock_and_free(); dev_rel(dev); if (cb != NULL) Index: sys/net/if_tuntap.c =================================================================== --- sys/net/if_tuntap.c +++ sys/net/if_tuntap.c @@ -548,8 +548,12 @@ dev = NULL; i = clone_create(&drv->clones, &drv->cdevsw, &unit, &dev, 0); /* No preexisting struct cdev *, create one */ - if (i != 0) + if (i != 0) { i = tun_create_device(drv, unit, NULL, &dev, name); + if (i != 0) + clone_failed(&drv->clones, &drv->cdevsw, unit); + } + if (i == 0) tuncreate(dev); @@ -610,6 +614,8 @@ } i = tun_create_device(drv, u, cred, dev, name); + if (i != 0) + clone_failed(&drv->clones, &drv->cdevsw, u); } if (i == 0) if_clone_create(name, namelen, NULL); @@ -811,8 +817,9 @@ tp->tun_drv = drv; make_dev_args_init(&args); + args.mda_flags = MAKEDEV_CHECKNAME; if (cr != NULL) - args.mda_flags = MAKEDEV_REF; + args.mda_flags |= MAKEDEV_REF; args.mda_devsw = &drv->cdevsw; args.mda_cr = cr; args.mda_uid = UID_UUCP; Index: sys/sys/conf.h =================================================================== --- sys/sys/conf.h +++ sys/sys/conf.h @@ -233,6 +233,7 @@ #define CLONE_UNITMASK 0xfffff #define CLONE_FLAG0 (CLONE_UNITMASK + 1) int clone_create(struct clonedevs **, struct cdevsw *, int *unit, struct cdev **dev, int extra); +void clone_failed(struct clonedevs **, struct cdevsw *, int unit); #define MAKEDEV_REF 0x01 #define MAKEDEV_WHTOUT 0x02