diff options
author | Sujith Manoharan <Sujith.Manoharan@atheros.com> | 2010-12-28 03:58:05 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-04 14:42:59 -0500 |
commit | ff8f59b5bbdf1527235b8c88d859c7d23691324f (patch) | |
tree | 7431f65095cef7dac5815816b0fb4ef8719feac4 /drivers/net/wireless | |
parent | ee832d3e9e72abf83931205a2f5379944be501c2 (diff) |
ath9k_htc: Handle pending URBs properly
When doing a channel change, the pending URBs have to be killed
properly on calling htc_stop().
This fixes the probe response timeout seen when sending UDP traffic at
a high rate and running background scan at the same time.
Cc: stable <stable@kernel.org>
Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.c | 40 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.h | 1 |
2 files changed, 37 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index c20c8c8201a2..5ab3084eb9cb 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c | |||
@@ -153,16 +153,36 @@ static void hif_usb_tx_cb(struct urb *urb) | |||
153 | case -ENODEV: | 153 | case -ENODEV: |
154 | case -ESHUTDOWN: | 154 | case -ESHUTDOWN: |
155 | /* | 155 | /* |
156 | * The URB has been killed, free the SKBs | 156 | * The URB has been killed, free the SKBs. |
157 | * and return. | ||
158 | */ | 157 | */ |
159 | ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); | 158 | ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); |
160 | return; | 159 | |
160 | /* | ||
161 | * If the URBs are being flushed, no need to add this | ||
162 | * URB to the free list. | ||
163 | */ | ||
164 | spin_lock(&hif_dev->tx.tx_lock); | ||
165 | if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) { | ||
166 | spin_unlock(&hif_dev->tx.tx_lock); | ||
167 | return; | ||
168 | } | ||
169 | spin_unlock(&hif_dev->tx.tx_lock); | ||
170 | |||
171 | /* | ||
172 | * In the stop() case, this URB has to be added to | ||
173 | * the free list. | ||
174 | */ | ||
175 | goto add_free; | ||
161 | default: | 176 | default: |
162 | break; | 177 | break; |
163 | } | 178 | } |
164 | 179 | ||
165 | /* Check if TX has been stopped */ | 180 | /* |
181 | * Check if TX has been stopped, this is needed because | ||
182 | * this CB could have been invoked just after the TX lock | ||
183 | * was released in hif_stop() and kill_urb() hasn't been | ||
184 | * called yet. | ||
185 | */ | ||
166 | spin_lock(&hif_dev->tx.tx_lock); | 186 | spin_lock(&hif_dev->tx.tx_lock); |
167 | if (hif_dev->tx.flags & HIF_USB_TX_STOP) { | 187 | if (hif_dev->tx.flags & HIF_USB_TX_STOP) { |
168 | spin_unlock(&hif_dev->tx.tx_lock); | 188 | spin_unlock(&hif_dev->tx.tx_lock); |
@@ -314,6 +334,7 @@ static void hif_usb_start(void *hif_handle, u8 pipe_id) | |||
314 | static void hif_usb_stop(void *hif_handle, u8 pipe_id) | 334 | static void hif_usb_stop(void *hif_handle, u8 pipe_id) |
315 | { | 335 | { |
316 | struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; | 336 | struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; |
337 | struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; | ||
317 | unsigned long flags; | 338 | unsigned long flags; |
318 | 339 | ||
319 | spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); | 340 | spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); |
@@ -321,6 +342,12 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id) | |||
321 | hif_dev->tx.tx_skb_cnt = 0; | 342 | hif_dev->tx.tx_skb_cnt = 0; |
322 | hif_dev->tx.flags |= HIF_USB_TX_STOP; | 343 | hif_dev->tx.flags |= HIF_USB_TX_STOP; |
323 | spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); | 344 | spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); |
345 | |||
346 | /* The pending URBs have to be canceled. */ | ||
347 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, | ||
348 | &hif_dev->tx.tx_pending, list) { | ||
349 | usb_kill_urb(tx_buf->urb); | ||
350 | } | ||
324 | } | 351 | } |
325 | 352 | ||
326 | static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, | 353 | static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, |
@@ -587,6 +614,7 @@ free: | |||
587 | static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) | 614 | static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) |
588 | { | 615 | { |
589 | struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; | 616 | struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; |
617 | unsigned long flags; | ||
590 | 618 | ||
591 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, | 619 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, |
592 | &hif_dev->tx.tx_buf, list) { | 620 | &hif_dev->tx.tx_buf, list) { |
@@ -597,6 +625,10 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) | |||
597 | kfree(tx_buf); | 625 | kfree(tx_buf); |
598 | } | 626 | } |
599 | 627 | ||
628 | spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); | ||
629 | hif_dev->tx.flags |= HIF_USB_TX_FLUSH; | ||
630 | spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); | ||
631 | |||
600 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, | 632 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, |
601 | &hif_dev->tx.tx_pending, list) { | 633 | &hif_dev->tx.tx_pending, list) { |
602 | usb_kill_urb(tx_buf->urb); | 634 | usb_kill_urb(tx_buf->urb); |
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index e4a5e2e79541..7b9d863d4035 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h | |||
@@ -64,6 +64,7 @@ struct tx_buf { | |||
64 | }; | 64 | }; |
65 | 65 | ||
66 | #define HIF_USB_TX_STOP BIT(0) | 66 | #define HIF_USB_TX_STOP BIT(0) |
67 | #define HIF_USB_TX_FLUSH BIT(1) | ||
67 | 68 | ||
68 | struct hif_usb_tx { | 69 | struct hif_usb_tx { |
69 | u8 flags; | 70 | u8 flags; |