summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2004-12-31 20:47:51 +0000
committerimp <imp@FreeBSD.org>2004-12-31 20:47:51 +0000
commit58871563b42047c363403fc016399a46b3244742 (patch)
treed70aa4cbded2ddd880f385cb4a7a42e0e1433402 /sys/kern
parent0b07314057605333318596908beea6b5c4dbf7e0 (diff)
downloadFreeBSD-src-58871563b42047c363403fc016399a46b3244742.zip
FreeBSD-src-58871563b42047c363403fc016399a46b3244742.tar.gz
Implement device_quiesce. This method means 'you are about to be
unloaded, cleanup, or return ebusy of that's inconvenient.' The default module hanlder for newbus will now call this when we get a MOD_QUIESCE event, but in the future may call this at other times. This shouldn't change any actual behavior until drivers start to use it.
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/device_if.m31
-rw-r--r--sys/kern/subr_bus.c101
2 files changed, 132 insertions, 0 deletions
diff --git a/sys/kern/device_if.m b/sys/kern/device_if.m
index 8b97f4e..d7609b9 100644
--- a/sys/kern/device_if.m
+++ b/sys/kern/device_if.m
@@ -57,6 +57,11 @@ CODE {
{
return 0;
}
+
+ static int null_quiesce(device_t dev)
+ {
+ return EOPNOTSUPP;
+ }
};
/**
@@ -283,3 +288,29 @@ METHOD int suspend {
METHOD int resume {
device_t dev;
} DEFAULT null_resume;
+
+/**
+ * @brief This is called when the driver is asked to quiesce itself.
+ *
+ * The driver should arrange for the orderly shutdown of this device.
+ * All further access to the device should be curtailed. Soon there
+ * will be a request to detach, but there won't necessarily be one.
+ *
+ * To include this method in a device driver, use a line like this
+ * in the driver's method list:
+ *
+ * @code
+ * KOBJMETHOD(device_quiesce, foo_quiesce)
+ * @endcode
+ *
+ * @param dev the device being quiesced
+ *
+ * @retval 0 success
+ * @retval non-zero an error occurred while attempting to quiesce the
+ * device
+ *
+ * @see DEVICE_DETACH()
+ */
+METHOD int quiesce {
+ device_t dev;
+} DEFAULT null_quiesce;
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index ca9b03b..be02279 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -948,6 +948,71 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver)
}
/**
+ * @brief Quiesces a set of device drivers from a device class
+ *
+ * Quiesce a device driver from a devclass. This is normally called
+ * automatically by DRIVER_MODULE().
+ *
+ * If the driver is currently attached to any devices,
+ * devclass_quiesece_driver() will first attempt to quiesce each
+ * device.
+ *
+ * @param dc the devclass to edit
+ * @param driver the driver to unregister
+ */
+int
+devclass_quiesce_driver(devclass_t busclass, driver_t *driver)
+{
+ devclass_t dc = devclass_find(driver->name);
+ driverlink_t dl;
+ device_t dev;
+ int i;
+ int error;
+
+ PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass)));
+
+ if (!dc)
+ return (0);
+
+ /*
+ * Find the link structure in the bus' list of drivers.
+ */
+ TAILQ_FOREACH(dl, &busclass->drivers, link) {
+ if (dl->driver == driver)
+ break;
+ }
+
+ if (!dl) {
+ PDEBUG(("%s not found in %s list", driver->name,
+ busclass->name));
+ return (ENOENT);
+ }
+
+ /*
+ * Quiesce all devices. We iterate through all the devices in
+ * the devclass of the driver and quiesce any which are using
+ * the driver and which have a parent in the devclass which we
+ * are quiescing.
+ *
+ * Note that since a driver can be in multiple devclasses, we
+ * should not quiesce devices which are not children of
+ * devices in the affected devclass.
+ */
+ for (i = 0; i < dc->maxunit; i++) {
+ if (dc->devices[i]) {
+ dev = dc->devices[i];
+ if (dev->driver == driver && dev->parent &&
+ dev->parent->devclass == busclass) {
+ if ((error = device_quiesce(dev)) != 0)
+ return (error);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/**
* @internal
*/
static driverlink_t
@@ -2314,6 +2379,32 @@ device_detach(device_t dev)
}
/**
+ * @brief Tells a driver to quiesce itself.
+ *
+ * This function is a wrapper around the DEVICE_QUIESCE() driver
+ * method. If the call to DEVICE_QUIESCE() succeeds.
+ *
+ * @param dev the device to quiesce
+ *
+ * @retval 0 success
+ * @retval ENXIO no driver was found
+ * @retval ENOMEM memory allocation failure
+ * @retval non-zero some other unix error code
+ */
+int
+device_quiesce(device_t dev)
+{
+
+ PDEBUG(("%s", DEVICENAME(dev)));
+ if (dev->state == DS_BUSY)
+ return (EBUSY);
+ if (dev->state != DS_ATTACHED)
+ return (0);
+
+ return (DEVICE_QUIESCE(dev));
+}
+
+/**
* @brief Notify a device of system shutdown
*
* This function calls the DEVICE_SHUTDOWN() driver method if the
@@ -3564,6 +3655,16 @@ driver_module_handler(module_t mod, int what, void *arg)
if (!error && dmd->dmd_chainevh)
error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg);
break;
+ case MOD_QUIESCE:
+ PDEBUG(("Quiesce module: driver %s from bus %s",
+ DRIVERNAME(dmd->dmd_driver),
+ dmd->dmd_busname));
+ error = devclass_quiesce_driver(bus_devclass,
+ dmd->dmd_driver);
+
+ if (!error && dmd->dmd_chainevh)
+ error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg);
+ break;
default:
error = EOPNOTSUPP;
break;
OpenPOWER on IntegriCloud