diff options
author | Denis 'GNUtoo' Carikli <GNUtoo@no-log.org> | 2010-08-27 17:48:19 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-08-31 14:20:40 -0400 |
commit | 9df86e2e702c6d5547aced7f241addd2d698bb11 (patch) | |
tree | 694c4e2a933148800639cad6818284aa439e6dbf | |
parent | 391a200a89bf85bd38f117f34898c24299e3d53d (diff) |
wl1251: Fix queue stopping/waking for TX path
This patch was adapted from 06f7bc7db79fabe6b2ec16eff0f59e4acc21eb72
(from linus's linux-2.6 tree of kernel.org)
here's the original message:
The queue stopping/waking functionality was broken in a way that could
cause huge latencies in TX transfers and even cause the TX to stall in the
right circumstances. Correct these problems.
Signed-off-by: Denis 'GNUtoo' Carikli <GNUtoo@no-log.org>
Acked-by: Kalle Valo <kvalo@adurom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1251.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_main.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_tx.c | 20 |
3 files changed, 24 insertions, 13 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 9bc4fea3315a..e113d4c1fb35 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h | |||
@@ -272,6 +272,8 @@ struct wl1251 { | |||
272 | int irq; | 272 | int irq; |
273 | bool use_eeprom; | 273 | bool use_eeprom; |
274 | 274 | ||
275 | spinlock_t wl_lock; | ||
276 | |||
275 | enum wl1251_state state; | 277 | enum wl1251_state state; |
276 | struct mutex mutex; | 278 | struct mutex mutex; |
277 | 279 | ||
@@ -399,7 +401,8 @@ void wl1251_disable_interrupts(struct wl1251 *wl); | |||
399 | 401 | ||
400 | #define WL1251_DEFAULT_POWER_LEVEL 20 | 402 | #define WL1251_DEFAULT_POWER_LEVEL 20 |
401 | 403 | ||
402 | #define WL1251_TX_QUEUE_MAX_LENGTH 20 | 404 | #define WL1251_TX_QUEUE_LOW_WATERMARK 10 |
405 | #define WL1251_TX_QUEUE_HIGH_WATERMARK 25 | ||
403 | 406 | ||
404 | #define WL1251_DEFAULT_BEACON_INT 100 | 407 | #define WL1251_DEFAULT_BEACON_INT 100 |
405 | #define WL1251_DEFAULT_DTIM_PERIOD 1 | 408 | #define WL1251_DEFAULT_DTIM_PERIOD 1 |
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index f9d9ad620cc9..faf221ca3f41 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c | |||
@@ -375,6 +375,7 @@ out: | |||
375 | static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | 375 | static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) |
376 | { | 376 | { |
377 | struct wl1251 *wl = hw->priv; | 377 | struct wl1251 *wl = hw->priv; |
378 | unsigned long flags; | ||
378 | 379 | ||
379 | skb_queue_tail(&wl->tx_queue, skb); | 380 | skb_queue_tail(&wl->tx_queue, skb); |
380 | 381 | ||
@@ -389,16 +390,13 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
389 | * The workqueue is slow to process the tx_queue and we need stop | 390 | * The workqueue is slow to process the tx_queue and we need stop |
390 | * the queue here, otherwise the queue will get too long. | 391 | * the queue here, otherwise the queue will get too long. |
391 | */ | 392 | */ |
392 | if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) { | 393 | if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_HIGH_WATERMARK) { |
393 | wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues"); | 394 | wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues"); |
394 | ieee80211_stop_queues(wl->hw); | ||
395 | 395 | ||
396 | /* | 396 | spin_lock_irqsave(&wl->wl_lock, flags); |
397 | * FIXME: this is racy, the variable is not properly | 397 | ieee80211_stop_queues(wl->hw); |
398 | * protected. Maybe fix this by removing the stupid | ||
399 | * variable altogether and checking the real queue state? | ||
400 | */ | ||
401 | wl->tx_queue_stopped = true; | 398 | wl->tx_queue_stopped = true; |
399 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
402 | } | 400 | } |
403 | 401 | ||
404 | return NETDEV_TX_OK; | 402 | return NETDEV_TX_OK; |
diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.c b/drivers/net/wireless/wl12xx/wl1251_tx.c index 6d618fb1fd95..388492a7f41f 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.c +++ b/drivers/net/wireless/wl12xx/wl1251_tx.c | |||
@@ -320,11 +320,6 @@ void wl1251_tx_work(struct work_struct *work) | |||
320 | 320 | ||
321 | ret = wl1251_tx_frame(wl, skb); | 321 | ret = wl1251_tx_frame(wl, skb); |
322 | if (ret == -EBUSY) { | 322 | if (ret == -EBUSY) { |
323 | /* firmware buffer is full, stop queues */ | ||
324 | wl1251_debug(DEBUG_TX, "tx_work: fw buffer full, " | ||
325 | "stop queues"); | ||
326 | ieee80211_stop_queues(wl->hw); | ||
327 | wl->tx_queue_stopped = true; | ||
328 | skb_queue_head(&wl->tx_queue, skb); | 323 | skb_queue_head(&wl->tx_queue, skb); |
329 | goto out; | 324 | goto out; |
330 | } else if (ret < 0) { | 325 | } else if (ret < 0) { |
@@ -447,6 +442,7 @@ void wl1251_tx_complete(struct wl1251 *wl) | |||
447 | { | 442 | { |
448 | int i, result_index, num_complete = 0; | 443 | int i, result_index, num_complete = 0; |
449 | struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; | 444 | struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; |
445 | unsigned long flags; | ||
450 | 446 | ||
451 | if (unlikely(wl->state != WL1251_STATE_ON)) | 447 | if (unlikely(wl->state != WL1251_STATE_ON)) |
452 | return; | 448 | return; |
@@ -475,6 +471,20 @@ void wl1251_tx_complete(struct wl1251 *wl) | |||
475 | } | 471 | } |
476 | } | 472 | } |
477 | 473 | ||
474 | if (wl->tx_queue_stopped | ||
475 | && | ||
476 | skb_queue_len(&wl->tx_queue) <= WL1251_TX_QUEUE_LOW_WATERMARK){ | ||
477 | |||
478 | /* firmware buffer has space, restart queues */ | ||
479 | wl1251_debug(DEBUG_TX, "tx_complete: waking queues"); | ||
480 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
481 | ieee80211_wake_queues(wl->hw); | ||
482 | wl->tx_queue_stopped = false; | ||
483 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
484 | ieee80211_queue_work(wl->hw, &wl->tx_work); | ||
485 | |||
486 | } | ||
487 | |||
478 | /* Every completed frame needs to be acknowledged */ | 488 | /* Every completed frame needs to be acknowledged */ |
479 | if (num_complete) { | 489 | if (num_complete) { |
480 | /* | 490 | /* |