summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_conf.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2015-01-19 17:36:52 +0000
committerkib <kib@FreeBSD.org>2015-01-19 17:36:52 +0000
commitf748dc7ade0eee53b5b866b6a9adc48ecf24b098 (patch)
treefffc9b3ef57a0cc6cac94e8ae746ff6d3fcf3f55 /sys/kern/kern_conf.c
parentb3741c8701d1d593047434400ecd44500cdc804f (diff)
downloadFreeBSD-src-f748dc7ade0eee53b5b866b6a9adc48ecf24b098.zip
FreeBSD-src-f748dc7ade0eee53b5b866b6a9adc48ecf24b098.tar.gz
Stop enforcing additional reference on all cdevs, which was introduced
in r277199. Acquire the neccessary reference in delist_dev_locked() and inform destroy_devl() about it using CDP_UNREF_DTR flag. Fix some style nits, add asserts. Discussed with: hselasky Tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 1 week
Diffstat (limited to 'sys/kern/kern_conf.c')
-rw-r--r--sys/kern/kern_conf.c33
1 files changed, 28 insertions, 5 deletions
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index bcd6fb9..79c8fea 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,7 +1046,18 @@ 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;
@@ -1103,19 +1117,27 @@ 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);
@@ -1124,6 +1146,7 @@ delist_dev_locked(struct cdev *dev)
void
delist_dev(struct cdev *dev)
{
+
dev_lock();
delist_dev_locked(dev);
dev_unlock();
OpenPOWER on IntegriCloud