Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Tested-by: Keith Busch <kbusch@kernel.org>
Link: https://lore.kernel.org/r/20240822202805.6379-4-stuart.w.hayes@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Stuart Hayes and committed by
Greg Kroah-Hartman
8064952c 95dc7565

+59 -1
+4
drivers/base/base.h
··· 10 10 * shared outside of the drivers/base/ directory. 11 11 * 12 12 */ 13 + #include <linux/async.h> 13 14 #include <linux/notifier.h> 14 15 15 16 /** ··· 98 97 * the device; typically because it depends on another driver getting 99 98 * probed first. 100 99 * @async_driver - pointer to device driver awaiting probe via async_probe 100 + * @shutdown_after - used during device shutdown to ensure correct shutdown 101 + * ordering. 101 102 * @device - pointer back to the struct device that this structure is 102 103 * associated with. 103 104 * @dead - This device is currently either in the process of or has been ··· 117 114 struct list_head deferred_probe; 118 115 const struct device_driver *async_driver; 119 116 char *deferred_probe_reason; 117 + async_cookie_t shutdown_after; 120 118 struct device *device; 121 119 u8 dead:1; 122 120 };
+53 -1
drivers/base/core.c
··· 9 9 */ 10 10 11 11 #include <linux/acpi.h> 12 + #include <linux/async.h> 12 13 #include <linux/blkdev.h> 13 14 #include <linux/cleanup.h> 14 15 #include <linux/cpufreq.h> ··· 3525 3524 klist_init(&dev->p->klist_children, klist_children_get, 3526 3525 klist_children_put); 3527 3526 INIT_LIST_HEAD(&dev->p->deferred_probe); 3527 + dev->p->shutdown_after = 0; 3528 3528 return 0; 3529 3529 } 3530 3530 ··· 4781 4779 } 4782 4780 EXPORT_SYMBOL_GPL(device_change_owner); 4783 4781 4782 + static ASYNC_DOMAIN(sd_domain); 4783 + 4784 4784 static void shutdown_one_device(struct device *dev) 4785 4785 { 4786 4786 /* hold lock to avoid race with probe/release */ ··· 4819 4815 } 4820 4816 4821 4817 /** 4818 + * shutdown_one_device_async 4819 + * @data: the pointer to the struct device to be shutdown 4820 + * @cookie: not used 4821 + * 4822 + * Shuts down one device, after waiting for shutdown_after to complete. 4823 + * shutdown_after should be set to the cookie of the last child or consumer 4824 + * of this device to be shutdown (if any), or to the cookie of the previous 4825 + * device to be shut down for devices that don't enable asynchronous shutdown. 4826 + */ 4827 + static void shutdown_one_device_async(void *data, async_cookie_t cookie) 4828 + { 4829 + struct device *dev = data; 4830 + 4831 + async_synchronize_cookie_domain(dev->p->shutdown_after + 1, &sd_domain); 4832 + 4833 + shutdown_one_device(dev); 4834 + } 4835 + 4836 + /** 4822 4837 * device_shutdown - call ->shutdown() on each device to shutdown. 4823 4838 */ 4824 4839 void device_shutdown(void) 4825 4840 { 4826 4841 struct device *dev, *parent; 4842 + async_cookie_t cookie = 0; 4843 + struct device_link *link; 4844 + int idx; 4827 4845 4828 4846 wait_for_device_probe(); 4829 4847 device_block_probing(); ··· 4876 4850 list_del_init(&dev->kobj.entry); 4877 4851 spin_unlock(&devices_kset->list_lock); 4878 4852 4879 - shutdown_one_device(dev); 4853 + 4854 + /* 4855 + * Set cookie for devices that will be shut down synchronously 4856 + */ 4857 + if (!dev->driver || !dev->driver->async_shutdown_enable) 4858 + dev->p->shutdown_after = cookie; 4859 + 4860 + get_device(dev); 4861 + get_device(parent); 4862 + 4863 + cookie = async_schedule_domain(shutdown_one_device_async, 4864 + dev, &sd_domain); 4865 + /* 4866 + * Ensure parent & suppliers wait for this device to shut down 4867 + */ 4868 + if (parent) { 4869 + parent->p->shutdown_after = cookie; 4870 + put_device(parent); 4871 + } 4872 + 4873 + idx = device_links_read_lock(); 4874 + list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, 4875 + device_links_read_lock_held()) 4876 + link->supplier->p->shutdown_after = cookie; 4877 + device_links_read_unlock(idx); 4878 + put_device(dev); 4880 4879 4881 4880 spin_lock(&devices_kset->list_lock); 4882 4881 } 4883 4882 spin_unlock(&devices_kset->list_lock); 4883 + async_synchronize_full_domain(&sd_domain); 4884 4884 } 4885 4885 4886 4886 /*
+2
include/linux/device/driver.h
··· 56 56 * @mod_name: Used for built-in modules. 57 57 * @suppress_bind_attrs: Disables bind/unbind via sysfs. 58 58 * @probe_type: Type of the probe (synchronous or asynchronous) to use. 59 + * @async_shutdown_enable: Enables devices to be shutdown asynchronously. 59 60 * @of_match_table: The open firmware table. 60 61 * @acpi_match_table: The ACPI match table. 61 62 * @probe: Called to query the existence of a specific device, ··· 103 102 104 103 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ 105 104 enum probe_type probe_type; 105 + bool async_shutdown_enable; 106 106 107 107 const struct of_device_id *of_match_table; 108 108 const struct acpi_device_id *acpi_match_table;