Skip to content

Commit 8064952

Browse files
Stuart Hayesgregkh
authored andcommitted
driver core: shut down devices asynchronously
Add code to allow asynchronous shutdown of devices, ensuring that each device is shut down before its parents & suppliers. Only devices with drivers that have async_shutdown_enable enabled will be shut down asynchronously. This can dramatically reduce system shutdown/reboot time on systems that have multiple devices that take many seconds to shut down (like certain NVMe drives). On one system tested, the shutdown time went from 11 minutes without this patch to 55 seconds with the patch. Signed-off-by: Stuart Hayes <[email protected]> Signed-off-by: David Jeffery <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Sagi Grimberg <[email protected]> Reviewed-by: Keith Busch <[email protected]> Tested-by: Keith Busch <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 95dc756 commit 8064952

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

drivers/base/base.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* shared outside of the drivers/base/ directory.
1111
*
1212
*/
13+
#include <linux/async.h>
1314
#include <linux/notifier.h>
1415

1516
/**
@@ -97,6 +98,8 @@ struct driver_private {
9798
* the device; typically because it depends on another driver getting
9899
* probed first.
99100
* @async_driver - pointer to device driver awaiting probe via async_probe
101+
* @shutdown_after - used during device shutdown to ensure correct shutdown
102+
* ordering.
100103
* @device - pointer back to the struct device that this structure is
101104
* associated with.
102105
* @dead - This device is currently either in the process of or has been
@@ -114,6 +117,7 @@ struct device_private {
114117
struct list_head deferred_probe;
115118
const struct device_driver *async_driver;
116119
char *deferred_probe_reason;
120+
async_cookie_t shutdown_after;
117121
struct device *device;
118122
u8 dead:1;
119123
};

drivers/base/core.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
#include <linux/acpi.h>
12+
#include <linux/async.h>
1213
#include <linux/blkdev.h>
1314
#include <linux/cleanup.h>
1415
#include <linux/cpufreq.h>
@@ -3524,6 +3525,7 @@ static int device_private_init(struct device *dev)
35243525
klist_init(&dev->p->klist_children, klist_children_get,
35253526
klist_children_put);
35263527
INIT_LIST_HEAD(&dev->p->deferred_probe);
3528+
dev->p->shutdown_after = 0;
35273529
return 0;
35283530
}
35293531

@@ -4779,6 +4781,8 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
47794781
}
47804782
EXPORT_SYMBOL_GPL(device_change_owner);
47814783

4784+
static ASYNC_DOMAIN(sd_domain);
4785+
47824786
static void shutdown_one_device(struct device *dev)
47834787
{
47844788
/* hold lock to avoid race with probe/release */
@@ -4814,12 +4818,34 @@ static void shutdown_one_device(struct device *dev)
48144818
put_device(dev->parent);
48154819
}
48164820

4821+
/**
4822+
* shutdown_one_device_async
4823+
* @data: the pointer to the struct device to be shutdown
4824+
* @cookie: not used
4825+
*
4826+
* Shuts down one device, after waiting for shutdown_after to complete.
4827+
* shutdown_after should be set to the cookie of the last child or consumer
4828+
* of this device to be shutdown (if any), or to the cookie of the previous
4829+
* device to be shut down for devices that don't enable asynchronous shutdown.
4830+
*/
4831+
static void shutdown_one_device_async(void *data, async_cookie_t cookie)
4832+
{
4833+
struct device *dev = data;
4834+
4835+
async_synchronize_cookie_domain(dev->p->shutdown_after + 1, &sd_domain);
4836+
4837+
shutdown_one_device(dev);
4838+
}
4839+
48174840
/**
48184841
* device_shutdown - call ->shutdown() on each device to shutdown.
48194842
*/
48204843
void device_shutdown(void)
48214844
{
48224845
struct device *dev, *parent;
4846+
async_cookie_t cookie = 0;
4847+
struct device_link *link;
4848+
int idx;
48234849

48244850
wait_for_device_probe();
48254851
device_block_probing();
@@ -4850,11 +4876,37 @@ void device_shutdown(void)
48504876
list_del_init(&dev->kobj.entry);
48514877
spin_unlock(&devices_kset->list_lock);
48524878

4853-
shutdown_one_device(dev);
4879+
4880+
/*
4881+
* Set cookie for devices that will be shut down synchronously
4882+
*/
4883+
if (!dev->driver || !dev->driver->async_shutdown_enable)
4884+
dev->p->shutdown_after = cookie;
4885+
4886+
get_device(dev);
4887+
get_device(parent);
4888+
4889+
cookie = async_schedule_domain(shutdown_one_device_async,
4890+
dev, &sd_domain);
4891+
/*
4892+
* Ensure parent & suppliers wait for this device to shut down
4893+
*/
4894+
if (parent) {
4895+
parent->p->shutdown_after = cookie;
4896+
put_device(parent);
4897+
}
4898+
4899+
idx = device_links_read_lock();
4900+
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
4901+
device_links_read_lock_held())
4902+
link->supplier->p->shutdown_after = cookie;
4903+
device_links_read_unlock(idx);
4904+
put_device(dev);
48544905

48554906
spin_lock(&devices_kset->list_lock);
48564907
}
48574908
spin_unlock(&devices_kset->list_lock);
4909+
async_synchronize_full_domain(&sd_domain);
48584910
}
48594911

48604912
/*

include/linux/device/driver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ enum probe_type {
5656
* @mod_name: Used for built-in modules.
5757
* @suppress_bind_attrs: Disables bind/unbind via sysfs.
5858
* @probe_type: Type of the probe (synchronous or asynchronous) to use.
59+
* @async_shutdown_enable: Enables devices to be shutdown asynchronously.
5960
* @of_match_table: The open firmware table.
6061
* @acpi_match_table: The ACPI match table.
6162
* @probe: Called to query the existence of a specific device,
@@ -102,6 +103,7 @@ struct device_driver {
102103

103104
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
104105
enum probe_type probe_type;
106+
bool async_shutdown_enable;
105107

106108
const struct of_device_id *of_match_table;
107109
const struct acpi_device_id *acpi_match_table;

0 commit comments

Comments
 (0)