aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/kern_conf.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2007-06-19 13:19:23 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2007-06-19 13:19:23 +0000
commit9bc911d4a272b69593e6e365c3d03507aa3cba01 (patch)
tree2ad6f686e4a07932178eb41d9afc06fbe53c1cb9 /sys/kern/kern_conf.c
parent7550e3eac458765f2e331cff65711ce0ee39e83b (diff)
downloadsrc-9bc911d4a272b69593e6e365c3d03507aa3cba01.tar.gz
src-9bc911d4a272b69593e6e365c3d03507aa3cba01.zip
devfs_free() calls free_unr(), that may sleep.
Postpone call to devfs_free() after cdev mutex is dropped. Reuse cdp_list link for queuing devices awaiting deletion in the cdevp_free_list. Reported by: Hans Petter Selasky <hselasky c2i net> Tested by: Peter Holm Approved by: re (kensmith) MFC after: 2 weeks
Notes
Notes: svn path=/head/; revision=170950
Diffstat (limited to 'sys/kern/kern_conf.c')
-rw-r--r--sys/kern/kern_conf.c40
1 files changed, 34 insertions, 6 deletions
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index dba6287cbc64..2241575418c6 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -54,6 +54,9 @@ static struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr,
struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
va_list ap);
+static struct cdev_priv_list cdevp_free_list =
+ TAILQ_HEAD_INITIALIZER(cdevp_free_list);
+
void
dev_lock(void)
{
@@ -61,6 +64,31 @@ dev_lock(void)
mtx_lock(&devmtx);
}
+static void
+dev_unlock_and_free(void)
+{
+ struct cdev_priv *cdp;
+
+ mtx_assert(&devmtx, MA_OWNED);
+ while ((cdp = TAILQ_FIRST(&cdevp_free_list)) != NULL) {
+ TAILQ_REMOVE(&cdevp_free_list, cdp, cdp_list);
+ mtx_unlock(&devmtx);
+ devfs_free(&cdp->cdp_c);
+ mtx_lock(&devmtx);
+ }
+ mtx_unlock(&devmtx);
+}
+
+static void
+dev_free_devlocked(struct cdev *cdev)
+{
+ struct cdev_priv *cdp;
+
+ mtx_assert(&devmtx, MA_OWNED);
+ cdp = cdev->si_priv;
+ TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list);
+}
+
void
dev_unlock(void)
{
@@ -417,7 +445,7 @@ newdev(struct cdevsw *csw, int y, struct cdev *si)
udev = y;
LIST_FOREACH(si2, &csw->d_devs, si_list) {
if (si2->si_drv0 == udev) {
- devfs_free(si);
+ dev_free_devlocked(si);
return (si2);
}
}
@@ -548,7 +576,7 @@ make_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
* simplifies cloning devices.
* XXX: still ??
*/
- dev_unlock();
+ dev_unlock_and_free();
return (dev);
}
KASSERT(!(dev->si_flags & SI_NAMED),
@@ -709,7 +737,7 @@ destroy_devl(struct cdev *dev)
if (dev->si_refcount > 0) {
LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
} else {
- devfs_free(dev);
+ dev_free_devlocked(dev);
}
}
@@ -719,7 +747,7 @@ destroy_dev(struct cdev *dev)
dev_lock();
destroy_devl(dev);
- dev_unlock();
+ dev_unlock_and_free();
}
const char *
@@ -839,8 +867,8 @@ clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **
u = dev2unit(dev);
if (u == (unit | extra)) {
*dp = dev;
- devfs_free(ndev);
dev_unlock();
+ devfs_free(ndev);
return (0);
}
if (unit == -1 && u == low) {
@@ -876,7 +904,7 @@ clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **
LIST_INSERT_HEAD(&cd->head, dev, si_clone);
dev->si_flags |= SI_CLONELIST;
*up = unit;
- dev_unlock();
+ dev_unlock_and_free();
return (1);
}