Date: Thu, 20 Nov 2008 23:00:48 GMT From: John Baldwin <jhb@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 153280 for review Message-ID: <200811202300.mAKN0mjk090256@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=153280 Change 153280 by jhb@jhb_mutex on 2008/11/20 23:00:08 First cut at multipass support. I think this actually covers the infrastructure and that all we really have to do now is start tying early drivers to passes. - Add BUS_NEW_PASS() method to bus_if.m that gets invoked on attached devices when the pass level is raised. - Add a "pass" to each driver attachment (driverlink object and driver_module_data) as well as an EARLY_DRIVER_MODULE() macro. - Maintain a "pass" list which stores one driverlink object per pass level that serves as an enumeration of the valid "pass" levels during boot. - Add a 'bus_set_pass()' method to raise the pass level. - Ignore drivers whose pass level is higher than the current pass level. - Don't invoke BUS_PROBE_NOMATCH() for devices that fail to attach a driver until the last pass. - Add bus_generic_new_pass() as a default implementation for BUS_NEW_PASS(). - Update root_bus_configure() to probe the tree by raising the bus level to the max pass level. Affected files ... .. //depot/projects/multipass/notes#3 edit .. //depot/projects/multipass/sys/kern/bus_if.m#2 edit .. //depot/projects/multipass/sys/kern/subr_bus.c#2 edit .. //depot/projects/multipass/sys/sys/bus.h#2 edit Differences ... ==== //depot/projects/multipass/notes#3 (text+ko) ==== @@ -3,8 +3,8 @@ Pass Overview: -------------- -- BUS_PASS_DEFAULT (0) -- BUS_PASS_ROOT (1) (root0 driver) +- BUS_PASS_ROOT (0) (root0 driver) (root is special, doesn't honor + passes since it is "manually" attached) - BUS_PASS_BUSSES (10) - enumerate busses, bridges usually should be here - BUS_PASS_CPUS (20) @@ -12,19 +12,24 @@ - BUS_PASS_TIMERS (40) - will want to start up SMP and schedulers after this point before continuing further, can clear cold as well then +- BUS_PASS_DEFAULT (INT_MAX) Milestones / Todo: ------------------ -- Add pass number to 'driver_module_data' and an EARLY_DRIVER_MODULE() that - takes an explicit pass number. DRIVER_MODULE() uses the default pass (0). -- May have to store pass number in driver link structure, so each attachment ++ Add pass number to 'driver_module_data' and an EARLY_DRIVER_MODULE() that + takes an explicit pass number. DRIVER_MODULE() uses the default pass. ++ May have to store pass number in driver link structure, so each attachment has its own pass number. -- Will need to change device_probe() to ignore drivers with a pass of 0 or - > current pass number until pass number becomes 0. -- Will need a 'device_set_pass()' to raise the pass number for use with - sysinit. The last step is to set it to 0. Each time it will raise the - pass number for each valid pass until it gets to the new setting. -- Will need a list of drivers sorted on pass number (just 1 per pass number ++ Will need to change device_probe() to ignore drivers with a pass > current + pass number. ++ Will need a 'bus_set_pass()' to raise the pass number for use with + sysinit. The last step is to set it to the default pass. Each time it + will raise the pass number for each valid pass until it gets to the new + setting. ++ Will need a list of drivers sorted on pass number (just 1 per pass number is fine) to have an enumeration of pass numbers. -- Need a new 'bus_new_pass' called when the pass number is changed. Will - need a 'bus_generic_pass' which just does 'bus_generic_attach()'. ++ Need a new 'bus_new_pass' called when the pass number is changed. Will + need a 'bus_generic_pass'. ++ Change root_bus_configure() to raise the pass level to kick off a scan. ++ Don't invoke BUS_PROBE_NOMATCH() until a device fails to match during + the last pass. ==== //depot/projects/multipass/sys/kern/bus_if.m#2 (text+ko) ==== @@ -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/projects/multipass/sys/kern/subr_bus.c#2 (text+ko) ==== @@ -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; }; /* @@ -704,7 +706,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. @@ -751,12 +753,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 @@ -858,13 +946,17 @@ * @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; int i; 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); @@ -885,6 +977,8 @@ dl->driver = driver; TAILQ_INSERT_TAIL(&dc->drivers, dl, link); driver->refs++; /* XXX: kobj_mtx */ + dl->pass = pass; + driver_register_pass(dl); /* * Call BUS_DRIVER_ADDED for any existing busses in this class. @@ -1754,6 +1848,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) @@ -2385,8 +2484,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; @@ -3158,6 +3258,27 @@ } /** + * @brief Helper function for implementing BUS_NEW_PASS(). + * + * This implementing of BUS_NEW_PASS() 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) +{ + device_t child; + + 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 @@ -3855,13 +3976,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); } /** @@ -3875,10 +3994,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); @@ -3889,10 +4008,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/projects/multipass/sys/sys/bus.h#2 (text+ko) ==== @@ -29,6 +29,7 @@ #ifndef _SYS_BUS_H_ #define _SYS_BUS_H_ +#include <machine/_limits.h> #include <sys/_bus_dma.h> /** @@ -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,26 @@ #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_INTERRUPT 30 /* Interrupt controllers. */ +#define BUS_PASS_TIMER 40 /* Timers and clocks. */ +#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 +557,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 +578,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 */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200811202300.mAKN0mjk090256>