Provide newbus infrastructure for initiating device reset.

The methods BUS_RESET_PREPARE(), BUS_RESET(), and BUS_RESET_POST()
should be implemented by bus which can provide reset to a device.  The
methods are described in inline doxygen comments.

Code only provides BUS_RESET_PREPARE() and BUS_RESET_POST() helpers
instead of default implementation, because actual bus needs to handle
device state around reset, while helpers provide the other half of
typical prepare/post code.

Reviewed by:	imp (previous version), jhb (previous version)
Sponsored by:	Mellanox Technologies
MFC after:	2 weeks
Differential revision:	https://reviews.freebsd.org/D19646
This commit is contained in:
Konstantin Belousov 2019-04-05 18:09:22 +00:00
parent 9cdd5c07ad
commit c53df6da4e
3 changed files with 151 additions and 0 deletions

View File

@ -66,6 +66,16 @@ CODE {
panic("bus_add_child is not implemented");
}
static int null_reset_post(device_t bus, device_t dev)
{
return (0);
}
static int null_reset_prepare(device_t bus, device_t dev)
{
return (0);
}
};
/**
@ -848,3 +858,48 @@ METHOD int get_cpus {
size_t _setsize;
cpuset_t *_cpuset;
} DEFAULT bus_generic_get_cpus;
/**
* @brief Prepares the given child of the bus for reset
*
* Typically bus detaches or suspends children' drivers, and then
* calls this method to save bus-specific information, for instance,
* PCI config space, which is damaged by reset.
*
* The bus_helper_reset_prepare() helper is provided to ease
* implementing bus reset methods.
*
* @param _dev the bus device
* @param _child the child device
*/
METHOD int reset_prepare {
device_t _dev;
device_t _child;
} DEFAULT null_reset_prepare;
/**
* @brief Restores the child operations after the reset
*
* The bus_helper_reset_post() helper is provided to ease
* implementing bus reset methods.
*
* @param _dev the bus device
* @param _child the child device
*/
METHOD int reset_post {
device_t _dev;
device_t _child;
} DEFAULT null_reset_post;
/**
* @brief Performs reset of the child
*
* @param _dev the bus device
* @param _child the child device
* @param _flags DEVF_RESET_ flags
*/
METHOD int reset_child {
device_t _dev;
device_t _child;
int _flags;
};

View File

@ -3864,6 +3864,96 @@ bus_generic_resume(device_t dev)
return (0);
}
/**
* @brief Helper function for implementing BUS_RESET_POST
*
* Bus can use this function to implement common operations of
* re-attaching or resuming the children after the bus itself was
* reset, and after restoring bus-unique state of children.
*
* @param dev The bus
* #param flags DEVF_RESET_*
*/
int
bus_helper_reset_post(device_t dev, int flags)
{
device_t child;
int error, error1;
error = 0;
TAILQ_FOREACH(child, &dev->children,link) {
BUS_RESET_POST(dev, child);
error1 = (flags & DEVF_RESET_DETACH) != 0 ?
device_probe_and_attach(child) :
BUS_RESUME_CHILD(dev, child);
if (error == 0 && error1 != 0)
error = error1;
}
return (error);
}
static void
bus_helper_reset_prepare_rollback(device_t dev, device_t child, int flags)
{
child = TAILQ_NEXT(child, link);
if (child == NULL)
return;
TAILQ_FOREACH_FROM(child, &dev->children,link) {
BUS_RESET_POST(dev, child);
if ((flags & DEVF_RESET_DETACH) != 0)
device_probe_and_attach(child);
else
BUS_RESUME_CHILD(dev, child);
}
}
/**
* @brief Helper function for implementing BUS_RESET_PREPARE
*
* Bus can use this function to implement common operations of
* detaching or suspending the children before the bus itself is
* reset, and then save bus-unique state of children that must
* persists around reset.
*
* @param dev The bus
* #param flags DEVF_RESET_*
*/
int
bus_helper_reset_prepare(device_t dev, int flags)
{
device_t child;
int error;
if (dev->state != DS_ATTACHED)
return (EBUSY);
TAILQ_FOREACH_REVERSE(child, &dev->children, device_list, link) {
if ((flags & DEVF_RESET_DETACH) != 0) {
error = device_get_state(child) == DS_ATTACHED ?
device_detach(child) : 0;
} else {
error = BUS_SUSPEND_CHILD(dev, child);
}
if (error == 0) {
error = BUS_RESET_PREPARE(dev, child);
if (error != 0) {
if ((flags & DEVF_RESET_DETACH) != 0)
device_probe_and_attach(child);
else
BUS_RESUME_CHILD(dev, child);
}
}
if (error != 0) {
bus_helper_reset_prepare_rollback(dev, child, flags);
return (error);
}
}
return (0);
}
/**
* @brief Helper function for implementing BUS_PRINT_CHILD().
*

View File

@ -143,6 +143,10 @@ struct devreq {
/* Flags for DEV_DELETE. */
#define DEVF_FORCE_DELETE 0x0000001
/* Flags for DEV_RESET */
#define DEVF_RESET_DETACH 0x0000001 /* Detach drivers vs suspend
device */
#ifdef _KERNEL
#include <sys/eventhandler.h>
@ -494,6 +498,8 @@ int bus_generic_unmap_resource(device_t dev, device_t child, int type,
struct resource_map *map);
int bus_generic_write_ivar(device_t dev, device_t child, int which,
uintptr_t value);
int bus_helper_reset_post(device_t dev, int flags);
int bus_helper_reset_prepare(device_t dev, int flags);
int bus_null_rescan(device_t dev);
/*