diff options
| author | Christian Lamparter <chunkeey@googlemail.com> | 2010-04-29 11:53:33 -0400 |
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2010-05-07 14:26:38 -0400 |
| commit | 160b82420ab41f1e67fbf2e56dc587837ef39ce0 (patch) | |
| tree | ac957e6495ce636c5181c792d822a6e60fb5921f | |
| parent | 96ff56419504ac6a610ff1af42330e0423242e16 (diff) | |
ar9170: wait for asynchronous firmware loading
This patch fixes a regression introduced by the following patch:
"ar9170: load firmware asynchronously"
When we kick off a firmware loading request and then unbind,
or disconnect the usb device right away, we get into trouble:
> ------------[ cut here ]------------
> WARNING: at lib/kref.c:44 kref_get+0x1c/0x20()
> Hardware name: 18666GU
> Modules linked in: ar9170usb [...]
> Pid: 6588, comm: firmware/ar9170 Not tainted 2.6.34-rc5-wl #43
> Call Trace:
> [<c102b05e>] ? warn_slowpath_common+0x6e/0xb0
> [<c117c93c>] ? kref_get+0x1c/0x20
> [<c102b0b3>] ? warn_slowpath_null+0x13/0x20
> [<c117c93c>] ? kref_get+0x1c/0x20
> [<c117bb2f>] ? kobject_get+0xf/0x20
> [<c124d630>] ? get_device+0x10/0x20
> [<c124e5a0>] ? device_add+0x60/0x530
> [<c117b8b5>] ? kobject_init+0x25/0xa0
> [<c12569f9>] ? _request_firmware+0x139/0x3e0
> [<c1256cc0>] ? request_firmware_work_func+0x20/0x70
> [<c1256ca0>] ? request_firmware_work_func+0x0/0x70
> [<c103ff24>] ? kthread+0x74/0x80
> [<c103feb0>] ? kthread+0x0/0x80
> [<c1003136>] ? kernel_thread_helper+0x6/0x10
>---[ end trace 2d50bd818f64a1b7 ]---
- followed by a random Oops -
Avoid that by waiting for the firmware loading to finish
(whether successfully or not) before the unbind in
ar9170_usb_disconnect.
Reported-by: Johannes Berg <johannes@sipsolutions.net>
Bug-fixed-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
| -rw-r--r-- | drivers/net/wireless/ath/ar9170/usb.c | 11 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/ar9170/usb.h | 1 |
2 files changed, 12 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index 6b1cb706e410..24dc555f5ff1 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c | |||
| @@ -726,12 +726,16 @@ static void ar9170_usb_firmware_failed(struct ar9170_usb *aru) | |||
| 726 | { | 726 | { |
| 727 | struct device *parent = aru->udev->dev.parent; | 727 | struct device *parent = aru->udev->dev.parent; |
| 728 | 728 | ||
| 729 | complete(&aru->firmware_loading_complete); | ||
| 730 | |||
| 729 | /* unbind anything failed */ | 731 | /* unbind anything failed */ |
| 730 | if (parent) | 732 | if (parent) |
| 731 | down(&parent->sem); | 733 | down(&parent->sem); |
| 732 | device_release_driver(&aru->udev->dev); | 734 | device_release_driver(&aru->udev->dev); |
| 733 | if (parent) | 735 | if (parent) |
| 734 | up(&parent->sem); | 736 | up(&parent->sem); |
| 737 | |||
| 738 | usb_put_dev(aru->udev); | ||
| 735 | } | 739 | } |
| 736 | 740 | ||
| 737 | static void ar9170_usb_firmware_finish(const struct firmware *fw, void *context) | 741 | static void ar9170_usb_firmware_finish(const struct firmware *fw, void *context) |
| @@ -760,6 +764,8 @@ static void ar9170_usb_firmware_finish(const struct firmware *fw, void *context) | |||
| 760 | if (err) | 764 | if (err) |
| 761 | goto err_unrx; | 765 | goto err_unrx; |
| 762 | 766 | ||
| 767 | complete(&aru->firmware_loading_complete); | ||
| 768 | usb_put_dev(aru->udev); | ||
| 763 | return; | 769 | return; |
| 764 | 770 | ||
| 765 | err_unrx: | 771 | err_unrx: |
| @@ -857,6 +863,7 @@ static int ar9170_usb_probe(struct usb_interface *intf, | |||
| 857 | init_usb_anchor(&aru->tx_pending); | 863 | init_usb_anchor(&aru->tx_pending); |
| 858 | init_usb_anchor(&aru->tx_submitted); | 864 | init_usb_anchor(&aru->tx_submitted); |
| 859 | init_completion(&aru->cmd_wait); | 865 | init_completion(&aru->cmd_wait); |
| 866 | init_completion(&aru->firmware_loading_complete); | ||
| 860 | spin_lock_init(&aru->tx_urb_lock); | 867 | spin_lock_init(&aru->tx_urb_lock); |
| 861 | 868 | ||
| 862 | aru->tx_pending_urbs = 0; | 869 | aru->tx_pending_urbs = 0; |
| @@ -876,6 +883,7 @@ static int ar9170_usb_probe(struct usb_interface *intf, | |||
| 876 | if (err) | 883 | if (err) |
| 877 | goto err_freehw; | 884 | goto err_freehw; |
| 878 | 885 | ||
| 886 | usb_get_dev(aru->udev); | ||
| 879 | return request_firmware_nowait(THIS_MODULE, 1, "ar9170.fw", | 887 | return request_firmware_nowait(THIS_MODULE, 1, "ar9170.fw", |
| 880 | &aru->udev->dev, GFP_KERNEL, aru, | 888 | &aru->udev->dev, GFP_KERNEL, aru, |
| 881 | ar9170_usb_firmware_step2); | 889 | ar9170_usb_firmware_step2); |
| @@ -895,6 +903,9 @@ static void ar9170_usb_disconnect(struct usb_interface *intf) | |||
| 895 | return; | 903 | return; |
| 896 | 904 | ||
| 897 | aru->common.state = AR9170_IDLE; | 905 | aru->common.state = AR9170_IDLE; |
| 906 | |||
| 907 | wait_for_completion(&aru->firmware_loading_complete); | ||
| 908 | |||
| 898 | ar9170_unregister(&aru->common); | 909 | ar9170_unregister(&aru->common); |
| 899 | ar9170_usb_cancel_urbs(aru); | 910 | ar9170_usb_cancel_urbs(aru); |
| 900 | 911 | ||
diff --git a/drivers/net/wireless/ath/ar9170/usb.h b/drivers/net/wireless/ath/ar9170/usb.h index a2ce3b169ceb..919b06046eb3 100644 --- a/drivers/net/wireless/ath/ar9170/usb.h +++ b/drivers/net/wireless/ath/ar9170/usb.h | |||
| @@ -71,6 +71,7 @@ struct ar9170_usb { | |||
| 71 | unsigned int tx_pending_urbs; | 71 | unsigned int tx_pending_urbs; |
| 72 | 72 | ||
| 73 | struct completion cmd_wait; | 73 | struct completion cmd_wait; |
| 74 | struct completion firmware_loading_complete; | ||
| 74 | int readlen; | 75 | int readlen; |
| 75 | u8 *readbuf; | 76 | u8 *readbuf; |
| 76 | 77 | ||
