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>
This commit is contained in:
Sujith Manoharan 2010-12-28 14:28:05 +05:30 committed by John W. Linville
parent ee832d3e9e
commit ff8f59b5bb
2 changed files with 37 additions and 4 deletions

View File

@ -153,16 +153,36 @@ static void hif_usb_tx_cb(struct urb *urb)
case -ENODEV:
case -ESHUTDOWN:
/*
* The URB has been killed, free the SKBs
* and return.
* The URB has been killed, free the SKBs.
*/
ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
return;
/*
* If the URBs are being flushed, no need to add this
* URB to the free list.
*/
spin_lock(&hif_dev->tx.tx_lock);
if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) {
spin_unlock(&hif_dev->tx.tx_lock);
return;
}
spin_unlock(&hif_dev->tx.tx_lock);
/*
* In the stop() case, this URB has to be added to
* the free list.
*/
goto add_free;
default:
break;
}
/* Check if TX has been stopped */
/*
* Check if TX has been stopped, this is needed because
* this CB could have been invoked just after the TX lock
* was released in hif_stop() and kill_urb() hasn't been
* called yet.
*/
spin_lock(&hif_dev->tx.tx_lock);
if (hif_dev->tx.flags & HIF_USB_TX_STOP) {
spin_unlock(&hif_dev->tx.tx_lock);
@ -314,6 +334,7 @@ static void hif_usb_start(void *hif_handle, u8 pipe_id)
static void hif_usb_stop(void *hif_handle, u8 pipe_id)
{
struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
unsigned long flags;
spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
@ -321,6 +342,12 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)
hif_dev->tx.tx_skb_cnt = 0;
hif_dev->tx.flags |= HIF_USB_TX_STOP;
spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
/* The pending URBs have to be canceled. */
list_for_each_entry_safe(tx_buf, tx_buf_tmp,
&hif_dev->tx.tx_pending, list) {
usb_kill_urb(tx_buf->urb);
}
}
static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb,
@ -587,6 +614,7 @@ free:
static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
{
struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
unsigned long flags;
list_for_each_entry_safe(tx_buf, tx_buf_tmp,
&hif_dev->tx.tx_buf, list) {
@ -597,6 +625,10 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
kfree(tx_buf);
}
spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
hif_dev->tx.flags |= HIF_USB_TX_FLUSH;
spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
list_for_each_entry_safe(tx_buf, tx_buf_tmp,
&hif_dev->tx.tx_pending, list) {
usb_kill_urb(tx_buf->urb);

View File

@ -64,6 +64,7 @@ struct tx_buf {
};
#define HIF_USB_TX_STOP BIT(0)
#define HIF_USB_TX_FLUSH BIT(1)
struct hif_usb_tx {
u8 flags;