From: David Jeffery <djeffery@redhat.com>
To: linux-kernel@vger.kernel.org, driver-core@lists.linux.dev,
linux-pci@vger.kernel.org, linux-scsi@vger.kernel.org,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
Danilo Krummrich <dakr@kernel.org>
Cc: "Tarun Sahu" <tarunsahu@google.com>,
"Pasha Tatashin" <tatashin@google.com>,
"Michał Cłapiński" <mclapinski@google.com>,
"Jordan Richards" <jordanrichards@google.com>,
"Ewan Milne" <emilne@redhat.com>,
"John Meneghini" <jmeneghi@redhat.com>,
"Lombardi, Maurizio" <mlombard@redhat.com>,
"David Jeffery" <djeffery@redhat.com>,
"Stuart Hayes" <stuart.w.hayes@gmail.com>,
"Laurence Oberman" <loberman@redhat.com>
Subject: [PATCH 3/5] driver core: async device shutdown infrastructure
Date: Wed, 11 Mar 2026 13:12:07 -0400 [thread overview]
Message-ID: <20260311171209.9205-3-djeffery@redhat.com> (raw)
In-Reply-To: <20260311171209.9205-1-djeffery@redhat.com>
Patterned after async suspend, allow devices to mark themselves as wanting
to perform async shutdown. Devices using async shutdown wait only for their
dependencies to shutdown before executing their shutdown routine.
Sync shutdown devices are shut down one at a time and will only wait for an
async shutdown device if the async device is a dependency.
Signed-off-by: David Jeffery <djeffery@redhat.com>
Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
drivers/base/base.h | 2 +
drivers/base/core.c | 104 ++++++++++++++++++++++++++++++++++++++++-
include/linux/device.h | 13 ++++++
3 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 79d031d2d845..ea2a039e7907 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -113,6 +113,7 @@ struct driver_type {
* @device - pointer back to the struct device that this structure is
* associated with.
* @driver_type - The type of the bound Rust driver.
+ * @complete - completion for device shutdown ordering
* @dead - This device is currently either in the process of or has been
* removed from the system. Any asynchronous events scheduled for this
* device should exit without taking any action.
@@ -132,6 +133,7 @@ struct device_private {
#ifdef CONFIG_RUST
struct driver_type driver_type;
#endif
+ struct completion complete;
u8 dead:1;
};
#define to_device_private_parent(obj) \
diff --git a/drivers/base/core.c b/drivers/base/core.c
index c2c35f95f751..07f564eb5823 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -9,6 +9,7 @@
*/
#include <linux/acpi.h>
+#include <linux/async.h>
#include <linux/blkdev.h>
#include <linux/cleanup.h>
#include <linux/cpufreq.h>
@@ -37,6 +38,10 @@
#include "physical_location.h"
#include "power/power.h"
+static bool async_shutdown = true;
+module_param(async_shutdown, bool, 0644);
+MODULE_PARM_DESC(async_shutdown, "Enable asynchronous device shutdown support");
+
/* Device links support. */
static LIST_HEAD(deferred_sync);
static unsigned int defer_sync_state_count = 1;
@@ -3538,6 +3543,7 @@ static int device_private_init(struct device *dev)
klist_init(&dev->p->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->p->deferred_probe);
+ init_completion(&dev->p->complete);
return 0;
}
@@ -4782,6 +4788,37 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
return error;
}
+static bool wants_async_shutdown(struct device *dev)
+{
+ return async_shutdown && dev->async_shutdown;
+}
+
+static int wait_for_device_shutdown(struct device *dev, void *data)
+{
+ bool async = *(bool *)data;
+
+ if (async || wants_async_shutdown(dev))
+ wait_for_completion(&dev->p->complete);
+
+ return 0;
+}
+
+static void wait_for_shutdown_dependencies(struct device *dev, bool async)
+{
+ struct device_link *link;
+ int idx;
+
+ device_for_each_child(dev, &async, wait_for_device_shutdown);
+
+ idx = device_links_read_lock();
+
+ dev_for_each_link_to_consumer(link, dev)
+ if (!device_link_flag_is_sync_state_only(link->flags))
+ wait_for_device_shutdown(link->consumer, &async);
+
+ device_links_read_unlock(idx);
+}
+
static void shutdown_one_device(struct device *dev)
{
/* hold lock to avoid race with probe/release */
@@ -4808,6 +4845,8 @@ static void shutdown_one_device(struct device *dev)
dev->driver->shutdown(dev);
}
+ complete_all(&dev->p->complete);
+
device_unlock(dev);
if (dev->parent && dev->bus && dev->bus->need_parent_lock)
device_unlock(dev->parent);
@@ -4816,6 +4855,58 @@ static void shutdown_one_device(struct device *dev)
put_device(dev);
}
+static void async_shutdown_handler(void *data, async_cookie_t cookie)
+{
+ struct device *dev = data;
+
+ wait_for_shutdown_dependencies(dev, true);
+ shutdown_one_device(dev);
+}
+
+static bool shutdown_device_async(struct device *dev)
+{
+ if (async_schedule_dev_nocall(async_shutdown_handler, dev))
+ return true;
+ return false;
+}
+
+
+static void early_async_shutdown_devices(void)
+{
+ struct device *dev, *next, *needs_put = NULL;
+
+ if (!async_shutdown)
+ return;
+
+ spin_lock(&devices_kset->list_lock);
+
+ list_for_each_entry_safe_reverse(dev, next, &devices_kset->list,
+ kobj.entry) {
+ if (wants_async_shutdown(dev)) {
+ get_device(dev->parent);
+ get_device(dev);
+
+ if (shutdown_device_async(dev)) {
+ list_del_init(&dev->kobj.entry);
+ } else {
+ /*
+ * async failed, clean up extra references
+ * and run from the standard shutdown loop
+ */
+ needs_put = dev;
+ break;
+ }
+ }
+ }
+
+ spin_unlock(&devices_kset->list_lock);
+
+ if (needs_put) {
+ put_device(needs_put->parent);
+ put_device(needs_put);
+ }
+}
+
/**
* device_shutdown - call ->shutdown() on each device to shutdown.
*/
@@ -4828,6 +4919,12 @@ void device_shutdown(void)
cpufreq_suspend();
+ /*
+ * Start async device threads where possible to maximize potential
+ * paralellism and minimize false dependency on unrelated sync devices
+ */
+ early_async_shutdown_devices();
+
spin_lock(&devices_kset->list_lock);
/*
* Walk the devices list backward, shutting down each in turn.
@@ -4852,11 +4949,16 @@ void device_shutdown(void)
list_del_init(&dev->kobj.entry);
spin_unlock(&devices_kset->list_lock);
- shutdown_one_device(dev);
+ if (!wants_async_shutdown(dev) || !shutdown_device_async(dev)) {
+ wait_for_shutdown_dependencies(dev, false);
+ shutdown_one_device(dev);
+ }
spin_lock(&devices_kset->list_lock);
}
spin_unlock(&devices_kset->list_lock);
+
+ async_synchronize_full();
}
/*
diff --git a/include/linux/device.h b/include/linux/device.h
index 0be95294b6e6..da1db7d235c9 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -551,6 +551,8 @@ struct device_physical_location {
* @dma_skip_sync: DMA sync operations can be skipped for coherent buffers.
* @dma_iommu: Device is using default IOMMU implementation for DMA and
* doesn't rely on dma_ops structure.
+ * @async_shutdown: Device shutdown may be run asynchronously and in parallel
+ * to the shutdown of unrelated devices
*
* At the lowest level, every device in a Linux system is represented by an
* instance of struct device. The device structure contains the information
@@ -669,6 +671,7 @@ struct device {
#ifdef CONFIG_IOMMU_DMA
bool dma_iommu:1;
#endif
+ bool async_shutdown:1;
};
/**
@@ -824,6 +827,16 @@ static inline bool device_async_suspend_enabled(struct device *dev)
return !!dev->power.async_suspend;
}
+static inline bool device_enable_async_shutdown(struct device *dev)
+{
+ return dev->async_shutdown = true;
+}
+
+static inline bool device_async_shutdown_enabled(struct device *dev)
+{
+ return !!dev->async_shutdown;
+}
+
static inline bool device_pm_not_required(struct device *dev)
{
return dev->power.no_pm;
--
2.53.0
next prev parent reply other threads:[~2026-03-11 17:12 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-11 17:12 [PATCH 1/5] driver core: do not always lock parent in shutdown David Jeffery
2026-03-11 17:12 ` [PATCH 2/5] driver core: separate function to shutdown one device David Jeffery
2026-03-11 18:00 ` Bart Van Assche
2026-03-11 21:37 ` Bjorn Helgaas
2026-03-11 21:42 ` Bart Van Assche
2026-03-12 13:39 ` David Jeffery
2026-03-12 1:49 ` kernel test robot
2026-03-12 5:10 ` kernel test robot
2026-03-11 17:12 ` David Jeffery [this message]
2026-03-11 19:40 ` [PATCH 3/5] driver core: async device shutdown infrastructure Randy Dunlap
2026-03-11 23:05 ` Bjorn Helgaas
2026-03-12 14:01 ` David Jeffery
2026-03-11 17:12 ` [PATCH 4/5] pci: enable async shutdown support David Jeffery
2026-03-11 23:08 ` Bjorn Helgaas
2026-03-12 13:46 ` David Jeffery
2026-03-12 5:09 ` Greg Kroah-Hartman
2026-03-12 13:54 ` David Jeffery
2026-03-11 17:12 ` [PATCH 5/5] scsi: " David Jeffery
-- strict thread matches above, loose matches on Subject: below --
2026-03-19 14:11 [PATCH v12 0/5] shut down devices asynchronously David Jeffery
2026-03-19 14:11 ` [PATCH 3/5] driver core: async device shutdown infrastructure David Jeffery
2026-03-23 9:43 ` Maurizio Lombardi
2026-03-23 14:07 ` David Jeffery
2026-04-07 15:35 [PATCH v13 0/5] shut down devices asynchronously David Jeffery
2026-04-07 15:35 ` [PATCH 3/5] driver core: async device shutdown infrastructure David Jeffery
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260311171209.9205-3-djeffery@redhat.com \
--to=djeffery@redhat.com \
--cc=dakr@kernel.org \
--cc=driver-core@lists.linux.dev \
--cc=emilne@redhat.com \
--cc=gregkh@linuxfoundation.org \
--cc=jmeneghi@redhat.com \
--cc=jordanrichards@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=loberman@redhat.com \
--cc=mclapinski@google.com \
--cc=mlombard@redhat.com \
--cc=rafael@kernel.org \
--cc=stuart.w.hayes@gmail.com \
--cc=tarunsahu@google.com \
--cc=tatashin@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox