|
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>
|
@@ -3524,6 +3525,7 @@ static int device_private_init(struct device *dev)
|
3524 | 3525 | klist_init(&dev->p->klist_children, klist_children_get,
|
3525 | 3526 | klist_children_put);
|
3526 | 3527 | INIT_LIST_HEAD(&dev->p->deferred_probe);
|
| 3528 | + dev->p->shutdown_after = 0; |
3527 | 3529 | return 0;
|
3528 | 3530 | }
|
3529 | 3531 |
|
@@ -4779,6 +4781,8 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
|
4779 | 4781 | }
|
4780 | 4782 | EXPORT_SYMBOL_GPL(device_change_owner);
|
4781 | 4783 |
|
| 4784 | +static ASYNC_DOMAIN(sd_domain); |
| 4785 | + |
4782 | 4786 | static void shutdown_one_device(struct device *dev)
|
4783 | 4787 | {
|
4784 | 4788 | /* hold lock to avoid race with probe/release */
|
@@ -4814,12 +4818,34 @@ static void shutdown_one_device(struct device *dev)
|
4814 | 4818 | put_device(dev->parent);
|
4815 | 4819 | }
|
4816 | 4820 |
|
| 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 | + |
4817 | 4840 | /**
|
4818 | 4841 | * device_shutdown - call ->shutdown() on each device to shutdown.
|
4819 | 4842 | */
|
4820 | 4843 | void device_shutdown(void)
|
4821 | 4844 | {
|
4822 | 4845 | struct device *dev, *parent;
|
| 4846 | + async_cookie_t cookie = 0; |
| 4847 | + struct device_link *link; |
| 4848 | + int idx; |
4823 | 4849 |
|
4824 | 4850 | wait_for_device_probe();
|
4825 | 4851 | device_block_probing();
|
@@ -4850,11 +4876,37 @@ void device_shutdown(void)
|
4850 | 4876 | list_del_init(&dev->kobj.entry);
|
4851 | 4877 | spin_unlock(&devices_kset->list_lock);
|
4852 | 4878 |
|
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); |
4854 | 4905 |
|
4855 | 4906 | spin_lock(&devices_kset->list_lock);
|
4856 | 4907 | }
|
4857 | 4908 | spin_unlock(&devices_kset->list_lock);
|
| 4909 | + async_synchronize_full_domain(&sd_domain); |
4858 | 4910 | }
|
4859 | 4911 |
|
4860 | 4912 | /*
|
|
0 commit comments