diff options
author | Ming Lei <ming.lei@canonical.com> | 2012-06-22 06:01:40 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-07-16 21:04:25 -0400 |
commit | d1c6c030fcec6f860d9bb6c632a3ebe62e28440b (patch) | |
tree | 1a6bbf92f2da6f0df73efb6725de990bb63e4853 /drivers/base | |
parent | 6fbfd0592ef88ba29cdce84ef92757351f1fa9c9 (diff) |
driver core: fix shutdown races with probe/remove(v3)
Firstly, .shutdown callback may touch a uninitialized hardware
if dev->driver is set and .probe is not completed.
Secondly, device_shutdown() may dereference a null pointer to cause
oops when dev->driver is cleared after it has been checked in
device_shutdown().
So just hold device lock and its parent lock(if it has) to
fix the races.
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/core.c | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 846d12b5d579..65849a9aeece 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -1812,6 +1812,13 @@ void device_shutdown(void) | |||
1812 | while (!list_empty(&devices_kset->list)) { | 1812 | while (!list_empty(&devices_kset->list)) { |
1813 | dev = list_entry(devices_kset->list.prev, struct device, | 1813 | dev = list_entry(devices_kset->list.prev, struct device, |
1814 | kobj.entry); | 1814 | kobj.entry); |
1815 | |||
1816 | /* | ||
1817 | * hold reference count of device's parent to | ||
1818 | * prevent it from being freed because parent's | ||
1819 | * lock is to be held | ||
1820 | */ | ||
1821 | get_device(dev->parent); | ||
1815 | get_device(dev); | 1822 | get_device(dev); |
1816 | /* | 1823 | /* |
1817 | * Make sure the device is off the kset list, in the | 1824 | * Make sure the device is off the kset list, in the |
@@ -1820,6 +1827,11 @@ void device_shutdown(void) | |||
1820 | list_del_init(&dev->kobj.entry); | 1827 | list_del_init(&dev->kobj.entry); |
1821 | spin_unlock(&devices_kset->list_lock); | 1828 | spin_unlock(&devices_kset->list_lock); |
1822 | 1829 | ||
1830 | /* hold lock to avoid race with probe/release */ | ||
1831 | if (dev->parent) | ||
1832 | device_lock(dev->parent); | ||
1833 | device_lock(dev); | ||
1834 | |||
1823 | /* Don't allow any more runtime suspends */ | 1835 | /* Don't allow any more runtime suspends */ |
1824 | pm_runtime_get_noresume(dev); | 1836 | pm_runtime_get_noresume(dev); |
1825 | pm_runtime_barrier(dev); | 1837 | pm_runtime_barrier(dev); |
@@ -1831,7 +1843,13 @@ void device_shutdown(void) | |||
1831 | dev_dbg(dev, "shutdown\n"); | 1843 | dev_dbg(dev, "shutdown\n"); |
1832 | dev->driver->shutdown(dev); | 1844 | dev->driver->shutdown(dev); |
1833 | } | 1845 | } |
1846 | |||
1847 | device_unlock(dev); | ||
1848 | if (dev->parent) | ||
1849 | device_unlock(dev->parent); | ||
1850 | |||
1834 | put_device(dev); | 1851 | put_device(dev); |
1852 | put_device(dev->parent); | ||
1835 | 1853 | ||
1836 | spin_lock(&devices_kset->list_lock); | 1854 | spin_lock(&devices_kset->list_lock); |
1837 | } | 1855 | } |