Commit 9494849e authored by Alexey Khoroshilov's avatar Alexey Khoroshilov Committed by John W. Linville

ath9k_htc: fix data race between request_firmware_nowait() callback and suspend()

ath9k_hif_usb_probe() requests firmware asynchronically and
there is some initialization postponed till firmware is ready.
In particular, ath9k_hif_usb_firmware_cb() callback initializes
hif_dev->tx.tx_buf and hif_dev->tx.tx_pending lists.

At the same time, ath9k_hif_usb_suspend() iterates that lists through
ath9k_hif_usb_dealloc_urbs(). If suspend happens before request_firmware_nowait()
callback is called, it can lead to oops.

Similar issue could be in ath9k_hif_usb_disconnect(), but it is prevented
using hif_dev->fw_done completion and HIF_USB_READY flag. The patch extends
this approach to suspend() as well.

Found by Linux Driver Verification project (linuxtesting.org).
Signed-off-by: default avatarAlexey Khoroshilov <khoroshilov@ispras.ru>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a4943ccb
...@@ -1082,7 +1082,7 @@ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) ...@@ -1082,7 +1082,7 @@ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev)
struct device *dev = &hif_dev->udev->dev; struct device *dev = &hif_dev->udev->dev;
struct device *parent = dev->parent; struct device *parent = dev->parent;
complete(&hif_dev->fw_done); complete_all(&hif_dev->fw_done);
if (parent) if (parent)
device_lock(parent); device_lock(parent);
...@@ -1131,7 +1131,7 @@ static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) ...@@ -1131,7 +1131,7 @@ static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context)
release_firmware(fw); release_firmware(fw);
hif_dev->flags |= HIF_USB_READY; hif_dev->flags |= HIF_USB_READY;
complete(&hif_dev->fw_done); complete_all(&hif_dev->fw_done);
return; return;
...@@ -1316,6 +1316,9 @@ static int ath9k_hif_usb_suspend(struct usb_interface *interface, ...@@ -1316,6 +1316,9 @@ static int ath9k_hif_usb_suspend(struct usb_interface *interface,
if (!(hif_dev->flags & HIF_USB_START)) if (!(hif_dev->flags & HIF_USB_START))
ath9k_htc_suspend(hif_dev->htc_handle); ath9k_htc_suspend(hif_dev->htc_handle);
wait_for_completion(&hif_dev->fw_done);
if (hif_dev->flags & HIF_USB_READY)
ath9k_hif_usb_dealloc_urbs(hif_dev); ath9k_hif_usb_dealloc_urbs(hif_dev);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment