Move a device-deactivation pattern that is replicated throughout

the system into config_deactivate(dev): deactivate dev and all of
its descendants.  Block all interrupts while calling each device's
activation hook, ca_activate.  Now it is possible to simplify or
to delete several device-activation hooks throughout the system.

Do not deactivate a driver while detaching it!  If the driver was
already deactivated (because of accidental/emergency removal), let
the driver cope with the knowledge that DVF_ACTIVE has been cleared.
Otherwise, let the driver access the underlying hardware (so that
it can flush caches, restore original register settings, et cetera)
until it exits its device-detachment hook.

Let multiple readers and writers simultaneously access the system's
device_t list, alldevs, from either interrupt or thread context:
postpone changing alldevs linkages and freeing autoconf device
structures until a garbage-collection phase that runs after all
readers & writers have left the list.

Give device iterators (deviter(9)) a consistent view of alldevs no
matter whether device_t's are added and deleted during iteration:
keep a global alldevs generation number.  When an iterator enters
alldevs, record the current generation number in the iterator and
increase the global number.  When a device_t is created, label it
with the current global generation number.  When a device_t is
deleted, add a second label, the current global generation number.
During iteration, compare a device_t's added- and deleted-generation
with the iterator's generation and skip a device_t that was deleted
before the iterator entered the list or added after the iterator
entered the list.

The alldevs generation number is never 0.  The garbage collector
reaps device_t's whose delete-generation number is non-zero.

Make alldevs private to sys/kern/subr_autoconf.c.  Use deviter(9)
to access it.
This commit is contained in:
dyoung 2009-11-12 19:10:30 +00:00
parent b839469ef3
commit 972989f5e3
2 changed files with 321 additions and 168 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: subr_autoconf.c,v 1.186 2009/10/12 23:33:02 yamt Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.187 2009/11/12 19:10:30 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.186 2009/10/12 23:33:02 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.187 2009/11/12 19:10:30 dyoung Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@ -165,10 +165,13 @@ struct matchinfo {
static char *number(char *, int);
static void mapply(struct matchinfo *, cfdata_t);
static device_t config_devalloc(const device_t, const cfdata_t, const int *);
static void config_devdealloc(device_t);
static void config_devdelete(device_t);
static void config_makeroom(int, struct cfdriver *);
static void config_devlink(device_t);
static void config_devunlink(device_t);
static void config_alldevs_unlock(int);
static int config_alldevs_lock(void);
static void config_collect_garbage(void);
static void pmflock_debug(device_t, const char *, int);
@ -202,12 +205,12 @@ static TAILQ_HEAD(, finalize_hook) config_finalize_list =
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;
static struct devicelist alldevs = TAILQ_HEAD_INITIALIZER(alldevs);
static kmutex_t alldevs_mtx;
static volatile bool alldevs_garbage = false;
static volatile devgen_t alldevs_gen = 1;
static volatile int alldevs_nread = 0;
static volatile int alldevs_nwrite = 0;
static int config_pending; /* semaphore for mountroot */
static kmutex_t config_misc_lock;
@ -238,8 +241,7 @@ config_init(void)
KASSERT(config_initialized == false);
mutex_init(&alldevs_mtx, MUTEX_DEFAULT, IPL_NONE);
cv_init(&alldevs_cv, "alldevs");
mutex_init(&alldevs_mtx, MUTEX_DEFAULT, IPL_HIGH);
mutex_init(&config_misc_lock, MUTEX_DEFAULT, IPL_NONE);
cv_init(&config_misc_cv, "cfgmisc");
@ -366,13 +368,21 @@ config_cfdriver_attach(struct cfdriver *cd)
int
config_cfdriver_detach(struct cfdriver *cd)
{
int i;
int i, rc = 0, s;
s = config_alldevs_lock();
config_collect_garbage();
/* Make sure there are no active instances. */
for (i = 0; i < cd->cd_ndevs; i++) {
if (cd->cd_devs[i] != NULL)
return EBUSY;
if (cd->cd_devs[i] != NULL) {
rc = EBUSY;
break;
}
}
config_alldevs_unlock(s);
if (rc != 0)
return rc;
/* ...and no attachments loaded. */
if (LIST_EMPTY(&cd->cd_attach) == 0)
@ -433,19 +443,27 @@ config_cfattach_detach(const char *driver, struct cfattach *ca)
{
struct cfdriver *cd;
device_t dev;
int i;
int i, rc = 0, s;
cd = config_cfdriver_lookup(driver);
if (cd == NULL)
return ESRCH;
s = config_alldevs_lock();
config_collect_garbage();
/* Make sure there are no active instances. */
for (i = 0; i < cd->cd_ndevs; i++) {
if ((dev = cd->cd_devs[i]) == NULL)
continue;
if (dev->dv_cfattach == ca)
return EBUSY;
if (dev->dv_cfattach == ca) {
rc = EBUSY;
break;
}
}
config_alldevs_unlock(s);
if (rc != 0)
return rc;
LIST_REMOVE(ca, ca_list);
@ -923,6 +941,11 @@ number(char *ep, int n)
/*
* Expand the size of the cd_devs array if necessary.
*
* The caller must hold alldevs_mtx. config_makeroom() may release and
* re-acquire alldevs_mtx, so callers should re-check conditions such
* as alldevs_nwrite == 0 and alldevs_nread == 0 when config_makeroom()
* returns.
*/
static void
config_makeroom(int n, struct cfdriver *cd)
@ -930,8 +953,11 @@ config_makeroom(int n, struct cfdriver *cd)
int old, new;
device_t *nsp;
alldevs_nwrite++;
mutex_exit(&alldevs_mtx);
if (n < cd->cd_ndevs)
return;
goto out;
/*
* Need to expand the array.
@ -943,7 +969,6 @@ config_makeroom(int n, struct cfdriver *cd)
new = old * 2;
while (new <= n)
new *= 2;
cd->cd_ndevs = new;
nsp = kmem_alloc(sizeof(device_t [new]), KM_SLEEP);
if (nsp == NULL)
panic("config_attach: %sing dev array",
@ -953,37 +978,50 @@ config_makeroom(int n, struct cfdriver *cd)
memcpy(nsp, cd->cd_devs, sizeof(device_t [old]));
kmem_free(cd->cd_devs, sizeof(device_t [old]));
}
cd->cd_ndevs = new;
cd->cd_devs = nsp;
out:
mutex_enter(&alldevs_mtx);
alldevs_nwrite--;
}
static void
config_devlink(device_t dev)
{
struct cfdriver *cd = dev->dv_cfdriver;
int s;
/* put this device in the devices array */
s = config_alldevs_lock();
config_makeroom(dev->dv_unit, cd);
if (cd->cd_devs[dev->dv_unit])
panic("config_attach: duplicate %s", device_xname(dev));
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.
* readers and writers are in the list.
*/
mutex_enter(&alldevs_mtx);
while (alldevs_nwrite != 0 && alldevs_writer != curlwp)
cv_wait(&alldevs_cv, &alldevs_mtx);
dev->dv_add_gen = alldevs_gen;
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */
cv_signal(&alldevs_cv);
mutex_exit(&alldevs_mtx);
config_alldevs_unlock(s);
}
/*
* Caller must hold alldevs_mtx. config_devdelete() may release and
* re-acquire alldevs_mtx, so callers should re-check conditions such as
* alldevs_nwrite == 0 && alldevs_nread == 0 after config_devdelete()
* returns.
*/
static void
config_devunlink(device_t dev)
config_devdelete(device_t dev)
{
device_lock_t dvl = device_getlock(dev);
int priv = (dev->dv_flags & DVF_PRIV_ALLOC);
struct cfdriver *cd = dev->dv_cfdriver;
int i;
device_t *devs = NULL;
int i, ndevs = 0;
KASSERT(mutex_owned(&alldevs_mtx));
/* Unlink from device list. */
TAILQ_REMOVE(&alldevs, dev, dv_list);
@ -996,14 +1034,47 @@ config_devunlink(device_t dev)
*/
for (i = 0; i < cd->cd_ndevs; i++) {
if (cd->cd_devs[i] != NULL)
return;
break;
}
/* nothing found; deallocate */
kmem_free(cd->cd_devs, sizeof(device_t [cd->cd_ndevs]));
cd->cd_devs = NULL;
cd->cd_ndevs = 0;
/* nothing found. unlink, now. deallocate below. */
if (i == cd->cd_ndevs) {
ndevs = cd->cd_ndevs;
devs = cd->cd_devs;
cd->cd_devs = NULL;
cd->cd_ndevs = 0;
}
mutex_exit(&alldevs_mtx);
KASSERT(!mutex_owned(&alldevs_mtx));
if (devs != NULL)
kmem_free(devs, sizeof(device_t [ndevs]));
cv_destroy(&dvl->dvl_cv);
mutex_destroy(&dvl->dvl_mtx);
KASSERT(dev->dv_properties != NULL);
prop_object_release(dev->dv_properties);
if (dev->dv_activity_handlers)
panic("%s with registered handlers", __func__);
if (dev->dv_locators) {
size_t amount = *--dev->dv_locators;
kmem_free(dev->dv_locators, amount);
}
if (dev->dv_cfattach->ca_devsize > 0)
kmem_free(dev->dv_private, dev->dv_cfattach->ca_devsize);
if (priv)
kmem_free(dev, sizeof(*dev));
KASSERT(!mutex_owned(&alldevs_mtx));
mutex_enter(&alldevs_mtx);
}
static device_t
config_devalloc(const device_t parent, const cfdata_t cf, const int *locs)
{
@ -1011,7 +1082,7 @@ config_devalloc(const device_t parent, const cfdata_t cf, const int *locs)
struct cfattach *ca;
size_t lname, lunit;
const char *xunit;
int myunit;
int myunit, s;
char num[10];
device_t dev;
void *dev_private;
@ -1031,6 +1102,8 @@ config_devalloc(const device_t parent, const cfdata_t cf, const int *locs)
panic("config_devalloc: %s", cf->cf_atname);
#ifndef __BROKEN_CONFIG_UNIT_USAGE
s = config_alldevs_lock();
config_collect_garbage();
if (cf->cf_fstate == FSTATE_STAR) {
for (myunit = cf->cf_unit; myunit < cd->cd_ndevs; myunit++)
if (cd->cd_devs[myunit] == NULL)
@ -1042,8 +1115,11 @@ config_devalloc(const device_t parent, const cfdata_t cf, const int *locs)
} else {
myunit = cf->cf_unit;
if (myunit < cd->cd_ndevs && cd->cd_devs[myunit] != NULL)
return NULL;
}
myunit = -1;
}
config_alldevs_unlock(s);
if (myunit == -1)
return NULL;
#else
myunit = cf->cf_unit;
#endif /* ! __BROKEN_CONFIG_UNIT_USAGE */
@ -1116,32 +1192,6 @@ config_devalloc(const device_t parent, const cfdata_t cf, const int *locs)
return dev;
}
static void
config_devdealloc(device_t dev)
{
device_lock_t dvl = device_getlock(dev);
int priv = (dev->dv_flags & DVF_PRIV_ALLOC);
cv_destroy(&dvl->dvl_cv);
mutex_destroy(&dvl->dvl_mtx);
KASSERT(dev->dv_properties != NULL);
prop_object_release(dev->dv_properties);
if (dev->dv_activity_handlers)
panic("config_devdealloc with registered handlers");
if (dev->dv_locators) {
size_t amount = *--dev->dv_locators;
kmem_free(dev->dv_locators, amount);
}
if (dev->dv_cfattach->ca_devsize > 0)
kmem_free(dev->dv_private, dev->dv_cfattach->ca_devsize);
if (priv)
kmem_free(dev, sizeof(*dev));
}
/*
* Attach a found device.
*/
@ -1283,6 +1333,33 @@ config_attach_pseudo(cfdata_t cf)
return dev;
}
/*
* Caller must hold alldevs_mtx. config_collect_garbage() may
* release and re-acquire alldevs_mtx, so callers should re-check
* conditions such as alldevs_nwrite == 0 && alldevs_nread == 0 after
* config_collect_garbage() returns.
*/
static void
config_collect_garbage(void)
{
device_t dv;
KASSERT(mutex_owned(&alldevs_mtx));
while (alldevs_nwrite == 0 && alldevs_nread == 0 && alldevs_garbage) {
TAILQ_FOREACH(dv, &alldevs, dv_list) {
if (dv->dv_del_gen != 0)
break;
}
if (dv == NULL) {
alldevs_garbage = false;
break;
}
config_devdelete(dv);
}
KASSERT(mutex_owned(&alldevs_mtx));
}
/*
* Detach a device. Optionally forced (e.g. because of hardware
* removal) and quiet. Returns zero if successful, non-zero
@ -1302,7 +1379,7 @@ config_detach(device_t dev, int flags)
#ifdef DIAGNOSTIC
device_t d;
#endif
int rv = 0;
int rv = 0, s;
#ifdef DIAGNOSTIC
cf = dev->dv_cfdata;
@ -1317,50 +1394,42 @@ 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);
s = config_alldevs_lock();
if (dev->dv_del_gen != 0) {
config_alldevs_unlock(s);
#ifdef DIAGNOSTIC
printf("%s: %s is already detached\n", __func__,
device_xname(dev));
#endif /* DIAGNOSTIC */
return ENOENT;
}
alldevs_nwrite++;
config_alldevs_unlock(s);
/*
* Ensure the device is deactivated. If the device doesn't
* have an activation entry point, we allow DVF_ACTIVE to
* remain set. Otherwise, if DVF_ACTIVE is still set, the
* device is busy, and the detach fails.
*/
if (!detachall &&
(flags & (DETACH_SHUTDOWN|DETACH_FORCE)) == DETACH_SHUTDOWN &&
(dev->dv_flags & DVF_DETACH_SHUTDOWN) == 0) {
rv = EOPNOTSUPP;
} else if ((rv = config_deactivate(dev)) == EOPNOTSUPP)
rv = 0; /* Do not treat EOPNOTSUPP as an error */
} else if (ca->ca_detach != NULL) {
rv = (*ca->ca_detach)(dev, flags);
} else
rv = EOPNOTSUPP;
/*
* Try to detach the device. If that's not possible, then
* we either panic() (for the forced but failed case), or
* return an error.
* If it was not possible to detach the device, then we either
* panic() (for the forced but failed case), or return an error.
*
* If it was possible to detach the device, ensure that the
* device is deactivated.
*/
if (rv == 0) {
if (ca->ca_detach != NULL)
rv = (*ca->ca_detach)(dev, flags);
else
rv = EOPNOTSUPP;
if (rv == 0)
dev->dv_flags &= ~DVF_ACTIVE;
else if ((flags & DETACH_FORCE) == 0)
goto out;
else {
panic("config_detach: forced detach of %s failed (%d)",
device_xname(dev), rv);
}
if (rv != 0) {
if ((flags & DETACH_FORCE) == 0)
goto out;
else
panic("config_detach: forced detach of %s failed (%d)",
device_xname(dev), rv);
}
dev->dv_flags &= ~DVF_ACTIVE;
/*
* The device has now been successfully detached.
@ -1378,7 +1447,7 @@ config_detach(device_t dev, int flags)
*/
for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
d = TAILQ_NEXT(d, dv_list)) {
if (d->dv_parent == dev) {
if (d->dv_parent == dev && d->dv_del_gen == 0) {
printf("config_detach: detached device %s"
" has children %s\n", device_xname(dev), device_xname(d));
panic("config_detach");
@ -1416,20 +1485,24 @@ config_detach(device_t dev, int flags)
}
}
config_devunlink(dev);
if (dev->dv_cfdata != NULL && (flags & DETACH_QUIET) == 0)
aprint_normal_dev(dev, "detached\n");
config_devdealloc(dev);
out:
mutex_enter(&alldevs_mtx);
s = config_alldevs_lock();
KASSERT(alldevs_nwrite != 0);
if (--alldevs_nwrite == 0)
alldevs_writer = NULL;
cv_signal(&alldevs_cv);
mutex_exit(&alldevs_mtx);
--alldevs_nwrite;
if (rv != 0)
;
else if (dev->dv_del_gen != 0)
;
else {
dev->dv_del_gen = alldevs_gen;
alldevs_garbage = true;
}
config_collect_garbage();
config_alldevs_unlock(s);
return rv;
}
@ -1497,21 +1570,49 @@ config_detach_all(int how)
return progress;
}
static bool
device_is_ancestor_of(device_t ancestor, device_t descendant)
{
device_t dv;
for (dv = descendant; dv != NULL; dv = device_parent(dv)) {
if (device_parent(dv) == ancestor)
return true;
}
return false;
}
int
config_deactivate(device_t dev)
{
const struct cfattach *ca = dev->dv_cfattach;
int rv = 0, oflags = dev->dv_flags;
deviter_t di;
const struct cfattach *ca;
device_t descendant;
int s, rv = 0, oflags;
if (ca->ca_activate == NULL)
return EOPNOTSUPP;
for (descendant = deviter_first(&di, DEVITER_F_ROOT_FIRST);
descendant != NULL;
descendant = deviter_next(&di)) {
if (dev != descendant &&
!device_is_ancestor_of(dev, descendant))
continue;
if (dev->dv_flags & DVF_ACTIVE) {
dev->dv_flags &= ~DVF_ACTIVE;
rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE);
if (rv)
dev->dv_flags = oflags;
if ((descendant->dv_flags & DVF_ACTIVE) == 0)
continue;
ca = descendant->dv_cfattach;
oflags = descendant->dv_flags;
descendant->dv_flags &= ~DVF_ACTIVE;
if (ca->ca_activate == NULL)
continue;
s = splhigh();
rv = (*ca->ca_activate)(descendant, DVACT_DEACTIVATE);
splx(s);
if (rv != 0)
descendant->dv_flags = oflags;
}
deviter_release(&di);
return rv;
}
@ -1740,6 +1841,23 @@ config_twiddle_fn(void *cookie)
mutex_exit(&config_misc_lock);
}
static int
config_alldevs_lock(void)
{
int s;
s = splhigh();
mutex_enter(&alldevs_mtx);
return s;
}
static void
config_alldevs_unlock(int s)
{
mutex_exit(&alldevs_mtx);
splx(s);
}
/*
* device_lookup:
*
@ -1748,11 +1866,20 @@ config_twiddle_fn(void *cookie)
device_t
device_lookup(cfdriver_t cd, int unit)
{
device_t dv;
int s;
s = config_alldevs_lock();
config_collect_garbage();
KASSERT(mutex_owned(&alldevs_mtx));
if (unit < 0 || unit >= cd->cd_ndevs)
return NULL;
return cd->cd_devs[unit];
dv = NULL;
else
dv = cd->cd_devs[unit];
config_alldevs_unlock(s);
KASSERT(!mutex_owned(&alldevs_mtx));
return dv;
}
/*
@ -1764,14 +1891,22 @@ void *
device_lookup_private(cfdriver_t cd, int unit)
{
device_t dv;
int s;
void *priv;
s = config_alldevs_lock();
config_collect_garbage();
KASSERT(mutex_owned(&alldevs_mtx));
if (unit < 0 || unit >= cd->cd_ndevs)
return NULL;
if ((dv = cd->cd_devs[unit]) == NULL)
return NULL;
priv = NULL;
else if ((dv = cd->cd_devs[unit]) == NULL)
priv = NULL;
else
priv = dv->dv_private;
config_alldevs_unlock(s);
return dv->dv_private;
KASSERT(!mutex_owned(&alldevs_mtx));
return priv;
}
/*
@ -2363,6 +2498,20 @@ device_active_deregister(device_t dev, void (*handler)(device_t, devactive_t))
old_handlers[i] = NULL;
}
/* Return true iff the device_t `dev' exists at generation `gen'. */
static bool
device_exists_at(device_t dv, devgen_t gen)
{
return (dv->dv_del_gen == 0 || dv->dv_del_gen > gen) &&
dv->dv_add_gen <= gen;
}
static bool
deviter_visits(const deviter_t *di, device_t dv)
{
return device_exists_at(dv, di->di_gen);
}
/*
* Device Iteration
*
@ -2423,43 +2572,37 @@ void
deviter_init(deviter_t *di, deviter_flags_t flags)
{
device_t dv;
bool rw;
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);
int s;
memset(di, 0, sizeof(*di));
s = config_alldevs_lock();
if ((flags & DEVITER_F_SHUTDOWN) != 0)
flags |= DEVITER_F_RW;
if ((flags & DEVITER_F_RW) != 0)
alldevs_nwrite++;
else
alldevs_nread++;
di->di_gen = alldevs_gen++;
config_alldevs_unlock(s);
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)
TAILQ_FOREACH(dv, &alldevs, dv_list) {
if (!deviter_visits(di, dv))
continue;
di->di_curdepth = MAX(di->di_curdepth, dv->dv_depth);
}
break;
case DEVITER_F_ROOT_FIRST:
TAILQ_FOREACH(dv, &alldevs, dv_list)
TAILQ_FOREACH(dv, &alldevs, dv_list) {
if (!deviter_visits(di, dv))
continue;
di->di_maxdepth = MAX(di->di_maxdepth, dv->dv_depth);
}
break;
default:
break;
@ -2485,7 +2628,7 @@ deviter_first(deviter_t *di, deviter_flags_t flags)
}
static device_t
deviter_next1(deviter_t *di)
deviter_next2(deviter_t *di)
{
device_t dv;
@ -2501,6 +2644,18 @@ deviter_next1(deviter_t *di)
return dv;
}
static device_t
deviter_next1(deviter_t *di)
{
device_t dv;
do {
dv = deviter_next2(di);
} while (dv != NULL && !deviter_visits(di, dv));
return dv;
}
device_t
deviter_next(deviter_t *di)
{
@ -2536,20 +2691,15 @@ void
deviter_release(deviter_t *di)
{
bool rw = (di->di_flags & DEVITER_F_RW) != 0;
int s;
mutex_enter(&alldevs_mtx);
if (!rw) {
s = config_alldevs_lock();
if (rw)
--alldevs_nwrite;
else
--alldevs_nread;
cv_signal(&alldevs_cv);
} else if (alldevs_nwrite > 0 && alldevs_writer == NULL) {
--alldevs_nwrite; /* shutting down: do not signal */
} else {
KASSERT(alldevs_nwrite != 0);
if (--alldevs_nwrite == 0)
alldevs_writer = NULL;
cv_signal(&alldevs_cv);
}
mutex_exit(&alldevs_mtx);
/* XXX wake a garbage-collection thread */
config_alldevs_unlock(s);
}
static void

View File

@ -1,4 +1,4 @@
/* $NetBSD: device.h,v 1.124 2009/09/21 12:14:47 pooka Exp $ */
/* $NetBSD: device.h,v 1.125 2009/11/12 19:10:30 dyoung Exp $ */
/*
* Copyright (c) 1996, 2000 Christopher G. Demetriou
@ -174,6 +174,9 @@ struct device {
bool (*dv_class_resume)(device_t PMF_FN_PROTO);
void (*dv_class_deregister)(device_t);
devgen_t dv_add_gen,
dv_del_gen;
struct device_lock dv_lock;
device_suspensor_t dv_bus_suspensors[DEVICE_SUSPENSORS_MAX];
device_suspensor_t dv_driver_suspensors[DEVICE_SUSPENSORS_MAX];
@ -205,6 +208,7 @@ struct deviter {
deviter_flags_t di_flags;
int di_curdepth;
int di_maxdepth;
devgen_t di_gen;
};
typedef struct deviter deviter_t;
@ -412,7 +416,6 @@ struct pdevinit {
#ifdef _KERNEL
extern struct cfdriverlist allcfdrivers;/* list of all cfdrivers */
extern struct devicelist alldevs; /* list of all devices */
extern struct cftablelist allcftables; /* list of all cfdata tables */
extern device_t booted_device; /* the device we booted from */
extern device_t booted_wedge; /* the wedge on that device */