[PATCH 1/3] driver core: Support asynchronous driver shutdown
Jeremy Allison
jallison at ciq.com
Thu Dec 14 16:03:56 PST 2023
From: Tanjore Suresh <tansuresh at google.com>
This changes the bus driver interface with additional entry points
to enable devices to implement asynchronous shutdown. The existing
synchronous interface to shutdown is unmodified and retained for
backward compatibility. shutdown_pre is only called if a matching
shutdown_post function is also registered, otherwise the synchronous
synchronous interface is used.
This changes the common device shutdown code to enable devices to
participate in asynchronous shutdown implementation.
Signed-off-by: Tanjore Suresh <tansuresh at google.com>
Signed-off-by: Jeremy Allison <jallison at ciq.com>
---
drivers/base/core.c | 41 +++++++++++++++++++++++++++++++++++++-
include/linux/device/bus.h | 11 ++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 67ba592afc77..a842d402a088 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4725,6 +4725,7 @@ EXPORT_SYMBOL_GPL(device_change_owner);
void device_shutdown(void)
{
struct device *dev, *parent;
+ LIST_HEAD(async_shutdown_list);
wait_for_device_probe();
device_block_probing();
@@ -4769,7 +4770,16 @@ void device_shutdown(void)
dev_info(dev, "shutdown_pre\n");
dev->class->shutdown_pre(dev);
}
- if (dev->bus && dev->bus->shutdown) {
+
+ /* Only call shutdown_pre if a shutdown_post is also defined. */
+ if (dev->bus && dev->bus->shutdown_pre &&
+ dev->bus->shutdown_post) {
+ if (initcall_debug)
+ dev_info(dev, "shutdown_pre\n");
+ dev->bus->shutdown_pre(dev);
+ list_add(&dev->kobj.entry,
+ &async_shutdown_list);
+ } else if (dev->bus && dev->bus->shutdown) {
if (initcall_debug)
dev_info(dev, "shutdown\n");
dev->bus->shutdown(dev);
@@ -4789,6 +4799,35 @@ void device_shutdown(void)
spin_lock(&devices_kset->list_lock);
}
spin_unlock(&devices_kset->list_lock);
+
+ /*
+ * Second pass spin for only devices, that have configured
+ * Asynchronous shutdown.
+ */
+ while (!list_empty(&async_shutdown_list)) {
+ dev = list_entry(async_shutdown_list.next, struct device,
+ kobj.entry);
+ parent = get_device(dev->parent);
+ get_device(dev);
+ /*
+ * Make sure the device is off the list
+ */
+ list_del_init(&dev->kobj.entry);
+ if (parent)
+ device_lock(parent);
+ device_lock(dev);
+ if (dev->bus && dev->bus->shutdown_post) {
+ if (initcall_debug)
+ dev_info(dev,
+ "shutdown_post called\n");
+ dev->bus->shutdown_post(dev);
+ }
+ device_unlock(dev);
+ if (parent)
+ device_unlock(parent);
+ put_device(dev);
+ put_device(parent);
+ }
}
/*
diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h
index ae10c4322754..d49dae1a280c 100644
--- a/include/linux/device/bus.h
+++ b/include/linux/device/bus.h
@@ -48,6 +48,15 @@ struct fwnode_handle;
* will never get called until they do.
* @remove: Called when a device removed from this bus.
* @shutdown: Called at shut-down time to quiesce the device.
+ * @shutdown_pre: Called at the shutdown-time to start the shutdown
+ * process on the device. This entry point will be called
+ * only when the bus driver has indicated it would like
+ * to participate in asynchronous shutdown completion
+ * and has also defined a shutdown_post function.
+ * @shutdown_post: Called at shutdown-time to complete the shutdown
+ * process of the device. This entry point will be called
+ * only when the bus drive has indicated it would like to
+ * participate in the asynchronous shutdown completion.
*
* @online: Called to put the device back online (after offlining it).
* @offline: Called to put the device offline for hot-removal. May fail.
@@ -90,6 +99,8 @@ struct bus_type {
void (*sync_state)(struct device *dev);
void (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
+ void (*shutdown_pre)(struct device *dev);
+ void (*shutdown_post)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
--
2.39.3
More information about the Linux-nvme
mailing list