Add support for multiple passes of the device tree during the boot-time

probe.  The current device order is unchanged.  This commit just adds the
infrastructure and ABI changes so that it is easier to merge later changes
into 8.x.
- Driver attachments now have an associated pass level.  Attachments are
  not allowed to probe or attach to drivers until the system-wide pass level
  is >= the attachment's pass level.  By default driver attachments use the
  "last" pass level (BUS_PASS_DEFAULT).  Driver's that wish to probe during
  an earlier pass use EARLY_DRIVER_MODULE() instead of DRIVER_MODULE() which
  accepts the pass level as an additional parameter.
- A new method BUS_NEW_PASS has been added to the bus interface.  This
  method is invoked when the system-wide pass level is changed to kick off
  a rescan of the device tree so that drivers that have just been made
  "eligible" can probe and attach.
- The bus_generic_new_pass() function provides a default implementation of
  BUS_NEW_PASS().  It first allows drivers that were just made eligible for
  this pass to identify new child devices.  Then it propogates the rescan to
  child devices that already have an attached driver by invoking their
  BUS_NEW_PASS() method.  It also reprobes devices without a driver.
- BUS_PROBE_NOMATCH() is only invoked for devices that do not have
  an attached driver after being scanned during the final pass.
- The bus_set_pass() function is used during boot to raise the pass level.
  Currently it is only called once during root_bus_configure() to raise
  the pass level to BUS_PASS_DEFAULT.  This has the effect of probing all
  devices in a single pass identical to previous behavior.

Reviewed by:	imp
Approved by:	re (kib)
This commit is contained in:
jhb 2009-06-09 14:26:23 +00:00
parent 7e92c698d9
commit 77373ed468
7 changed files with 363 additions and 15 deletions

View File

@ -0,0 +1,56 @@
.\" -*- nroff -*-
.\"
.\" Copyright (c) 2009 Advanced Computing Technologies LLC
.\" Written by: John H. Baldwin <jhb@FreeBSD.org>
.\" 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

View File

@ -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 \

View File

@ -0,0 +1,57 @@
.\" -*- nroff -*-
.\"
.\" Copyright (c) 2009 Advanced Computing Technologies LLC
.\" Written by: John H. Baldwin <jhb@FreeBSD.org>
.\" 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

View File

@ -0,0 +1,54 @@
.\" -*- nroff -*-
.\"
.\" Copyright (c) 2009 Advanced Computing Technologies LLC
.\" Written by: John H. Baldwin <jhb@FreeBSD.org>
.\" 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

View File

@ -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;

View File

@ -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;

View File

@ -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_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
*/