]>
Commit | Line | Data |
---|---|---|
1143c684 SL |
1 | From 5a3f0d5b05b218ff5ce7d69105c822bcfbd0cd4a Mon Sep 17 00:00:00 2001 |
2 | From: Piotr Figiel <p.figiel@camlintechnologies.com> | |
3 | Date: Fri, 8 Mar 2019 15:25:04 +0000 | |
4 | Subject: brcmfmac: fix race during disconnect when USB completion is in | |
5 | progress | |
6 | ||
7 | [ Upstream commit db3b9e2e1d58080d0754bdf9293dabf8c6491b67 ] | |
8 | ||
9 | It was observed that rarely during USB disconnect happening shortly after | |
10 | connect (before full initialization completes) usb_hub_wq would wait | |
11 | forever for the dev_init_lock to be unlocked. dev_init_lock would remain | |
12 | locked though because of infinite wait during usb_kill_urb: | |
13 | ||
14 | [ 2730.656472] kworker/0:2 D 0 260 2 0x00000000 | |
15 | [ 2730.660700] Workqueue: events request_firmware_work_func | |
16 | [ 2730.664807] [<809dca20>] (__schedule) from [<809dd164>] (schedule+0x4c/0xac) | |
17 | [ 2730.670587] [<809dd164>] (schedule) from [<8069af44>] (usb_kill_urb+0xdc/0x114) | |
18 | [ 2730.676815] [<8069af44>] (usb_kill_urb) from [<7f258b50>] (brcmf_usb_free_q+0x34/0xa8 [brcmfmac]) | |
19 | [ 2730.684833] [<7f258b50>] (brcmf_usb_free_q [brcmfmac]) from [<7f2517d4>] (brcmf_detach+0xa0/0xb8 [brcmfmac]) | |
20 | [ 2730.693557] [<7f2517d4>] (brcmf_detach [brcmfmac]) from [<7f251a34>] (brcmf_attach+0xac/0x3d8 [brcmfmac]) | |
21 | [ 2730.702094] [<7f251a34>] (brcmf_attach [brcmfmac]) from [<7f2587ac>] (brcmf_usb_probe_phase2+0x468/0x4a0 [brcmfmac]) | |
22 | [ 2730.711601] [<7f2587ac>] (brcmf_usb_probe_phase2 [brcmfmac]) from [<7f252888>] (brcmf_fw_request_done+0x194/0x220 [brcmfmac]) | |
23 | [ 2730.721795] [<7f252888>] (brcmf_fw_request_done [brcmfmac]) from [<805748e4>] (request_firmware_work_func+0x4c/0x88) | |
24 | [ 2730.731125] [<805748e4>] (request_firmware_work_func) from [<80141474>] (process_one_work+0x228/0x808) | |
25 | [ 2730.739223] [<80141474>] (process_one_work) from [<80141a80>] (worker_thread+0x2c/0x564) | |
26 | [ 2730.746105] [<80141a80>] (worker_thread) from [<80147bcc>] (kthread+0x13c/0x16c) | |
27 | [ 2730.752227] [<80147bcc>] (kthread) from [<801010b4>] (ret_from_fork+0x14/0x20) | |
28 | ||
29 | [ 2733.099695] kworker/0:3 D 0 1065 2 0x00000000 | |
30 | [ 2733.103926] Workqueue: usb_hub_wq hub_event | |
31 | [ 2733.106914] [<809dca20>] (__schedule) from [<809dd164>] (schedule+0x4c/0xac) | |
32 | [ 2733.112693] [<809dd164>] (schedule) from [<809e2a8c>] (schedule_timeout+0x214/0x3e4) | |
33 | [ 2733.119621] [<809e2a8c>] (schedule_timeout) from [<809dde2c>] (wait_for_common+0xc4/0x1c0) | |
34 | [ 2733.126810] [<809dde2c>] (wait_for_common) from [<7f258d00>] (brcmf_usb_disconnect+0x1c/0x4c [brcmfmac]) | |
35 | [ 2733.135206] [<7f258d00>] (brcmf_usb_disconnect [brcmfmac]) from [<8069e0c8>] (usb_unbind_interface+0x5c/0x1e4) | |
36 | [ 2733.143943] [<8069e0c8>] (usb_unbind_interface) from [<8056d3e8>] (device_release_driver_internal+0x164/0x1fc) | |
37 | [ 2733.152769] [<8056d3e8>] (device_release_driver_internal) from [<8056c078>] (bus_remove_device+0xd0/0xfc) | |
38 | [ 2733.161138] [<8056c078>] (bus_remove_device) from [<8056977c>] (device_del+0x11c/0x310) | |
39 | [ 2733.167939] [<8056977c>] (device_del) from [<8069cba8>] (usb_disable_device+0xa0/0x1cc) | |
40 | [ 2733.174743] [<8069cba8>] (usb_disable_device) from [<8069507c>] (usb_disconnect+0x74/0x1dc) | |
41 | [ 2733.181823] [<8069507c>] (usb_disconnect) from [<80695e88>] (hub_event+0x478/0xf88) | |
42 | [ 2733.188278] [<80695e88>] (hub_event) from [<80141474>] (process_one_work+0x228/0x808) | |
43 | [ 2733.194905] [<80141474>] (process_one_work) from [<80141a80>] (worker_thread+0x2c/0x564) | |
44 | [ 2733.201724] [<80141a80>] (worker_thread) from [<80147bcc>] (kthread+0x13c/0x16c) | |
45 | [ 2733.207913] [<80147bcc>] (kthread) from [<801010b4>] (ret_from_fork+0x14/0x20) | |
46 | ||
47 | It was traced down to a case where usb_kill_urb would be called on an URB | |
48 | structure containing more or less random data, including large number in | |
49 | its use_count. During the debugging it appeared that in brcmf_usb_free_q() | |
50 | the traversal over URBs' lists is not synchronized with operations on those | |
51 | lists in brcmf_usb_rx_complete() leading to handling | |
52 | brcmf_usbdev_info structure (holding lists' head) as lists' element and in | |
53 | result causing above problem. | |
54 | ||
55 | Fix it by walking through all URBs during brcmf_cancel_all_urbs using the | |
56 | arrays of requests instead of linked lists. | |
57 | ||
58 | Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> | |
59 | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | |
60 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
61 | --- | |
62 | drivers/net/wireless/brcm80211/brcmfmac/usb.c | 10 ++++++++-- | |
63 | 1 file changed, 8 insertions(+), 2 deletions(-) | |
64 | ||
65 | diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c | |
66 | index 32b7b8a8f80c6..8a7da04a9ed4b 100644 | |
67 | --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c | |
68 | +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c | |
69 | @@ -669,12 +669,18 @@ static int brcmf_usb_up(struct device *dev) | |
70 | ||
71 | static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) | |
72 | { | |
73 | + int i; | |
74 | + | |
75 | if (devinfo->ctl_urb) | |
76 | usb_kill_urb(devinfo->ctl_urb); | |
77 | if (devinfo->bulk_urb) | |
78 | usb_kill_urb(devinfo->bulk_urb); | |
79 | - brcmf_usb_free_q(&devinfo->tx_postq, true); | |
80 | - brcmf_usb_free_q(&devinfo->rx_postq, true); | |
81 | + if (devinfo->tx_reqs) | |
82 | + for (i = 0; i < devinfo->bus_pub.ntxq; i++) | |
83 | + usb_kill_urb(devinfo->tx_reqs[i].urb); | |
84 | + if (devinfo->rx_reqs) | |
85 | + for (i = 0; i < devinfo->bus_pub.nrxq; i++) | |
86 | + usb_kill_urb(devinfo->rx_reqs[i].urb); | |
87 | } | |
88 | ||
89 | static void brcmf_usb_down(struct device *dev) | |
90 | -- | |
91 | 2.20.1 | |
92 |