diff options
author | kib <kib@FreeBSD.org> | 2015-01-19 17:36:52 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2015-01-19 17:36:52 +0000 |
commit | f748dc7ade0eee53b5b866b6a9adc48ecf24b098 (patch) | |
tree | fffc9b3ef57a0cc6cac94e8ae746ff6d3fcf3f55 /sys | |
parent | b3741c8701d1d593047434400ecd44500cdc804f (diff) | |
download | FreeBSD-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')
-rw-r--r-- | sys/fs/devfs/devfs_devs.c | 6 | ||||
-rw-r--r-- | sys/fs/devfs/devfs_int.h | 1 | ||||
-rw-r--r-- | sys/kern/kern_conf.c | 33 |
3 files changed, 29 insertions, 11 deletions
diff --git a/sys/fs/devfs/devfs_devs.c b/sys/fs/devfs/devfs_devs.c index 986d066..6620aef 100644 --- a/sys/fs/devfs/devfs_devs.c +++ b/sys/fs/devfs/devfs_devs.c @@ -137,12 +137,6 @@ devfs_alloc(int flags) vfs_timestamp(&ts); cdev->si_atime = cdev->si_mtime = cdev->si_ctime = ts; cdev->si_cred = NULL; - /* - * Avoid race with dev_rel() by setting the initial - * reference count to 1. This last reference is taken - * by the destroy_dev() function. - */ - cdev->si_refcount = 1; return (cdev); } diff --git a/sys/fs/devfs/devfs_int.h b/sys/fs/devfs/devfs_int.h index ce55416..6c57109 100644 --- a/sys/fs/devfs/devfs_int.h +++ b/sys/fs/devfs/devfs_int.h @@ -56,6 +56,7 @@ struct cdev_priv { u_int cdp_flags; #define CDP_ACTIVE (1 << 0) #define CDP_SCHED_DTR (1 << 1) +#define CDP_UNREF_DTR (1 << 2) u_int cdp_inuse; u_int cdp_maxdirent; 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(); |