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.
This commit is contained in:
Warner Losh 2004-12-31 20:47:51 +00:00
parent 7432b7cc84
commit d0d4cc63e3
3 changed files with 134 additions and 0 deletions

View File

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

View File

@ -947,6 +947,71 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver)
return (0);
}
/**
* @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
*/
@ -2313,6 +2378,32 @@ device_detach(device_t dev)
return (0);
}
/**
* @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
*
@ -3561,6 +3652,16 @@ driver_module_handler(module_t mod, int what, void *arg)
error = devclass_delete_driver(bus_devclass,
dmd->dmd_driver);
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;

View File

@ -357,6 +357,7 @@ int device_is_quiet(device_t dev);
int device_print_prettyname(device_t dev);
int device_printf(device_t dev, const char *, ...) __printflike(2, 3);
int device_probe_and_attach(device_t dev);
int device_quiesce(device_t dev);
void device_quiet(device_t dev);
void device_set_desc(device_t dev, const char* desc);
void device_set_desc_copy(device_t dev, const char* desc);
@ -388,6 +389,7 @@ void devclass_set_parent(devclass_t dc, devclass_t pdc);
devclass_t devclass_get_parent(devclass_t dc);
struct sysctl_ctx_list *devclass_get_sysctl_ctx(devclass_t dc);
struct sysctl_oid *devclass_get_sysctl_tree(devclass_t dc);
int devclass_quiesce_driver(devclass_t dc, kobj_class_t driver);
/*
* Access functions for device resources.