Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 5 Apr 2019 18:09:22 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r345960 - in head/sys: kern sys
Message-ID:  <201904051809.x35I9Ml8043784@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Fri Apr  5 18:09:22 2019
New Revision: 345960
URL: https://svnweb.freebsd.org/changeset/base/345960

Log:
  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

Modified:
  head/sys/kern/bus_if.m
  head/sys/kern/subr_bus.c
  head/sys/sys/bus.h

Modified: head/sys/kern/bus_if.m
==============================================================================
--- head/sys/kern/bus_if.m	Fri Apr  5 17:54:31 2019	(r345959)
+++ head/sys/kern/bus_if.m	Fri Apr  5 18:09:22 2019	(r345960)
@@ -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;
+};

Modified: head/sys/kern/subr_bus.c
==============================================================================
--- head/sys/kern/subr_bus.c	Fri Apr  5 17:54:31 2019	(r345959)
+++ head/sys/kern/subr_bus.c	Fri Apr  5 18:09:22 2019	(r345960)
@@ -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().
  *

Modified: head/sys/sys/bus.h
==============================================================================
--- head/sys/sys/bus.h	Fri Apr  5 17:54:31 2019	(r345959)
+++ head/sys/sys/bus.h	Fri Apr  5 18:09:22 2019	(r345960)
@@ -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 
 				   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);
 
 /*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904051809.x35I9Ml8043784>