Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 7 Jun 2009 14:37:56 GMT
From:      Scott Long <scottl@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 163715 for review
Message-ID:  <200906071437.n57EbuLu016844@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=163715

Change 163715 by scottl@scottl-deimos on 2009/06/07 14:37:21

	Initial locking for devclass using the global devclasses_mtx lock.
	Reference counts are added for device_t and devclass objects so
	that the global lock can be dropped when engresses out of
	the subsystem.  Refcounting is still incomplete.

Affected files ...

.. //depot/projects/scottl-camlock/src/sys/kern/subr_bus.c#22 edit

Differences ...

==== //depot/projects/scottl-camlock/src/sys/kern/subr_bus.c#22 (text+ko) ====

@@ -66,6 +66,7 @@
 struct driverlink {
 	kobj_class_t	driver;
 	TAILQ_ENTRY(driverlink) link;	/* list of drivers in devclass */
+	TAILQ_ENTRY(driverlink) probe_link;
 };
 
 /*
@@ -87,8 +88,14 @@
 
 	struct sysctl_ctx_list sysctl_ctx;
 	struct sysctl_oid *sysctl_tree;
+
+	int		class_ref;
+	int		class_busy;
 };
 
+#define DC_REF(dc)	(dc)->class_ref++
+#define DC_UNREF(dc)	(dc)->class_ref--
+
 /**
  * @brief Implementation of device.
  */
@@ -134,8 +141,14 @@
 
 	struct sysctl_ctx_list sysctl_ctx; /**< state for sysctl variables  */
 	struct sysctl_oid *sysctl_tree;	/**< state for sysctl variables */
+
+	int		device_ref;
+	int		device_busy;
 };
 
+#define DT_REF(dt)	(dt)->device_ref++
+#define DT_UNREF(dt)	(dt)->device_ref--
+
 static MALLOC_DEFINE(M_BUS, "bus", "Bus data structures");
 static MALLOC_DEFINE(M_BUS_SC, "bus-sc", "Bus data structures, softc");
 
@@ -890,15 +903,34 @@
 static void
 devclass_driver_added(devclass_t dc, driver_t *driver)
 {
+	device_t *devlist;
 	devclass_t parent;
-	int i;
+	int i, maxunit = 0;
+
+	mtx_assert(&devclasses_mtx, MA_OWNED);
 
 	/*
 	 * Call BUS_DRIVER_ADDED for any existing busses in this class.
 	 */
-	for (i = 0; i < dc->maxunit; i++)
-		if (dc->devices[i])
-			BUS_DRIVER_ADDED(dc->devices[i], driver);
+	devlist = malloc(sizeof(device_t)*dc->maxunit, M_BUS, M_NOWAIT|M_ZERO);
+	if (devlist == NULL)
+		/* XXX error? */
+		return;
+
+	for (i = 0; i < dc->maxunit; i++) {
+		if (dc->devices[i]) {
+			DT_REF(dc->devices[i]);
+			devlist[maxunit++] = dc->devices[i];
+		}
+	}
+
+	mtx_unlock(&devclasses_mtx);
+	for (i = 0; i < maxunit; i++) {
+		BUS_DRIVER_ADDED(devlist[i], driver);
+		DT_UNREF(devlist[i]);
+	}
+	free(devlist, M_BUS);
+	mtx_lock(&devclasses_mtx);
 
 	/*
 	 * Walk through the children classes.  Since we only keep a
@@ -912,8 +944,11 @@
 		return;
 	parent = dc;
 	TAILQ_FOREACH(dc, &devclasses, link) {
-		if (dc->parent == parent)
+		if (dc->parent == parent) {
+			DC_REF(dc);
 			devclass_driver_added(dc, driver);
+			DC_UNREF(dc);
+		}
 	}
 }
 
@@ -945,19 +980,23 @@
 	 * goes. This means we can safely use static methods and avoids a
 	 * double-free in devclass_delete_driver.
 	 */
+	mtx_lock(&devclasses_mtx);
 	kobj_class_compile((kobj_class_t) driver);
 
 	/*
 	 * Make sure the devclass which the driver is implementing exists.
 	 */
-	devclass_find_internal(driver->name, NULL, TRUE);
+	devclass_find_internal_locked(driver->name, NULL, TRUE);
 
 	dl->driver = driver;
 	TAILQ_INSERT_TAIL(&dc->drivers, dl, link);
 	driver->refs++;		/* XXX: kobj_mtx */
 
+	DC_REF(dc);
 	devclass_driver_added(dc, driver);
+	DC_UNREF(dc);
 	bus_data_generation_update();
+	mtx_unlock(&devclasses_mtx);
 	return (0);
 }
 
@@ -980,8 +1019,8 @@
 {
 	devclass_t dc = devclass_find(driver->name);
 	driverlink_t dl;
-	device_t dev;
-	int i;
+	device_t *devlist, dev;
+	int i, maxunit = 0;
 	int error;
 
 	PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass)));
@@ -992,6 +1031,7 @@
 	/*
 	 * Find the link structure in the bus' list of drivers.
 	 */
+	mtx_lock(&devclasses_mtx);
 	TAILQ_FOREACH(dl, &busclass->drivers, link) {
 		if (dl->driver == driver)
 			break;
@@ -1000,6 +1040,7 @@
 	if (!dl) {
 		PDEBUG(("%s not found in %s list", driver->name,
 		    busclass->name));
+		mtx_unlock(&devclasses_mtx);
 		return (ENOENT);
 	}
 
@@ -1013,18 +1054,39 @@
 	 * should not detach devices which are not children of devices in
 	 * the affected devclass.
 	 */
+	devlist = malloc(sizeof(device_t)*dc->maxunit, M_BUS, M_NOWAIT|M_ZERO);
+	if (devlist == NULL) {
+		mtx_unlock(&devclasses_mtx);
+		return (ENOMEM);
+	}
+
 	for (i = 0; i < dc->maxunit; i++) {
 		if (dc->devices[i]) {
 			dev = dc->devices[i];
 			if (dev->driver == driver && dev->parent &&
 			    dev->parent->devclass == busclass) {
-				if ((error = device_detach(dev)) != 0)
-					return (error);
-				device_set_driver(dev, NULL);
+				DT_REF(dev);
+				devlist[maxunit++] = dev;
 			}
 		}
 	}
+	mtx_unlock(&devclasses_mtx);
 
+	error = 0;
+	for (i = 0; i < maxunit; i++) {
+		dev = devlist[i];
+		if ((error = device_detach(dev)) != 0) {
+			DT_UNREF(dev);
+			break;
+		}
+		device_set_driver(dev, NULL);
+		DT_UNREF(dev);
+	}
+	free(devlist, M_BUS);
+	if (error)
+		return (error);
+
+	mtx_lock(&devclasses_mtx);
 	TAILQ_REMOVE(&busclass->drivers, dl, link);
 	free(dl, M_BUS);
 
@@ -1034,6 +1096,7 @@
 		kobj_class_free((kobj_class_t) driver);
 
 	bus_data_generation_update();
+	mtx_unlock(&devclasses_mtx);
 	return (0);
 }
 
@@ -1055,8 +1118,8 @@
 {
 	devclass_t dc = devclass_find(driver->name);
 	driverlink_t dl;
-	device_t dev;
-	int i;
+	device_t *devlist, dev;
+	int i, maxunit = 0;
 	int error;
 
 	PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass)));
@@ -1067,6 +1130,7 @@
 	/*
 	 * Find the link structure in the bus' list of drivers.
 	 */
+	mtx_lock(&devclasses_mtx);
 	TAILQ_FOREACH(dl, &busclass->drivers, link) {
 		if (dl->driver == driver)
 			break;
@@ -1075,6 +1139,7 @@
 	if (!dl) {
 		PDEBUG(("%s not found in %s list", driver->name,
 		    busclass->name));
+		mtx_unlock(&devclasses_mtx);
 		return (ENOENT);
 	}
 
@@ -1088,18 +1153,35 @@
 	 * should not quiesce devices which are not children of
 	 * devices in the affected devclass.
 	 */
+	devlist = malloc(sizeof(device_t)*dc->maxunit, M_BUS, M_NOWAIT|M_ZERO);
+	if (devlist == NULL) {
+		mtx_unlock(&devclasses_mtx);
+		return (ENOMEM);
+	}
+
 	for (i = 0; i < dc->maxunit; i++) {
 		if (dc->devices[i]) {
 			dev = dc->devices[i];
 			if (dev->driver == driver && dev->parent &&
 			    dev->parent->devclass == busclass) {
-				if ((error = device_quiesce(dev)) != 0)
-					return (error);
+				DT_REF(dev);
+				devlist[maxunit++] = dev;
 			}
 		}
 	}
+	mtx_unlock(&devclasses_mtx);
+
+	error = 0;
+	for (i = 0; i < maxunit; i++) {
+		dev = devlist[i];
+		error = device_quiesce(dev);
+		DT_UNREF(dev);
+		if (error)
+			break;
+	}
 
-	return (0);
+	free(devlist, M_BUS);
+	return (error);
 }
 
 /**
@@ -1112,6 +1194,8 @@
 
 	PDEBUG(("%s in devclass %s", classname, DEVCLANAME(dc)));
 
+	mtx_assert(&devclasses_mtx, MA_OWNED);
+
 	TAILQ_FOREACH(dl, &dc->drivers, link) {
 		if (!strcmp(dl->driver->name, classname))
 			return (dl);
@@ -1136,7 +1220,9 @@
 {
 	driverlink_t dl;
 
+	mtx_lock(&devclasses_mtx);
 	dl = devclass_find_driver_internal(dc, classname);
+	mtx_unlock(&devclasses_mtx);
 	if (dl)
 		return (dl->driver);
 	return (NULL);
@@ -1163,9 +1249,17 @@
 device_t
 devclass_get_device(devclass_t dc, int unit)
 {
+	device_t dev;
+
 	if (dc == NULL || unit < 0 || unit >= dc->maxunit)
 		return (NULL);
-	return (dc->devices[unit]);
+
+	mtx_lock(&devclasses_mtx);
+	dev = dc->devices[unit];
+	DT_REF(dev);
+	mtx_unlock(&devclasses_mtx);
+
+	return (dev);
 }
 
 /**
@@ -1182,12 +1276,17 @@
 devclass_get_softc(devclass_t dc, int unit)
 {
 	device_t dev;
+	void *sc;
 
 	dev = devclass_get_device(dc, unit);
 	if (!dev)
 		return (NULL);
 
-	return (device_get_softc(dev));
+	sc = device_get_softc(dev);
+	mtx_lock(&devclasses_mtx);
+	DT_UNREF(dev);
+	mtx_unlock(&devclasses_mtx);
+	return (sc);
 }
 
 /**
@@ -1210,20 +1309,27 @@
 devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp)
 {
 	int count, i;
+	device_t dev;
 	device_t *list;
 
 	count = devclass_get_count(dc);
 	list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT|M_ZERO);
-	if (!list)
+	if (!list) {
+		mtx_unlock(&devclasses_mtx);
 		return (ENOMEM);
+	}
 
 	count = 0;
+	mtx_lock(&devclasses_mtx);
 	for (i = 0; i < dc->maxunit; i++) {
-		if (dc->devices[i]) {
-			list[count] = dc->devices[i];
+		dev = dc->devices[i];
+		if (dev) {
+			DT_REF(dev);
+			list[count] = dev;
 			count++;
 		}
 	}
+	mtx_unlock(&devclasses_mtx);
 
 	*devlistp = list;
 	*devcountp = count;
@@ -1255,17 +1361,21 @@
 	int count;
 
 	count = 0;
+	mtx_lock(&devclasses_mtx);
 	TAILQ_FOREACH(dl, &dc->drivers, link)
 		count++;
 	list = malloc(count * sizeof(driver_t *), M_TEMP, M_NOWAIT);
-	if (list == NULL)
+	if (list == NULL) {
+		mtx_unlock(&devclasses_mtx);
 		return (ENOMEM);
+	}
 
 	count = 0;
 	TAILQ_FOREACH(dl, &dc->drivers, link) {
 		list[count] = dl->driver;
 		count++;
 	}
+	mtx_unlock(&devclasses_mtx);
 	*listp = list;
 	*countp = count;
 
@@ -1283,9 +1393,11 @@
 	int count, i;
 
 	count = 0;
+	mtx_lock(&devclasses_mtx);
 	for (i = 0; i < dc->maxunit; i++)
 		if (dc->devices[i])
 			count++;
+	mtx_unlock(&devclasses_mtx);
 	return (count);
 }
 
@@ -1798,6 +1910,7 @@
 	devclass_t dc;
 	driverlink_t best = NULL;
 	driverlink_t dl;
+	driver_list_t dllist;
 	int result, pri = 0;
 	int hasclass = (child->devclass != NULL);
 
@@ -1814,10 +1927,20 @@
 	if (child->state == DS_ALIVE && (child->flags & DF_REBID) == 0)
 		return (0);
 
+	mtx_lock(&devclasses_mtx);
+	TAILQ_INIT(&dllist);
 	for (; dc; dc = dc->parent) {
 		for (dl = first_matching_driver(dc, child);
 		     dl;
 		     dl = next_matching_driver(dc, child, dl)) {
+			TAILQ_INSERT_TAIL(&dllist, dl, probe_link);
+		}
+	}
+	mtx_unlock(&devclasses_mtx);
+
+	while ((dl = TAILQ_FIRST(&dllist)) != NULL) {
+		TAILQ_REMOVE(&dllist, dl, probe_link);
+		{
 			PDEBUG(("Trying %s", DRIVERNAME(dl->driver)));
 			device_set_driver(child, dl->driver);
 			if (!hasclass) {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200906071437.n57EbuLu016844>