aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2019-04-05 18:09:22 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2019-04-05 18:09:22 +0000
commitc53df6da4e396b3bacdc7870a2815e69497b739e (patch)
tree3ee1e5be52933656427373ab7f00ae944998a4ea
parent9cdd5c07ad924249d620437d00458c3b6434dbe5 (diff)
downloadsrc-c53df6da4e396b3bacdc7870a2815e69497b739e.tar.gz
src-c53df6da4e396b3bacdc7870a2815e69497b739e.zip
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
Notes
Notes: svn path=/head/; revision=345960
-rw-r--r--sys/kern/bus_if.m55
-rw-r--r--sys/kern/subr_bus.c90
-rw-r--r--sys/sys/bus.h6
3 files changed, 151 insertions, 0 deletions
diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m
index 501e8f827915..ef47f0fb59b4 100644
--- a/sys/kern/bus_if.m
+++ b/sys/kern/bus_if.m
@@ -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;
+};
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index ba177f7895b3..6c82f6de0644 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -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().
*
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index 4ad4231a9512..a9fc2387eccf 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -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);
/*