Module Name:    src
Committed By:   dyoung
Date:           Thu Nov 12 19:10:31 UTC 2009

Modified Files:
        src/sys/kern: subr_autoconf.c
        src/sys/sys: device.h

Log Message:
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.


To generate a diff of this commit:
cvs rdiff -u -r1.186 -r1.187 src/sys/kern/subr_autoconf.c
cvs rdiff -u -r1.124 -r1.125 src/sys/sys/device.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/subr_autoconf.c
diff -u src/sys/kern/subr_autoconf.c:1.186 src/sys/kern/subr_autoconf.c:1.187
--- src/sys/kern/subr_autoconf.c:1.186	Mon Oct 12 23:33:02 2009
+++ src/sys/kern/subr_autoconf.c	Thu Nov 12 19:10:30 2009
@@ -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 @@
 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 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 @@
 
 	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 @@
 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 @@
 {
 	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 @@
 
 /*
  * 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 @@
 	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 @@
 		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 @@
 		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 @@
 	 */
 	for (i = 0; i < cd->cd_ndevs; i++) {
 		if (cd->cd_devs[i] != NULL)
-			return;
+			break;
+	}
+	/* 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);
 	}
-	/* nothing found; deallocate */
-	kmem_free(cd->cd_devs, sizeof(device_t [cd->cd_ndevs]));
-	cd->cd_devs = NULL;
-	cd->cd_ndevs = 0;
+
+	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 @@
 	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 @@
 		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 @@
 	} 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 @@
 	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.
  */
@@ -1284,6 +1334,33 @@
 }
 
 /*
+ * 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
  * (an error code) otherwise.
@@ -1302,7 +1379,7 @@
 #ifdef DIAGNOSTIC
 	device_t d;
 #endif
-	int rv = 0;
+	int rv = 0, s;
 
 #ifdef DIAGNOSTIC
 	cf = dev->dv_cfdata;
@@ -1317,51 +1394,43 @@
 	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) {
-		if ((flags & DETACH_FORCE) == 0)
-			goto out;
-		else
-			panic("config_detach: forced detach of %s failed (%d)",
-			    device_xname(dev), rv);
+	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);
 	}
 
-	dev->dv_flags &= ~DVF_ACTIVE;
-
 	/*
 	 * The device has now been successfully detached.
 	 */
@@ -1378,7 +1447,7 @@
 	 */
 	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_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 @@
 	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 @@
 	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 @@
 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 @@
 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 @@
 	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 @@
 deviter_init(deviter_t *di, deviter_flags_t flags)
 {
 	device_t dv;
-	bool rw;
+	int s;
 
-	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;
+	memset(di, 0, sizeof(*di));
 
-		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);
+	s = config_alldevs_lock();
+	if ((flags & DEVITER_F_SHUTDOWN) != 0)
+		flags |= DEVITER_F_RW;
 
-	memset(di, 0, sizeof(*di));
+	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 @@
 }
 
 static device_t
-deviter_next1(deviter_t *di)
+deviter_next2(deviter_t *di)
 {
 	device_t dv;
 
@@ -2501,6 +2644,18 @@
 	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 @@
 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

Index: src/sys/sys/device.h
diff -u src/sys/sys/device.h:1.124 src/sys/sys/device.h:1.125
--- src/sys/sys/device.h:1.124	Mon Sep 21 12:14:47 2009
+++ src/sys/sys/device.h	Thu Nov 12 19:10:30 2009
@@ -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 @@
 	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 @@
 	deviter_flags_t	di_flags;
 	int		di_curdepth;
 	int		di_maxdepth;
+	devgen_t	di_gen;
 };
 
 typedef struct deviter deviter_t;
@@ -412,7 +416,6 @@
 #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 */

Reply via email to