diff --git a/sys/kern/kern_drvctl.c b/sys/kern/kern_drvctl.c index 1f5022858083..d2dd32a5cfc4 100644 --- a/sys/kern/kern_drvctl.c +++ b/sys/kern/kern_drvctl.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_drvctl.c,v 1.14 2008/02/12 17:30:59 joerg Exp $ */ +/* $NetBSD: kern_drvctl.c,v 1.15 2008/03/05 07:09:18 dyoung Exp $ */ /* * Copyright (c) 2004 @@ -27,7 +27,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kern_drvctl.c,v 1.14 2008/02/12 17:30:59 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_drvctl.c,v 1.15 2008/03/05 07:09:18 dyoung Exp $"); #include #include @@ -78,12 +78,14 @@ static int listdevbyname(struct devlistargs *l) { device_t d, child; - int cnt = 0, idx, error; + deviter_t di; + int cnt = 0, idx, error = 0; if ((d = device_find_by_xname(l->l_devname)) == NULL) return ENXIO; - TAILQ_FOREACH(child, &alldevs, dv_list) { + for (child = deviter_first(&di, 0); child != NULL; + child = deviter_next(&di)) { if (device_parent(child) != d) continue; idx = cnt++; @@ -91,12 +93,13 @@ listdevbyname(struct devlistargs *l) continue; error = copyoutstr(device_xname(child), l->l_childname[idx], sizeof(l->l_childname[idx]), NULL); - if (error) - return error; + if (error != 0) + break; } + deviter_release(&di); l->l_children = cnt; - return 0; + return error; } static int @@ -244,6 +247,7 @@ drvctl_command_get_properties(struct lwp *l, prop_dictionary_t args_dict; prop_string_t devname_string; device_t dev; + deviter_t di; args_dict = prop_dictionary_get(command_dict, "drvctl-arguments"); if (args_dict == NULL) @@ -253,18 +257,21 @@ drvctl_command_get_properties(struct lwp *l, if (devname_string == NULL) return (EINVAL); - TAILQ_FOREACH(dev, &alldevs, dv_list) { + for (dev = deviter_first(&di, 0); dev != NULL; + dev = deviter_next(&di)) { if (prop_string_equals_cstring(devname_string, - device_xname(dev))) + device_xname(dev))) { + prop_dictionary_set(results_dict, "drvctl-result-data", + device_properties(dev)); break; + } } + deviter_release(&di); + if (dev == NULL) return (ESRCH); - - prop_dictionary_set(results_dict, "drvctl-result-data", - device_properties(dev)); - + return (0); } diff --git a/sys/kern/kern_pmf.c b/sys/kern/kern_pmf.c index d3d57ff488c4..2391e2995004 100644 --- a/sys/kern/kern_pmf.c +++ b/sys/kern/kern_pmf.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_pmf.c,v 1.14 2008/03/05 04:54:24 dyoung Exp $ */ +/* $NetBSD: kern_pmf.c,v 1.15 2008/03/05 07:09:18 dyoung Exp $ */ /*- * Copyright (c) 2007 Jared D. McNeill @@ -33,7 +33,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.14 2008/03/05 04:54:24 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.15 2008/03/05 07:09:18 dyoung Exp $"); #include #include @@ -47,6 +47,11 @@ __KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.14 2008/03/05 04:54:24 dyoung Exp $") #include /* for sys_sync */ #include #include +#include +#include +#include +#include /* for RB_NOSYNC */ +#include /* XXX ugly special case, but for now the only client */ #include "wsdisplay.h" @@ -94,6 +99,14 @@ typedef struct pmf_event_workitem { device_t pew_device; } pmf_event_workitem_t; +struct shutdown_state { + bool initialized; + deviter_t di; +}; + +static device_t shutdown_first(struct shutdown_state *); +static device_t shutdown_next(struct shutdown_state *); + static void pmf_event_worker(struct work *wk, void *dummy) { @@ -121,9 +134,11 @@ pmf_check_system_drivers(void) { device_t curdev; bool unsupported_devs; + deviter_t di; unsupported_devs = false; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { + for (curdev = deviter_first(&di, 0); curdev != NULL; + curdev = deviter_next(&di)) { if (device_pmf_is_registered(curdev)) continue; if (!unsupported_devs) @@ -131,6 +146,7 @@ pmf_check_system_drivers(void) printf(" %s", device_xname(curdev)); unsupported_devs = true; } + deviter_release(&di); if (unsupported_devs) { printf("\n"); return false; @@ -139,85 +155,67 @@ pmf_check_system_drivers(void) } bool -pmf_system_bus_resume(void) +pmf_system_bus_resume(PMF_FN_ARGS1) { - int depth, maxdepth; bool rv; device_t curdev; - - maxdepth = 0; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { - if (curdev->dv_depth > maxdepth) - maxdepth = curdev->dv_depth; - } - ++maxdepth; + deviter_t di; aprint_debug("Powering devices:"); /* D0 handlers are run in order */ - depth = 0; rv = true; - for (depth = 0; depth < maxdepth; ++depth) { - TAILQ_FOREACH(curdev, &alldevs, dv_list) { - if (!device_pmf_is_registered(curdev)) - continue; - if (device_is_active(curdev) || - !device_is_enabled(curdev)) - continue; - if (curdev->dv_depth != depth) - continue; + for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL; + curdev = deviter_next(&di)) { + if (!device_pmf_is_registered(curdev)) + continue; + if (device_is_active(curdev) || + !device_is_enabled(curdev)) + continue; - aprint_debug(" %s", device_xname(curdev)); + aprint_debug(" %s", device_xname(curdev)); - if (!device_pmf_bus_resume(curdev)) - aprint_debug("(failed)"); + if (!device_pmf_bus_resume(curdev PMF_FN_CALL)) { + rv = false; + aprint_debug("(failed)"); } } + deviter_release(&di); aprint_debug("\n"); return rv; } bool -pmf_system_resume(void) +pmf_system_resume(PMF_FN_ARGS1) { - int depth, maxdepth; bool rv; device_t curdev, parent; + deviter_t di; if (!pmf_check_system_drivers()) return false; - maxdepth = 0; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { - if (curdev->dv_depth > maxdepth) - maxdepth = curdev->dv_depth; - } - ++maxdepth; - aprint_debug("Resuming devices:"); /* D0 handlers are run in order */ - depth = 0; rv = true; - for (depth = 0; depth < maxdepth; ++depth) { - TAILQ_FOREACH(curdev, &alldevs, dv_list) { - if (device_is_active(curdev) || - !device_is_enabled(curdev)) - continue; - if (curdev->dv_depth != depth) - continue; - parent = device_parent(curdev); - if (parent != NULL && - !device_is_active(parent)) - continue; + for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL; + curdev = deviter_next(&di)) { + if (device_is_active(curdev) || + !device_is_enabled(curdev)) + continue; + parent = device_parent(curdev); + if (parent != NULL && + !device_is_active(parent)) + continue; - aprint_debug(" %s", device_xname(curdev)); + aprint_debug(" %s", device_xname(curdev)); - if (!pmf_device_resume(curdev)) { - rv = false; - aprint_debug("(failed)"); - } + if (!pmf_device_resume(curdev PMF_FN_CALL)) { + rv = false; + aprint_debug("(failed)"); } } + deviter_release(&di); aprint_debug(".\n"); KERNEL_UNLOCK_ONE(0); @@ -229,10 +227,10 @@ pmf_system_resume(void) } bool -pmf_system_suspend(void) +pmf_system_suspend(PMF_FN_ARGS1) { - int depth, maxdepth; device_t curdev; + deviter_t di; if (!pmf_check_system_drivers()) return false; @@ -257,72 +255,68 @@ pmf_system_suspend(void) aprint_debug("Suspending devices:"); - maxdepth = 0; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { - if (curdev->dv_depth > maxdepth) - maxdepth = curdev->dv_depth; - } - - for (depth = maxdepth; depth >= 0; --depth) { - TAILQ_FOREACH_REVERSE(curdev, &alldevs, devicelist, dv_list) { - if (curdev->dv_depth != depth) - continue; - if (!device_is_active(curdev)) - continue; - - aprint_debug(" %s", device_xname(curdev)); - - /* XXX joerg check return value and abort suspend */ - if (!pmf_device_suspend(curdev)) - aprint_debug("(failed)"); - } + for (curdev = deviter_first(&di, DEVITER_F_LEAVES_FIRST); + curdev != NULL; + curdev = deviter_next(&di)) { + if (!device_is_active(curdev)) + continue; + + aprint_debug(" %s", device_xname(curdev)); + + /* XXX joerg check return value and abort suspend */ + if (!pmf_device_suspend(curdev PMF_FN_CALL)) + aprint_debug("(failed)"); } + deviter_release(&di); aprint_debug(".\n"); return true; } +static device_t +shutdown_first(struct shutdown_state *s) +{ + if (!s->initialized) { + deviter_init(&s->di, DEVITER_F_SHUTDOWN|DEVITER_F_LEAVES_FIRST); + s->initialized = true; + } + return shutdown_next(s); +} + +static device_t +shutdown_next(struct shutdown_state *s) +{ + device_t dv; + + while ((dv = deviter_next(&s->di)) != NULL && !device_is_active(dv)) + ; + + return dv; +} + void pmf_system_shutdown(int how) { - int depth, maxdepth; + static struct shutdown_state s; device_t curdev; aprint_debug("Shutting down devices:"); - maxdepth = 0; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { - if (curdev->dv_depth > maxdepth) - maxdepth = curdev->dv_depth; - } - - for (depth = maxdepth; depth >= 0; --depth) { - TAILQ_FOREACH_REVERSE(curdev, &alldevs, devicelist, dv_list) { - if (curdev->dv_depth != depth) - continue; - if (!device_is_active(curdev)) - continue; - - aprint_debug(" %s", device_xname(curdev)); - - if (!device_pmf_is_registered(curdev)) - continue; + for (curdev = shutdown_first(&s); curdev != NULL; + curdev = shutdown_next(&s)) { + aprint_debug(" attempting %s shutdown", + device_xname(curdev)); + if (!device_pmf_is_registered(curdev)) + aprint_debug("(skipped)"); #if 0 /* needed? */ - if (!device_pmf_class_shutdown(curdev, how)) { - aprint_debug("(failed)"); - continue; - } + else if (!device_pmf_class_shutdown(curdev, how)) + aprint_debug("(failed)"); #endif - if (!device_pmf_driver_shutdown(curdev, how)) { - aprint_debug("(failed)"); - continue; - } - if (!device_pmf_bus_shutdown(curdev, how)) { - aprint_debug("(failed)"); - continue; - } - } + else if (!device_pmf_driver_shutdown(curdev, how)) + aprint_debug("(failed)"); + else if (!device_pmf_bus_shutdown(curdev, how)) + aprint_debug("(failed)"); } aprint_debug(".\n"); @@ -379,63 +373,69 @@ pmf_device_deregister(device_t dev) } bool -pmf_device_suspend(device_t dev) +pmf_device_suspend(device_t dev PMF_FN_ARGS) { PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev))); if (!device_pmf_is_registered(dev)) return false; PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev))); - if (!device_pmf_class_suspend(dev)) + if (!device_pmf_class_suspend(dev PMF_FN_CALL)) return false; PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev))); - if (!device_pmf_driver_suspend(dev)) + if (!device_pmf_driver_suspend(dev PMF_FN_CALL)) return false; PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev))); - if (!device_pmf_bus_suspend(dev)) + if (!device_pmf_bus_suspend(dev PMF_FN_CALL)) return false; PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev))); return true; } bool -pmf_device_resume(device_t dev) +pmf_device_resume(device_t dev PMF_FN_ARGS) { PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev))); if (!device_pmf_is_registered(dev)) return false; PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev))); - if (!device_pmf_bus_resume(dev)) + if (!device_pmf_bus_resume(dev PMF_FN_CALL)) return false; PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev))); - if (!device_pmf_driver_resume(dev)) + if (!device_pmf_driver_resume(dev PMF_FN_CALL)) return false; PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev))); - if (!device_pmf_class_resume(dev)) + if (!device_pmf_class_resume(dev PMF_FN_CALL)) return false; PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev))); return true; } bool -pmf_device_recursive_suspend(device_t dv) +pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS) { + bool rv = true; device_t curdev; + deviter_t di; if (!device_is_active(dv)) return true; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { + for (curdev = deviter_first(&di, 0); curdev != NULL; + curdev = deviter_next(&di)) { if (device_parent(curdev) != dv) continue; - if (!pmf_device_recursive_suspend(curdev)) - return false; + if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) { + rv = false; + break; + } } + deviter_release(&di); - return pmf_device_suspend(dv); + return rv && pmf_device_suspend(dv PMF_FN_CALL); } bool -pmf_device_recursive_resume(device_t dv) +pmf_device_recursive_resume(device_t dv PMF_FN_ARGS) { device_t parent; @@ -444,34 +444,40 @@ pmf_device_recursive_resume(device_t dv) parent = device_parent(dv); if (parent != NULL) { - if (!pmf_device_recursive_resume(parent)) + if (!pmf_device_recursive_resume(parent PMF_FN_CALL)) return false; } - return pmf_device_resume(dv); + return pmf_device_resume(dv PMF_FN_CALL); } bool -pmf_device_resume_subtree(device_t dv) +pmf_device_resume_subtree(device_t dv PMF_FN_ARGS) { + bool rv = true; device_t curdev; + deviter_t di; - if (!pmf_device_recursive_resume(dv)) + if (!pmf_device_recursive_resume(dv PMF_FN_CALL)) return false; - TAILQ_FOREACH(curdev, &alldevs, dv_list) { + for (curdev = deviter_first(&di, 0); curdev != NULL; + curdev = deviter_next(&di)) { if (device_parent(curdev) != dv) continue; - if (!pmf_device_resume_subtree(curdev)) - return false; + if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) { + rv = false; + break; + } } - return true; + deviter_release(&di); + return rv; } #include static bool -pmf_class_network_suspend(device_t dev) +pmf_class_network_suspend(device_t dev PMF_FN_ARGS) { struct ifnet *ifp = device_pmf_class_private(dev); int s; diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c index 7071c83c15ab..90ce476530c9 100644 --- a/sys/kern/subr_autoconf.c +++ b/sys/kern/subr_autoconf.c @@ -1,4 +1,4 @@ -/* $NetBSD: subr_autoconf.c,v 1.135 2008/03/05 04:54:24 dyoung Exp $ */ +/* $NetBSD: subr_autoconf.c,v 1.136 2008/03/05 07:09:18 dyoung Exp $ */ /* * Copyright (c) 1996, 2000 Christopher G. Demetriou @@ -77,7 +77,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.135 2008/03/05 04:54:24 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.136 2008/03/05 07:09:18 dyoung Exp $"); #include "opt_multiprocessor.h" #include "opt_ddb.h" @@ -103,6 +103,8 @@ __KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.135 2008/03/05 04:54:24 dyoung E #include #include #include +#include +#include #include @@ -170,6 +172,9 @@ static void config_makeroom(int, struct cfdriver *); static void config_devlink(device_t); static void config_devunlink(device_t); +static device_t deviter_next1(deviter_t *); +static void deviter_reinit(deviter_t *); + struct deferred_config { TAILQ_ENTRY(deferred_config) dc_queue; device_t dc_dev; @@ -197,6 +202,11 @@ static int config_finalize_done; /* list of all devices */ struct devicelist alldevs = TAILQ_HEAD_INITIALIZER(alldevs); +kcondvar_t alldevs_cv; +kmutex_t alldevs_mtx; +static int alldevs_nread = 0; +static int alldevs_nwrite = 0; +static lwp_t *alldevs_writer = NULL; volatile int config_pending; /* semaphore for mountroot */ @@ -327,6 +337,9 @@ config_init(void) if (config_initialized) return; + mutex_init(&alldevs_mtx, MUTEX_DEFAULT, IPL_NONE); + cv_init(&alldevs_cv, "alldevs"); + /* allcfdrivers is statically initialized. */ for (i = 0; cfdriver_list_initial[i] != NULL; i++) { if (config_cfdriver_attach(cfdriver_list_initial[i]) != 0) @@ -709,12 +722,14 @@ rescan_with_cfdata(const struct cfdata *cf) { device_t d; const struct cfdata *cf1; + deviter_t di; + /* * "alldevs" is likely longer than an LKM's cfdata, so make it * the outer loop. */ - TAILQ_FOREACH(d, &alldevs, dv_list) { + for (d = deviter_first(&di, 0); d != NULL; d = deviter_next(&di)) { if (!(d->dv_cfattach->ca_rescan)) continue; @@ -728,6 +743,7 @@ rescan_with_cfdata(const struct cfdata *cf) cf1->cf_pspec->cfp_iattr, cf1->cf_loc); } } + deviter_release(&di); } /* @@ -773,20 +789,21 @@ int config_cfdata_detach(cfdata_t cf) { device_t d; - int error; + int error = 0; struct cftable *ct; + deviter_t di; -again: - TAILQ_FOREACH(d, &alldevs, dv_list) { - if (dev_in_cfdata(d, cf)) { - error = config_detach(d, 0); - if (error) { - aprint_error("%s: unable to detach instance\n", - d->dv_xname); - return (error); - } - goto again; - } + for (d = deviter_first(&di, DEVITER_F_RW); d != NULL; + d = deviter_next(&di)) { + if (!dev_in_cfdata(d, cf)) + continue; + if ((error = config_detach(d, 0)) != 0) + break; + } + deviter_release(&di); + if (error) { + aprint_error_dev(d, "unable to detach instance\n"); + return error; } TAILQ_FOREACH(ct, &allcftables, ct_list) { @@ -1049,7 +1066,16 @@ config_devlink(device_t dev) panic("config_attach: duplicate %s", dev->dv_xname); cd->cd_devs[dev->dv_unit] = dev; + /* It is safe to add a device to the tail of the list while + * readers are in the list, but not while a writer is in + * the list. Wait for any writer to complete. + */ + mutex_enter(&alldevs_mtx); + while (alldevs_nwrite != 0 && alldevs_writer != curlwp) + cv_wait(&alldevs_cv, &alldevs_mtx); TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */ + cv_signal(&alldevs_cv); + mutex_exit(&alldevs_mtx); } static void @@ -1364,6 +1390,17 @@ config_detach(device_t dev, int flags) ca = dev->dv_cfattach; KASSERT(ca != NULL); + KASSERT(curlwp != NULL); + mutex_enter(&alldevs_mtx); + if (alldevs_nwrite > 0 && alldevs_writer == NULL) + ; + else while (alldevs_nread != 0 || + (alldevs_nwrite != 0 && alldevs_writer != curlwp)) + cv_wait(&alldevs_cv, &alldevs_mtx); + if (alldevs_nwrite++ == 0) + alldevs_writer = curlwp; + mutex_exit(&alldevs_mtx); + /* * Ensure the device is deactivated. If the device doesn't * have an activation entry point, we allow DVF_ACTIVE to @@ -1386,7 +1423,7 @@ config_detach(device_t dev, int flags) } if (rv != 0) { if ((flags & DETACH_FORCE) == 0) - return (rv); + goto out; else panic("config_detach: forced detach of %s failed (%d)", dev->dv_xname, rv); @@ -1446,34 +1483,34 @@ config_detach(device_t dev, int flags) config_devunlink(dev); if (dev->dv_cfdata != NULL && (flags & DETACH_QUIET) == 0) - aprint_normal("%s detached\n", dev->dv_xname); + aprint_normal_dev(dev, "detached\n"); config_devdealloc(dev); - return (0); +out: + mutex_enter(&alldevs_mtx); + if (--alldevs_nwrite == 0) + alldevs_writer = NULL; + cv_signal(&alldevs_cv); + mutex_exit(&alldevs_mtx); + return rv; } int config_detach_children(device_t parent, int flags) { device_t dv; - int progress, error = 0; + deviter_t di; + int error = 0; - /* - * config_detach() can work recursively, thus it can - * delete any number of devices from the linked list. - * For that reason, start over after each hit. - */ - do { - progress = 0; - TAILQ_FOREACH(dv, &alldevs, dv_list) { - if (device_parent(dv) != parent) - continue; - progress++; - error = config_detach(dv, flags); + for (dv = deviter_first(&di, DEVITER_F_RW); dv != NULL; + dv = deviter_next(&di)) { + if (device_parent(dv) != parent) + continue; + if ((error = config_detach(dv, flags)) != 0) break; - } - } while (progress && !error); + } + deviter_release(&di); return error; } @@ -1824,11 +1861,13 @@ device_t device_find_by_xname(const char *name) { device_t dv; + deviter_t di; - TAILQ_FOREACH(dv, &alldevs, dv_list) { + for (dv = deviter_first(&di, 0); dv != NULL; dv = deviter_next(&di)) { if (strcmp(device_xname(dv), name) == 0) break; } + deviter_release(&di); return dv; } @@ -2150,3 +2189,197 @@ device_active_deregister(device_t dev, void (*handler)(device_t, devactive_t)) free(old_handlers, M_DEVBUF); } + +/* + * Device Iteration + * + * deviter_t: a device iterator. Holds state for a "walk" visiting + * each device_t's in the device tree. + * + * deviter_init(di, flags): initialize the device iterator `di' + * to "walk" the device tree. deviter_next(di) will return + * the first device_t in the device tree, or NULL if there are + * no devices. + * + * `flags' is one or more of DEVITER_F_RW, indicating that the + * caller intends to modify the device tree by calling + * config_detach(9) on devices in the order that the iterator + * returns them; DEVITER_F_ROOT_FIRST, asking for the devices + * nearest the "root" of the device tree to be returned, first; + * DEVITER_F_LEAVES_FIRST, asking for the devices furthest from + * the root of the device tree, first; and DEVITER_F_SHUTDOWN, + * indicating both that deviter_init() should not respect any + * locks on the device tree, and that deviter_next(di) may run + * in more than one LWP before the walk has finished. + * + * Only one DEVITER_F_RW iterator may be in the device tree at + * once. + * + * DEVITER_F_SHUTDOWN implies DEVITER_F_RW. + * + * Results are undefined if the flags DEVITER_F_ROOT_FIRST and + * DEVITER_F_LEAVES_FIRST are used in combination. + * + * deviter_first(di, flags): initialize the device iterator `di' + * and return the first device_t in the device tree, or NULL + * if there are no devices. The statement + * + * dv = deviter_first(di); + * + * is shorthand for + * + * deviter_init(di); + * dv = deviter_next(di); + * + * deviter_next(di): return the next device_t in the device tree, + * or NULL if there are no more devices. deviter_next(di) + * is undefined if `di' was not initialized with deviter_init() or + * deviter_first(). + * + * deviter_release(di): stops iteration (subsequent calls to + * deviter_next() will return NULL), releases any locks and + * resources held by the device iterator. + * + * Device iteration does not return device_t's in any particular + * order. An iterator will never return the same device_t twice. + * Device iteration is guaranteed to complete---i.e., if deviter_next(di) + * is called repeatedly on the same `di', it will eventually return + * NULL. It is ok to attach/detach devices during device iteration. + */ +void +deviter_init(deviter_t *di, deviter_flags_t flags) +{ + device_t dv; + bool rw; + + KASSERT(!rw || curlwp != NULL); + + mutex_enter(&alldevs_mtx); + if ((flags & DEVITER_F_SHUTDOWN) != 0) { + flags |= DEVITER_F_RW; + alldevs_nwrite++; + alldevs_writer = NULL; + alldevs_nread = 0; + } else { + rw = (flags & DEVITER_F_RW) != 0; + + if (alldevs_nwrite > 0 && alldevs_writer == NULL) + ; + else while ((alldevs_nwrite != 0 && alldevs_writer != curlwp) || + (rw && alldevs_nread != 0)) + cv_wait(&alldevs_cv, &alldevs_mtx); + + if (rw) { + if (alldevs_nwrite++ == 0) + alldevs_writer = curlwp; + } else + alldevs_nread++; + } + mutex_exit(&alldevs_mtx); + + memset(di, 0, sizeof(*di)); + + di->di_flags = flags; + + switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { + case DEVITER_F_LEAVES_FIRST: + TAILQ_FOREACH(dv, &alldevs, dv_list) + di->di_curdepth = MAX(di->di_curdepth, dv->dv_depth); + break; + case DEVITER_F_ROOT_FIRST: + TAILQ_FOREACH(dv, &alldevs, dv_list) + di->di_maxdepth = MAX(di->di_maxdepth, dv->dv_depth); + break; + default: + break; + } + + deviter_reinit(di); +} + +static void +deviter_reinit(deviter_t *di) +{ + if ((di->di_flags & DEVITER_F_RW) != 0) + di->di_prev = TAILQ_LAST(&alldevs, devicelist); + else + di->di_prev = TAILQ_FIRST(&alldevs); +} + +device_t +deviter_first(deviter_t *di, deviter_flags_t flags) +{ + deviter_init(di, flags); + return deviter_next(di); +} + +static device_t +deviter_next1(deviter_t *di) +{ + device_t dv; + + dv = di->di_prev; + + if (dv == NULL) + ; + else if ((di->di_flags & DEVITER_F_RW) != 0) + di->di_prev = TAILQ_PREV(dv, devicelist, dv_list); + else + di->di_prev = TAILQ_NEXT(dv, dv_list); + + return dv; +} + +device_t +deviter_next(deviter_t *di) +{ + device_t dv = NULL; + + switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { + case 0: + return deviter_next1(di); + case DEVITER_F_LEAVES_FIRST: + while (di->di_curdepth >= 0) { + if ((dv = deviter_next1(di)) == NULL) { + di->di_curdepth--; + deviter_reinit(di); + } else if (dv->dv_depth == di->di_curdepth) + break; + } + return dv; + case DEVITER_F_ROOT_FIRST: + while (di->di_curdepth <= di->di_maxdepth) { + if ((dv = deviter_next1(di)) == NULL) { + di->di_curdepth++; + deviter_reinit(di); + } else if (dv->dv_depth == di->di_curdepth) + break; + } + return dv; + default: + return NULL; + } +} + +void +deviter_release(deviter_t *di) +{ + bool rw = (di->di_flags & DEVITER_F_RW) != 0; + + KASSERT(!rw || alldevs_nwrite > 0); + + mutex_enter(&alldevs_mtx); + if (alldevs_nwrite > 0 && alldevs_writer == NULL) + --alldevs_nwrite; + else { + + if (rw) { + if (--alldevs_nwrite == 0) + alldevs_writer = NULL; + } else + --alldevs_nread; + + cv_signal(&alldevs_cv); + } + mutex_exit(&alldevs_mtx); +} diff --git a/sys/sys/device.h b/sys/sys/device.h index 659f11161073..cf0629cf8319 100644 --- a/sys/sys/device.h +++ b/sys/sys/device.h @@ -1,4 +1,4 @@ -/* $NetBSD: device.h,v 1.105 2008/03/05 04:54:24 dyoung Exp $ */ +/* $NetBSD: device.h,v 1.106 2008/03/05 07:09:18 dyoung Exp $ */ /* * Copyright (c) 1996, 2000 Christopher G. Demetriou @@ -167,6 +167,24 @@ struct device { TAILQ_HEAD(devicelist, device); +enum deviter_flags { + DEVITER_F_RW = 0x1 + , DEVITER_F_SHUTDOWN = 0x2 + , DEVITER_F_LEAVES_FIRST = 0x4 + , DEVITER_F_ROOT_FIRST = 0x8 +}; + +typedef enum deviter_flags deviter_flags_t; + +struct deviter { + device_t di_prev; + deviter_flags_t di_flags; + int di_curdepth; + int di_maxdepth; +}; + +typedef struct deviter deviter_t; + /* * Description of a locator, as part of interface attribute definitions. */ @@ -437,6 +455,11 @@ int device_locator(device_t, u_int); void *device_private(device_t); prop_dictionary_t device_properties(device_t); +device_t deviter_first(deviter_t *, deviter_flags_t); +void deviter_init(deviter_t *, deviter_flags_t); +device_t deviter_next(deviter_t *); +void deviter_release(deviter_t *); + bool device_active(device_t, devactive_t); bool device_active_register(device_t, void (*)(device_t, devactive_t));