Skip to content

Commit

Permalink
rt2x00: Cleanup rt2x00usb_watchdog_reset_tx
Browse files Browse the repository at this point in the history
rt2x00usb_watchdog_reset_tx performs the same task
as rt2x00usb_kill_tx_queue, with the only difference
is that it waits for all entries to be returned to
the driver and for all frames the status has been
reported to mac80211.

We can easily split this task by calling rt2x00usb_kill_tx_queue,
sleep for a short period and invoke the TX status reporting
function. By adding the sleep() to the kill_entry we make sure
that even during shutdown we guarentee the entry has been killed when
the function returns. To make this work correctly the interrupt
handlers have to be updated to prevent checking for the RADIO_ENABLED
flag too early which prevents the ownership of the entry to be reset.
Additionally a check for the DEVICE_PRESENT flag is not required but
is nice to prevent race conditions when the device was unplugged.

Additionally rather then calling rt2x00usb_work_txdone() for
status reporting we let the driver perform the TX status reporting
first. If this is not sufficient then rt2x00usb_work_txdone() will
still be used to cleanup the mess.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Ivo van Doorn authored and John W. Linville committed Aug 31, 2010
1 parent 652a9dd commit cd35a39
Showing 1 changed file with 40 additions and 26 deletions.
66 changes: 40 additions & 26 deletions drivers/net/wireless/rt2x00/rt2x00usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
struct queue_entry *entry = (struct queue_entry *)urb->context;
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;

if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
if (!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
return;

/*
Expand All @@ -227,7 +226,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
* Schedule the delayed work for reading the TX status
* from the device.
*/
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
}

static void rt2x00usb_kick_tx_entry(struct queue_entry *entry)
Expand Down Expand Up @@ -279,6 +280,14 @@ static void rt2x00usb_kill_tx_entry(struct queue_entry *entry)
if ((entry->queue->qid == QID_BEACON) &&
(test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)))
usb_kill_urb(bcn_priv->guardian_urb);

/*
* We need a short delay here to wait for
* the URB to be canceled
*/
do {
udelay(100);
} while (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags));
}

void rt2x00usb_kill_tx_queue(struct data_queue *queue)
Expand All @@ -290,8 +299,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);

static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
{
struct queue_entry *entry;
struct queue_entry_priv_usb *entry_priv;
struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
unsigned short threshold = queue->threshold;

WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
Expand All @@ -305,36 +313,41 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
* queue from being enabled during the txdone handler.
*/
queue->threshold = queue->limit;
ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
ieee80211_stop_queue(rt2x00dev->hw, queue->qid);

/*
* Reset all currently uploaded TX frames.
* Kill all entries in the queue, afterwards we need to
* wait a bit for all URBs to be cancelled.
*/
while (!rt2x00queue_empty(queue)) {
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
entry_priv = entry->priv_data;
usb_kill_urb(entry_priv->urb);
rt2x00usb_kill_tx_queue(queue);

/*
* We need a short delay here to wait for
* the URB to be canceled
*/
do {
udelay(100);
} while (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags));
/*
* In case that a driver has overriden the txdone_work
* function, we invoke the TX done through there.
*/
rt2x00dev->txdone_work.func(&rt2x00dev->txdone_work);

/*
* Invoke the TX done handler
*/
rt2x00usb_work_txdone_entry(entry);
/*
* Security measure: if the driver did override the
* txdone_work function, and the hardware did arrive
* in a state which causes it to malfunction, it is
* possible that the driver couldn't handle the txdone
* event correctly. So after giving the driver the
* chance to cleanup, we now force a cleanup of any
* leftovers.
*/
if (!rt2x00queue_empty(queue)) {
WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
" status handling failed, invoke hard reset", queue->qid);
rt2x00usb_work_txdone(&rt2x00dev->txdone_work);
}

/*
* The queue has been reset, and mac80211 is allowed to use the
* queue again.
*/
queue->threshold = threshold;
ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
ieee80211_wake_queue(rt2x00dev->hw, queue->qid);
}

static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
Expand Down Expand Up @@ -394,8 +407,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
struct queue_entry *entry = (struct queue_entry *)urb->context;
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;

if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
if (!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
return;

/*
Expand All @@ -415,7 +427,9 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
* Schedule the delayed work for reading the RX status
* from the device.
*/
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work);
if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work);
}

/*
Expand Down

0 comments on commit cd35a39

Please sign in to comment.