Subject: device tree traversal, round 2
To: None <tech-kern@netbsd.org>
From: Jachym Holecek <freza@dspfpga.com>
List: tech-kern
Date: 07/21/2006 14:15:42
--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hello,
I made some changes to the device tree traversal API [*]:
* Change names as per chap's comment (s/DIF_TOPDOWN/DIF_PREORDER,
s/DIF_DOWNTOP/DIF_POSTORDER).
* Remove traversal stack depth limit from device_iterator_alloc().
* Make it possible to reuse one iterator object for either traversal
direction.
* Fix allocation bug (thanks Magnus Larsson).
New usage example:
deviter_t di;
device_t dv;
di = device_iterator_alloc();
if (di == NULL)
panic("borked");
device_iterator_init(di, TAILQ_FIRST(&alldevs),
DIF_PREORDER | DIF_WAITOK);
while ((dv = device_iterator_foreach(di)) != NULL)
printf("FOO %s\n", device_xname(dv));
device_iterator_init(di, TAILQ_FIRST(&alldevs),
DIF_POSTORDER | DIF_WAITOK);
while ((dv = device_iterator_foreach(di)) != NULL)
printf("BAR %s\n", device_xname(dv));
device_iterator_free(di);
I've kept DIF_NOWAIT at place -- while I agree we should avoid allocations
from interrupt context, I'd like to keep the option available _for now_.
Attached is the implementation itself (subr_devtree.c -> sys/kern/) and
a patch to be applied under sys/.
Looks OK to commit?
-- Jachym
[*] http://mail-index.netbsd.org/tech-kern/2006/07/02/0010.html
--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="devtree.diff"
Index: sys/device.h
===================================================================
RCS file: /cvsroot/src/sys/sys/device.h,v
retrieving revision 1.90
diff -d -p -u -r1.90 device.h
--- sys/device.h 5 May 2006 18:04:43 -0000 1.90
+++ sys/device.h 21 Jul 2006 12:01:03 -0000
@@ -107,6 +107,12 @@ typedef struct cfdata *cfdata_t;
typedef struct cfdriver *cfdriver_t;
typedef struct cfattach *cfattach_t;
typedef struct device *device_t;
+typedef struct devchild *devchild_t;
+
+struct devchild {
+ TAILQ_ENTRY(devchild) dvc_list;
+ device_t dvc_device;
+};
struct device {
devclass_t dv_class; /* this device's classification */
@@ -122,6 +128,7 @@ struct device {
int dv_flags; /* misc. flags; see below */
int *dv_locators; /* our actual locators (optional) */
prop_dictionary_t dv_properties;/* properties dictionary */
+ TAILQ_HEAD(, devchild) dv_child_list; /* this device's children */
};
/* dv_flags */
@@ -129,6 +136,14 @@ struct device {
TAILQ_HEAD(devicelist, device);
+typedef struct deviter *deviter_t; /* kern/subr_devtree.c */
+
+/* deviter_t flags, bits 8-15 for internal needs */
+#define DIF_PREORDER 0x0000
+#define DIF_WAITOK 0x0000 /* M_WAITOK */
+#define DIF_POSTORDER 0x0001
+#define DIF_NOWAIT 0x0002 /* M_NOWAIT */
+
/*
* Description of a locator, as part of interface attribute definitions.
*/
@@ -357,6 +372,11 @@ void *device_lookup(cfdriver_t, int);
void device_register(device_t, void *);
#endif
+deviter_t device_iterator_alloc(void);
+void device_iterator_free(deviter_t);
+void device_iterator_init(deviter_t, device_t, int);
+device_t device_iterator_foreach(deviter_t);
+
devclass_t device_class(device_t);
cfdata_t device_cfdata(device_t);
cfdriver_t device_cfdriver(device_t);
Index: conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.784
diff -d -p -u -r1.784 files
--- conf/files 26 Jun 2006 16:13:21 -0000 1.784
+++ conf/files 21 Jul 2006 12:01:25 -0000
@@ -1264,6 +1264,7 @@ file kern/subr_blist.c vmswap
file kern/subr_bufq.c
file kern/subr_callback.c
file kern/subr_devsw.c
+file kern/subr_devtree.c
file kern/subr_disk.c
file kern/subr_iostat.c
file kern/subr_evcnt.c
Index: kern/subr_autoconf.c
===================================================================
RCS file: /cvsroot/src/sys/kern/subr_autoconf.c,v
retrieving revision 1.114
diff -d -p -u -r1.114 subr_autoconf.c
--- kern/subr_autoconf.c 14 May 2006 05:26:59 -0000 1.114
+++ kern/subr_autoconf.c 21 Jul 2006 12:01:38 -0000
@@ -160,6 +160,8 @@ struct deferred_config_head deferred_con
struct deferred_config_head interrupt_config_queue;
static void config_process_deferred(struct deferred_config_head *, device_t);
+static void config_insert_child(device_t);
+static void config_remove_child(device_t);
/* Hooks to finalize configuration once all real devices have been found. */
struct finalize_hook {
@@ -965,6 +967,7 @@ config_attach_loc(device_t parent, cfdat
panic("config_attach: memory allocation for device softc failed");
memset(dev, 0, ca->ca_devsize);
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */
+ TAILQ_INIT(&dev->dv_child_list); /* no children yet */
dev->dv_class = cd->cd_class;
dev->dv_cfdata = cf;
dev->dv_cfdriver = cd;
@@ -997,6 +1000,7 @@ config_attach_loc(device_t parent, cfdat
aprint_naive("%s (root)", dev->dv_xname);
aprint_normal("%s (root)", dev->dv_xname);
} else {
+ config_insert_child(dev);
aprint_naive("%s at %s", dev->dv_xname, parent->dv_xname);
aprint_normal("%s at %s", dev->dv_xname, parent->dv_xname);
if (print)
@@ -1164,9 +1168,6 @@ config_detach(device_t dev, int flags)
cfdata_t cf;
const struct cfattach *ca;
struct cfdriver *cd;
-#ifdef DIAGNOSTIC
- device_t d;
-#endif
int rv = 0, i;
#ifdef DIAGNOSTIC
@@ -1216,18 +1217,12 @@ config_detach(device_t dev, int flags)
#ifdef DIAGNOSTIC
/*
* Sanity: If you're successfully detached, you should have no
- * children. (Note that because children must be attached
- * after parents, we only need to search the latter part of
- * the list.)
+ * children.
*/
- for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
- d = TAILQ_NEXT(d, dv_list)) {
- if (d->dv_parent == dev) {
- printf("config_detach: detached device %s"
- " has children %s\n", dev->dv_xname, d->dv_xname);
- panic("config_detach");
- }
- }
+ if (! TAILQ_EMPTY(&dev->dv_child_list))
+ panic("config_detach: detached device %s has children %s",
+ device_xname(dev),
+ device_xname(TAILQ_FIRST(&dev->dv_child_list)->dvc_device));
#endif
/* notify the parent that the child is gone */
@@ -1265,6 +1260,9 @@ config_detach(device_t dev, int flags)
*/
TAILQ_REMOVE(&alldevs, dev, dv_list);
+ if (dev->dv_parent != ROOT)
+ config_remove_child(dev);
+
/*
* Remove from cfdriver's array, tell the world (unless it was
* a pseudo-device), and free softc.
@@ -1608,3 +1606,50 @@ device_is_a(device_t dev, const char *dn
return (strcmp(dev->dv_cfdriver->cd_name, dname) == 0);
}
+
+/*
+ * config_remove_child:
+ *
+ * Remove child from its parent. Called from process context.
+ */
+static void
+config_remove_child(device_t dev)
+{
+ struct devchild *dc;
+ device_t par = dev->dv_parent;
+
+ KASSERT(par != ROOT);
+
+ TAILQ_FOREACH(dc, &par->dv_child_list, dvc_list)
+ if (dc->dvc_device == dev)
+ break;
+ if (dc == NULL)
+ panic("config_remove_child: %s not listed as child of %s",
+ device_xname(dev), device_xname(par));
+
+ TAILQ_REMOVE(&par->dv_child_list, dc, dvc_list);
+ free(dc, M_DEVBUF);
+}
+
+/*
+ * config_insert_child:
+ *
+ * Insert child to its parent. Called from process context.
+ */
+static void
+config_insert_child(device_t dev)
+{
+ struct devchild *dc;
+ device_t par = dev->dv_parent;
+
+ KASSERT(par != ROOT);
+
+ dc = (struct devchild *)malloc(sizeof(struct devchild), M_DEVBUF,
+ cold ? M_NOWAIT : M_WAITOK);
+ if (dc == NULL)
+ panic("config_insert_child: could not alloc %s child %s",
+ device_xname(par), device_xname(dev));
+
+ dc->dvc_device = dev;
+ TAILQ_INSERT_TAIL(&par->dv_child_list, dc, dvc_list);
+}
--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="subr_devtree.c"
/* $NetBSD$ */
/*
* Copyright (c)2006 Jachym Holecek,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/kernel.h> /* cold */
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/types.h>
struct devstack {
devchild_t dvs_child;
device_t dvs_parent; /* only for postorder */
};
struct deviter {
int dvi_flags;
int dvi_depth;
int dvi_index; /* current entry */
device_t dvi_root; /* root of subtree */
struct devstack *dvi_stack; /* traversal stack */
};
#define CURCLD(di) (di)->dvi_stack[(di)->dvi_index].dvs_child
#define CURPAR(di) (di)->dvi_stack[(di)->dvi_index].dvs_parent
/* dvi_flags bits, DIF_* from <sys/device.h> and internal below */
#define DIF_FIRST 0x8000
/* iterator stack depth incerement */
#define DEPTHINCR 8
static device_t device_iterator_foreach_preorder(deviter_t);
static device_t device_iterator_foreach_postorder(deviter_t);
static int device_iterator_grow(deviter_t di, int mflags);
static inline const char *
device_iterator_name(deviter_t di)
{
if (di->dvi_root == NULL)
return ("<initial>");
return (device_xname(di->dvi_root));
}
/*
* device_iterator_grow: [internal]
*
* Grow traversal stack of iterator ${di} by a fixed increment,
* use allocation flags ${mflags}.
*/
static int
device_iterator_grow(deviter_t di, int mflags)
{
void *p;
di->dvi_depth += DEPTHINCR;
KASSERT(di->dvi_depth > 0);
p = realloc(di->dvi_stack, di->dvi_depth * sizeof(struct devstack),
M_DEVBUF, mflags);
if (p == NULL) {
if (di->dvi_stack != NULL) {
free(di->dvi_stack, M_DEVBUF);
di->dvi_stack = NULL;
}
return (ENOMEM);
}
di->dvi_stack = (struct devstack *)p;
return (0);
}
/*
* device_iterator_descend: [internal]
*
* Descend traversal stack by one "frame", grow the stack if needed.
*/
static int
device_iterator_descend(deviter_t di)
{
if (++di->dvi_index == di->dvi_depth)
return (device_iterator_grow(di,
ISSET(di->dvi_flags, DIF_NOWAIT) ? M_NOWAIT : M_WAITOK));
return (0);
}
/*
* device_iterator_alloc:
*
* Create device iterator object. Assumes process context.
*/
deviter_t
device_iterator_alloc(void)
{
deviter_t di;
di = (deviter_t)malloc(sizeof(struct deviter), M_DEVBUF,
(cold ? M_NOWAIT : M_WAITOK));
if (di == NULL)
return (NULL);
di->dvi_stack = NULL;
di->dvi_flags = 0;
di->dvi_depth = 0;
di->dvi_index = 0;
di->dvi_root = NULL;
/* Allocate initial stack. */
if (device_iterator_grow(di, (cold ? M_NOWAIT : M_WAITOK))) {
free(di, M_DEVBUF);
return (NULL);
}
return (di);
}
/*
* device_iterator_free:
*
* Release any resources allocated by device iterator ${di}.
*/
void
device_iterator_free(deviter_t di)
{
KASSERT(di != NULL);
KASSERT(di->dvi_stack != NULL);
free(di->dvi_stack, M_DEVBUF);
free(di, M_DEVBUF);
}
/*
* device_iterator_init:
*
* Setup iterator object ${di} to process subtree rooted by device
* ${dev} in a way specified by ${flags}.
*/
void
device_iterator_init(deviter_t di, device_t dev, int flags)
{
KASSERT(di->dvi_depth > 0);
di->dvi_flags = flags | DIF_FIRST;
di->dvi_root = dev;
di->dvi_index = 0;
CURCLD(di) = TAILQ_FIRST(&dev->dv_child_list);
if (ISSET(di->dvi_flags, DIF_POSTORDER))
CURPAR(di) = dev;
}
/*
* device_iterator_foreach:
*
* Iterate devices according to earlier call to device_iterator_init().
*/
device_t
device_iterator_foreach(deviter_t di)
{
if (ISSET(di->dvi_flags, DIF_POSTORDER))
return (device_iterator_foreach_postorder(di));
else
return (device_iterator_foreach_preorder(di));
}
/*
* device_iterator_foreach_preorder: [internal]
*
* Iterate devices so that "parents" go before their "children".
* In other words, a device is processed and if it has any children,
* they're processed next (recursively).
*/
static device_t
device_iterator_foreach_preorder(deviter_t di)
{
devchild_t dc;
device_t dv;
/* Handle subtree root, entered once. */
if (ISSET(di->dvi_flags, DIF_FIRST)) {
CLR(di->dvi_flags, DIF_FIRST);
return (di->dvi_root);
}
/* Unwind until we get a valid entry. */
while ((dc = CURCLD(di)) == NULL) {
/* Top of the stack, we're done. */
if (di->dvi_index == 0)
return (NULL);
di->dvi_index--;
}
dv = dc->dvc_device;
CURCLD(di) = TAILQ_NEXT(dc, dvc_list);
/* Setup descent into !leaf device. */
if (! TAILQ_EMPTY(&dv->dv_child_list)) {
if (device_iterator_descend(di))
panic("device_iterator_foreach_preorder: %s devstack "
"overflow, depth = %d",
device_iterator_name(di), di->dvi_depth);
CURCLD(di) = TAILQ_FIRST(&dv->dv_child_list);
}
return (dv);
}
/*
* device_iterator_foreach_postorder: [internal]
*
* Iterate devices so that "children" go before their "parents".
* In other words, device is processed as soon as _all_ of its
* children are processed (recursively).
*/
static device_t
device_iterator_foreach_postorder(deviter_t di)
{
devchild_t dc;
device_t dv;
/* We're done when we've finished all layers. */
if (di->dvi_index == -1)
return (NULL);
descend:
dc = CURCLD(di);
/* Subtree finished in past step, handle its root and unwind. */
if (dc == NULL) {
dv = CURPAR(di);
di->dvi_index--; /* underflow is ok */
return (dv);
}
/* Get ready for next child at this level. */
CURCLD(di) = TAILQ_NEXT(dc, dvc_list);
dv = dc->dvc_device;
/* Descend until we see a leaf device. */
if (! TAILQ_EMPTY(&dv->dv_child_list)) {
if (device_iterator_descend(di))
panic("device_iterator_foreach_postorder: %s devstack "
"overflow, depth = %d",
device_iterator_name(di), di->dvi_depth);
CURCLD(di) = TAILQ_FIRST(&dv->dv_child_list);
CURPAR(di) = dv;
goto descend;
}
return (dv);
}
--UlVJffcvxoiEqYs2--