mirror of https://gitee.com/openkylin/linux.git
wl1251: fix queue stopping/waking for TX path
The queue stopping/waking functionality was broken in a way that could
cause the TX to stall if the right circumstances are met.
The problem was caused by tx_work, which is scheduled on each TX operation.
If the firmware buffer is full, tx_work does nothing. In combinition with
stopped queues or non-continues transfers, tx_work is never scheduled again.
Moreover the low watermark introduced by
9df86e2e70
never takes effect because of some
old code.
Solve this by scheduling tx_work every time tx_queue is non-empty and
firmware buffer is freed on tx_complete.
This also solves a possible but unlikely case: If less frames than the high
watermark are queued, but more than firmware buffer can hold. This results
in queues staying awake but the only scheduled tx_work doesn't transfer all
frames, so the remaining frames are stuck in the queue until more frames
get queued and tx_work is scheduled again.
Signed-off-by: David Gnedt <david.gnedt@davizone.at>
Acked-by: Kalle Valo <kvalo@adurom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
172710bf83
commit
e7332a4144
|
@ -368,7 +368,7 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl,
|
|||
{
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb;
|
||||
int hdrlen, ret;
|
||||
int hdrlen;
|
||||
u8 *frame;
|
||||
|
||||
skb = wl->tx_frames[result->id];
|
||||
|
@ -407,40 +407,12 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl,
|
|||
ieee80211_tx_status(wl->hw, skb);
|
||||
|
||||
wl->tx_frames[result->id] = NULL;
|
||||
|
||||
if (wl->tx_queue_stopped) {
|
||||
wl1251_debug(DEBUG_TX, "cb: queue was stopped");
|
||||
|
||||
skb = skb_dequeue(&wl->tx_queue);
|
||||
|
||||
/* The skb can be NULL because tx_work might have been
|
||||
scheduled before the queue was stopped making the
|
||||
queue empty */
|
||||
|
||||
if (skb) {
|
||||
ret = wl1251_tx_frame(wl, skb);
|
||||
if (ret == -EBUSY) {
|
||||
/* firmware buffer is still full */
|
||||
wl1251_debug(DEBUG_TX, "cb: fw buffer "
|
||||
"still full");
|
||||
skb_queue_head(&wl->tx_queue, skb);
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wl1251_debug(DEBUG_TX, "cb: waking queues");
|
||||
ieee80211_wake_queues(wl->hw);
|
||||
wl->tx_queue_stopped = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called upon reception of a TX complete interrupt */
|
||||
void wl1251_tx_complete(struct wl1251 *wl)
|
||||
{
|
||||
int i, result_index, num_complete = 0;
|
||||
int i, result_index, num_complete = 0, queue_len;
|
||||
struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -471,18 +443,22 @@ void wl1251_tx_complete(struct wl1251 *wl)
|
|||
}
|
||||
}
|
||||
|
||||
if (wl->tx_queue_stopped
|
||||
&&
|
||||
skb_queue_len(&wl->tx_queue) <= WL1251_TX_QUEUE_LOW_WATERMARK){
|
||||
queue_len = skb_queue_len(&wl->tx_queue);
|
||||
|
||||
/* firmware buffer has space, restart queues */
|
||||
if ((num_complete > 0) && (queue_len > 0)) {
|
||||
/* firmware buffer has space, reschedule tx_work */
|
||||
wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work");
|
||||
ieee80211_queue_work(wl->hw, &wl->tx_work);
|
||||
}
|
||||
|
||||
if (wl->tx_queue_stopped &&
|
||||
queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) {
|
||||
/* tx_queue has space, restart queues */
|
||||
wl1251_debug(DEBUG_TX, "tx_complete: waking queues");
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
ieee80211_wake_queues(wl->hw);
|
||||
wl->tx_queue_stopped = false;
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
ieee80211_queue_work(wl->hw, &wl->tx_work);
|
||||
|
||||
}
|
||||
|
||||
/* Every completed frame needs to be acknowledged */
|
||||
|
|
Loading…
Reference in New Issue