diff options
author | David Gnedt <david.gnedt@davizone.at> | 2011-01-30 14:10:46 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-02-03 16:42:44 -0500 |
commit | e7332a41442bde1c14550998cadceca34c967dfc (patch) | |
tree | aa5902ac620f2fbab7d98bbdda079959b97f5bc5 /drivers/net/wireless/wl1251 | |
parent | 172710bf8305c1b145796e34426c865480884024 (diff) |
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
9df86e2e702c6d5547aced7f241addd2d698bb11 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>
Diffstat (limited to 'drivers/net/wireless/wl1251')
-rw-r--r-- | drivers/net/wireless/wl1251/tx.c | 48 |
1 files changed, 12 insertions, 36 deletions
diff --git a/drivers/net/wireless/wl1251/tx.c b/drivers/net/wireless/wl1251/tx.c index 554b4f9a3d3e..10112de55493 100644 --- a/drivers/net/wireless/wl1251/tx.c +++ b/drivers/net/wireless/wl1251/tx.c | |||
@@ -368,7 +368,7 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, | |||
368 | { | 368 | { |
369 | struct ieee80211_tx_info *info; | 369 | struct ieee80211_tx_info *info; |
370 | struct sk_buff *skb; | 370 | struct sk_buff *skb; |
371 | int hdrlen, ret; | 371 | int hdrlen; |
372 | u8 *frame; | 372 | u8 *frame; |
373 | 373 | ||
374 | skb = wl->tx_frames[result->id]; | 374 | skb = wl->tx_frames[result->id]; |
@@ -407,40 +407,12 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, | |||
407 | ieee80211_tx_status(wl->hw, skb); | 407 | ieee80211_tx_status(wl->hw, skb); |
408 | 408 | ||
409 | wl->tx_frames[result->id] = NULL; | 409 | wl->tx_frames[result->id] = NULL; |
410 | |||
411 | if (wl->tx_queue_stopped) { | ||
412 | wl1251_debug(DEBUG_TX, "cb: queue was stopped"); | ||
413 | |||
414 | skb = skb_dequeue(&wl->tx_queue); | ||
415 | |||
416 | /* The skb can be NULL because tx_work might have been | ||
417 | scheduled before the queue was stopped making the | ||
418 | queue empty */ | ||
419 | |||
420 | if (skb) { | ||
421 | ret = wl1251_tx_frame(wl, skb); | ||
422 | if (ret == -EBUSY) { | ||
423 | /* firmware buffer is still full */ | ||
424 | wl1251_debug(DEBUG_TX, "cb: fw buffer " | ||
425 | "still full"); | ||
426 | skb_queue_head(&wl->tx_queue, skb); | ||
427 | return; | ||
428 | } else if (ret < 0) { | ||
429 | dev_kfree_skb(skb); | ||
430 | return; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | wl1251_debug(DEBUG_TX, "cb: waking queues"); | ||
435 | ieee80211_wake_queues(wl->hw); | ||
436 | wl->tx_queue_stopped = false; | ||
437 | } | ||
438 | } | 410 | } |
439 | 411 | ||
440 | /* Called upon reception of a TX complete interrupt */ | 412 | /* Called upon reception of a TX complete interrupt */ |
441 | void wl1251_tx_complete(struct wl1251 *wl) | 413 | void wl1251_tx_complete(struct wl1251 *wl) |
442 | { | 414 | { |
443 | int i, result_index, num_complete = 0; | 415 | int i, result_index, num_complete = 0, queue_len; |
444 | struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; | 416 | struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; |
445 | unsigned long flags; | 417 | unsigned long flags; |
446 | 418 | ||
@@ -471,18 +443,22 @@ void wl1251_tx_complete(struct wl1251 *wl) | |||
471 | } | 443 | } |
472 | } | 444 | } |
473 | 445 | ||
474 | if (wl->tx_queue_stopped | 446 | queue_len = skb_queue_len(&wl->tx_queue); |
475 | && | ||
476 | skb_queue_len(&wl->tx_queue) <= WL1251_TX_QUEUE_LOW_WATERMARK){ | ||
477 | 447 | ||
478 | /* firmware buffer has space, restart queues */ | 448 | if ((num_complete > 0) && (queue_len > 0)) { |
449 | /* firmware buffer has space, reschedule tx_work */ | ||
450 | wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work"); | ||
451 | ieee80211_queue_work(wl->hw, &wl->tx_work); | ||
452 | } | ||
453 | |||
454 | if (wl->tx_queue_stopped && | ||
455 | queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) { | ||
456 | /* tx_queue has space, restart queues */ | ||
479 | wl1251_debug(DEBUG_TX, "tx_complete: waking queues"); | 457 | wl1251_debug(DEBUG_TX, "tx_complete: waking queues"); |
480 | spin_lock_irqsave(&wl->wl_lock, flags); | 458 | spin_lock_irqsave(&wl->wl_lock, flags); |
481 | ieee80211_wake_queues(wl->hw); | 459 | ieee80211_wake_queues(wl->hw); |
482 | wl->tx_queue_stopped = false; | 460 | wl->tx_queue_stopped = false; |
483 | spin_unlock_irqrestore(&wl->wl_lock, flags); | 461 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
484 | ieee80211_queue_work(wl->hw, &wl->tx_work); | ||
485 | |||
486 | } | 462 | } |
487 | 463 | ||
488 | /* Every completed frame needs to be acknowledged */ | 464 | /* Every completed frame needs to be acknowledged */ |