--- //depot/vendor/freebsd/src/sys/kern/bus_if.m 2008/11/18 21:05:27 +++ //depot/projects/multipass/sys/kern/bus_if.m 2008/11/20 23:00:08 @@ -574,3 +574,11 @@ int *_unitp; }; +/** + * @brief Notify a bus that the bus pass level has been changed + * + * @param _dev the bus device + */ +METHOD void new_pass { + device_t _dev; +} DEFAULT bus_generic_new_pass; --- //depot/vendor/freebsd/src/sys/kern/subr_bus.c 2009/03/25 17:05:14 +++ //depot/projects/multipass/sys/kern/subr_bus.c 2009/04/14 19:29:23 @@ -66,6 +66,8 @@ struct driverlink { kobj_class_t driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ + int pass; + TAILQ_ENTRY(driverlink) passlink; }; /* @@ -712,7 +714,7 @@ /* * Called when there's no match for this device. This is only called - * the first time that no match happens, so we don't keep getitng this + * the first time that no match happens, so we don't keep getting this * message. Should that prove to be undesirable, we can change it. * This is called when all drivers that can attach to a given bus * decline to accept this device. Other errrors may not be detected. @@ -759,12 +761,98 @@ DEFINE_CLASS(null, null_methods, 0); /* + * Bus pass implementation + */ + +static driver_list_t passes = TAILQ_HEAD_INITIALIZER(passes); +int bus_current_pass = BUS_PASS_ROOT; + +/** + * @internal + * @brief Register the pass level of a new driver attachment + * + * Register a new driver attachment's pass level. If no driver + * attachment with the same pass level has been added, then @p new + * will be added to the global passes list. + * + * @param new the new driver attachment + */ +static void +driver_register_pass(struct driverlink *new) +{ + struct driverlink *dl; + + /* We only consider pass numbers during boot. */ + if (bus_current_pass == BUS_PASS_DEFAULT) + return; + + /* + * Walk the passes list. If we already know about this pass + * then there is nothing to do. If we don't, then insert this + * driver link into the list. + */ + TAILQ_FOREACH(dl, &passes, passlink) { + if (dl->pass < new->pass) + continue; + if (dl->pass == new->pass) + return; + TAILQ_INSERT_BEFORE(dl, new, passlink); + return; + } + TAILQ_INSERT_TAIL(&passes, new, passlink); +} + +/** + * @brief Raise the current bus pass + * + * Raise the current bus pass level to @p pass. Call the BUS_NEW_PASS() + * method on the root bus to kick off a new device tree scan for each + * new pass level that has at least one driver. + */ +void +bus_set_pass(int pass) +{ + struct driverlink *dl; + + if (bus_current_pass > pass) + panic("Attempt to lower bus pass level"); + + TAILQ_FOREACH(dl, &passes, passlink) { + /* Skip pass values below the current pass level. */ + if (dl->pass <= bus_current_pass) + continue; + + /* + * Bail once we hit a driver with a pass level that is + * too high. + */ + if (dl->pass > pass) + break; + + /* + * Raise the pass level to the next level and rescan + * the tree. + */ + bus_current_pass = dl->pass; + BUS_NEW_PASS(root_bus); + } + + /* + * If there isn't a driver registered for the requested pass, + * then bus_current_pass might still be less than 'pass'. Set + * it to 'pass' in that case. + */ + if (bus_current_pass < pass) + bus_current_pass = pass; + KASSERT(bus_current_pass == pass, ("Failed to update bus pass level")); +} + +/* * Devclass implementation */ static devclass_list_t devclasses = TAILQ_HEAD_INITIALIZER(devclasses); - /** * @internal * @brief Find or create a device class @@ -912,12 +1000,16 @@ * @param driver the driver to register */ int -devclass_add_driver(devclass_t dc, driver_t *driver) +devclass_add_driver(devclass_t dc, driver_t *driver, int pass) { driverlink_t dl; PDEBUG(("%s", DRIVERNAME(driver))); + /* Don't allow invalid pass values. */ + if (pass <= BUS_PASS_ROOT) + return (EINVAL); + dl = malloc(sizeof *dl, M_BUS, M_NOWAIT|M_ZERO); if (!dl) return (ENOMEM); @@ -938,6 +1030,8 @@ dl->driver = driver; TAILQ_INSERT_TAIL(&dc->drivers, dl, link); driver->refs++; /* XXX: kobj_mtx */ + dl->pass = pass; + driver_register_pass(dl); devclass_driver_added(dc, driver); bus_data_generation_update(); @@ -1801,6 +1895,11 @@ for (dl = first_matching_driver(dc, child); dl; dl = next_matching_driver(dc, child, dl)) { + + /* If this driver's pass is too high, then ignore it. */ + if (dl->pass > bus_current_pass) + continue; + PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); device_set_driver(child, dl->driver); if (!hasclass) { @@ -2442,8 +2541,9 @@ } return (-1); } - if ((error = device_probe_child(dev->parent, dev)) != 0) { - if (!(dev->flags & DF_DONENOMATCH)) { + if ((error = device_probe_child(dev->parent, dev)) != 0) { + if (bus_current_pass == BUS_PASS_DEFAULT && + !(dev->flags & DF_DONENOMATCH)) { BUS_PROBE_NOMATCH(dev->parent, dev); devnomatch(dev); dev->flags |= DF_DONENOMATCH; @@ -2988,6 +3088,17 @@ driverlink_t dl; TAILQ_FOREACH(dl, &dc->drivers, link) { + /* + * If this driver's pass is too high, then ignore it. + * For most drivers in the default pass, this will + * never be true. For early-pass drivers they will + * only call the identify routines of eligible drivers + * when this routine is called. Drivers for later + * passes should have their identify routines called + * on early-pass busses during BUS_NEW_PASS(). + */ + if (dl->pass > bus_current_pass) + continue; DEVICE_IDENTIFY(dl->driver, dev); } @@ -3215,6 +3326,36 @@ } /** + * @brief Helper function for implementing BUS_NEW_PASS(). + * + * This implementing of BUS_NEW_PASS() first calls the identify + * routines for any drivers that probe at the current pass. Then it + * walks the list of devices for this bus. If a device is already + * attached, then it calls BUS_NEW_PASS() on that device. If the + * device is not already attached, it attempts to attach a driver to + * it. + */ +void +bus_generic_new_pass(device_t dev) +{ + driverlink_t dl; + devclass_t dc; + device_t child; + + dc = dev->devclass; + TAILQ_FOREACH(dl, &dc->drivers, link) { + if (dl->pass == bus_current_pass) + DEVICE_IDENTIFY(dl->driver, dev); + } + TAILQ_FOREACH(child, &dev->children, link) { + if (child->state >= DS_ATTACHED) + BUS_NEW_PASS(child); + else if (child->state == DS_NOTPRESENT) + device_probe_and_attach(child); + } +} + +/** * @brief Helper function for implementing BUS_SETUP_INTR(). * * This simple implementation of BUS_SETUP_INTR() simply calls the @@ -3912,13 +4053,11 @@ void root_bus_configure(void) { - device_t dev; PDEBUG((".")); - TAILQ_FOREACH(dev, &root_bus->children, link) { - device_probe_and_attach(dev); - } + /* Eventually this will be split up, but this is sufficient for now. */ + bus_set_pass(BUS_PASS_DEFAULT); } /** @@ -3932,10 +4071,10 @@ int driver_module_handler(module_t mod, int what, void *arg) { - int error; struct driver_module_data *dmd; devclass_t bus_devclass; kobj_class_t driver; + int error, pass; dmd = (struct driver_module_data *)arg; bus_devclass = devclass_find_internal(dmd->dmd_busname, NULL, TRUE); @@ -3946,10 +4085,11 @@ if (dmd->dmd_chainevh) error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg); + pass = dmd->dmd_pass; driver = dmd->dmd_driver; - PDEBUG(("Loading module: driver %s on bus %s", - DRIVERNAME(driver), dmd->dmd_busname)); - error = devclass_add_driver(bus_devclass, driver); + PDEBUG(("Loading module: driver %s on bus %s (pass %d)", + DRIVERNAME(driver), dmd->dmd_busname, pass)); + error = devclass_add_driver(bus_devclass, driver, pass); if (error) break; --- //depot/vendor/freebsd/src/sys/sys/bus.h 2008/06/20 17:00:23 +++ //depot/projects/multipass/sys/sys/bus.h 2008/12/09 20:54:19 @@ -29,6 +29,7 @@ #ifndef _SYS_BUS_H_ #define _SYS_BUS_H_ +#include #include /** @@ -299,6 +300,7 @@ bus_generic_get_dma_tag(device_t dev, device_t child); struct resource_list * bus_generic_get_resource_list (device_t, device_t); +void bus_generic_new_pass(device_t dev); int bus_print_child_header(device_t dev, device_t child); int bus_print_child_footer(device_t dev, device_t child); int bus_generic_print_child(device_t dev, device_t child); @@ -433,7 +435,7 @@ /* * Access functions for devclass. */ -int devclass_add_driver(devclass_t dc, kobj_class_t driver); +int devclass_add_driver(devclass_t dc, kobj_class_t driver, int pass); int devclass_delete_driver(devclass_t dc, kobj_class_t driver); devclass_t devclass_create(const char *classname); devclass_t devclass_find(const char *classname); @@ -512,6 +514,28 @@ #define BUS_PROBE_NOWILDCARD (-2000000000) /* No wildcard device matches */ /** + * During boot, the device tree is scanned multiple times. Each scan, + * or pass, drivers may be attached to devices. Each driver + * attachment is assigned a pass number. Drivers may only probe and + * attach to devices if their pass number is less than or equal to the + * current system-wide pass number. The default pass is the last pass + * and is used by most drivers. Drivers needed by the scheduler are + * probed in earlier passes. + */ +#define BUS_PASS_ROOT 0 /* Used to attach root0. */ +#define BUS_PASS_BUS 10 /* Busses and bridges. */ +#define BUS_PASS_CPU 20 /* CPU devices. */ +#define BUS_PASS_RESOURCE 30 /* Resource discovery. */ +#define BUS_PASS_INTERRUPT 40 /* Interrupt controllers. */ +#define BUS_PASS_TIMER 50 /* Timers and clocks. */ +#define BUS_PASS_SCHEDULER 60 /* Start scheduler. */ +#define BUS_PASS_DEFAULT __INT_MAX /* Everything else. */ + +extern int bus_current_pass; + +void bus_set_pass(int pass); + +/** * Shorthand for constructing method tables. */ #define DEVMETHOD KOBJMETHOD @@ -535,15 +559,17 @@ const char *dmd_busname; kobj_class_t dmd_driver; devclass_t *dmd_devclass; + int dmd_pass; }; -#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \ +#define EARLY_DRIVER_MODULE(name, busname, driver, devclass, evh, arg, pass) \ \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ (kobj_class_t) &driver, \ - &devclass \ + &devclass, \ + pass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ @@ -554,6 +580,10 @@ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) +#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \ + EARLY_DRIVER_MODULE(name, busname, driver, devclass, evh, arg, \ + BUS_PASS_DEFAULT) + /** * Generic ivar accessor generation macros for bus drivers */