From: Grazvydas Ignotas Date: Mon, 16 Feb 2015 20:43:37 +0000 (+0200) Subject: wl1251: do tx as soon as there's space X-Git-Tag: sz_173~58 X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=commitdiff_plain;h=7f6752f70dc50da1c2a26efb1c05dc07b1806c03;hp=49a086ec91d906f90799a4937300a60107c27df4 wl1251: do tx as soon as there's space This way firmware will get tx packet without having to wait for rx of other irqs to be processed first. (not sure about improvement, if any, from this patch, as always it's too unstable/inconsistent to make a proper measurement) --- diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index e6a14754dd06..42b8df58da31 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -279,9 +279,18 @@ irqreturn_t wl1251_irq(int irq, void *cookie) goto out_sleep; } + if (intr & WL1251_ACX_INTR_TX_RESULT) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); + wl1251_tx_complete(wl); + } + if (intr & WL1251_ACX_INTR_RX0_DATA) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); wl1251_rx(wl); + + if ((intr & WL1251_ACX_INTR_RX1_DATA) + && skb_queue_len(&wl->tx_queue) > 0) + wl1251_tx_work_unlocked(wl, false); } if (intr & WL1251_ACX_INTR_RX1_DATA) { @@ -289,11 +298,6 @@ irqreturn_t wl1251_irq(int irq, void *cookie) wl1251_rx(wl); } - if (intr & WL1251_ACX_INTR_TX_RESULT) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); - wl1251_tx_complete(wl); - } - if (intr & WL1251_ACX_INTR_EVENT_A) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A"); wl1251_event_handle(wl, 0); @@ -308,21 +312,8 @@ irqreturn_t wl1251_irq(int irq, void *cookie) wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); - while (skb_queue_len(&wl->tx_queue) > 0 - && wl1251_tx_path_status(wl) == 0) { - - struct sk_buff *skb = skb_dequeue(&wl->tx_queue); - if (skb == NULL) - goto out_sleep; - - ret = wl1251_tx_frame(wl, skb); - if (ret == -EBUSY) { - skb_queue_head(&wl->tx_queue, skb); - break; - } else if (ret < 0) { - dev_kfree_skb(skb); - } - } + if (skb_queue_len(&wl->tx_queue) > 0) + wl1251_tx_work_unlocked(wl, false); if (--ctr == 0) break; diff --git a/drivers/net/wireless/wl1251/tx.c b/drivers/net/wireless/wl1251/tx.c index 90379a87b78b..421afe461f7b 100644 --- a/drivers/net/wireless/wl1251/tx.c +++ b/drivers/net/wireless/wl1251/tx.c @@ -345,20 +345,17 @@ int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) return ret; } -void wl1251_tx_work(struct work_struct *work) +void wl1251_tx_work_unlocked(struct wl1251 *wl, bool need_pm) { - struct wl1251 *wl = container_of(work, struct wl1251, tx_work); struct sk_buff *skb; bool woken_up = false; int ret; - mutex_lock(&wl->mutex); - if (unlikely(wl->state == WL1251_STATE_OFF)) goto out; while ((skb = skb_dequeue(&wl->tx_queue))) { - if (!woken_up) { + if (need_pm && !woken_up) { ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -378,7 +375,14 @@ void wl1251_tx_work(struct work_struct *work) out: if (woken_up) wl1251_ps_elp_sleep(wl); +} +void wl1251_tx_work(struct work_struct *work) +{ + struct wl1251 *wl = container_of(work, struct wl1251, tx_work); + + mutex_lock(&wl->mutex); + wl1251_tx_work_unlocked(wl, true); mutex_unlock(&wl->mutex); } @@ -491,24 +495,6 @@ void wl1251_tx_complete(struct wl1251 *wl) } } - queue_len = skb_queue_len(&wl->tx_queue); - - 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); - } - /* Every completed frame needs to be acknowledged */ if (num_complete) { /* @@ -557,6 +543,29 @@ void wl1251_tx_complete(struct wl1251 *wl) } wl->next_tx_complete = result_index; + + queue_len = skb_queue_len(&wl->tx_queue); + if (queue_len > 0) { + /* avoid stalling tx */ + wl1251_tx_work_unlocked(wl, false); + queue_len = skb_queue_len(&wl->tx_queue); + } + + if (queue_len > 0) { + /* still something to send? Schedule for later */ + 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); + } } /* caller must hold wl->mutex */ diff --git a/drivers/net/wireless/wl1251/tx.h b/drivers/net/wireless/wl1251/tx.h index da5b6ecdad4a..4541a58e5c75 100644 --- a/drivers/net/wireless/wl1251/tx.h +++ b/drivers/net/wireless/wl1251/tx.h @@ -224,6 +224,7 @@ static inline int wl1251_tx_get_queue(int queue) } } +void wl1251_tx_work_unlocked(struct wl1251 *wl, bool need_pm); void wl1251_tx_work(struct work_struct *work); void wl1251_tx_complete(struct wl1251 *wl); void wl1251_tx_flush(struct wl1251 *wl);