From patchwork Fri Dec 4 13:17:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 338702 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 890C8C0018C for ; Fri, 4 Dec 2020 13:18:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 62007207CD for ; Fri, 4 Dec 2020 13:18:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730254AbgLDNSi (ORCPT ); Fri, 4 Dec 2020 08:18:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54506 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726312AbgLDNSh (ORCPT ); Fri, 4 Dec 2020 08:18:37 -0500 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F3EA7C061A4F for ; Fri, 4 Dec 2020 05:17:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:Message-Id:Date:Subject :To:From:Sender:Reply-To:Cc:Content-Type:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=t8pBDJi0dyYUTKOnPVNaknc1WMsZPzCmuNMZgL6xdnQ=; b=ct2lJSgEuMuKjvie8j/9jUsIXU ZjmSsf1TRkOclVEFsRCJqbGXN89gM2RPod8Hio5+ya7JlPykzHrKCiby3DzxVEEExmkdFAett32Ap lQmxindhEM588+nVO2djubFMEY7Z2b9ZY4llbO4KikWX08ZuPxseJuQlHD9iPcfpBCfE=; Received: from p4ff13815.dip0.t-ipconnect.de ([79.241.56.21] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1klAxu-00033k-A2 for linux-wireless@vger.kernel.org; Fri, 04 Dec 2020 14:17:38 +0100 From: Felix Fietkau To: linux-wireless@vger.kernel.org Subject: [PATCH v2 1/3] mt76: improve tx queue stop/wake Date: Fri, 4 Dec 2020 14:17:35 +0100 Message-Id: <20201204131737.41086-1-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Instead of stopping and waking only a single queue, handle all phy tx queues mapped ot the same hardware queue. Also allow the driver to block tx queues Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 10 ---------- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/tx.c | 23 ++++++++++++----------- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index bab961ebdf1c..6255f4f0a455 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -220,7 +220,6 @@ static void mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) { struct mt76_queue_entry entry; - bool wake = false; int last; if (!q) @@ -238,7 +237,6 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) if (entry.txwi) { if (!(dev->drv->drv_flags & MT_DRV_TXWI_NO_FREE)) mt76_put_txwi(dev, entry.txwi); - wake = !flush; } if (!flush && q->tail == last) @@ -253,16 +251,8 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) spin_unlock_bh(&q->lock); } - wake = wake && q->stopped && - q->qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8; - if (wake) - q->stopped = false; - if (!q->queued) wake_up(&dev->tx_wait); - - if (wake) - ieee80211_wake_queue(dev->hw, q->qid); } static void * diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 15d52af24d12..ea80ae188dd6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -136,6 +136,7 @@ struct mt76_queue { int queued; int buf_size; bool stopped; + bool blocked; u8 buf_offset; u8 hw_idx; diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 65360067b8fa..4026e5f4f3f3 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -291,12 +291,6 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta, spin_lock_bh(&q->lock); __mt76_tx_queue_skb(phy, qid, skb, wcid, sta, NULL); dev->queue_ops->kick(dev, q); - - if (q->queued > q->ndesc - 8 && !q->stopped) { - ieee80211_stop_queue(phy->hw, skb_get_queue_mapping(skb)); - q->stopped = true; - } - spin_unlock_bh(&q->lock); } EXPORT_SYMBOL_GPL(mt76_tx); @@ -381,6 +375,13 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, } EXPORT_SYMBOL_GPL(mt76_release_buffered_frames); +static bool +mt76_txq_stopped(struct mt76_queue *q) +{ + return q->stopped || q->blocked || + q->queued + MT_TXQ_FREE_THR >= q->ndesc; +} + static int mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, struct mt76_txq *mtxq) @@ -419,10 +420,7 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, test_bit(MT76_RESET, &phy->state)) return -EBUSY; - if (stop) - break; - - if (q->queued + MT_TXQ_FREE_THR >= q->ndesc) + if (stop || mt76_txq_stopped(q)) break; skb = mt76_txq_dequeue(phy, mtxq); @@ -463,7 +461,7 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) break; } - if (q->queued + MT_TXQ_FREE_THR >= q->ndesc) + if (mt76_txq_stopped(q)) break; txq = ieee80211_next_txq(phy->hw, qid); @@ -498,11 +496,14 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid) { + struct mt76_queue *q; int len; if (qid >= 4) return; + q = phy->q_tx[qid]; + rcu_read_lock(); do { From patchwork Fri Dec 4 13:17:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 338223 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 79FCDC4361B for ; Fri, 4 Dec 2020 13:18:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4021D22AAE for ; Fri, 4 Dec 2020 13:18:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730249AbgLDNSh (ORCPT ); Fri, 4 Dec 2020 08:18:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54508 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730201AbgLDNSh (ORCPT ); Fri, 4 Dec 2020 08:18:37 -0500 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0062CC061A51 for ; Fri, 4 Dec 2020 05:17:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=S6aYWsAVNaDQoBD9i8t8tLYKQBLzFCYn/0clja1ZRsw=; b=EpsEzHNE6mfRJuLiBGSEVdlAch I+PKtBJrNa2tPmgxlajSFiQZIPQq1Z1wfq3zN4sMZxeoWosRCfn5aOP2E+bwP3EeGtJsyv2kCLAPI 1QewezfUMUB8hBuGpsKkfR36i4xn8CUIb2IChxtcet3XYh9sC/9AorTnnl70TFYe+JhQ=; Received: from p4ff13815.dip0.t-ipconnect.de ([79.241.56.21] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1klAxu-00033k-Fy for linux-wireless@vger.kernel.org; Fri, 04 Dec 2020 14:17:38 +0100 From: Felix Fietkau To: linux-wireless@vger.kernel.org Subject: [PATCH v2 2/3] mt76: mt7915: stop queues when running out of tx tokens Date: Fri, 4 Dec 2020 14:17:36 +0100 Message-Id: <20201204131737.41086-2-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201204131737.41086-1-nbd@nbd.name> References: <20201204131737.41086-1-nbd@nbd.name> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Avoids packet drops under load with lots of stations Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 1 + .../net/wireless/mediatek/mt76/mt7915/mac.c | 39 +++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + 3 files changed, 42 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index ff29a8090739..ed4635bd151a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -690,6 +690,7 @@ void mt7915_unregister_device(struct mt7915_dev *dev) ieee80211_free_txskb(hw, txwi->skb); } mt76_put_txwi(&dev->mt76, txwi); + dev->token_count--; } spin_unlock_bh(&dev->token_lock); idr_destroy(&dev->token); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index a7fa6fffffff..71714b22188a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -918,6 +918,26 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi, mt7915_mac_write_txwi_tm(dev, mphy, txwi, skb); } +static void +mt7915_set_tx_blocked(struct mt7915_dev *dev, bool blocked) +{ + struct mt76_phy *mphy = &dev->mphy, *mphy2 = dev->mt76.phy2; + struct mt76_queue *q, *q2 = NULL; + + q = mphy->q_tx[0]; + if (blocked == q->blocked) + return; + + q->blocked = blocked; + if (mphy2) { + q2 = mphy2->q_tx[0]; + q2->blocked = blocked; + } + + if (!blocked) + mt76_worker_schedule(&dev->mt76.tx_worker); +} + int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, enum mt76_txq_id qid, struct mt76_wcid *wcid, struct ieee80211_sta *sta, @@ -974,7 +994,13 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, spin_lock_bh(&dev->token_lock); id = idr_alloc(&dev->token, t, 0, MT7915_TOKEN_SIZE, GFP_ATOMIC); + if (id >= 0) + dev->token_count++; + + if (dev->token_count >= MT7915_TOKEN_SIZE - MT7915_TOKEN_FREE_THR) + mt7915_set_tx_blocked(dev, true); spin_unlock_bh(&dev->token_lock); + if (id < 0) return id; @@ -1083,6 +1109,7 @@ void mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb) LIST_HEAD(free_list); struct sk_buff *tmp; u8 i, count; + bool wake = false; /* clean DMA queues and unmap buffers first */ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); @@ -1135,6 +1162,11 @@ void mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb) spin_lock_bh(&dev->token_lock); txwi = idr_remove(&dev->token, msdu); + if (txwi) + dev->token_count--; + if (dev->token_count < MT7915_TOKEN_SIZE - MT7915_TOKEN_FREE_THR && + dev->mphy.q_tx[0]->blocked) + wake = true; spin_unlock_bh(&dev->token_lock); if (!txwi) @@ -1165,6 +1197,13 @@ void mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb) } mt7915_mac_sta_poll(dev); + + if (wake) { + spin_lock_bh(&dev->token_lock); + mt7915_set_tx_blocked(dev, false); + spin_unlock_bh(&dev->token_lock); + } + mt76_worker_schedule(&dev->mt76.tx_worker); napi_consume_skb(skb, 1); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 30e53a0f01fb..0339abf360d3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -32,6 +32,7 @@ #define MT7915_EEPROM_SIZE 3584 #define MT7915_TOKEN_SIZE 8192 +#define MT7915_TOKEN_FREE_THR 64 #define MT7915_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ #define MT7915_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ @@ -161,6 +162,7 @@ struct mt7915_dev { u32 hw_pattern; spinlock_t token_lock; + int token_count; struct idr token; s8 **rate_power; /* TODO: use mt76_rate_power */ From patchwork Fri Dec 4 13:17:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 338703 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 452B5C433FE for ; Fri, 4 Dec 2020 13:18:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 12ED3207CD for ; Fri, 4 Dec 2020 13:18:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730157AbgLDNSW (ORCPT ); Fri, 4 Dec 2020 08:18:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54510 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726432AbgLDNSW (ORCPT ); Fri, 4 Dec 2020 08:18:22 -0500 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 00AF4C061A52 for ; Fri, 4 Dec 2020 05:17:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=nxdHDwvnog+6liZ8DSPVmI11iuBYNOqeQIAYQIcuZ3A=; b=SQTenh+NH/H0bk9G3+tZ+PxSaN FCYRi2bjuMzJTeIHavDvMLUPzo+MZS9gnxaqU3ZWAZt3EuATIX8sJ3hd71rc1QVERY3/x6qM/cVue 5rJy62AAzhsVbQk8hApzsCn6JrBwjPsuzqocPRLOVJBRy+0UGvw1DbD+wpTjOuZj6wZ8=; Received: from p4ff13815.dip0.t-ipconnect.de ([79.241.56.21] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1klAxu-00033k-M0 for linux-wireless@vger.kernel.org; Fri, 04 Dec 2020 14:17:38 +0100 From: Felix Fietkau To: linux-wireless@vger.kernel.org Subject: [PATCH v2 3/3] mt76: attempt to free up more room when filling the tx queue Date: Fri, 4 Dec 2020 14:17:37 +0100 Message-Id: <20201204131737.41086-3-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201204131737.41086-1-nbd@nbd.name> References: <20201204131737.41086-1-nbd@nbd.name> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Run dma cleanup immediately if the queue is almost full, instead of waiting for the tx interrupt Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/tx.c | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 6255f4f0a455..73eeb00d5aa6 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -88,6 +88,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q, int i; spin_lock_init(&q->lock); + spin_lock_init(&q->cleanup_lock); q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE; q->ndesc = n_desc; @@ -225,6 +226,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) if (!q) return; + spin_lock_bh(&q->cleanup_lock); if (flush) last = -1; else @@ -243,6 +245,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) last = readl(&q->regs->dma_idx); } + spin_unlock_bh(&q->cleanup_lock); if (flush) { spin_lock_bh(&q->lock); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index ea80ae188dd6..3e496a188bf0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -126,6 +126,7 @@ struct mt76_queue { struct mt76_queue_regs __iomem *regs; spinlock_t lock; + spinlock_t cleanup_lock; struct mt76_queue_entry *entry; struct mt76_desc *desc; diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 4026e5f4f3f3..1e20afb70fc1 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -448,6 +448,7 @@ static int mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) { struct mt76_queue *q = phy->q_tx[qid]; + struct mt76_dev *dev = phy->dev; struct ieee80211_txq *txq; struct mt76_txq *mtxq; struct mt76_wcid *wcid; @@ -461,6 +462,13 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) break; } + if (dev->queue_ops->tx_cleanup && + q->queued + 2 * MT_TXQ_FREE_THR >= q->ndesc) { + spin_unlock_bh(&q->lock); + dev->queue_ops->tx_cleanup(dev, q, false); + spin_lock_bh(&q->lock); + } + if (mt76_txq_stopped(q)) break;