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:
parent
ade3d15dc2
commit
4902571538
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user