Skip site navigation (1)Skip section navigation (2)
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>