summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/subr_autoconf.c40
1 files changed, 24 insertions, 16 deletions
diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c
index bced38e75c3..fc58c93f7eb 100644
--- a/sys/kern/subr_autoconf.c
+++ b/sys/kern/subr_autoconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_autoconf.c,v 1.41 2005/12/09 09:09:52 jsg Exp $ */
+/* $OpenBSD: subr_autoconf.c,v 1.42 2006/01/13 19:22:54 miod Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */
/*
@@ -716,24 +716,32 @@ config_pending_decr(void)
int
config_detach_children(struct device *parent, int flags)
{
- struct device *dev, *next_dev;
- int rv = 0;
+ struct device *dev, *next_dev, *prev_dev;
+ int rv = 0;
- /* The config_detach routine may sleep, meaning devices
- may be added to the queue. However, all devices will
- be added to the tail of the queue, the queue won't
- be re-organized, and the subtree of parent here should be locked
- for purposes of adding/removing children.
- */
- for (dev = TAILQ_FIRST(&alldevs);
- dev != NULL; dev = next_dev) {
- next_dev = TAILQ_NEXT(dev, dv_list);
- if (dev->dv_parent == parent &&
- (rv = config_detach(dev, flags)))
- return (rv);
+ /*
+ * The config_detach routine may sleep, meaning devices
+ * may be added to the queue. However, all devices will
+ * be added to the tail of the queue, the queue won't
+ * be re-organized, and the subtree of parent here should be locked
+ * for purposes of adding/removing children.
+ *
+ * Note that we can not afford trying to walk the device list
+ * once - our ``next'' device might be a child of the device
+ * we are about to detach, so it would disappear.
+ * Just play it safe and restart from the parent.
+ */
+ for (prev_dev = NULL, dev = TAILQ_FIRST(&alldevs);
+ dev != NULL; dev = next_dev) {
+ if (dev->dv_parent == parent) {
+ if ((rv = config_detach(dev, flags)) != 0)
+ return (rv);
+ next_dev = prev_dev ? prev_dev : TAILQ_FIRST(&alldevs);
+ } else
+ next_dev = TAILQ_NEXT(prev_dev = dev, dv_list);
}
- return (rv);
+ return (0);
}
int