diff options
author | imp <imp@FreeBSD.org> | 2009-03-09 13:20:23 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2009-03-09 13:20:23 +0000 |
commit | ee8ea378715a33154aa6b3c374e115815a89fd2b (patch) | |
tree | 025f97293f50b267c597a6d98aae70d648ee2345 /sys | |
parent | 0e6c8b2a3584a7a99d2b0609bb325b6d3b79b2c5 (diff) | |
download | FreeBSD-src-ee8ea378715a33154aa6b3c374e115815a89fd2b.zip FreeBSD-src-ee8ea378715a33154aa6b3c374e115815a89fd2b.tar.gz |
Fix a long-standing bug in newbus. It was introduced when subclassing
was introduced. If you have a bus, say cardbus, that is derived from
a base-bus (say PCI), then ordinarily all PCI drivers would attach to
cardbus devices. However, there had been one exception: kldload
wouldn't work.
The problem is in devclass_add_driver. In this routine, all we did
was call to the pci device's BUS_DRIVER_ADDED routine. However, since
cardbus bus instances had a different devclass, none of them were
called.
The solution is to call all subclass devclasses, recursively down the
tree, of the class that was loaded. Since we don't have a 'children
class' pointer, we search the whole list of devclasses for a class
whose parent matches. Since just done a kldload time, this isn't as
bad as it sounds. In addition, we short-circuit the whole process by
marking those classes with subclasses with a flag. We'll likely have
to reevaluate this method the number of devclasses with subclasses
gets large.
This means we can remove the "cardbus" lines from all the PCI drivers
since we have no cardbus specific attach device attachments in the
tree.
# Also: minor tweak to an error message
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/subr_bus.c | 62 |
1 files changed, 53 insertions, 9 deletions
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 38651fb..68d3873 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -82,6 +82,8 @@ struct devclass { char *name; device_t *devices; /* array of devices indexed by unit */ int maxunit; /* size of devices array */ + int flags; +#define DC_HAS_CHILDREN 1 struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; @@ -813,6 +815,7 @@ devclass_find_internal(const char *classname, const char *parentname, if (parentname && dc && !dc->parent && strcmp(classname, parentname) != 0) { dc->parent = devclass_find_internal(parentname, NULL, FALSE); + dc->parent->flags |= DC_HAS_CHILDREN; } return (dc); @@ -846,6 +849,52 @@ devclass_find(const char *classname) return (devclass_find_internal(classname, NULL, FALSE)); } + +/** + * @brief Register that a device driver has been added to a devclass + * + * Register that a device driver has been added to a devclass. This + * 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... + * + * @param dc the devclass to edit + * @param driver the driver that was just added + */ +static void +devclass_driver_added(devclass_t dc, driver_t *driver) +{ + int i; + devclass_t parent; + + /* + * 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); + + /* + * 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 thoe list for those devclasses + * that have children. + */ + if (!(dc->flags & DC_HAS_CHILDREN)) + return; + parent = dc; + TAILQ_FOREACH(dc, &devclasses, link) { + if (dc->parent == parent) + devclass_driver_added(dc, driver); + } +} + /** * @brief Add a device driver to a device class * @@ -861,7 +910,6 @@ int devclass_add_driver(devclass_t dc, driver_t *driver) { driverlink_t dl; - int i; PDEBUG(("%s", DRIVERNAME(driver))); @@ -886,13 +934,7 @@ devclass_add_driver(devclass_t dc, driver_t *driver) TAILQ_INSERT_TAIL(&dc->drivers, dl, link); driver->refs++; /* XXX: kobj_mtx */ - /* - * 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); - + devclass_driver_added(dc, driver); bus_data_generation_update(); return (0); } @@ -1758,7 +1800,9 @@ device_probe_child(device_t dev, device_t child) device_set_driver(child, dl->driver); if (!hasclass) { if (device_set_devclass(child, dl->driver->name)) { - PDEBUG(("Unable to set device class")); + printf("driver bug: Unable to set devclass (devname: %s)\n", + (child ? device_get_name(child) : + "no device")); device_set_driver(child, NULL); continue; } |