diff options
author | imp <imp@FreeBSD.org> | 2004-12-31 20:47:51 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2004-12-31 20:47:51 +0000 |
commit | 58871563b42047c363403fc016399a46b3244742 (patch) | |
tree | d70aa4cbded2ddd880f385cb4a7a42e0e1433402 /sys/kern | |
parent | 0b07314057605333318596908beea6b5c4dbf7e0 (diff) | |
download | FreeBSD-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.m | 31 | ||||
-rw-r--r-- | sys/kern/subr_bus.c | 101 |
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; |