diff options
author | Ming Lei <ming.lei@canonical.com> | 2012-06-22 12:01:40 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-07-17 03:04:25 +0200 |
commit | d1c6c030fcec6f860d9bb6c632a3ebe62e28440b (patch) | |
tree | 1a6bbf92f2da6f0df73efb6725de990bb63e4853 | |
parent | Merge v3.5-rc5 into driver-core-next (diff) | |
download | linux-d1c6c030fcec6f860d9bb6c632a3ebe62e28440b.tar.xz linux-d1c6c030fcec6f860d9bb6c632a3ebe62e28440b.zip |
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>
-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) while (!list_empty(&devices_kset->list)) { dev = list_entry(devices_kset->list.prev, struct device, kobj.entry); + + /* + * hold reference count of device's parent to + * prevent it from being freed because parent's + * lock is to be held + */ + get_device(dev->parent); get_device(dev); /* * Make sure the device is off the kset list, in the @@ -1820,6 +1827,11 @@ void device_shutdown(void) list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); + /* hold lock to avoid race with probe/release */ + if (dev->parent) + device_lock(dev->parent); + device_lock(dev); + /* Don't allow any more runtime suspends */ pm_runtime_get_noresume(dev); pm_runtime_barrier(dev); @@ -1831,7 +1843,13 @@ void device_shutdown(void) dev_dbg(dev, "shutdown\n"); dev->driver->shutdown(dev); } + + device_unlock(dev); + if (dev->parent) + device_unlock(dev->parent); + put_device(dev); + put_device(dev->parent); spin_lock(&devices_kset->list_lock); } |