diff --git a/share/man/man9/BUS_NEW_PASS.9 b/share/man/man9/BUS_NEW_PASS.9 new file mode 100644 index 00000000000..5b31c6db1c6 --- /dev/null +++ b/share/man/man9/BUS_NEW_PASS.9 @@ -0,0 +1,56 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 2009 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 8, 2009 +.Dt BUS_NEW_PASS 9 +.Os +.Sh NAME +.Nm BUS_NEW_PASS +.Nd "notify a bus that the pass level has been changed" +.Sh SYNOPSIS +.In sys/param.h +.In sys/bus.h +.Ft void +.Fn BUS_NEW_PASS "device_t dev" +.Sh DESCRIPTION +The +.Fn BUS_NEW_PASS +method is called on each bus device to rescan the device tree when the pass +level has been changed. +This method is responsible for invoking +.Xr BUS_NEW_PASS 9 +on child bus devices to propogate the rescan to child devices. +It is also responsible for reprobing any unattached child devices and +allowing drivers for the current pass to identify new children. +A default implementation is provided by +.Xr bus_generic_new_pass 9 . +.Sh SEE ALSO +.Xr bus_generic_new_pass 9 , +.Xr bus_set_pass 9 , +.Xr device 9 diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 3e5b9496efa..b1faffadc4e 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -28,12 +28,15 @@ MAN= accept_filter.9 \ bus_dma.9 \ bus_generic_attach.9 \ bus_generic_detach.9 \ + bus_generic_new_pass.9 \ bus_generic_print_child.9 \ bus_generic_read_ivar.9 \ bus_generic_shutdown.9 \ + BUS_NEW_PASS.9 \ BUS_PRINT_CHILD.9 \ BUS_READ_IVAR.9 \ bus_release_resource.9 \ + bus_set_pass.9 \ bus_set_resource.9 \ BUS_SETUP_INTR.9 \ bus_space.9 \ diff --git a/share/man/man9/bus_generic_new_pass.9 b/share/man/man9/bus_generic_new_pass.9 new file mode 100644 index 00000000000..51bb4b20757 --- /dev/null +++ b/share/man/man9/bus_generic_new_pass.9 @@ -0,0 +1,57 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 2009 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 8, 2009 +.Dt bus_generic_new_pass 9 +.Os +.Sh NAME +.Nm bus_generic_new_pass +.Nd "generic implementation of BUS_NEW_PASS for bus devices" +.Sh SYNOPSIS +.In sys/param.h +.In sys/bus.h +.Ft void +.Fn bus_generic_new_pass "device_t dev" +.Sh DESCRIPTION +This function provides an implementation of the +.Xr BUS_NEW_PASS 9 +method which can be used by bus drivers. +It first invokes the +.Xr DEVICE_IDENTIFY 9 +method for any drivers whose pass level is equal to the new pass level. +Then, for each attached child device it calls +.Xr BUS_NEW_PASS 9 +to rescan child busses, +and for each unattached child device it calls +.Xr device_probe_and_attach 9 . +.Sh SEE ALSO +.Xr BUS_NEW_PASS 9 , +.Xr bus_set_pass 9 , +.Xr device 9 , +.Xr DEVICE_IDENTIFY 9 diff --git a/share/man/man9/bus_set_pass.9 b/share/man/man9/bus_set_pass.9 new file mode 100644 index 00000000000..1c882f40a27 --- /dev/null +++ b/share/man/man9/bus_set_pass.9 @@ -0,0 +1,54 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 2009 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 8, 2009 +.Dt bus_set_pass 9 +.Os +.Sh NAME +.Nm bus_set_pass +.Nd "raise the bus pass level" +.Sh SYNOPSIS +.In sys/param.h +.In sys/bus.h +.Ft void +.Fn bus_set_pass "int pass" +.Sh DESCRIPTION +The +.Nm +function is called during boot to raise the bus pass level to +.Fa pass . +The function will rescan the device tree for each pass level between the +current pass level and the new level that has at least one associated +driver. +The device tree rescans are implemented by invoking the +.Xr BUS_NEW_PASS 9 +method on the root bus device. +.Sh SEE ALSO +.Xr BUS_NEW_PASS 9 , +.Xr device 9 diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m index f826cb12632..c1c0e348eb0 100644 --- a/sys/kern/bus_if.m +++ b/sys/kern/bus_if.m @@ -574,3 +574,11 @@ METHOD void hint_device_unit { 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; diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 39ec6d4e7bf..c8560637fb6 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -66,6 +66,8 @@ typedef struct driverlink *driverlink_t; struct driverlink { kobj_class_t driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ + int pass; + TAILQ_ENTRY(driverlink) passlink; }; /* @@ -758,13 +760,99 @@ static kobj_method_t null_methods[] = { 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 @@ devclass_driver_added(devclass_t dc, driver_t *driver) * @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 @@ devclass_add_driver(devclass_t dc, driver_t *driver) 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 @@ device_probe_child(device_t dev, device_t child) 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 @@ device_probe(device_t dev) } 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 @@ bus_generic_probe(device_t dev) 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); } @@ -3214,6 +3325,36 @@ bus_generic_driver_added(device_t dev, driver_t *driver) } } +/** + * @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(). * @@ -3912,13 +4053,11 @@ DECLARE_MODULE(rootbus, root_bus_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 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 @@ root_bus_configure(void) 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 @@ driver_module_handler(module_t mod, int what, void *arg) 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; diff --git a/sys/sys/bus.h b/sys/sys/bus.h index c11751425e8..354d659e994 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -29,6 +29,7 @@ #ifndef _SYS_BUS_H_ #define _SYS_BUS_H_ +#include #include /** @@ -299,6 +300,7 @@ bus_dma_tag_t 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 @@ void device_verbose(device_t dev); /* * 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); @@ -511,6 +513,28 @@ void bus_data_generation_update(void); #define BUS_PROBE_HOOVER (-500) /* Generic dev for all devs on bus */ #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. */ @@ -535,15 +559,17 @@ struct driver_module_data { 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 @@ static moduledata_t name##_##busname##_mod = { \ 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 */