PERFORCE change 153280 for review
John Baldwin
jhb at FreeBSD.org
Thu Nov 20 15:00:49 PST 2008
http://perforce.freebsd.org/chv.cgi?CH=153280
Change 153280 by jhb at 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
*/
More information about the p4-projects
mailing list