aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorIdo Yariv <ido@wizery.com>2010-10-12 08:49:10 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-11-15 13:25:05 -0500
commita522550a283de31c7cfc30c7a129ce584e38c582 (patch)
tree3cee18d0f0dbbb3c5bff27488c0d302821e4f1d8 /drivers/net
parent6c6e669ed6282788d6045397ce0f201edc400d9d (diff)
wl1271: Fix TX starvation
While wl1271_irq_work handles RX directly (by calling wl1271_rx), a different work is scheduled for transmitting packets. The IRQ work might handle more than one interrupt during a single call, including multiple TX completion interrupts. This might starve TX, since no packets are transmitted until all interrupts are handled. Fix this by calling the TX work function directly, instead of deferring it. Signed-off-by: Ido Yariv <ido@wizery.com> Reviewed-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h1
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c19
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.c14
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.h1
4 files changed, 27 insertions, 8 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 8a4cd763e5a2..4a034a3f7148 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -351,6 +351,7 @@ struct wl1271 {
351#define WL1271_FLAG_IDLE_REQUESTED (11) 351#define WL1271_FLAG_IDLE_REQUESTED (11)
352#define WL1271_FLAG_PSPOLL_FAILURE (12) 352#define WL1271_FLAG_PSPOLL_FAILURE (12)
353#define WL1271_FLAG_STA_STATE_SENT (13) 353#define WL1271_FLAG_STA_STATE_SENT (13)
354#define WL1271_FLAG_FW_TX_BUSY (14)
354 unsigned long flags; 355 unsigned long flags;
355 356
356 struct wl1271_partition_set part; 357 struct wl1271_partition_set part;
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 48a4b9961ae6..18aff225bf82 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -481,9 +481,9 @@ static void wl1271_fw_status(struct wl1271 *wl,
481 total += cnt; 481 total += cnt;
482 } 482 }
483 483
484 /* if more blocks are available now, schedule some tx work */ 484 /* if more blocks are available now, tx work can be scheduled */
485 if (total && !skb_queue_empty(&wl->tx_queue)) 485 if (total)
486 ieee80211_queue_work(wl->hw, &wl->tx_work); 486 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
487 487
488 /* update the host-chipset time offset */ 488 /* update the host-chipset time offset */
489 getnstimeofday(&ts); 489 getnstimeofday(&ts);
@@ -537,6 +537,16 @@ static void wl1271_irq_work(struct work_struct *work)
537 (wl->tx_results_count & 0xff)) 537 (wl->tx_results_count & 0xff))
538 wl1271_tx_complete(wl); 538 wl1271_tx_complete(wl);
539 539
540 /* Check if any tx blocks were freed */
541 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
542 !skb_queue_empty(&wl->tx_queue)) {
543 /*
544 * In order to avoid starvation of the TX path,
545 * call the work function directly.
546 */
547 wl1271_tx_work_locked(wl);
548 }
549
540 wl1271_rx(wl, wl->fw_status); 550 wl1271_rx(wl, wl->fw_status);
541 } 551 }
542 552
@@ -867,7 +877,8 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
867 * before that, the tx_work will not be initialized! 877 * before that, the tx_work will not be initialized!
868 */ 878 */
869 879
870 ieee80211_queue_work(wl->hw, &wl->tx_work); 880 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
881 ieee80211_queue_work(wl->hw, &wl->tx_work);
871 882
872 /* 883 /*
873 * The workqueue is slow to process the tx_queue and we need stop 884 * The workqueue is slow to process the tx_queue and we need stop
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index b13b37330036..cf32a77a4ff5 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -204,9 +204,8 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
204 return enabled_rates; 204 return enabled_rates;
205} 205}
206 206
207void wl1271_tx_work(struct work_struct *work) 207void wl1271_tx_work_locked(struct wl1271 *wl)
208{ 208{
209 struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
210 struct sk_buff *skb; 209 struct sk_buff *skb;
211 bool woken_up = false; 210 bool woken_up = false;
212 u32 sta_rates = 0; 211 u32 sta_rates = 0;
@@ -223,8 +222,6 @@ void wl1271_tx_work(struct work_struct *work)
223 spin_unlock_irqrestore(&wl->wl_lock, flags); 222 spin_unlock_irqrestore(&wl->wl_lock, flags);
224 } 223 }
225 224
226 mutex_lock(&wl->mutex);
227
228 if (unlikely(wl->state == WL1271_STATE_OFF)) 225 if (unlikely(wl->state == WL1271_STATE_OFF))
229 goto out; 226 goto out;
230 227
@@ -260,6 +257,8 @@ void wl1271_tx_work(struct work_struct *work)
260 * Queue back last skb, and stop aggregating. 257 * Queue back last skb, and stop aggregating.
261 */ 258 */
262 skb_queue_head(&wl->tx_queue, skb); 259 skb_queue_head(&wl->tx_queue, skb);
260 /* No work left, avoid scheduling redundant tx work */
261 set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
263 goto out_ack; 262 goto out_ack;
264 } else if (ret < 0) { 263 } else if (ret < 0) {
265 dev_kfree_skb(skb); 264 dev_kfree_skb(skb);
@@ -283,7 +282,14 @@ out_ack:
283out: 282out:
284 if (woken_up) 283 if (woken_up)
285 wl1271_ps_elp_sleep(wl); 284 wl1271_ps_elp_sleep(wl);
285}
286 286
287void wl1271_tx_work(struct work_struct *work)
288{
289 struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
290
291 mutex_lock(&wl->mutex);
292 wl1271_tx_work_locked(wl);
287 mutex_unlock(&wl->mutex); 293 mutex_unlock(&wl->mutex);
288} 294}
289 295
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h
index d12a129ad11c..f1c906519b7d 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.h
@@ -140,6 +140,7 @@ static inline int wl1271_tx_get_queue(int queue)
140} 140}
141 141
142void wl1271_tx_work(struct work_struct *work); 142void wl1271_tx_work(struct work_struct *work);
143void wl1271_tx_work_locked(struct wl1271 *wl);
143void wl1271_tx_complete(struct wl1271 *wl); 144void wl1271_tx_complete(struct wl1271 *wl);
144void wl1271_tx_reset(struct wl1271 *wl); 145void wl1271_tx_reset(struct wl1271 *wl);
145void wl1271_tx_flush(struct wl1271 *wl); 146void wl1271_tx_flush(struct wl1271 *wl);