svn commit: r219135 - head/sys/kern
John Baldwin
jhb at FreeBSD.org
Tue Mar 1 14:43:38 UTC 2011
Author: jhb
Date: Tue Mar 1 14:43:37 2011
New Revision: 219135
URL: http://svn.freebsd.org/changeset/base/219135
Log:
Similar to 189574, properly handle subclasses of bus drivers when deleting
a driver during kldunload. Specifically, recursively walk the tree of
subclasses of a given driver attachment's bus device class detaching all
instances of that driver for each class and its subclasses.
Reported by: bschmidt
Reviewed by: imp
MFC after: 1 week
Modified:
head/sys/kern/subr_bus.c
Modified: head/sys/kern/subr_bus.c
==============================================================================
--- head/sys/kern/subr_bus.c Tue Mar 1 13:35:48 2011 (r219134)
+++ head/sys/kern/subr_bus.c Tue Mar 1 14:43:37 2011 (r219135)
@@ -987,10 +987,12 @@ devclass_find(const char *classname)
* is called by devclass_add_driver to accomplish the recursive
* notification of all the children classes of dc, as well as dc.
* Each layer will have BUS_DRIVER_ADDED() called for all instances of
- * the devclass. We do a full search here of the devclass list at
- * each iteration level to save storing children-lists in the devclass
- * structure. If we ever move beyond a few dozen devices doing this,
- * we may need to reevaluate...
+ * the devclass.
+ *
+ * We do a full search here of the devclass list at each iteration
+ * level to save storing children-lists in the devclass structure. If
+ * we ever move beyond a few dozen devices doing this, we may need to
+ * reevaluate...
*
* @param dc the devclass to edit
* @param driver the driver that was just added
@@ -1085,6 +1087,78 @@ devclass_add_driver(devclass_t dc, drive
}
/**
+ * @brief Register that a device driver has been deleted from a devclass
+ *
+ * Register that a device driver has been removed from a devclass.
+ * This is called by devclass_delete_driver to accomplish the
+ * recursive notification of all the children classes of busclass, as
+ * well as busclass. Each layer will attempt to detach the driver
+ * from any devices that are children of the bus's devclass. The function
+ * will return an error if a device fails to detach.
+ *
+ * We do a full search here of the devclass list at each iteration
+ * level to save storing children-lists in the devclass structure. If
+ * we ever move beyond a few dozen devices doing this, we may need to
+ * reevaluate...
+ *
+ * @param busclass the devclass of the parent bus
+ * @param dc the devclass of the driver being deleted
+ * @param driver the driver being deleted
+ */
+static int
+devclass_driver_deleted(devclass_t busclass, devclass_t dc, driver_t *driver)
+{
+ devclass_t parent;
+ device_t dev;
+ int error, i;
+
+ /*
+ * Disassociate from any devices. We iterate through all the
+ * devices in the devclass of the driver and detach any which are
+ * using the driver and which have a parent in the devclass which
+ * we are deleting from.
+ *
+ * Note that since a driver can be in multiple devclasses, we
+ * should not detach devices which are not children of devices in
+ * the affected devclass.
+ */
+ 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);
+ BUS_PROBE_NOMATCH(dev->parent, dev);
+ devnomatch(dev);
+ dev->flags |= DF_DONENOMATCH;
+ }
+ }
+ }
+
+ /*
+ * Walk through the children classes. Since we only keep a
+ * single parent pointer around, we walk the entire list of
+ * devclasses looking for children. We set the
+ * DC_HAS_CHILDREN flag when a child devclass is created on
+ * the parent, so we only walk the list for those devclasses
+ * that have children.
+ */
+ if (!(busclass->flags & DC_HAS_CHILDREN))
+ return (0);
+ parent = busclass;
+ TAILQ_FOREACH(busclass, &devclasses, link) {
+ if (busclass->parent == parent) {
+ error = devclass_driver_deleted(busclass, dc, driver);
+ if (error)
+ return (error);
+ }
+ }
+ return (0);
+}
+
+/**
* @brief Delete a device driver from a device class
*
* Delete a device driver from a devclass. This is normally called
@@ -1103,8 +1177,6 @@ devclass_delete_driver(devclass_t buscla
{
devclass_t dc = devclass_find(driver->name);
driverlink_t dl;
- device_t dev;
- int i;
int error;
PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass)));
@@ -1126,30 +1198,9 @@ devclass_delete_driver(devclass_t buscla
return (ENOENT);
}
- /*
- * Disassociate from any devices. We iterate through all the
- * devices in the devclass of the driver and detach any which are
- * using the driver and which have a parent in the devclass which
- * we are deleting from.
- *
- * Note that since a driver can be in multiple devclasses, we
- * should not detach devices which are not children of devices in
- * the affected devclass.
- */
- 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);
- BUS_PROBE_NOMATCH(dev->parent, dev);
- devnomatch(dev);
- dev->flags |= DF_DONENOMATCH;
- }
- }
- }
+ error = devclass_driver_deleted(busclass, dc, driver);
+ if (error != 0)
+ return (error);
TAILQ_REMOVE(&busclass->drivers, dl, link);
free(dl, M_BUS);
More information about the svn-src-head
mailing list