mt7601u: fix tx status reporting contexts
authorJakub Kicinski <kubakici@wp.pl>
Fri, 31 Jul 2015 13:04:48 +0000 (15:04 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 10 Aug 2015 19:19:34 +0000 (22:19 +0300)
mac80211 requires that rx path does not run concurrently with
tx status reporting.  Since rx path is run in driver tasklet,
tx status cannot be reported directly from interrupt context
(there would be no way to lock it out).

Add tasklet for tx and move all possible code from irq handler
there.

Note: tx tasklet is needed because workqueue is queued very
      rarely and that kills TCP performance.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mediatek/mt7601u/dma.c
drivers/net/wireless/mediatek/mt7601u/init.c
drivers/net/wireless/mediatek/mt7601u/mac.c
drivers/net/wireless/mediatek/mt7601u/mt7601u.h

index fb183e369d921e3febfa5cc93124d6977cc14f62..63c485443a389b6c5ff3902b0e5ab7d1f996708d 100644 (file)
@@ -236,23 +236,42 @@ static void mt7601u_complete_tx(struct urb *urb)
        skb = q->e[q->start].skb;
        trace_mt_tx_dma_done(dev, skb);
 
-       mt7601u_tx_status(dev, skb);
+       __skb_queue_tail(&dev->tx_skb_done, skb);
+       tasklet_schedule(&dev->tx_tasklet);
 
        if (q->used == q->entries - q->entries / 8)
                ieee80211_wake_queue(dev->hw, skb_get_queue_mapping(skb));
 
        q->start = (q->start + 1) % q->entries;
        q->used--;
+out:
+       spin_unlock_irqrestore(&dev->tx_lock, flags);
+}
 
-       if (urb->status)
-               goto out;
+static void mt7601u_tx_tasklet(unsigned long data)
+{
+       struct mt7601u_dev *dev = (struct mt7601u_dev *) data;
+       struct sk_buff_head skbs;
+       unsigned long flags;
+
+       __skb_queue_head_init(&skbs);
+
+       spin_lock_irqsave(&dev->tx_lock, flags);
 
        set_bit(MT7601U_STATE_MORE_STATS, &dev->state);
        if (!test_and_set_bit(MT7601U_STATE_READING_STATS, &dev->state))
                queue_delayed_work(dev->stat_wq, &dev->stat_work,
                                   msecs_to_jiffies(10));
-out:
+
+       skb_queue_splice_init(&dev->tx_skb_done, &skbs);
+
        spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+       while (!skb_queue_empty(&skbs)) {
+               struct sk_buff *skb = __skb_dequeue(&skbs);
+
+               mt7601u_tx_status(dev, skb);
+       }
 }
 
 static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev,
@@ -475,6 +494,7 @@ int mt7601u_dma_init(struct mt7601u_dev *dev)
 {
        int ret = -ENOMEM;
 
+       tasklet_init(&dev->tx_tasklet, mt7601u_tx_tasklet, (unsigned long) dev);
        tasklet_init(&dev->rx_tasklet, mt7601u_rx_tasklet, (unsigned long) dev);
 
        ret = mt7601u_alloc_tx(dev);
@@ -502,4 +522,6 @@ void mt7601u_dma_cleanup(struct mt7601u_dev *dev)
 
        mt7601u_free_rx(dev);
        mt7601u_free_tx(dev);
+
+       tasklet_kill(&dev->tx_tasklet);
 }
index df3dd56199a7ec8a43202c8d40b8d919df846006..38eb20ba6e58e13e5668e02c6d7f90e09649ea49 100644 (file)
@@ -456,6 +456,7 @@ struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev)
        spin_lock_init(&dev->lock);
        spin_lock_init(&dev->con_mon_lock);
        atomic_set(&dev->avg_ampdu_len, 1);
+       skb_queue_head_init(&dev->tx_skb_done);
 
        dev->stat_wq = alloc_workqueue("mt7601u", WQ_UNBOUND, 0);
        if (!dev->stat_wq) {
index 7514bce1ac91dfa61bfe82e822c92eac57f1a129..e3928cfa3d639e19a17cf576532a2e1eee3b0879 100644 (file)
@@ -181,7 +181,11 @@ void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat)
        }
 
        mt76_mac_fill_tx_status(dev, &info, stat);
+
+       local_bh_disable();
        ieee80211_tx_status_noskb(dev->hw, sta, &info);
+       local_bh_enable();
+
        rcu_read_unlock();
 }
 
index 6bdfc1103fccafabacf5572d04821366131e91c5..bc5e294feb8c08c88c217acbf8d63d4dfe26f6b2 100644 (file)
@@ -199,7 +199,9 @@ struct mt7601u_dev {
 
        /* TX */
        spinlock_t tx_lock;
+       struct tasklet_struct tx_tasklet;
        struct mt7601u_tx_queue *tx_q;
+       struct sk_buff_head tx_skb_done;
 
        atomic_t avg_ampdu_len;