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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6fbfd0592e
commit
d1c6c030fc
@@ -1812,6 +1812,13 @@ void device_shutdown(void)
|
|||||||
while (!list_empty(&devices_kset->list)) {
|
while (!list_empty(&devices_kset->list)) {
|
||||||
dev = list_entry(devices_kset->list.prev, struct device,
|
dev = list_entry(devices_kset->list.prev, struct device,
|
||||||
kobj.entry);
|
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);
|
get_device(dev);
|
||||||
/*
|
/*
|
||||||
* Make sure the device is off the kset list, in the
|
* 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);
|
list_del_init(&dev->kobj.entry);
|
||||||
spin_unlock(&devices_kset->list_lock);
|
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 */
|
/* Don't allow any more runtime suspends */
|
||||||
pm_runtime_get_noresume(dev);
|
pm_runtime_get_noresume(dev);
|
||||||
pm_runtime_barrier(dev);
|
pm_runtime_barrier(dev);
|
||||||
@@ -1831,7 +1843,13 @@ void device_shutdown(void)
|
|||||||
dev_dbg(dev, "shutdown\n");
|
dev_dbg(dev, "shutdown\n");
|
||||||
dev->driver->shutdown(dev);
|
dev->driver->shutdown(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device_unlock(dev);
|
||||||
|
if (dev->parent)
|
||||||
|
device_unlock(dev->parent);
|
||||||
|
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
|
put_device(dev->parent);
|
||||||
|
|
||||||
spin_lock(&devices_kset->list_lock);
|
spin_lock(&devices_kset->list_lock);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user