diff options
author | hselasky <hselasky@FreeBSD.org> | 2015-02-05 20:49:13 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2015-02-05 20:49:13 +0000 |
commit | 34369aa374076807c478e6f5977f216d893c74fc (patch) | |
tree | ab0849694fbf99b99b61390be9f21dd68b7ae99d /sys/kern/kern_conf.c | |
parent | 181bf69d80b7bd5865db0ef46e187479e37c79cf (diff) | |
download | FreeBSD-src-34369aa374076807c478e6f5977f216d893c74fc.zip FreeBSD-src-34369aa374076807c478e6f5977f216d893c74fc.tar.gz |
MFC r277179, r277199 and r277391:
Add a kernel function to delist our kernel character devices, so that
the device name can be re-used right away in case we are destroying
the character devices in the background.
Diffstat (limited to 'sys/kern/kern_conf.c')
-rw-r--r-- | sys/kern/kern_conf.c | 52 |
1 files changed, 45 insertions, 7 deletions
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index b1ae7a1..b2835e2 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -116,6 +116,8 @@ dev_free_devlocked(struct cdev *cdev) mtx_assert(&devmtx, MA_OWNED); cdp = cdev2priv(cdev); + KASSERT((cdp->cdp_flags & CDP_UNREF_DTR) == 0, + ("destroy_dev() was not called after delist_dev(%p)", cdev)); TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list); } @@ -1035,6 +1037,7 @@ destroy_devl(struct cdev *dev) { struct cdevsw *csw; struct cdev_privdata *p; + struct cdev_priv *cdp; mtx_assert(&devmtx, MA_OWNED); KASSERT(dev->si_flags & SI_NAMED, @@ -1043,13 +1046,22 @@ destroy_devl(struct cdev *dev) ("WARNING: Driver mistake: destroy_dev on eternal %d\n", dev2unit(dev))); - devfs_destroy(dev); + cdp = cdev2priv(dev); + if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { + /* + * Avoid race with dev_rel(), e.g. from the populate + * loop. If CDP_UNREF_DTR flag is set, the reference + * to be dropped at the end of destroy_devl() was + * already taken by delist_dev_locked(). + */ + dev_refl(dev); + + devfs_destroy(dev); + } /* Remove name marking */ dev->si_flags &= ~SI_NAMED; - dev->si_refcount++; /* Avoid race with dev_rel() */ - /* If we are a child, remove us from the parents list */ if (dev->si_flags & SI_CHILD) { LIST_REMOVE(dev, si_siblings); @@ -1105,13 +1117,39 @@ destroy_devl(struct cdev *dev) } } dev->si_flags &= ~SI_ALIAS; - dev->si_refcount--; /* Avoid race with dev_rel() */ + cdp->cdp_flags &= ~CDP_UNREF_DTR; + dev->si_refcount--; - if (dev->si_refcount > 0) { + if (dev->si_refcount > 0) LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); - } else { + else dev_free_devlocked(dev); - } +} + +static void +delist_dev_locked(struct cdev *dev) +{ + struct cdev_priv *cdp; + struct cdev *child; + + mtx_assert(&devmtx, MA_OWNED); + cdp = cdev2priv(dev); + if ((cdp->cdp_flags & CDP_UNREF_DTR) != 0) + return; + cdp->cdp_flags |= CDP_UNREF_DTR; + dev_refl(dev); + devfs_destroy(dev); + LIST_FOREACH(child, &dev->si_children, si_siblings) + delist_dev_locked(child); +} + +void +delist_dev(struct cdev *dev) +{ + + dev_lock(); + delist_dev_locked(dev); + dev_unlock(); } void |