Synchronize readers and writers of the device tree.

Add a device iterator object, deviter_t, and methods deviter_init(),
deviter_first(), and deviter_next() for visiting each device in
the device tree.

Take care not to re-shutdown a device in the event that the machine
panics during reboot and the operator types 'reboot' at the kernel
debugger prompt.

While I'm here, sprinkle PMF_FN_ARGS, PMF_FN_PROTO, et cetera.
This commit is contained in:
dyoung 2008-03-05 07:09:18 +00:00
parent ade3d15dc2
commit 4902571538
4 changed files with 443 additions and 174 deletions

View File

@ -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 <sys/cdefs.h>
__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 <sys/param.h>
#include <sys/systm.h>
@ -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);
}

View File

@ -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 <jmcneill@invisible.ca>
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/param.h>
@ -47,6 +47,11 @@ __KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.14 2008/03/05 04:54:24 dyoung Exp $")
#include <sys/syscallargs.h> /* for sys_sync */
#include <sys/workqueue.h>
#include <prop/proplib.h>
#include <sys/condvar.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/reboot.h> /* for RB_NOSYNC */
#include <sys/sched.h>
/* 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 <net/if.h>
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;

View File

@ -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 <sys/cdefs.h>
__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 <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/callout.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/disk.h>
@ -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);
}

View File

@ -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));