* [PATCH 2/3] mt76: mt76x02: when setting a key, use PN from mac80211
From: Felix Fietkau @ 2019-03-01 14:20 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20190301142023.18179-1-nbd@nbd.name>
Preparation for full device restart support
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt76x02_mac.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 91ff6598eccf..8109bac5aee6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -73,6 +73,7 @@ int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
enum mt76x02_cipher_type cipher;
u8 key_data[32];
u8 iv_data[8];
+ u64 pn;
cipher = mt76x02_mac_get_key_info(key, key_data);
if (cipher == MT_CIPHER_NONE && key)
@@ -85,9 +86,22 @@ int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
if (key) {
mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE,
!!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE));
+
+ pn = atomic64_read(&key->tx_pn);
+
iv_data[3] = key->keyidx << 6;
- if (cipher >= MT_CIPHER_TKIP)
+ if (cipher >= MT_CIPHER_TKIP) {
iv_data[3] |= 0x20;
+ put_unaligned_le32(pn >> 16, &iv_data[4]);
+ }
+
+ if (cipher == MT_CIPHER_TKIP) {
+ iv_data[0] = (pn >> 8) & 0xff;
+ iv_data[1] = (iv_data[0] | 0x20) & 0x7f;
+ iv_data[2] = pn & 0xff;
+ } else if (cipher >= MT_CIPHER_AES_CCMP) {
+ put_unaligned_le16((pn & 0xffff), &iv_data[0]);
+ }
}
mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data));
--
2.17.0
^ permalink raw reply related
* [PATCH 3/3] mt76: mt76x2: implement full device restart on watchdog reset
From: Felix Fietkau @ 2019-03-01 14:20 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20190301142023.18179-1-nbd@nbd.name>
Restart the firmware and re-initialize the MAC to be able to recover
from more kinds of hang states
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
.../net/wireless/mediatek/mt76/mt76x02_mac.c | 26 ++++++
.../net/wireless/mediatek/mt76/mt76x02_mac.h | 2 +
.../net/wireless/mediatek/mt76/mt76x02_mmio.c | 81 +++++++++++++++++--
.../net/wireless/mediatek/mt76/mt76x02_util.c | 4 +
.../wireless/mediatek/mt76/mt76x2/mt76x2.h | 1 +
.../wireless/mediatek/mt76/mt76x2/pci_init.c | 2 +-
.../wireless/mediatek/mt76/mt76x2/pci_mcu.c | 21 +++++
drivers/net/wireless/mediatek/mt76/tx.c | 3 +
9 files changed, 132 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 1dcbddec6621..477027bb9aaf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -144,6 +144,7 @@ struct mt76_mcu_ops {
const struct mt76_reg_pair *rp, int len);
int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base,
struct mt76_reg_pair *rp, int len);
+ int (*mcu_restart)(struct mt76_dev *dev);
};
struct mt76_queue_ops {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 8109bac5aee6..e1e0c8da5a8c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -67,6 +67,32 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
}
EXPORT_SYMBOL_GPL(mt76x02_mac_shared_key_setup);
+void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx,
+ struct ieee80211_key_conf *key)
+{
+ enum mt76x02_cipher_type cipher;
+ u8 key_data[32];
+ u32 iv, eiv;
+ u64 pn;
+
+ cipher = mt76x02_mac_get_key_info(key, key_data);
+ iv = mt76_rr(dev, MT_WCID_IV(idx));
+ eiv = mt76_rr(dev, MT_WCID_IV(idx) + 4);
+
+ pn = (u64)eiv << 16;
+ if (cipher == MT_CIPHER_TKIP) {
+ pn |= (iv >> 16) & 0xff;
+ pn |= (iv & 0xff) << 8;
+ } else if (cipher >= MT_CIPHER_AES_CCMP) {
+ pn |= iv & 0xffff;
+ } else {
+ return;
+ }
+
+ atomic64_set(&key->tx_pn, pn);
+}
+
+
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
index 6b1f25d2f64c..caeeef96c42f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
@@ -177,6 +177,8 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
u8 key_idx, struct ieee80211_key_conf *key);
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key);
+void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx,
+ struct ieee80211_key_conf *key);
void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx, u8 vif_idx,
u8 *mac);
void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
index 1a7926de1dec..6a34a6afcfe4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include "mt76x02.h"
+#include "mt76x02_mcu.h"
#include "mt76x02_trace.h"
struct beacon_bc_data {
@@ -400,9 +401,65 @@ static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
return i < 4;
}
+static void mt76x02_key_sync(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key, void *data)
+{
+ struct mt76x02_dev *dev = hw->priv;
+ struct mt76_wcid *wcid;
+
+ if (!sta)
+ return;
+
+ wcid = (struct mt76_wcid *) sta->drv_priv;
+
+ if (wcid->hw_key_idx != key->keyidx || wcid->sw_iv)
+ return;
+
+ mt76x02_mac_wcid_sync_pn(dev, wcid->idx, key);
+}
+
+static void mt76x02_reset_state(struct mt76x02_dev *dev)
+{
+ int i;
+
+ clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
+
+ rcu_read_lock();
+
+ ieee80211_iter_keys_rcu(dev->mt76.hw, NULL, mt76x02_key_sync, NULL);
+
+ for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid); i++) {
+ struct mt76_wcid *wcid = rcu_dereference(dev->mt76.wcid[i]);
+ struct mt76x02_sta *msta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
+ void *priv;
+
+ if (!wcid)
+ continue;
+
+ priv = msta = container_of(wcid, struct mt76x02_sta, wcid);
+ sta = container_of(priv, struct ieee80211_sta, drv_priv);
+
+ priv = msta->vif;
+ vif = container_of(priv, struct ieee80211_vif, drv_priv);
+
+ mt76_sta_state(dev->mt76.hw, vif, sta,
+ IEEE80211_STA_NONE, IEEE80211_STA_NOTEXIST);
+ memset(msta, 0, sizeof(*msta));
+ }
+
+ rcu_read_unlock();
+
+ dev->vif_mask = 0;
+ dev->beacon_mask = 0;
+}
+
static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
{
u32 mask = dev->mt76.mmio.irqmask;
+ bool restart = dev->mt76.mcu_ops->mcu_restart;
int i;
ieee80211_stop_queues(dev->mt76.hw);
@@ -414,6 +471,9 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++)
napi_disable(&dev->mt76.napi[i]);
+ if (restart)
+ mt76x02_reset_state(dev);
+
mutex_lock(&dev->mt76.mutex);
if (dev->beacon_mask)
@@ -434,20 +494,21 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
/* let fw reset DMA */
mt76_set(dev, 0x734, 0x3);
+ if (restart)
+ dev->mt76.mcu_ops->mcu_restart(&dev->mt76);
+
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++)
mt76_queue_tx_cleanup(dev, i, true);
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++)
mt76_queue_rx_reset(dev, i);
- mt76_wr(dev, MT_MAC_SYS_CTRL,
- MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
- mt76_set(dev, MT_WPDMA_GLO_CFG,
- MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN);
+ mt76x02_mac_start(dev);
+
if (dev->ed_monitor)
mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
- if (dev->beacon_mask)
+ if (dev->beacon_mask && !restart)
mt76_set(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_BEACON_TX |
MT_BEACON_TIME_CFG_TBTT_EN);
@@ -468,9 +529,13 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
napi_schedule(&dev->mt76.napi[i]);
}
- ieee80211_wake_queues(dev->mt76.hw);
-
- mt76_txq_schedule_all(&dev->mt76);
+ if (restart) {
+ mt76x02_mcu_function_select(dev, Q_SELECT, 1);
+ ieee80211_restart_hw(dev->mt76.hw);
+ } else {
+ ieee80211_wake_queues(dev->mt76.hw);
+ mt76_txq_schedule_all(&dev->mt76);
+ }
}
static void mt76x02_check_tx_hang(struct mt76x02_dev *dev)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
index a48c261b0c63..28eccb3119d1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
@@ -237,6 +237,8 @@ int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
int idx = 0;
+ memset(msta, 0, sizeof(*msta));
+
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, ARRAY_SIZE(dev->mt76.wcid));
if (idx < 0)
return -ENOSPC;
@@ -274,6 +276,8 @@ mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
struct mt76_txq *mtxq;
+ memset(mvif, 0, sizeof(*mvif));
+
mvif->idx = idx;
mvif->group_wcid.idx = MT_VIF_WCID(idx);
mvif->group_wcid.hw_key_idx = -1;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
index 6c619f1c65c9..d7abe3d73bad 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
@@ -71,6 +71,7 @@ int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
void mt76x2_cleanup(struct mt76x02_dev *dev);
+int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard);
void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable);
void mt76x2_init_txpower(struct mt76x02_dev *dev,
struct ieee80211_supported_band *sband);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
index 984d9c4c2e1a..d3927a13e92e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
@@ -77,7 +77,7 @@ mt76x2_fixup_xtal(struct mt76x02_dev *dev)
}
}
-static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
+int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
{
const u8 *macaddr = dev->mt76.macaddr;
u32 val;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
index 03e24ae7f66c..605dc66ae83b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
@@ -165,9 +165,30 @@ mt76pci_load_firmware(struct mt76x02_dev *dev)
return -ENOENT;
}
+static int
+mt76pci_mcu_restart(struct mt76_dev *mdev)
+{
+ struct mt76x02_dev *dev;
+ int ret;
+
+ dev = container_of(mdev, struct mt76x02_dev, mt76);
+
+ mt76x02_mcu_cleanup(dev);
+ mt76x2_mac_reset(dev, true);
+
+ ret = mt76pci_load_firmware(dev);
+ if (ret)
+ return ret;
+
+ mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
+
+ return 0;
+}
+
int mt76x2_mcu_init(struct mt76x02_dev *dev)
{
static const struct mt76_mcu_ops mt76x2_mcu_ops = {
+ .mcu_restart = mt76pci_mcu_restart,
.mcu_send_msg = mt76x02_mcu_msg_send,
};
int ret;
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 4d38a54014e8..fc7dffe066be 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -580,6 +580,9 @@ void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
struct mt76_queue *hwq = mtxq->hwq;
+ if (!test_bit(MT76_STATE_RUNNING, &dev->state))
+ return;
+
spin_lock_bh(&hwq->lock);
if (list_empty(&mtxq->list))
list_add_tail(&mtxq->list, &hwq->swq);
--
2.17.0
^ permalink raw reply related
* [PATCH 1/3] mt76: rewrite dma descriptor base and ring size on queue reset
From: Felix Fietkau @ 2019-03-01 14:20 UTC (permalink / raw)
To: linux-wireless
Useful in case the hardware reset clobbers these values
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/dma.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index 4db1f088ee91..4e2dfb0f20c3 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -137,6 +137,8 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
+ iowrite32(q->desc_dma, &q->regs->desc_base);
+ iowrite32(q->ndesc, &q->regs->ring_size);
q->head = ioread32(&q->regs->dma_idx);
q->tail = q->head;
iowrite32(q->head, &q->regs->cpu_idx);
--
2.17.0
^ permalink raw reply related
* [PATCH] mac80211: do not call driver wake_tx_queue op during reconfig
From: Felix Fietkau @ 2019-03-01 13:48 UTC (permalink / raw)
To: linux-wireless; +Cc: johannes, stable
There are several scenarios in which mac80211 can call drv_wake_tx_queue
after ieee80211_restart_hw has been called and has not yet completed.
Driver private structs are considered uninitialized until mac80211 has
uploaded the vifs, stations and keys again, so using private tx queue
data during that time is not safe.
The driver can also not rely on drv_reconfig_complete to figure out when
it is safe to accept drv_wake_tx_queue calls again, because it is only
called after all tx queues are woken again.
To fix this, bail out early in drv_wake_tx_queue if local->in_reconfig
is set.
Cc: stable@vger.kernel.org
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
net/mac80211/driver-ops.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 28d022a3eee3..ae4f0be3b393 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1195,6 +1195,9 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local,
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
+ if (local->in_reconfig)
+ return;
+
if (!check_sdata_in_driver(sdata))
return;
--
2.17.0
^ permalink raw reply related
* Re: [PATCH v3 0/2] move dma shared code in mt76 module
From: Felix Fietkau @ 2019-03-01 11:59 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: linux-wireless, sgruszka, lorenzo.bianconi
In-Reply-To: <cover.1551370209.git.lorenzo@kernel.org>
On 2019-02-28 17:54, Lorenzo Bianconi wrote:
> Move mt76x02/mt7603 dma shared code in mmio.c/dma.c in order to be
> reused adding support for mt7603 driver
>
> Changes since v2:
> - squash mt7603 changes
>
> Changes since v1:
> - remove mt76_irq_enable/mt76_irq_disable
> - move dma queue initialization in mt76_dma_alloc_queue
> - move mt76_irq_enable call into the driver specific
> inline functions
>
> Lorenzo Bianconi (2):
> mt76: mmio: move mt76x02_set_irq_mask in mt76 module
> mt76: dma: move mt76x02_init_{tx,rx}_queue in mt76 module
Applied, thanks.
- Felix
^ permalink raw reply
* Re: wireless workshop coming soon :-)
From: Toke Høiland-Jørgensen @ 2019-03-01 11:23 UTC (permalink / raw)
To: Johannes Berg, linux-wireless
In-Reply-To: <5d26367411cf2b50d4cf237640598d1952f17673.camel@sipsolutions.net>
Johannes Berg <johannes@sipsolutions.net> writes:
> On Fri, 2019-03-01 at 12:11 +0100, Toke Høiland-Jørgensen wrote:
>> Johannes Berg <johannes@sipsolutions.net> writes:
>>
>> > Please add your favourite topics to
>> >
>> > https://wireless.wiki.kernel.org/en/developers/summits/prague-2019
>> >
>> > I've added a few I think we should discuss at this stage, so also see if
>> > you have an opinion on those :-)
>>
>> Cool! Could we schedule the scheduling and airtime fairness topics after
>> 10:30? Have a tutorial to run in the morning and would obviously like to
>> participate in those discussions ;)
>
> Yeah, I'm aware. I also have a conflict with the netlink workshop, so
> Kalle will have to take over for me some of the time :-)
Great! Thought you probably were, just wanted an explicit ACK ;)
-Toke
^ permalink raw reply
* Re: wireless workshop coming soon :-)
From: Johannes Berg @ 2019-03-01 11:12 UTC (permalink / raw)
To: Toke Høiland-Jørgensen, linux-wireless
In-Reply-To: <871s3qn91h.fsf@toke.dk>
On Fri, 2019-03-01 at 12:11 +0100, Toke Høiland-Jørgensen wrote:
> Johannes Berg <johannes@sipsolutions.net> writes:
>
> > Please add your favourite topics to
> >
> > https://wireless.wiki.kernel.org/en/developers/summits/prague-2019
> >
> > I've added a few I think we should discuss at this stage, so also see if
> > you have an opinion on those :-)
>
> Cool! Could we schedule the scheduling and airtime fairness topics after
> 10:30? Have a tutorial to run in the morning and would obviously like to
> participate in those discussions ;)
Yeah, I'm aware. I also have a conflict with the netlink workshop, so
Kalle will have to take over for me some of the time :-)
johannes
^ permalink raw reply
* Re: wireless workshop coming soon :-)
From: Toke Høiland-Jørgensen @ 2019-03-01 11:11 UTC (permalink / raw)
To: Johannes Berg, linux-wireless
In-Reply-To: <f90f210372b8c7f7efae0c43ab3ae925c334bc0e.camel@sipsolutions.net>
Johannes Berg <johannes@sipsolutions.net> writes:
> Please add your favourite topics to
>
> https://wireless.wiki.kernel.org/en/developers/summits/prague-2019
>
> I've added a few I think we should discuss at this stage, so also see if
> you have an opinion on those :-)
Cool! Could we schedule the scheduling and airtime fairness topics after
10:30? Have a tutorial to run in the morning and would obviously like to
participate in those discussions ;)
-Toke
^ permalink raw reply
* wireless workshop coming soon :-)
From: Johannes Berg @ 2019-03-01 10:49 UTC (permalink / raw)
To: linux-wireless
Please add your favourite topics to
https://wireless.wiki.kernel.org/en/developers/summits/prague-2019
I've added a few I think we should discuss at this stage, so also see if
you have an opinion on those :-)
johannes
^ permalink raw reply
* Re: [RFC 2/2] mt76: introduce mt76_hw_queue data structure
From: Lorenzo Bianconi @ 2019-03-01 10:31 UTC (permalink / raw)
To: Stanislaw Gruszka
Cc: Lorenzo Bianconi, nbd, linux-wireless, ryder.lee, roychl666
In-Reply-To: <20190301095852.GA31324@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 1179 bytes --]
> On Fri, Mar 01, 2019 at 10:22:04AM +0100, Lorenzo Bianconi wrote:
> > -struct mt76_queue {
> > +struct mt76_hw_queue {
> >
> > u16 first;
> > u16 head;
> > u16 tail;
> > - int ndesc;
> > - int queued;
> > - int buf_size;
> >
> > + dma_addr_t desc_dma;
> > +
> > + int buf_size;
> > u8 buf_offset;
> > +
> > u8 hw_idx;
> >
> > - dma_addr_t desc_dma;
> > - struct sk_buff *rx_head;
> > struct page_frag_cache rx_page;
> > spinlock_t rx_page_lock;
> > + struct sk_buff *rx_head;
> > +};
> > +
> > +struct mt76_queue {
> > + struct mt76_hw_queue *hwq;
> > +
> > + struct list_head swq;
> > + int swq_queued;
> > };
>
> I don't think massive renaming is necessary. It makes patch more complex
> and eventual future backporting of fixes harder. Simpler approach would
> be doing:
>
> struct mt76_sw_queue {
> struct mt76_queue *q;
>
> struct list_head swq;
> int swq_queued;
> }
ack, it sounds good to me. Anyway mt76_queue is pretty spread as well :(
I will post a v2 squashing this change.
Regards,
Lorenzo
>
> what should achieve the same goal without massive changes in the code.
>
> Stanislaw
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [RFC 2/2] mt76: introduce mt76_hw_queue data structure
From: Stanislaw Gruszka @ 2019-03-01 9:58 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: nbd, linux-wireless, ryder.lee, roychl666, lorenzo.bianconi
In-Reply-To: <e42f2de79766a2bd507d66ea2dabb0e79a684450.1551431791.git.lorenzo@kernel.org>
On Fri, Mar 01, 2019 at 10:22:04AM +0100, Lorenzo Bianconi wrote:
> -struct mt76_queue {
> +struct mt76_hw_queue {
>
> u16 first;
> u16 head;
> u16 tail;
> - int ndesc;
> - int queued;
> - int buf_size;
>
> + dma_addr_t desc_dma;
> +
> + int buf_size;
> u8 buf_offset;
> +
> u8 hw_idx;
>
> - dma_addr_t desc_dma;
> - struct sk_buff *rx_head;
> struct page_frag_cache rx_page;
> spinlock_t rx_page_lock;
> + struct sk_buff *rx_head;
> +};
> +
> +struct mt76_queue {
> + struct mt76_hw_queue *hwq;
> +
> + struct list_head swq;
> + int swq_queued;
> };
I don't think massive renaming is necessary. It makes patch more complex
and eventual future backporting of fixes harder. Simpler approach would
be doing:
struct mt76_sw_queue {
struct mt76_queue *q;
struct list_head swq;
int swq_queued;
}
what should achieve the same goal without massive changes in the code.
Stanislaw
^ permalink raw reply
* [RFC 2/2] mt76: introduce mt76_hw_queue data structure
From: Lorenzo Bianconi @ 2019-03-01 9:22 UTC (permalink / raw)
To: nbd; +Cc: linux-wireless, ryder.lee, roychl666, lorenzo.bianconi
In-Reply-To: <cover.1551431791.git.lorenzo@kernel.org>
Introduce mt76_hw_queue data structure in order to support new
chipsets (mt7615) that have a shared hardware queue for all traffic
identifier. mt76_queue will be used to track outstanding packets
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/wireless/mediatek/mt76/debugfs.c | 6 +-
drivers/net/wireless/mediatek/mt76/dma.c | 249 +++++++++---------
drivers/net/wireless/mediatek/mt76/mac80211.c | 4 +-
drivers/net/wireless/mediatek/mt76/mt76.h | 34 ++-
.../wireless/mediatek/mt76/mt7603/beacon.c | 26 +-
.../net/wireless/mediatek/mt76/mt7603/dma.c | 21 +-
.../net/wireless/mediatek/mt76/mt7603/mac.c | 12 +-
.../net/wireless/mediatek/mt76/mt7603/main.c | 2 +-
.../net/wireless/mediatek/mt76/mt76x02_mmio.c | 38 +--
.../wireless/mediatek/mt76/mt76x02_usb_core.c | 3 +-
.../net/wireless/mediatek/mt76/mt76x02_util.c | 2 +-
drivers/net/wireless/mediatek/mt76/tx.c | 50 ++--
drivers/net/wireless/mediatek/mt76/usb.c | 222 +++++++++-------
13 files changed, 363 insertions(+), 306 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
index a5adf22c3ffa..52764732e0c5 100644
--- a/drivers/net/wireless/mediatek/mt76/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -44,13 +44,15 @@ mt76_queues_read(struct seq_file *s, void *data)
for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) {
struct mt76_queue *q = &dev->q_tx[i];
+ struct mt76_hw_queue *hwq = q->hwq;
- if (!q->ndesc)
+ if (!hwq->ndesc)
continue;
seq_printf(s,
"%d: queued=%d head=%d tail=%d swq_queued=%d\n",
- i, q->queued, q->head, q->tail, q->swq_queued);
+ i, hwq->queued, hwq->head, hwq->tail,
+ q->swq_queued);
}
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index d2b7fa2c76d1..d43b9def3b89 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -20,46 +20,50 @@
#define DMA_DUMMY_TXWI ((void *) ~0)
-static int
-mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
- int idx, int n_desc, int bufsize,
- u32 ring_base)
+static struct mt76_hw_queue *
+mt76_dma_alloc_queue(struct mt76_dev *dev, int idx, int n_desc,
+ int bufsize, u32 ring_base)
{
+ struct mt76_hw_queue *hwq;
int size;
int i;
- spin_lock_init(&q->lock);
- INIT_LIST_HEAD(&q->swq);
+ hwq = devm_kzalloc(dev->dev, sizeof(*hwq), GFP_KERNEL);
+ if (!hwq)
+ return ERR_PTR(-ENOMEM);
- q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE;
- q->ndesc = n_desc;
- q->buf_size = bufsize;
- q->hw_idx = idx;
+ spin_lock_init(&hwq->lock);
- size = q->ndesc * sizeof(struct mt76_desc);
- q->desc = dmam_alloc_coherent(dev->dev, size, &q->desc_dma, GFP_KERNEL);
- if (!q->desc)
- return -ENOMEM;
+ hwq->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE;
+ hwq->ndesc = n_desc;
+ hwq->buf_size = bufsize;
+ hwq->hw_idx = idx;
- size = q->ndesc * sizeof(*q->entry);
- q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
- if (!q->entry)
- return -ENOMEM;
+ size = hwq->ndesc * sizeof(struct mt76_desc);
+ hwq->desc = dmam_alloc_coherent(dev->dev, size, &hwq->desc_dma,
+ GFP_KERNEL);
+ if (!hwq->desc)
+ return ERR_PTR(-ENOMEM);
+
+ size = hwq->ndesc * sizeof(*hwq->entry);
+ hwq->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
+ if (!hwq->entry)
+ return ERR_PTR(-ENOMEM);
/* clear descriptors */
- for (i = 0; i < q->ndesc; i++)
- q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+ for (i = 0; i < hwq->ndesc; i++)
+ hwq->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
- iowrite32(q->desc_dma, &q->regs->desc_base);
- iowrite32(0, &q->regs->cpu_idx);
- iowrite32(0, &q->regs->dma_idx);
- iowrite32(q->ndesc, &q->regs->ring_size);
+ iowrite32(hwq->desc_dma, &hwq->regs->desc_base);
+ iowrite32(0, &hwq->regs->cpu_idx);
+ iowrite32(0, &hwq->regs->dma_idx);
+ iowrite32(hwq->ndesc, &hwq->regs->ring_size);
- return 0;
+ return hwq;
}
static int
-mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
+mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_hw_queue *hwq,
struct mt76_queue_buf *buf, int nbufs, u32 info,
struct sk_buff *skb, void *txwi)
{
@@ -68,7 +72,7 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
int i, idx = -1;
if (txwi)
- q->entry[q->head].txwi = DMA_DUMMY_TXWI;
+ hwq->entry[hwq->head].txwi = DMA_DUMMY_TXWI;
for (i = 0; i < nbufs; i += 2, buf += 2) {
u32 buf0 = buf[0].addr, buf1 = 0;
@@ -84,21 +88,21 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
else if (i == nbufs - 2)
ctrl |= MT_DMA_CTL_LAST_SEC1;
- idx = q->head;
- q->head = (q->head + 1) % q->ndesc;
+ idx = hwq->head;
+ hwq->head = (hwq->head + 1) % hwq->ndesc;
- desc = &q->desc[idx];
+ desc = &hwq->desc[idx];
WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
WRITE_ONCE(desc->info, cpu_to_le32(info));
WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
- q->queued++;
+ hwq->queued++;
}
- q->entry[idx].txwi = txwi;
- q->entry[idx].skb = skb;
+ hwq->entry[idx].txwi = txwi;
+ hwq->entry[idx].skb = skb;
return idx;
}
@@ -107,12 +111,13 @@ static void
mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
struct mt76_queue_entry *prev_e)
{
- struct mt76_queue_entry *e = &q->entry[idx];
- __le32 __ctrl = READ_ONCE(q->desc[idx].ctrl);
+ struct mt76_hw_queue *hwq = q->hwq;
+ struct mt76_queue_entry *e = &hwq->entry[idx];
+ __le32 __ctrl = READ_ONCE(hwq->desc[idx].ctrl);
u32 ctrl = le32_to_cpu(__ctrl);
if (!e->txwi || !e->skb) {
- __le32 addr = READ_ONCE(q->desc[idx].buf0);
+ __le32 addr = READ_ONCE(hwq->desc[idx].buf0);
u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
@@ -120,7 +125,7 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
}
if (!(ctrl & MT_DMA_CTL_LAST_SEC0)) {
- __le32 addr = READ_ONCE(q->desc[idx].buf1);
+ __le32 addr = READ_ONCE(hwq->desc[idx].buf1);
u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN1, ctrl);
dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
@@ -135,42 +140,43 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
}
static void
-mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
+mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_hw_queue *hwq)
{
- q->head = ioread32(&q->regs->dma_idx);
- q->tail = q->head;
- iowrite32(q->head, &q->regs->cpu_idx);
+ hwq->head = ioread32(&hwq->regs->dma_idx);
+ hwq->tail = hwq->head;
+ iowrite32(hwq->head, &hwq->regs->cpu_idx);
}
static void
mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
{
struct mt76_queue *q = &dev->q_tx[qid];
+ struct mt76_hw_queue *hwq = q->hwq;
struct mt76_queue_entry entry;
bool wake = false;
int last;
- if (!q->ndesc)
+ if (!hwq->ndesc)
return;
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
if (flush)
last = -1;
else
- last = ioread32(&q->regs->dma_idx);
+ last = ioread32(&hwq->regs->dma_idx);
- while (q->queued && q->tail != last) {
- mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
+ while (hwq->queued && hwq->tail != last) {
+ mt76_dma_tx_cleanup_idx(dev, q, hwq->tail, &entry);
if (entry.schedule)
- q->swq_queued--;
+ dev->q_tx[entry.qid].swq_queued--;
- q->tail = (q->tail + 1) % q->ndesc;
- q->queued--;
+ hwq->tail = (hwq->tail + 1) % hwq->ndesc;
+ hwq->queued--;
if (entry.skb) {
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
dev->drv->tx_complete_skb(dev, q, &entry, flush);
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
}
if (entry.txwi) {
@@ -178,35 +184,35 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
wake = !flush;
}
- if (!flush && q->tail == last)
- last = ioread32(&q->regs->dma_idx);
+ if (!flush && hwq->tail == last)
+ last = ioread32(&hwq->regs->dma_idx);
}
if (!flush)
mt76_txq_schedule(dev, q);
else
- mt76_dma_sync_idx(dev, q);
+ mt76_dma_sync_idx(dev, hwq);
- wake = wake && qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
+ wake = wake && qid < IEEE80211_NUM_ACS && hwq->queued < hwq->ndesc - 8;
- if (!q->queued)
+ if (!hwq->queued)
wake_up(&dev->tx_wait);
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
if (wake)
ieee80211_wake_queue(dev->hw, qid);
}
static void *
-mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- int *len, u32 *info, bool *more)
+mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_hw_queue *hwq,
+ int idx, int *len, u32 *info, bool *more)
{
- struct mt76_queue_entry *e = &q->entry[idx];
- struct mt76_desc *desc = &q->desc[idx];
+ struct mt76_queue_entry *e = &hwq->entry[idx];
+ struct mt76_desc *desc = &hwq->desc[idx];
dma_addr_t buf_addr;
void *buf = e->buf;
- int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
+ int buf_len = SKB_WITH_OVERHEAD(hwq->buf_size);
buf_addr = le32_to_cpu(READ_ONCE(desc->buf0));
if (len) {
@@ -225,28 +231,29 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
}
static void *
-mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
- int *len, u32 *info, bool *more)
+mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_hw_queue *hwq,
+ bool flush, int *len, u32 *info, bool *more)
{
- int idx = q->tail;
+ int idx = hwq->tail;
*more = false;
- if (!q->queued)
+ if (!hwq->queued)
return NULL;
- if (!flush && !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
+ if (!flush && !(hwq->desc[idx].ctrl &
+ cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
return NULL;
- q->tail = (q->tail + 1) % q->ndesc;
- q->queued--;
+ hwq->tail = (hwq->tail + 1) % hwq->ndesc;
+ hwq->queued--;
- return mt76_dma_get_buf(dev, q, idx, len, info, more);
+ return mt76_dma_get_buf(dev, hwq, idx, len, info, more);
}
static void
-mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
+mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_hw_queue *hwq)
{
- iowrite32(q->head, &q->regs->cpu_idx);
+ iowrite32(hwq->head, &hwq->regs->cpu_idx);
}
static int
@@ -254,6 +261,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
struct sk_buff *skb, u32 tx_info)
{
struct mt76_queue *q = &dev->q_tx[qid];
+ struct mt76_hw_queue *hwq = q->hwq;
struct mt76_queue_buf buf;
dma_addr_t addr;
@@ -265,10 +273,10 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
buf.addr = addr;
buf.len = skb->len;
- spin_lock_bh(&q->lock);
- mt76_dma_add_buf(dev, q, &buf, 1, tx_info, skb, NULL);
- mt76_dma_kick_queue(dev, q);
- spin_unlock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
+ mt76_dma_add_buf(dev, hwq, &buf, 1, tx_info, skb, NULL);
+ mt76_dma_kick_queue(dev, hwq);
+ spin_unlock_bh(&hwq->lock);
return 0;
}
@@ -277,6 +285,7 @@ int mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_sta *sta)
{
+ struct mt76_hw_queue *hwq = q->hwq;
struct mt76_queue_entry e;
struct mt76_txwi_cache *t;
struct mt76_queue_buf buf[32];
@@ -328,10 +337,10 @@ int mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
buf[n++].len = iter->len;
}
- if (q->queued + (n + 1) / 2 >= q->ndesc - 1)
+ if (hwq->queued + (n + 1) / 2 >= hwq->ndesc - 1)
goto unmap;
- return mt76_dma_add_buf(dev, q, buf, n, tx_info, skb, t);
+ return mt76_dma_add_buf(dev, hwq, buf, n, tx_info, skb, t);
unmap:
ret = -ENOMEM;
@@ -349,21 +358,21 @@ int mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
EXPORT_SYMBOL_GPL(mt76_dma_tx_queue_skb);
static int
-mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_hw_queue *hwq)
{
dma_addr_t addr;
void *buf;
int frames = 0;
- int len = SKB_WITH_OVERHEAD(q->buf_size);
- int offset = q->buf_offset;
+ int len = SKB_WITH_OVERHEAD(hwq->buf_size);
+ int offset = hwq->buf_offset;
int idx;
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
- while (q->queued < q->ndesc - 1) {
+ while (hwq->queued < hwq->ndesc - 1) {
struct mt76_queue_buf qbuf;
- buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
+ buf = page_frag_alloc(&hwq->rx_page, hwq->buf_size, GFP_ATOMIC);
if (!buf)
break;
@@ -375,55 +384,55 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
qbuf.addr = addr + offset;
qbuf.len = len - offset;
- idx = mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL);
+ idx = mt76_dma_add_buf(dev, hwq, &qbuf, 1, 0, buf, NULL);
frames++;
}
if (frames)
- mt76_dma_kick_queue(dev, q);
+ mt76_dma_kick_queue(dev, hwq);
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
return frames;
}
static void
-mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_hw_queue *hwq)
{
struct page *page;
void *buf;
bool more;
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
do {
- buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more);
+ buf = mt76_dma_dequeue(dev, hwq, true, NULL, NULL, &more);
if (!buf)
break;
skb_free_frag(buf);
} while (1);
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
- if (!q->rx_page.va)
+ if (!hwq->rx_page.va)
return;
- page = virt_to_page(q->rx_page.va);
- __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
- memset(&q->rx_page, 0, sizeof(q->rx_page));
+ page = virt_to_page(hwq->rx_page.va);
+ __page_frag_cache_drain(page, hwq->rx_page.pagecnt_bias);
+ memset(&hwq->rx_page, 0, sizeof(hwq->rx_page));
}
static void
mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
{
- struct mt76_queue *q = &dev->q_rx[qid];
+ struct mt76_hw_queue *hwq = dev->q_rx[qid].hwq;
int i;
- for (i = 0; i < q->ndesc; i++)
- q->desc[i].ctrl &= ~cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+ for (i = 0; i < hwq->ndesc; i++)
+ hwq->desc[i].ctrl &= ~cpu_to_le32(MT_DMA_CTL_DMA_DONE);
- mt76_dma_rx_cleanup(dev, q);
- mt76_dma_sync_idx(dev, q);
- mt76_dma_rx_fill(dev, q);
+ mt76_dma_rx_cleanup(dev, hwq);
+ mt76_dma_sync_idx(dev, hwq);
+ mt76_dma_rx_fill(dev, hwq);
}
static void
@@ -432,22 +441,24 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
{
struct page *page = virt_to_head_page(data);
int offset = data - page_address(page);
- struct sk_buff *skb = q->rx_head;
+ struct mt76_hw_queue *hwq = q->hwq;
+ struct sk_buff *skb = hwq->rx_head;
- offset += q->buf_offset;
+ offset += hwq->buf_offset;
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, len,
- q->buf_size);
+ hwq->buf_size);
if (more)
return;
- q->rx_head = NULL;
+ hwq->rx_head = NULL;
dev->drv->rx_skb(dev, q - dev->q_rx, skb);
}
static int
mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
{
+ struct mt76_hw_queue *hwq = q->hwq;
int len, data_len, done = 0;
struct sk_buff *skb;
unsigned char *data;
@@ -456,34 +467,34 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
while (done < budget) {
u32 info;
- data = mt76_dma_dequeue(dev, q, false, &len, &info, &more);
+ data = mt76_dma_dequeue(dev, hwq, false, &len, &info, &more);
if (!data)
break;
- if (q->rx_head)
- data_len = q->buf_size;
+ if (hwq->rx_head)
+ data_len = hwq->buf_size;
else
- data_len = SKB_WITH_OVERHEAD(q->buf_size);
+ data_len = SKB_WITH_OVERHEAD(hwq->buf_size);
- if (data_len < len + q->buf_offset) {
- dev_kfree_skb(q->rx_head);
- q->rx_head = NULL;
+ if (data_len < len + hwq->buf_offset) {
+ dev_kfree_skb(hwq->rx_head);
+ hwq->rx_head = NULL;
skb_free_frag(data);
continue;
}
- if (q->rx_head) {
+ if (hwq->rx_head) {
mt76_add_fragment(dev, q, data, len, more);
continue;
}
- skb = build_skb(data, q->buf_size);
+ skb = build_skb(data, hwq->buf_size);
if (!skb) {
skb_free_frag(data);
continue;
}
- skb_reserve(skb, q->buf_offset);
+ skb_reserve(skb, hwq->buf_offset);
if (q == &dev->q_rx[MT_RXQ_MCU]) {
u32 *rxfce = (u32 *) skb->cb;
@@ -494,14 +505,14 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
done++;
if (more) {
- q->rx_head = skb;
+ hwq->rx_head = skb;
continue;
}
dev->drv->rx_skb(dev, q - dev->q_rx, skb);
}
- mt76_dma_rx_fill(dev, q);
+ mt76_dma_rx_fill(dev, hwq);
return done;
}
@@ -542,7 +553,7 @@ mt76_dma_init(struct mt76_dev *dev)
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
netif_napi_add(&dev->napi_dev, &dev->napi[i], mt76_dma_rx_poll,
64);
- mt76_dma_rx_fill(dev, &dev->q_rx[i]);
+ mt76_dma_rx_fill(dev, dev->q_rx[i].hwq);
skb_queue_head_init(&dev->rx_skb[i]);
napi_enable(&dev->napi[i]);
}
@@ -575,7 +586,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
netif_napi_del(&dev->napi[i]);
- mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
+ mt76_dma_rx_cleanup(dev, dev->q_rx[i].hwq);
}
}
EXPORT_SYMBOL_GPL(mt76_dma_cleanup);
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index a033745adb2f..b267abcb47f6 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -386,10 +386,12 @@ EXPORT_SYMBOL_GPL(mt76_rx);
static bool mt76_has_tx_pending(struct mt76_dev *dev)
{
+ struct mt76_hw_queue *hwq;
int i;
for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) {
- if (dev->q_tx[i].queued)
+ hwq = dev->q_tx[i].hwq;
+ if (hwq && hwq->queued)
return true;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 5a87bb03cf05..8d97c575b967 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -100,6 +100,7 @@ struct mt76_queue_entry {
struct mt76_txwi_cache *txwi;
struct mt76u_buf ubuf;
};
+ enum mt76_txq_id qid;
bool schedule;
};
@@ -110,30 +111,37 @@ struct mt76_queue_regs {
u32 dma_idx;
} __packed __aligned(4);
-struct mt76_queue {
+struct mt76_hw_queue {
struct mt76_queue_regs __iomem *regs;
- spinlock_t lock;
struct mt76_queue_entry *entry;
struct mt76_desc *desc;
+ spinlock_t lock;
- struct list_head swq;
- int swq_queued;
+ int queued;
+ int ndesc;
u16 first;
u16 head;
u16 tail;
- int ndesc;
- int queued;
- int buf_size;
+ dma_addr_t desc_dma;
+
+ int buf_size;
u8 buf_offset;
+
u8 hw_idx;
- dma_addr_t desc_dma;
- struct sk_buff *rx_head;
struct page_frag_cache rx_page;
spinlock_t rx_page_lock;
+ struct sk_buff *rx_head;
+};
+
+struct mt76_queue {
+ struct mt76_hw_queue *hwq;
+
+ struct list_head swq;
+ int swq_queued;
};
struct mt76_mcu_ops {
@@ -148,9 +156,9 @@ struct mt76_mcu_ops {
struct mt76_queue_ops {
int (*init)(struct mt76_dev *dev);
- int (*alloc)(struct mt76_dev *dev, struct mt76_queue *q,
- int idx, int n_desc, int bufsize,
- u32 ring_base);
+ struct mt76_hw_queue *(*alloc)(struct mt76_dev *dev,
+ int idx, int n_desc, int bufsize,
+ u32 ring_base);
int (*add_buf)(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_buf *buf, int nbufs, u32 info,
@@ -171,7 +179,7 @@ struct mt76_queue_ops {
void (*tx_cleanup)(struct mt76_dev *dev, enum mt76_txq_id qid,
bool flush);
- void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+ void (*kick)(struct mt76_dev *dev, struct mt76_hw_queue *hwq);
};
enum mt76_wcid_flags {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
index afcd86f735b4..438bb3cd55a5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
@@ -30,7 +30,7 @@ mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY |
FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, mvif->sta.wcid.idx) |
FIELD_PREP(MT_DMA_FQCR0_TARGET_QID,
- dev->mt76.q_tx[MT_TXQ_CAB].hw_idx) |
+ dev->mt76.q_tx[MT_TXQ_CAB].hwq->hw_idx) |
FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, 3) |
FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, 8));
@@ -68,7 +68,7 @@ mt7603_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
void mt7603_pre_tbtt_tasklet(unsigned long arg)
{
struct mt7603_dev *dev = (struct mt7603_dev *)arg;
- struct mt76_queue *q;
+ struct mt76_hw_queue *hwq;
struct beacon_bc_data data = {};
struct sk_buff *skb;
int i, nframes;
@@ -76,13 +76,13 @@ void mt7603_pre_tbtt_tasklet(unsigned long arg)
data.dev = dev;
__skb_queue_head_init(&data.q);
- q = &dev->mt76.q_tx[MT_TXQ_BEACON];
- spin_lock_bh(&q->lock);
+ hwq = dev->mt76.q_tx[MT_TXQ_BEACON].hwq;
+ spin_lock_bh(&hwq->lock);
ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7603_update_beacon_iter, dev);
- mt76_queue_kick(dev, q);
- spin_unlock_bh(&q->lock);
+ mt76_queue_kick(dev, hwq);
+ spin_unlock_bh(&hwq->lock);
/* Flush all previous CAB queue packets */
mt76_wr(dev, MT_WF_ARB_CAB_FLUSH, GENMASK(30, 16) | BIT(0));
@@ -93,7 +93,7 @@ void mt7603_pre_tbtt_tasklet(unsigned long arg)
if (dev->mt76.csa_complete)
goto out;
- q = &dev->mt76.q_tx[MT_TXQ_CAB];
+ hwq = dev->mt76.q_tx[MT_TXQ_CAB].hwq;
do {
nframes = skb_queue_len(&data.q);
ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
@@ -112,17 +112,17 @@ void mt7603_pre_tbtt_tasklet(unsigned long arg)
mt76_skb_set_moredata(data.tail[i], false);
}
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
while ((skb = __skb_dequeue(&data.q)) != NULL) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;
- mt76_dma_tx_queue_skb(&dev->mt76, q, skb, &mvif->sta.wcid,
- NULL);
+ mt76_dma_tx_queue_skb(&dev->mt76, &dev->mt76.q_tx[MT_TXQ_CAB],
+ skb, &mvif->sta.wcid, NULL);
}
- mt76_queue_kick(dev, q);
- spin_unlock_bh(&q->lock);
+ mt76_queue_kick(dev, hwq);
+ spin_unlock_bh(&hwq->lock);
for (i = 0; i < ARRAY_SIZE(data.count); i++)
mt76_wr(dev, MT_WF_ARB_CAB_COUNT_B0_REG(i),
@@ -135,7 +135,7 @@ void mt7603_pre_tbtt_tasklet(unsigned long arg)
out:
mt76_queue_tx_cleanup(dev, MT_TXQ_BEACON, false);
- if (dev->mt76.q_tx[MT_TXQ_BEACON].queued >
+ if (dev->mt76.q_tx[MT_TXQ_BEACON].hwq->queued >
__sw_hweight8(dev->beacon_mask))
dev->beacon_check++;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
index 5067c49142f7..d0bba87a7d13 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
@@ -8,12 +8,14 @@ static int
mt7603_init_tx_queue(struct mt7603_dev *dev, struct mt76_queue *q,
int idx, int n_desc)
{
- int err;
+ struct mt76_hw_queue *hwq;
- err = mt76_queue_alloc(dev, q, idx, n_desc, 0,
- MT_TX_RING_BASE);
- if (err < 0)
- return err;
+ hwq = mt76_queue_alloc(dev, idx, n_desc, 0, MT_TX_RING_BASE);
+ if (IS_ERR(hwq))
+ return PTR_ERR(hwq);
+
+ INIT_LIST_HEAD(&q->swq);
+ q->hwq = hwq;
mt7603_irq_enable(dev, MT_INT_TX_DONE(idx));
@@ -103,13 +105,14 @@ static int
mt7603_init_rx_queue(struct mt7603_dev *dev, struct mt76_queue *q,
int idx, int n_desc, int bufsize)
{
- int err;
+ struct mt76_hw_queue *hwq;
- err = mt76_queue_alloc(dev, q, idx, n_desc, bufsize,
+ hwq = mt76_queue_alloc(dev, idx, n_desc, bufsize,
MT_RX_RING_BASE);
- if (err < 0)
- return err;
+ if (IS_ERR(hwq))
+ return PTR_ERR(hwq);
+ q->hwq = hwq;
mt7603_irq_enable(dev, MT_INT_RX_DONE(idx));
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
index 0a0115861b51..35e4f202580e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
@@ -860,7 +860,7 @@ mt7603_mac_write_txwi(struct mt7603_dev *dev, __le32 *txwi,
frame_subtype = (fc & IEEE80211_FCTL_STYPE) >> 4;
val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) |
- FIELD_PREP(MT_TXD0_Q_IDX, q->hw_idx);
+ FIELD_PREP(MT_TXD0_Q_IDX, q->hwq->hw_idx);
txwi[0] = cpu_to_le32(val);
val = MT_TXD1_LONG_FORMAT |
@@ -1405,22 +1405,22 @@ static bool mt7603_tx_dma_busy(struct mt7603_dev *dev)
static bool mt7603_tx_hang(struct mt7603_dev *dev)
{
- struct mt76_queue *q;
+ struct mt76_hw_queue *hwq;
u32 dma_idx, prev_dma_idx;
int i;
for (i = 0; i < 4; i++) {
- q = &dev->mt76.q_tx[i];
+ hwq = dev->mt76.q_tx[i].hwq;
- if (!q->queued)
+ if (!hwq->queued)
continue;
prev_dma_idx = dev->tx_dma_idx[i];
- dma_idx = ioread32(&q->regs->dma_idx);
+ dma_idx = ioread32(&hwq->regs->dma_idx);
dev->tx_dma_idx[i] = dma_idx;
if (dma_idx == prev_dma_idx &&
- dma_idx != ioread32(&q->regs->cpu_idx))
+ dma_idx != ioread32(&hwq->regs->cpu_idx))
break;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c
index b10775ed92e6..124a9bd1d4a9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c
@@ -476,7 +476,7 @@ mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
u16 cw_max = (1 << 10) - 1;
u32 val;
- queue = dev->mt76.q_tx[queue].hw_idx;
+ queue = dev->mt76.q_tx[queue].hwq->hw_idx;
if (params->cw_min)
cw_min = params->cw_min;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
index 1a7926de1dec..5fecfc0062bd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
@@ -139,7 +139,7 @@ static void mt76x02_pre_tbtt_tasklet(unsigned long arg)
mt76_skb_set_moredata(data.tail[i], false);
}
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&q->hwq->lock);
while ((skb = __skb_dequeue(&data.q)) != NULL) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
@@ -148,19 +148,21 @@ static void mt76x02_pre_tbtt_tasklet(unsigned long arg)
mt76_dma_tx_queue_skb(&dev->mt76, q, skb, &mvif->group_wcid,
NULL);
}
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&q->hwq->lock);
}
static int
mt76x02_init_tx_queue(struct mt76x02_dev *dev, struct mt76_queue *q,
int idx, int n_desc)
{
- int err;
+ struct mt76_hw_queue *hwq;
- err = mt76_queue_alloc(dev, q, idx, n_desc, 0,
- MT_TX_RING_BASE);
- if (err < 0)
- return err;
+ hwq = mt76_queue_alloc(dev, idx, n_desc, 0, MT_TX_RING_BASE);
+ if (IS_ERR(hwq))
+ return PTR_ERR(hwq);
+
+ INIT_LIST_HEAD(&q->swq);
+ q->hwq = hwq;
mt76x02_irq_enable(dev, MT_INT_TX_DONE(idx));
@@ -171,13 +173,14 @@ static int
mt76x02_init_rx_queue(struct mt76x02_dev *dev, struct mt76_queue *q,
int idx, int n_desc, int bufsize)
{
- int err;
+ struct mt76_hw_queue *hwq;
- err = mt76_queue_alloc(dev, q, idx, n_desc, bufsize,
+ hwq = mt76_queue_alloc(dev, idx, n_desc, bufsize,
MT_RX_RING_BASE);
- if (err < 0)
- return err;
+ if (IS_ERR(hwq))
+ return PTR_ERR(hwq);
+ q->hwq = hwq;
mt76x02_irq_enable(dev, MT_INT_RX_DONE(idx));
return 0;
@@ -255,12 +258,13 @@ int mt76x02_dma_init(struct mt76x02_dev *dev)
return ret;
q = &dev->mt76.q_rx[MT_RXQ_MAIN];
- q->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x02_rxwi);
ret = mt76x02_init_rx_queue(dev, q, 0, MT76X02_RX_RING_SIZE,
MT_RX_BUF_SIZE);
if (ret)
return ret;
+ q->hwq->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x02_rxwi);
+
return mt76_init_queues(dev);
}
EXPORT_SYMBOL_GPL(mt76x02_dma_init);
@@ -312,7 +316,7 @@ irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance)
if (dev->mt76.csa_complete)
mt76_csa_finish(&dev->mt76);
else
- mt76_queue_kick(dev, &dev->mt76.q_tx[MT_TXQ_PSD]);
+ mt76_queue_kick(dev, dev->mt76.q_tx[MT_TXQ_PSD].hwq);
}
if (intr & MT_INT_TX_STAT) {
@@ -380,17 +384,17 @@ EXPORT_SYMBOL_GPL(mt76x02_mac_start);
static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
{
u32 dma_idx, prev_dma_idx;
- struct mt76_queue *q;
+ struct mt76_hw_queue *hwq;
int i;
for (i = 0; i < 4; i++) {
- q = &dev->mt76.q_tx[i];
+ hwq = dev->mt76.q_tx[i].hwq;
- if (!q->queued)
+ if (!hwq->queued)
continue;
prev_dma_idx = dev->mt76.tx_dma_idx[i];
- dma_idx = ioread32(&q->regs->dma_idx);
+ dma_idx = ioread32(&hwq->regs->dma_idx);
dev->mt76.tx_dma_idx[i] = dma_idx;
if (prev_dma_idx == dma_idx)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
index 43f07461c8d3..d8fc7b29e85f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
@@ -91,7 +91,8 @@ int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data,
pid = mt76_tx_status_skb_add(mdev, wcid, skb);
txwi->pktid = pid;
- if (pid >= MT_PACKET_ID_FIRST || q2ep(q->hw_idx) == MT_EP_OUT_HCCA)
+ if (pid >= MT_PACKET_ID_FIRST ||
+ q2ep(q->hwq->hw_idx) == MT_EP_OUT_HCCA)
qsel = MT_QSEL_MGMT;
else
qsel = MT_QSEL_EDCA;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
index a48c261b0c63..d7f4f07f0a09 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
@@ -459,7 +459,7 @@ int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u8 cw_min = 5, cw_max = 10, qid;
u32 val;
- qid = dev->mt76.q_tx[queue].hw_idx;
+ qid = dev->mt76.q_tx[queue].hwq->hw_idx;
if (params->cw_min)
cw_min = fls(params->cw_min);
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 8babda95d283..698c302b4499 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -258,8 +258,9 @@ mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta,
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct mt76_queue *q;
int qid = skb_get_queue_mapping(skb);
+ struct mt76_hw_queue *hwq;
+ struct mt76_queue *q;
if (WARN_ON(qid >= MT_TXQ_PSD)) {
qid = MT_TXQ_BE;
@@ -284,14 +285,15 @@ mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta,
}
q = &dev->q_tx[qid];
+ hwq = q->hwq;
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
dev->queue_ops->tx_queue_skb(dev, q, skb, wcid, sta);
- dev->queue_ops->kick(dev, q);
+ dev->queue_ops->kick(dev, hwq);
- if (q->queued > q->ndesc - 8)
+ if (hwq->queued > hwq->ndesc - 8)
ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb));
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
}
EXPORT_SYMBOL_GPL(mt76_tx);
@@ -343,10 +345,10 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
{
struct mt76_dev *dev = hw->priv;
struct sk_buff *last_skb = NULL;
- struct mt76_queue *q = &dev->q_tx[MT_TXQ_PSD];
+ struct mt76_hw_queue *hwq = dev->q_tx[MT_TXQ_PSD].hwq;
int i;
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
for (i = 0; tids && nframes; i++, tids >>= 1) {
struct ieee80211_txq *txq = sta->txq[i];
struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
@@ -373,9 +375,9 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
if (last_skb) {
mt76_queue_ps_skb(dev, sta, last_skb, true);
- dev->queue_ops->kick(dev, q);
+ dev->queue_ops->kick(dev, hwq);
}
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
}
EXPORT_SYMBOL_GPL(mt76_release_buffered_frames);
@@ -461,11 +463,12 @@ mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *q,
} while (n_frames < limit);
if (!probe) {
+ q->hwq->entry[idx].qid = q - dev->q_tx;
+ q->hwq->entry[idx].schedule = true;
q->swq_queued++;
- q->entry[idx].schedule = true;
}
- dev->queue_ops->kick(dev, q);
+ dev->queue_ops->kick(dev, q->hwq);
return n_frames;
}
@@ -495,9 +498,9 @@ mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *q)
u8 tid = txq->tid;
mtxq->send_bar = false;
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&q->hwq->lock);
ieee80211_send_bar(vif, sta->addr, tid, agg_ssn);
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&q->hwq->lock);
goto restart;
}
@@ -541,9 +544,9 @@ void mt76_txq_schedule_all(struct mt76_dev *dev)
for (i = 0; i <= MT_TXQ_BK; i++) {
struct mt76_queue *q = &dev->q_tx[i];
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&q->hwq->lock);
mt76_txq_schedule(dev, q);
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&q->hwq->lock);
}
}
EXPORT_SYMBOL_GPL(mt76_txq_schedule_all);
@@ -555,18 +558,20 @@ void mt76_stop_tx_queues(struct mt76_dev *dev, struct ieee80211_sta *sta,
for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
struct ieee80211_txq *txq = sta->txq[i];
+ struct mt76_hw_queue *hwq;
struct mt76_txq *mtxq;
if (!txq)
continue;
mtxq = (struct mt76_txq *)txq->drv_priv;
+ hwq = mtxq->q->hwq;
- spin_lock_bh(&mtxq->q->lock);
+ spin_lock_bh(&hwq->lock);
mtxq->send_bar = mtxq->aggr && send_bar;
if (!list_empty(&mtxq->list))
list_del_init(&mtxq->list);
- spin_unlock_bh(&mtxq->q->lock);
+ spin_unlock_bh(&hwq->lock);
}
}
EXPORT_SYMBOL_GPL(mt76_stop_tx_queues);
@@ -574,18 +579,20 @@ EXPORT_SYMBOL_GPL(mt76_stop_tx_queues);
void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
struct mt76_txq *mtxq = (struct mt76_txq *)txq->drv_priv;
+ struct mt76_hw_queue *hwq = mtxq->q->hwq;
struct mt76_dev *dev = hw->priv;
- spin_lock_bh(&mtxq->q->lock);
+ spin_lock_bh(&hwq->lock);
if (list_empty(&mtxq->list))
list_add_tail(&mtxq->list, &mtxq->q->swq);
mt76_txq_schedule(dev, mtxq->q);
- spin_unlock_bh(&mtxq->q->lock);
+ spin_unlock_bh(&hwq->lock);
}
EXPORT_SYMBOL_GPL(mt76_wake_tx_queue);
void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq)
{
+ struct mt76_hw_queue *hwq;
struct mt76_txq *mtxq;
struct sk_buff *skb;
@@ -593,11 +600,12 @@ void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq)
return;
mtxq = (struct mt76_txq *)txq->drv_priv;
+ hwq = mtxq->q->hwq;
- spin_lock_bh(&mtxq->q->lock);
+ spin_lock_bh(&hwq->lock);
if (!list_empty(&mtxq->list))
list_del_init(&mtxq->list);
- spin_unlock_bh(&mtxq->q->lock);
+ spin_unlock_bh(&hwq->lock);
while ((skb = skb_dequeue(&mtxq->retry_q)) != NULL)
ieee80211_free_txskb(dev->hw, skb);
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index ae6ada370597..b1c6ed34ad41 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -288,17 +288,17 @@ static int
mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf,
int nsgs, int len, int sglen)
{
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
struct urb *urb = buf->urb;
int i;
- spin_lock_bh(&q->rx_page_lock);
+ spin_lock_bh(&hwq->rx_page_lock);
for (i = 0; i < nsgs; i++) {
struct page *page;
void *data;
int offset;
- data = page_frag_alloc(&q->rx_page, len, GFP_ATOMIC);
+ data = page_frag_alloc(&hwq->rx_page, len, GFP_ATOMIC);
if (!data)
break;
@@ -306,7 +306,7 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf,
offset = data - page_address(page);
sg_set_page(&urb->sg[i], page, sglen, offset);
}
- spin_unlock_bh(&q->rx_page_lock);
+ spin_unlock_bh(&hwq->rx_page_lock);
if (i < nsgs) {
int j;
@@ -324,14 +324,14 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf,
}
static int
-mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
+mt76u_refill_rx(struct mt76_dev *dev, struct mt76_hw_queue *hwq,
struct mt76u_buf *buf, int nsgs, gfp_t gfp)
{
if (dev->usb.sg_en) {
- return mt76u_fill_rx_sg(dev, buf, nsgs, q->buf_size,
- SKB_WITH_OVERHEAD(q->buf_size));
+ return mt76u_fill_rx_sg(dev, buf, nsgs, hwq->buf_size,
+ SKB_WITH_OVERHEAD(hwq->buf_size));
} else {
- buf->buf = page_frag_alloc(&q->rx_page, q->buf_size, gfp);
+ buf->buf = page_frag_alloc(&hwq->rx_page, hwq->buf_size, gfp);
return buf->buf ? 0 : -ENOMEM;
}
}
@@ -339,9 +339,9 @@ mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
static int
mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf)
{
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
- buf->len = SKB_WITH_OVERHEAD(q->buf_size);
+ buf->len = SKB_WITH_OVERHEAD(hwq->buf_size);
buf->dev = dev;
buf->urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -358,7 +358,7 @@ mt76u_buf_alloc(struct mt76_dev *dev, struct mt76u_buf *buf)
sg_init_table(buf->urb->sg, MT_SG_MAX_SIZE);
}
- return mt76u_refill_rx(dev, q, buf, MT_SG_MAX_SIZE, GFP_KERNEL);
+ return mt76u_refill_rx(dev, hwq, buf, MT_SG_MAX_SIZE, GFP_KERNEL);
}
static void mt76u_buf_free(struct mt76u_buf *buf)
@@ -407,18 +407,18 @@ mt76u_submit_buf(struct mt76_dev *dev, int dir, int index,
}
static inline struct mt76u_buf
-*mt76u_get_next_rx_entry(struct mt76_queue *q)
+*mt76u_get_next_rx_entry(struct mt76_hw_queue *hwq)
{
struct mt76u_buf *buf = NULL;
unsigned long flags;
- spin_lock_irqsave(&q->lock, flags);
- if (q->queued > 0) {
- buf = &q->entry[q->head].ubuf;
- q->head = (q->head + 1) % q->ndesc;
- q->queued--;
+ spin_lock_irqsave(&hwq->lock, flags);
+ if (hwq->queued > 0) {
+ buf = &hwq->entry[hwq->head].ubuf;
+ hwq->head = (hwq->head + 1) % hwq->ndesc;
+ hwq->queued--;
}
- spin_unlock_irqrestore(&q->lock, flags);
+ spin_unlock_irqrestore(&hwq->lock, flags);
return buf;
}
@@ -441,7 +441,7 @@ static int mt76u_get_rx_entry_len(u8 *data, u32 data_len)
static int
mt76u_process_rx_entry(struct mt76_dev *dev, struct mt76u_buf *buf)
{
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
struct urb *urb = buf->urb;
u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : buf->buf;
int data_len, len, nsgs = 1;
@@ -456,10 +456,10 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct mt76u_buf *buf)
data_len = urb->num_sgs ? urb->sg[0].length : buf->len;
data_len = min_t(int, len, data_len - MT_DMA_HDR_LEN);
- if (MT_DMA_HDR_LEN + data_len > SKB_WITH_OVERHEAD(q->buf_size))
+ if (MT_DMA_HDR_LEN + data_len > SKB_WITH_OVERHEAD(hwq->buf_size))
return 0;
- skb = build_skb(data, q->buf_size);
+ skb = build_skb(data, hwq->buf_size);
if (!skb)
return 0;
@@ -472,7 +472,7 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct mt76u_buf *buf)
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
sg_page(&urb->sg[nsgs]),
urb->sg[nsgs].offset,
- data_len, q->buf_size);
+ data_len, hwq->buf_size);
len -= data_len;
nsgs++;
}
@@ -484,7 +484,7 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct mt76u_buf *buf)
static void mt76u_complete_rx(struct urb *urb)
{
struct mt76_dev *dev = urb->context;
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
unsigned long flags;
trace_rx_urb(dev, urb);
@@ -502,34 +502,35 @@ static void mt76u_complete_rx(struct urb *urb)
break;
}
- spin_lock_irqsave(&q->lock, flags);
- if (WARN_ONCE(q->entry[q->tail].ubuf.urb != urb, "rx urb mismatch"))
+ spin_lock_irqsave(&hwq->lock, flags);
+ if (WARN_ONCE(hwq->entry[hwq->tail].ubuf.urb != urb,
+ "rx urb mismatch"))
goto out;
- q->tail = (q->tail + 1) % q->ndesc;
- q->queued++;
+ hwq->tail = (hwq->tail + 1) % hwq->ndesc;
+ hwq->queued++;
tasklet_schedule(&dev->usb.rx_tasklet);
out:
- spin_unlock_irqrestore(&q->lock, flags);
+ spin_unlock_irqrestore(&hwq->lock, flags);
}
static void mt76u_rx_tasklet(unsigned long data)
{
struct mt76_dev *dev = (struct mt76_dev *)data;
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
struct mt76u_buf *buf;
int err, count;
rcu_read_lock();
while (true) {
- buf = mt76u_get_next_rx_entry(q);
+ buf = mt76u_get_next_rx_entry(hwq);
if (!buf)
break;
count = mt76u_process_rx_entry(dev, buf);
if (count > 0) {
- err = mt76u_refill_rx(dev, q, buf, count,
+ err = mt76u_refill_rx(dev, hwq, buf, count,
GFP_ATOMIC);
if (err < 0)
break;
@@ -545,21 +546,21 @@ static void mt76u_rx_tasklet(unsigned long data)
int mt76u_submit_rx_buffers(struct mt76_dev *dev)
{
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
unsigned long flags;
int i, err = 0;
- spin_lock_irqsave(&q->lock, flags);
- for (i = 0; i < q->ndesc; i++) {
+ spin_lock_irqsave(&hwq->lock, flags);
+ for (i = 0; i < hwq->ndesc; i++) {
err = mt76u_submit_buf(dev, USB_DIR_IN, MT_EP_IN_PKT_RX,
- &q->entry[i].ubuf, GFP_ATOMIC,
+ &hwq->entry[i].ubuf, GFP_ATOMIC,
mt76u_complete_rx, dev);
if (err < 0)
break;
}
- q->head = q->tail = 0;
- q->queued = 0;
- spin_unlock_irqrestore(&q->lock, flags);
+ hwq->head = hwq->tail = 0;
+ hwq->queued = 0;
+ spin_unlock_irqrestore(&hwq->lock, flags);
return err;
}
@@ -568,25 +569,31 @@ EXPORT_SYMBOL_GPL(mt76u_submit_rx_buffers);
static int mt76u_alloc_rx(struct mt76_dev *dev)
{
struct mt76_usb *usb = &dev->usb;
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq;
int i, err;
+ hwq = devm_kzalloc(dev->dev, sizeof(*hwq), GFP_KERNEL);
+ if (!hwq)
+ return -ENOMEM;
+
+ dev->q_rx[MT_RXQ_MAIN].hwq = hwq;
+
usb->mcu.data = devm_kmalloc(dev->dev, MCU_RESP_URB_SIZE, GFP_KERNEL);
if (!usb->mcu.data)
return -ENOMEM;
- spin_lock_init(&q->rx_page_lock);
- spin_lock_init(&q->lock);
- q->entry = devm_kcalloc(dev->dev,
- MT_NUM_RX_ENTRIES, sizeof(*q->entry),
- GFP_KERNEL);
- if (!q->entry)
+ spin_lock_init(&hwq->rx_page_lock);
+ spin_lock_init(&hwq->lock);
+ hwq->entry = devm_kcalloc(dev->dev,
+ MT_NUM_RX_ENTRIES, sizeof(*hwq->entry),
+ GFP_KERNEL);
+ if (!hwq->entry)
return -ENOMEM;
- q->buf_size = dev->usb.sg_en ? MT_RX_BUF_SIZE : PAGE_SIZE;
- q->ndesc = MT_NUM_RX_ENTRIES;
- for (i = 0; i < q->ndesc; i++) {
- err = mt76u_buf_alloc(dev, &q->entry[i].ubuf);
+ hwq->buf_size = dev->usb.sg_en ? MT_RX_BUF_SIZE : PAGE_SIZE;
+ hwq->ndesc = MT_NUM_RX_ENTRIES;
+ for (i = 0; i < hwq->ndesc; i++) {
+ err = mt76u_buf_alloc(dev, &hwq->entry[i].ubuf);
if (err < 0)
return err;
}
@@ -596,37 +603,38 @@ static int mt76u_alloc_rx(struct mt76_dev *dev)
static void mt76u_free_rx(struct mt76_dev *dev)
{
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
struct page *page;
int i;
- for (i = 0; i < q->ndesc; i++)
- mt76u_buf_free(&q->entry[i].ubuf);
+ for (i = 0; i < hwq->ndesc; i++)
+ mt76u_buf_free(&hwq->entry[i].ubuf);
- spin_lock_bh(&q->rx_page_lock);
- if (!q->rx_page.va)
+ spin_lock_bh(&hwq->rx_page_lock);
+ if (!hwq->rx_page.va)
goto out;
- page = virt_to_page(q->rx_page.va);
- __page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
- memset(&q->rx_page, 0, sizeof(q->rx_page));
+ page = virt_to_page(hwq->rx_page.va);
+ __page_frag_cache_drain(page, hwq->rx_page.pagecnt_bias);
+ memset(&hwq->rx_page, 0, sizeof(hwq->rx_page));
out:
- spin_unlock_bh(&q->rx_page_lock);
+ spin_unlock_bh(&hwq->rx_page_lock);
}
static void mt76u_stop_rx(struct mt76_dev *dev)
{
- struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ struct mt76_hw_queue *hwq = dev->q_rx[MT_RXQ_MAIN].hwq;
int i;
- for (i = 0; i < q->ndesc; i++)
- usb_kill_urb(q->entry[i].ubuf.urb);
+ for (i = 0; i < hwq->ndesc; i++)
+ usb_kill_urb(hwq->entry[i].ubuf.urb);
}
static void mt76u_tx_tasklet(unsigned long data)
{
struct mt76_dev *dev = (struct mt76_dev *)data;
struct mt76_queue_entry entry;
+ struct mt76_hw_queue *hwq;
struct mt76u_buf *buf;
struct mt76_queue *q;
bool wake;
@@ -634,32 +642,33 @@ static void mt76u_tx_tasklet(unsigned long data)
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
q = &dev->q_tx[i];
+ hwq = q->hwq;
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
while (true) {
- buf = &q->entry[q->head].ubuf;
- if (!buf->done || !q->queued)
+ buf = &hwq->entry[hwq->head].ubuf;
+ if (!buf->done || !hwq->queued)
break;
- if (q->entry[q->head].schedule) {
- q->entry[q->head].schedule = false;
+ if (hwq->entry[hwq->head].schedule) {
+ hwq->entry[hwq->head].schedule = false;
q->swq_queued--;
}
- entry = q->entry[q->head];
- q->head = (q->head + 1) % q->ndesc;
- q->queued--;
+ entry = hwq->entry[hwq->head];
+ hwq->head = (hwq->head + 1) % hwq->ndesc;
+ hwq->queued--;
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
dev->drv->tx_complete_skb(dev, q, &entry, false);
- spin_lock_bh(&q->lock);
+ spin_lock_bh(&hwq->lock);
}
mt76_txq_schedule(dev, q);
- wake = i < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
- if (!q->queued)
+ wake = i < IEEE80211_NUM_ACS && hwq->queued < hwq->ndesc - 8;
+ if (!hwq->queued)
wake_up(&dev->tx_wait);
- spin_unlock_bh(&q->lock);
+ spin_unlock_bh(&hwq->lock);
if (!test_and_set_bit(MT76_READING_STATS, &dev->state))
ieee80211_queue_delayed_work(dev->hw,
@@ -726,11 +735,12 @@ mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_sta *sta)
{
+ struct mt76_hw_queue *hwq = q->hwq;
struct mt76u_buf *buf;
- u16 idx = q->tail;
+ u16 idx = hwq->tail;
int err;
- if (q->queued == q->ndesc)
+ if (hwq->queued == hwq->ndesc)
return -ENOSPC;
skb->prev = skb->next = NULL;
@@ -738,7 +748,7 @@ mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
if (err < 0)
return err;
- buf = &q->entry[idx].ubuf;
+ buf = &hwq->entry[idx].ubuf;
buf->buf = skb->data;
buf->len = skb->len;
buf->done = false;
@@ -747,23 +757,24 @@ mt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
if (err < 0)
return err;
- mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q2ep(q->hw_idx),
+ mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q2ep(hwq->hw_idx),
buf, mt76u_complete_tx, buf);
- q->tail = (q->tail + 1) % q->ndesc;
- q->entry[idx].skb = skb;
- q->queued++;
+ hwq->tail = (hwq->tail + 1) % hwq->ndesc;
+ hwq->entry[idx].skb = skb;
+ hwq->queued++;
return idx;
}
-static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
+static void mt76u_tx_kick(struct mt76_dev *dev,
+ struct mt76_hw_queue *hwq)
{
struct mt76u_buf *buf;
int err;
- while (q->first != q->tail) {
- buf = &q->entry[q->first].ubuf;
+ while (hwq->first != hwq->tail) {
+ buf = &hwq->entry[hwq->first].ubuf;
trace_submit_urb(dev, buf->urb);
err = usb_submit_urb(buf->urb, GFP_ATOMIC);
@@ -775,31 +786,38 @@ static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
err);
break;
}
- q->first = (q->first + 1) % q->ndesc;
+ hwq->first = (hwq->first + 1) % hwq->ndesc;
}
}
static int mt76u_alloc_tx(struct mt76_dev *dev)
{
+ struct mt76_hw_queue *hwq;
struct mt76u_buf *buf;
struct mt76_queue *q;
int i, j;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ hwq = devm_kzalloc(dev->dev, sizeof(*hwq), GFP_KERNEL);
+ if (!hwq)
+ return -ENOMEM;
+
q = &dev->q_tx[i];
- spin_lock_init(&q->lock);
INIT_LIST_HEAD(&q->swq);
- q->hw_idx = mt76_ac_to_hwq(i);
+ q->hwq = hwq;
- q->entry = devm_kcalloc(dev->dev,
- MT_NUM_TX_ENTRIES, sizeof(*q->entry),
- GFP_KERNEL);
- if (!q->entry)
+ spin_lock_init(&hwq->lock);
+ hwq->hw_idx = mt76_ac_to_hwq(i);
+
+ hwq->entry = devm_kcalloc(dev->dev,
+ MT_NUM_TX_ENTRIES,
+ sizeof(*hwq->entry), GFP_KERNEL);
+ if (!hwq->entry)
return -ENOMEM;
- q->ndesc = MT_NUM_TX_ENTRIES;
- for (j = 0; j < q->ndesc; j++) {
- buf = &q->entry[j].ubuf;
+ hwq->ndesc = MT_NUM_TX_ENTRIES;
+ for (j = 0; j < hwq->ndesc; j++) {
+ buf = &hwq->entry[j].ubuf;
buf->dev = dev;
buf->urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -822,25 +840,25 @@ static int mt76u_alloc_tx(struct mt76_dev *dev)
static void mt76u_free_tx(struct mt76_dev *dev)
{
- struct mt76_queue *q;
+ struct mt76_hw_queue *hwq;
int i, j;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- q = &dev->q_tx[i];
- for (j = 0; j < q->ndesc; j++)
- usb_free_urb(q->entry[j].ubuf.urb);
+ hwq = dev->q_tx[i].hwq;
+ for (j = 0; j < hwq->ndesc; j++)
+ usb_free_urb(hwq->entry[j].ubuf.urb);
}
}
static void mt76u_stop_tx(struct mt76_dev *dev)
{
- struct mt76_queue *q;
+ struct mt76_hw_queue *hwq;
int i, j;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- q = &dev->q_tx[i];
- for (j = 0; j < q->ndesc; j++)
- usb_kill_urb(q->entry[j].ubuf.urb);
+ hwq = dev->q_tx[i].hwq;
+ for (j = 0; j < hwq->ndesc; j++)
+ usb_kill_urb(hwq->entry[j].ubuf.urb);
}
}
--
2.20.1
^ permalink raw reply related
* [RFC 1/2] mt76: rename mt76_queue pointer occurrences from hwq to q
From: Lorenzo Bianconi @ 2019-03-01 9:22 UTC (permalink / raw)
To: nbd; +Cc: linux-wireless, ryder.lee, roychl666, lorenzo.bianconi
In-Reply-To: <cover.1551431791.git.lorenzo@kernel.org>
This is a preliminary patch for the introduction on mt76_hw_queue
structure needed to properly support new chipsets (e.g. mt7615)
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/wireless/mediatek/mt76/mt76.h | 2 +-
drivers/net/wireless/mediatek/mt76/tx.c | 71 +++++++++++------------
2 files changed, 35 insertions(+), 38 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 29409f0871b7..5a87bb03cf05 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -212,7 +212,7 @@ struct mt76_wcid {
struct mt76_txq {
struct list_head list;
- struct mt76_queue *hwq;
+ struct mt76_queue *q;
struct mt76_wcid *wcid;
struct sk_buff_head retry_q;
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 5a349fe3e576..8babda95d283 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -324,7 +324,7 @@ mt76_queue_ps_skb(struct mt76_dev *dev, struct ieee80211_sta *sta,
{
struct mt76_wcid *wcid = (struct mt76_wcid *) sta->drv_priv;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct mt76_queue *hwq = &dev->q_tx[MT_TXQ_PSD];
+ struct mt76_queue *q = &dev->q_tx[MT_TXQ_PSD];
info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
if (last)
@@ -332,7 +332,7 @@ mt76_queue_ps_skb(struct mt76_dev *dev, struct ieee80211_sta *sta,
IEEE80211_TX_CTL_REQ_TX_STATUS;
mt76_skb_set_moredata(skb, !last);
- dev->queue_ops->tx_queue_skb(dev, hwq, skb, wcid, sta);
+ dev->queue_ops->tx_queue_skb(dev, q, skb, wcid, sta);
}
void
@@ -343,10 +343,10 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
{
struct mt76_dev *dev = hw->priv;
struct sk_buff *last_skb = NULL;
- struct mt76_queue *hwq = &dev->q_tx[MT_TXQ_PSD];
+ struct mt76_queue *q = &dev->q_tx[MT_TXQ_PSD];
int i;
- spin_lock_bh(&hwq->lock);
+ spin_lock_bh(&q->lock);
for (i = 0; tids && nframes; i++, tids >>= 1) {
struct ieee80211_txq *txq = sta->txq[i];
struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
@@ -373,14 +373,14 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
if (last_skb) {
mt76_queue_ps_skb(dev, sta, last_skb, true);
- dev->queue_ops->kick(dev, hwq);
+ dev->queue_ops->kick(dev, q);
}
- spin_unlock_bh(&hwq->lock);
+ spin_unlock_bh(&q->lock);
}
EXPORT_SYMBOL_GPL(mt76_release_buffered_frames);
static int
-mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
+mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_txq *mtxq, bool *empty)
{
struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
@@ -417,7 +417,7 @@ mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
if (ampdu)
mt76_check_agg_ssn(mtxq, skb);
- idx = dev->queue_ops->tx_queue_skb(dev, hwq, skb, wcid, txq->sta);
+ idx = dev->queue_ops->tx_queue_skb(dev, q, skb, wcid, txq->sta);
if (idx < 0)
return idx;
@@ -452,7 +452,7 @@ mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
if (cur_ampdu)
mt76_check_agg_ssn(mtxq, skb);
- idx = dev->queue_ops->tx_queue_skb(dev, hwq, skb, wcid,
+ idx = dev->queue_ops->tx_queue_skb(dev, q, skb, wcid,
txq->sta);
if (idx < 0)
return idx;
@@ -461,24 +461,24 @@ mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
} while (n_frames < limit);
if (!probe) {
- hwq->swq_queued++;
- hwq->entry[idx].schedule = true;
+ q->swq_queued++;
+ q->entry[idx].schedule = true;
}
- dev->queue_ops->kick(dev, hwq);
+ dev->queue_ops->kick(dev, q);
return n_frames;
}
static int
-mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *hwq)
+mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *q)
{
struct mt76_txq *mtxq, *mtxq_last;
int len = 0;
restart:
- mtxq_last = list_last_entry(&hwq->swq, struct mt76_txq, list);
- while (!list_empty(&hwq->swq)) {
+ mtxq_last = list_last_entry(&q->swq, struct mt76_txq, list);
+ while (!list_empty(&q->swq)) {
bool empty = false;
int cur;
@@ -486,7 +486,7 @@ mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *hwq)
test_bit(MT76_RESET, &dev->state))
return -EBUSY;
- mtxq = list_first_entry(&hwq->swq, struct mt76_txq, list);
+ mtxq = list_first_entry(&q->swq, struct mt76_txq, list);
if (mtxq->send_bar && mtxq->aggr) {
struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
struct ieee80211_sta *sta = txq->sta;
@@ -495,17 +495,17 @@ mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *hwq)
u8 tid = txq->tid;
mtxq->send_bar = false;
- spin_unlock_bh(&hwq->lock);
+ spin_unlock_bh(&q->lock);
ieee80211_send_bar(vif, sta->addr, tid, agg_ssn);
- spin_lock_bh(&hwq->lock);
+ spin_lock_bh(&q->lock);
goto restart;
}
list_del_init(&mtxq->list);
- cur = mt76_txq_send_burst(dev, hwq, mtxq, &empty);
+ cur = mt76_txq_send_burst(dev, q, mtxq, &empty);
if (!empty)
- list_add_tail(&mtxq->list, &hwq->swq);
+ list_add_tail(&mtxq->list, &q->swq);
if (cur < 0)
return cur;
@@ -519,16 +519,16 @@ mt76_txq_schedule_list(struct mt76_dev *dev, struct mt76_queue *hwq)
return len;
}
-void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq)
+void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *q)
{
int len;
rcu_read_lock();
do {
- if (hwq->swq_queued >= 4 || list_empty(&hwq->swq))
+ if (q->swq_queued >= 4 || list_empty(&q->swq))
break;
- len = mt76_txq_schedule_list(dev, hwq);
+ len = mt76_txq_schedule_list(dev, q);
} while (len > 0);
rcu_read_unlock();
}
@@ -562,45 +562,42 @@ void mt76_stop_tx_queues(struct mt76_dev *dev, struct ieee80211_sta *sta,
mtxq = (struct mt76_txq *)txq->drv_priv;
- spin_lock_bh(&mtxq->hwq->lock);
+ spin_lock_bh(&mtxq->q->lock);
mtxq->send_bar = mtxq->aggr && send_bar;
if (!list_empty(&mtxq->list))
list_del_init(&mtxq->list);
- spin_unlock_bh(&mtxq->hwq->lock);
+ spin_unlock_bh(&mtxq->q->lock);
}
}
EXPORT_SYMBOL_GPL(mt76_stop_tx_queues);
void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
+ struct mt76_txq *mtxq = (struct mt76_txq *)txq->drv_priv;
struct mt76_dev *dev = hw->priv;
- struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
- struct mt76_queue *hwq = mtxq->hwq;
- spin_lock_bh(&hwq->lock);
+ spin_lock_bh(&mtxq->q->lock);
if (list_empty(&mtxq->list))
- list_add_tail(&mtxq->list, &hwq->swq);
- mt76_txq_schedule(dev, hwq);
- spin_unlock_bh(&hwq->lock);
+ list_add_tail(&mtxq->list, &mtxq->q->swq);
+ mt76_txq_schedule(dev, mtxq->q);
+ spin_unlock_bh(&mtxq->q->lock);
}
EXPORT_SYMBOL_GPL(mt76_wake_tx_queue);
void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq)
{
struct mt76_txq *mtxq;
- struct mt76_queue *hwq;
struct sk_buff *skb;
if (!txq)
return;
- mtxq = (struct mt76_txq *) txq->drv_priv;
- hwq = mtxq->hwq;
+ mtxq = (struct mt76_txq *)txq->drv_priv;
- spin_lock_bh(&hwq->lock);
+ spin_lock_bh(&mtxq->q->lock);
if (!list_empty(&mtxq->list))
list_del_init(&mtxq->list);
- spin_unlock_bh(&hwq->lock);
+ spin_unlock_bh(&mtxq->q->lock);
while ((skb = skb_dequeue(&mtxq->retry_q)) != NULL)
ieee80211_free_txskb(dev->hw, skb);
@@ -614,7 +611,7 @@ void mt76_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq)
INIT_LIST_HEAD(&mtxq->list);
skb_queue_head_init(&mtxq->retry_q);
- mtxq->hwq = &dev->q_tx[mt76_txq_get_qid(txq)];
+ mtxq->q = &dev->q_tx[mt76_txq_get_qid(txq)];
}
EXPORT_SYMBOL_GPL(mt76_txq_init);
--
2.20.1
^ permalink raw reply related
* [RFC 0/2] introduce mt76_hw_queue data structure
From: Lorenzo Bianconi @ 2019-03-01 9:22 UTC (permalink / raw)
To: nbd; +Cc: linux-wireless, ryder.lee, roychl666, lorenzo.bianconi
Add mt76_hw_queue data structure since new chipsets (e.g 7615) rely on
a different tx queue enumeration scheme.
This series implements the approach suggested by Felix in
https://patchwork.kernel.org/cover/10816521/
Lorenzo Bianconi (2):
mt76: rename mt76_queue pointer occurrences from hwq to q
mt76: introduce mt76_hw_queue data structure
drivers/net/wireless/mediatek/mt76/debugfs.c | 6 +-
drivers/net/wireless/mediatek/mt76/dma.c | 249 +++++++++---------
drivers/net/wireless/mediatek/mt76/mac80211.c | 4 +-
drivers/net/wireless/mediatek/mt76/mt76.h | 36 ++-
.../wireless/mediatek/mt76/mt7603/beacon.c | 26 +-
.../net/wireless/mediatek/mt76/mt7603/dma.c | 21 +-
.../net/wireless/mediatek/mt76/mt7603/mac.c | 12 +-
.../net/wireless/mediatek/mt76/mt7603/main.c | 2 +-
.../net/wireless/mediatek/mt76/mt76x02_mmio.c | 38 +--
.../wireless/mediatek/mt76/mt76x02_usb_core.c | 3 +-
.../net/wireless/mediatek/mt76/mt76x02_util.c | 2 +-
drivers/net/wireless/mediatek/mt76/tx.c | 79 +++---
drivers/net/wireless/mediatek/mt76/usb.c | 222 +++++++++-------
13 files changed, 377 insertions(+), 323 deletions(-)
--
2.20.1
^ permalink raw reply
* Re: [PATCH RFC] mac80211: Use IFF_ECHO to force delivery of tx_status frames
From: Johannes Berg @ 2019-03-01 8:32 UTC (permalink / raw)
To: Julius Niedworok
Cc: Oliver Hartkopp, linux-wireless, ga58taw, David Hildenbrand, nc,
David S. Miller, Edward Cree, Jiri Pirko, Ido Schimmel,
Petr Machata, Kirill Tkhai, Alexander Duyck, Amritha Nambiar,
Li RongQing, netdev, linux-kernel
In-Reply-To: <AD1F7A49-B3F3-4033-8B0A-D1F8C2C6C834@gmx.net>
> Let us briefly describe our test setup to ensure everyone on this mailing
> list is one the same page.
>
> Our general setup looks like this:
> 1 $ iw wlp1s0 info
> Interface wlp1s0
> ifindex 5
> wdev 0x1
> addr 4c:5e:0c:11:43:ac
> type managed
> wiphy 0
> txpower 30.00 dBm
> 1 $ iw phy phy0 interface add mon0 type monitor
> 1 $ iw phy phy0 interface add mon1 type monitor
>
> When we send (raw) packets on mon0 using packetspammer [1] and listen on
> the _other_ monitor mode interface mon1, we receive frames that were sent
> on the first one:
> 1 $ packetspammer mon0
> 2 $ tcpdump -i mon1 'wlan addr2 13:22:33:44:55:66'
>
> This is due to the fact that frames sent on mon0 are echoed back as TX
> status frames, because REQ_TX_STATUS is always set for frames sent from
> monitor mode interfaces.
Yes, I understand :-)
> But when we replace mon0 with an interface in managed mode (wlp1s0), the
> receipt of frames stops, because in managed mode REQ_TX_STATUS is cleared
> in most frames:
> 1 $ ifup wlp1s0
> 1 $ ping -I wlp1s0 192.168.254.1 # this address is not assigned to any host
> 2 $ tcpdump -i mon1 ‚wlan addr2 4c:5e:0c:11:43:ac‘
Yes, also understand.
> > What you're proposing is to use IFF_ECHO to show frames transmitted
> > through *other* interfaces on the monitor interface.
> >
> > I don’t think the IFF_ECHO semantics really match this.
>
> What we propose is to use IFF_ECHO to force REQ_TX_STATUS being set for all
> frames sent on the interface. But you are right: The goal is that frames
> transmitted through the other interface show up on the monitor interface
> (but only after passing the driver). However, this is exactly how we
> understand the semantics of IFF_ECHO in the kernel documentation.
I disagree.
First of all, IFF_ECHO is only documented/used *inside* the kernel, and
cannot be set by userspace today. It's documented by CAN as such:
Documentation/networking/can.rst:
Local Loopback of Sent Frames
-----------------------------
As described in :ref:`socketcan-local-loopback1` the CAN network
device driver should
support a local loopback functionality similar to the local echo
e.g. of tty devices. In this case the driver flag IFF_ECHO has to be
set to prevent the PF_CAN core from locally echoing sent frames
(aka loopback) as fallback solution::
dev->flags = (IFF_NOARP | IFF_ECHO);
Note that everything here is specific to a single interface.
Also note that it's a signal from the *driver* to the *stack* to not do
the loopback itself, because the driver will do it.
I think in the case of all other sockets/interfaces, the stack will do
the echo anyway, for tcpdump etc. purposes.
The documentation in the uapi just states:
@IFF_ECHO: echo sent packets. Volatile.
and makes no representation about which interface, but I'd argue that
all the flags are specific to a single interface and thus you'd expect
this to also be.
Thus, I don't think this was ever intended for any cross-interface
behaviour, even if it may be on the same physical NIC.
> As far as we know, drivers must return a TX status frame, if REQ_TX_STATUS
> is set, but can do whatever they want, if it is clear.
Not all drivers can and do this, I believe. Some things don't work very
well if they don't do it, but I _think_ you've just been lucky and used
hardware that does in fact support it.
Also note that for some hardware that does support this, there's
sometimes significant overhead - not just the performance overhead of
actually reporting the frames, but sometimes also overhead in how the
hardware is programmed and used, and how TX status is extracted.
> This is no problem for our
> functionality, because we force the delivery of TX status frames by
> permanently setting REQ_TX_STATUS. As long as the semantics of
> REQ_TX_STATUS remains like it is now, the functionality will always be
> as expected from our API.
Sure, for now, for your specific case of ath9k :-)
> We could also achieve the functionality by modifying the drivers but this
> would mean that we had to add this functionality to every driver.
> Moreover, the feature of TX status frames, how it is implemented currently
> for monitor mode interfaces, is part of the mac80211 implementation. The
> decision to force TX status frames for monitor mode interfaces is made in
> the common mac80211 implementation.
I suppose it could be in mac80211 (perhaps debugfs?) too. I just really
don't think IFF_ECHO is the right approach.
johannes
^ permalink raw reply
* Re: Realtek r8822be kernel module does not negotiate 802.11ac connection
From: David R. Bergstein @ 2019-03-01 2:32 UTC (permalink / raw)
To: Tony Chuang
Cc: Pkshih, linux-wireless@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <F7CD281DE3E379468C6D07993EA72F84D17AF74C@RTITMBSVM04.realtek.com.tw>
Tony,
Thanks for your response. Can you advise as to the availability of the
new rtw88 driver? As it appears to be under development, I could not
locate a copy of the code for local compilation.
Sincerely,
David R. Bergstein
On 2/27/19 5:23 AM, Tony Chuang wrote:
>> This message is in regard to a bug I have open on bugs.launchpad.net, 1813372,
>> linked below. This issue, originally identified in an Ubuntu kernel, has been
>> duplicated in the most current mainline kernel, 5.0-rc8, and is in regard to
>> problems attaining a wireless connection at 802.11ac speeds.
>>
>> https://bugs.launchpad.net/bugs/1813372
>>
>> At your earliest convenience, please see the bug report above, and advise if a fix
>> will be available for the r8822be kernel module.
>>
>> Sincerely,
>>
>> David R. Bergstein
>>
> Hi,
>
> If it's possible I will suggest that you can use the new driver "rtw88" being
> reviewed now. rtw88 is better supported by Realtek and has significant
> improvement in contrast to r8822be. And also it is aim to better support
> 802.11ac series ICs, so it may help resolve your problem about 802.11ac
> connection speeds.
> Thanks!
>
> Yan-Hsuan
>
^ permalink raw reply
* Re: [RFC] ath10k: Fix DMA errors related to beacons (CT FW only)
From: Michał Kazior @ 2019-02-28 21:31 UTC (permalink / raw)
To: Ben Greear; +Cc: linux-wireless
In-Reply-To: <c67eeb9a-3444-dd49-58eb-4e633063fd0e@candelatech.com>
On Thu, 28 Feb 2019 at 22:11, Ben Greear <greearb@candelatech.com> wrote:
> On 2/28/19 10:54 AM, Michał Kazior wrote:
> > On Thu, 28 Feb 2019 at 16:59, <greearb@candelatech.com> wrote:
> >>
> >> From: Ben Greear <greearb@candelatech.com>
[...]
> >> diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
> >> index 154dcdabc48a..02a8efa2e783 100644
> >> --- a/drivers/net/wireless/ath/ath10k/mac.c
> >> +++ b/drivers/net/wireless/ath/ath10k/mac.c
> > [...]
> >> @@ -6147,6 +6151,16 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
> >> ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
> >> arvif->vdev_id, ret);
> >>
> >> + if (test_bit(ATH10K_FW_FEATURE_BEACON_TX_CB_CT,
> >> + ar->running_fw->fw_file.fw_features)) {
> >> + int time_left;
> >> +
> >> + time_left = wait_for_completion_timeout(&arvif->beacon_tx_done, (3 * HZ));
> >> + if (!time_left)
> >> + ath10k_warn(ar, "WARNING: failed to wait for beacon tx callback for vdev %i: %d\n",
> >> + arvif->vdev_id, ret);
> >> + }
> >
> > I think this can race against wmi tx credits replenishment and maybe
> > other wmi commands if they get issued (not many of them I suppose
> > though). The ordering would need to be something like this:
> >
> > cpu0 cpu1
> > ath10k_wmi_op_ep_tx_credits
> > ieee80211_iterate_active_interfaces_atomic
> > ath10k_wmi_beacon_send_ref_nowait
> > # preempted
> > ieee80211_do_stop
> > clear sdata running
> > drv_remove_interface
> > wait_for_completion_timeout
> > ath10k_mac_vif_beacon_cleanup
> > free/unmap
> > gen_beacon_dma(bcn->xxx)
> > reinit_completion()
> > ath10k_wmi_cmd_send_nowait()
> >
> > Interestingly, this also shows it has been a possible use-after-free
> > without your patch. This is probably a unlikely scenario because
> > there's a disproportionate amount of code between these flows for this
> > to happen.
>
> Thanks for the thorough review. One thing, the firmware
> cannot guarantee it can send the event (due to potential WMI event buffer
> exhaustion). It would be rare that you got unlucky enough to hit the
> case where FW ran out of msg buffers right as you were deleting an
> interface though.
Right.
> As far as I can tell, my patch at least does not make the use-after-free
> scenario worse, so I'm tempted to ignore that for now.
Correct. Your patch doesn't make it worse.
> > There's also a memory leak because ath10k_mac_vif_beacon_free() exits
> > without freeing arvif->beacon if beacon_state is SENDING, which it
> > will be in in the above race case.
>
> Ok, this could be fixed by definitely freeing the mem in the remove_interface
> logic, and is not new to my patch, right?
It's not new to your patch. It's just something I've noticed in the
codebase while reviewing your patch. Freeing it is going to be tricky
with the current design though.
> > With the per-skb beaconing case (which is dead code by the way it
> > seems..) iommu wouldn't be happy either because we'd hand over an
> > unmapped paddr to ath10k_wmi_cmd_send_nowait(). That's assuming kernel
> > didn't crash due to use-after-free on arvif->beacon (stored in `bcn`
> > on stack) before that >
> > This feels like a candidate for rcu if you wanted to have it fixed
> > neatly. It would fix both use-after-free and the locking conundrum -
> > ath10k_remove_interface() could NULL the pointer, call_rcu() and
> > wait_for_completion() in that call handler, and when its called back
> > unmap/free the beacon. This would require a refactor to how beacon
> > stuff is stored/maintained - a helper structure that would need carry
> > the beacon stuff + rcu_head, and this structure would be allocated and
> > assigned with rcu_assign_pointer() to a pointer in ath10k_vif. But I
> > feel like it's asking for unbound allocations or other bugs,
> > especially since I'm...
>
> This callback only happens at all with CT fw, and I definitely don't want to
> deal with an out-of-tree rcu patch. And, you are not guaranteed to get
> the callback as previously mentioned. So I think rcu might be overkill
> for this?
You'd still need wait_for_completion with a timeout so even if you
don't get the beacon tx completion it'd still (have to) work. However
*it is* an overkill because the arvif->beacon can be simply removed
making the entire problem go away without adding extra logic.
Michał
^ permalink raw reply
* Re: long startup delay ath10k_pci known issue?
From: Johannes Berg @ 2019-02-28 21:23 UTC (permalink / raw)
To: Kalle Valo, Robert White; +Cc: linux-wireless
In-Reply-To: <877edk1f6j.fsf@purkki.adurom.net>
On Thu, 2019-02-28 at 10:37 +0200, Kalle Valo wrote:
>
> > Is this long delay after module and firmware load some expected effect
> > that I need to code around or what?
>
> Usually these 60 second (or it's multiply) delays are caused by kernel
> requesting a firmware image from user space but the corresponding user
> space component is not replying and kernel waits for the reply until it
> timeouts and continues. IIRC there's a kernel config option you can use
> to disable this feature, or you could also try to fix the user space.
Regardless though, fixing this will just make the race condition less
likely, not fix it.
Most wifi NICs these days will load firmware because they need to run
it, and they will typically have to load the firmware *before* they
register their wiphy/netdev. Due to technical considerations (built-in
drivers etc.) the firmware load pretty much has to happen
asynchronously.
As a result, modprobe will return before the netdev exists. Now, in your
case you likely have some sort of firmware problem like Kalle says,
which causes it to take an excessive amount of time, perhaps by calling
out to a userspace helper that doesn't work and thus doesn't answer
negatively?
However, the fact remains that you shouldn't assume that modprobe
returning means anything - the netdev will exists when that happens, and
you should trigger your networking startup on the event that says it
exists now.
johannes
^ permalink raw reply
* Re: [RFC] ath10k: Fix DMA errors related to beacons (CT FW only)
From: Ben Greear @ 2019-02-28 21:11 UTC (permalink / raw)
To: Michał Kazior; +Cc: linux-wireless
In-Reply-To: <CABvG-CUTn3aHAUUvQ6P+BtaWPtrBeeO8b3vfm5w=hB753_+g0g@mail.gmail.com>
On 2/28/19 10:54 AM, Michał Kazior wrote:
> On Thu, 28 Feb 2019 at 16:59, <greearb@candelatech.com> wrote:
>>
>> From: Ben Greear <greearb@candelatech.com>
>>
>> I often saw the ath10k-ct wave-1 firmware spit DMA errors and
>> hang the entire system, requiring a hard power-cycle to revoer.
>>
>> It appears the issue is that there is no beacon-tx callback in
>> stock firmware, so the driver can delete the beacon DMA buffer
>> while firmware is still trying to access it.
>>
>> So, wave-1 ath10k-ct firmware now sends a beacon-tx-complete
>> wmi message and that allows the driver to safely know when it
>> can clean up the buffer.
>>
>> Signed-off-by: Ben Greear <greearb@candelatech.com>
>> ---
>>
>> NOTE: This will not apply or work in upstream kernels since the
>> rest of the CT fw support will not be accepted. But, I'd appreciate
>> any technical feedback on this in case I missed any corner cases
>> on locking or similar.
>
> For the record this patch seems to be based on code with "ath10k: Free
> beacon buf later in vdev teardown" included.
>
>
> [...]
>> diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
>> index 154dcdabc48a..02a8efa2e783 100644
>> --- a/drivers/net/wireless/ath/ath10k/mac.c
>> +++ b/drivers/net/wireless/ath/ath10k/mac.c
> [...]
>> @@ -6147,6 +6151,16 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
>> ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
>> arvif->vdev_id, ret);
>>
>> + if (test_bit(ATH10K_FW_FEATURE_BEACON_TX_CB_CT,
>> + ar->running_fw->fw_file.fw_features)) {
>> + int time_left;
>> +
>> + time_left = wait_for_completion_timeout(&arvif->beacon_tx_done, (3 * HZ));
>> + if (!time_left)
>> + ath10k_warn(ar, "WARNING: failed to wait for beacon tx callback for vdev %i: %d\n",
>> + arvif->vdev_id, ret);
>> + }
>
> I think this can race against wmi tx credits replenishment and maybe
> other wmi commands if they get issued (not many of them I suppose
> though). The ordering would need to be something like this:
>
> cpu0 cpu1
> ath10k_wmi_op_ep_tx_credits
> ieee80211_iterate_active_interfaces_atomic
> ath10k_wmi_beacon_send_ref_nowait
> # preempted
> ieee80211_do_stop
> clear sdata running
> drv_remove_interface
> wait_for_completion_timeout
> ath10k_mac_vif_beacon_cleanup
> free/unmap
> gen_beacon_dma(bcn->xxx)
> reinit_completion()
> ath10k_wmi_cmd_send_nowait()
>
> Interestingly, this also shows it has been a possible use-after-free
> without your patch. This is probably a unlikely scenario because
> there's a disproportionate amount of code between these flows for this
> to happen.
Thanks for the thorough review. One thing, the firmware
cannot guarantee it can send the event (due to potential WMI event buffer
exhaustion). It would be rare that you got unlucky enough to hit the
case where FW ran out of msg buffers right as you were deleting an
interface though.
As far as I can tell, my patch at least does not make the use-after-free
scenario worse, so I'm tempted to ignore that for now.
>
> There's also a memory leak because ath10k_mac_vif_beacon_free() exits
> without freeing arvif->beacon if beacon_state is SENDING, which it
> will be in in the above race case.
Ok, this could be fixed by definitely freeing the mem in the remove_interface
logic, and is not new to my patch, right?
>
> With the per-skb beaconing case (which is dead code by the way it
> seems..) iommu wouldn't be happy either because we'd hand over an
> unmapped paddr to ath10k_wmi_cmd_send_nowait(). That's assuming kernel
> didn't crash due to use-after-free on arvif->beacon (stored in `bcn`
> on stack) before that >
> This feels like a candidate for rcu if you wanted to have it fixed
> neatly. It would fix both use-after-free and the locking conundrum -
> ath10k_remove_interface() could NULL the pointer, call_rcu() and
> wait_for_completion() in that call handler, and when its called back
> unmap/free the beacon. This would require a refactor to how beacon
> stuff is stored/maintained - a helper structure that would need carry
> the beacon stuff + rcu_head, and this structure would be allocated and
> assigned with rcu_assign_pointer() to a pointer in ath10k_vif. But I
> feel like it's asking for unbound allocations or other bugs,
> especially since I'm...
This callback only happens at all with CT fw, and I definitely don't want to
deal with an out-of-tree rcu patch. And, you are not guaranteed to get
the callback as previously mentioned. So I think rcu might be overkill
for this?
>
> Not sure if it makes any sense. I would probably just rip out the
> arvif->beacon code completely instead and keep arvif->beacon_buf only
> (memcpy on swba and immediatelly free the skbuff). No point in fixing
> dead code, is there?
>
> Since you have beacon tx completion you can consider preventing beacon
> corruption, similar to how disabled vsync causes frame tearing, by
> avoiding memcpy() if completion for previous one didn't come. Not sure
> how relevant that is though because if fw/hw are having hard time
> transmitting a beacon then the RF condition must be really harsh and
> unusably bad anyway.
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: iw 5.0.1 broken when compiled with lto
From: Johannes Berg @ 2019-02-28 20:30 UTC (permalink / raw)
To: Hauke Mehrtens; +Cc: linux-wireless, stijn, fercerpav, Mantas Pucka
In-Reply-To: <b398e5e4-45f7-e680-b43e-ae3440ba568f@hauke-m.de>
On Tue, 2019-02-26 at 23:34 +0100, Hauke Mehrtens wrote:
>
>
> Paul Fertser did some further analysis of this problem and we can now
> reproduce it also on normal x86 systems when the binary is compiled like
> this:
> CFLAGS=" -Os -flto" LDFLAGS=" -flto" make
>
> -O3 or -Os and link time optimization is needed to see this problem.
>
> With LTO the compiler randomly reorders the __section_* variables
> including __section_set and __section_get and that leads to wrong offset
> used to traverse the __cmd section.
Yes, looks like stuff going into the __cmd section is no longer
guaranteed to be in the same order as in the files, which sort of makes
sense. Therefore, the trick I used to determine how densely the linker
packs this no longer works.
However, we can use the same trick with a different section, so I've
just committed a fix for this.
johannes
^ permalink raw reply
* Re: [RFC] ath10k: Fix DMA errors related to beacons (CT FW only)
From: Michał Kazior @ 2019-02-28 18:54 UTC (permalink / raw)
To: Ben Greear; +Cc: linux-wireless
In-Reply-To: <20190228155823.24749-1-greearb@candelatech.com>
On Thu, 28 Feb 2019 at 16:59, <greearb@candelatech.com> wrote:
>
> From: Ben Greear <greearb@candelatech.com>
>
> I often saw the ath10k-ct wave-1 firmware spit DMA errors and
> hang the entire system, requiring a hard power-cycle to revoer.
>
> It appears the issue is that there is no beacon-tx callback in
> stock firmware, so the driver can delete the beacon DMA buffer
> while firmware is still trying to access it.
>
> So, wave-1 ath10k-ct firmware now sends a beacon-tx-complete
> wmi message and that allows the driver to safely know when it
> can clean up the buffer.
>
> Signed-off-by: Ben Greear <greearb@candelatech.com>
> ---
>
> NOTE: This will not apply or work in upstream kernels since the
> rest of the CT fw support will not be accepted. But, I'd appreciate
> any technical feedback on this in case I missed any corner cases
> on locking or similar.
For the record this patch seems to be based on code with "ath10k: Free
beacon buf later in vdev teardown" included.
[...]
> diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
> index 154dcdabc48a..02a8efa2e783 100644
> --- a/drivers/net/wireless/ath/ath10k/mac.c
> +++ b/drivers/net/wireless/ath/ath10k/mac.c
[...]
> @@ -6147,6 +6151,16 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
> ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
> arvif->vdev_id, ret);
>
> + if (test_bit(ATH10K_FW_FEATURE_BEACON_TX_CB_CT,
> + ar->running_fw->fw_file.fw_features)) {
> + int time_left;
> +
> + time_left = wait_for_completion_timeout(&arvif->beacon_tx_done, (3 * HZ));
> + if (!time_left)
> + ath10k_warn(ar, "WARNING: failed to wait for beacon tx callback for vdev %i: %d\n",
> + arvif->vdev_id, ret);
> + }
I think this can race against wmi tx credits replenishment and maybe
other wmi commands if they get issued (not many of them I suppose
though). The ordering would need to be something like this:
cpu0 cpu1
ath10k_wmi_op_ep_tx_credits
ieee80211_iterate_active_interfaces_atomic
ath10k_wmi_beacon_send_ref_nowait
# preempted
ieee80211_do_stop
clear sdata running
drv_remove_interface
wait_for_completion_timeout
ath10k_mac_vif_beacon_cleanup
free/unmap
gen_beacon_dma(bcn->xxx)
reinit_completion()
ath10k_wmi_cmd_send_nowait()
Interestingly, this also shows it has been a possible use-after-free
without your patch. This is probably a unlikely scenario because
there's a disproportionate amount of code between these flows for this
to happen.
There's also a memory leak because ath10k_mac_vif_beacon_free() exits
without freeing arvif->beacon if beacon_state is SENDING, which it
will be in in the above race case.
With the per-skb beaconing case (which is dead code by the way it
seems..) iommu wouldn't be happy either because we'd hand over an
unmapped paddr to ath10k_wmi_cmd_send_nowait(). That's assuming kernel
didn't crash due to use-after-free on arvif->beacon (stored in `bcn`
on stack) before that.
This feels like a candidate for rcu if you wanted to have it fixed
neatly. It would fix both use-after-free and the locking conundrum -
ath10k_remove_interface() could NULL the pointer, call_rcu() and
wait_for_completion() in that call handler, and when its called back
unmap/free the beacon. This would require a refactor to how beacon
stuff is stored/maintained - a helper structure that would need carry
the beacon stuff + rcu_head, and this structure would be allocated and
assigned with rcu_assign_pointer() to a pointer in ath10k_vif. But I
feel like it's asking for unbound allocations or other bugs,
especially since I'm...
Not sure if it makes any sense. I would probably just rip out the
arvif->beacon code completely instead and keep arvif->beacon_buf only
(memcpy on swba and immediatelly free the skbuff). No point in fixing
dead code, is there?
Since you have beacon tx completion you can consider preventing beacon
corruption, similar to how disabled vsync causes frame tearing, by
avoiding memcpy() if completion for previous one didn't come. Not sure
how relevant that is though because if fw/hw are having hard time
transmitting a beacon then the RF condition must be really harsh and
unusably bad anyway.
Michał
^ permalink raw reply
* RE: pull-request mwifiex-firmware 2019-02-28
From: Ganapathi Bhat @ 2019-02-28 18:13 UTC (permalink / raw)
To: linux-firmware@kernel.org
Cc: linux-wireless@vger.kernel.org, James Cao, Cathy Luo,
Rakesh Parmar, Zhiyuan Yang
In-Reply-To: <CY4PR18MB1237F8ABB98E9DACA5C5EE97A0750@CY4PR18MB1237.namprd18.prod.outlook.com>
Kindly ignore this; I have sent a new request(and it is final)
Regards,
Ganapathi
> -----Original Message-----
> From: Ganapathi Bhat
> Sent: Thursday, February 28, 2019 11:07 PM
> To: linux-firmware@kernel.org
> Cc: linux-wireless@vger.kernel.org; James Cao <jcao@marvell.com>; Cathy
> Luo <cluo@marvell.com>; Rakesh Parmar <rakeshp@marvell.com>; Zhiyuan
> Yang <yangzy@marvell.com>
> Subject: pull-request mwifiex-firmware 2019-02-28
>
> The following changes since commit
> bd72387b8e49b1b7268ee60c97a131419284fb39:
>
> linux-firmware: update Marvell PCIe-USB8997 firmware image (2019-02-12
> 11:42:24 +0530)
>
> are available in the git repository at:
>
> git://git.marvell.com/mwifiex-firmware.git
>
> for you to fetch changes up to
> f0f63ed58fd49c42d06e05d1a6a8a1bd8e8b861c:
>
> linux-firmware: update Marvell 8801/8887 firmware images (2019-02-28
> 23:02:36 +0530)
>
> ----------------------------------------------------------------
> Ganapathi Bhat (2):
> linux-firmware: update Marvell 8897/8997 firmware images
> linux-firmware: update Marvell 8801/8887 firmware images
>
> WHENCE | 10 +++++-----
> mrvl/pcie8897_uapsta.bin | Bin 843828 -> 723540 bytes
> mrvl/pcie8997_wlan_v4.bin | Bin 428788 -> 456488 bytes
> mrvl/sd8801_uapsta.bin | Bin 258680 -> 255988 bytes
> mrvl/sd8887_uapsta.bin | Bin 695532 -> 616840 bytes
> mrvl/usbusb8997_combo_v4.bin | Bin 604536 -> 610252 bytes
> 6 files changed, 5 insertions(+), 5 deletions(-)
^ permalink raw reply
* RE: pull-request mwifiex-firmware 2019-02-28
From: Ganapathi Bhat @ 2019-02-28 18:12 UTC (permalink / raw)
To: linux-firmware@kernel.org
Cc: linux-wireless@vger.kernel.org, Cathy Luo, James Cao,
Rakesh Parmar, Zhiyuan Yang
In-Reply-To: <CY4PR18MB1237089CC3822DBF13CDB86BA0750@CY4PR18MB1237.namprd18.prod.outlook.com>
Kindly ignore this; I have sent a new request.
Regards,
Ganapathi
> -----Original Message-----
> From: Ganapathi Bhat
> Sent: Thursday, February 28, 2019 9:25 PM
> To: linux-firmware@kernel.org
> Cc: linux-wireless@vger.kernel.org; Cathy Luo <cluo@marvell.com>; James
> Cao <jcao@marvell.com>; Rakesh Parmar <rakeshp@marvell.com>; Zhiyuan
> Yang <yangzy@marvell.com>
> Subject: pull-request mwifiex-firmware 2019-02-28
>
> The following changes since commit
> bd72387b8e49b1b7268ee60c97a131419284fb39:
>
> linux-firmware: update Marvell PCIe-USB8997 firmware image (2019-02-12
> 11:42:24 +0530)
>
> are available in the git repository at:
>
> git://git.marvell.com/mwifiex-firmware.git
>
> for you to fetch changes up to
> 1a5773c0c89ee44cee51a285d5c7c1063cdb0891:
>
> linux-firmware: update Marvell 8897/8997 firmware images (2019-02-28
> 21:11:26 +0530)
>
> ----------------------------------------------------------------
> Ganapathi Bhat (1):
> linux-firmware: update Marvell 8897/8997 firmware images
>
> WHENCE | 6 +++---
> mrvl/pcie8897_uapsta.bin | Bin 843828 -> 723540 bytes
> mrvl/pcie8997_wlan_v4.bin | Bin 428788 -> 456488 bytes
> mrvl/usbusb8997_combo_v4.bin | Bin 604536 -> 610252 bytes
> 4 files changed, 3 insertions(+), 3 deletions(-)
^ permalink raw reply
* pull-request mwifiex-firmware 2019-02-28
From: Ganapathi Bhat @ 2019-02-28 18:10 UTC (permalink / raw)
To: linux-firmware@kernel.org
Cc: linux-wireless@vger.kernel.org, James Cao, Cathy Luo,
Rakesh Parmar, Zhiyuan Yang, Mayur Arakere
The following changes since commit bd72387b8e49b1b7268ee60c97a131419284fb39:
linux-firmware: update Marvell PCIe-USB8997 firmware image (2019-02-12 11:42:24 +0530)
are available in the git repository at:
git://git.marvell.com/mwifiex-firmware.git
for you to fetch changes up to 78d2b16af093783d40f0b8990d5d9ede8c3281d3:
linux-firmware: update Marvell 8787/8801/8887 firmware images (2019-02-28 23:35:32 +0530)
----------------------------------------------------------------
Ganapathi Bhat (2):
linux-firmware: update Marvell 8897/8997 firmware images
linux-firmware: update Marvell 8787/8801/8887 firmware images
WHENCE | 12 ++++++------
mrvl/pcie8897_uapsta.bin | Bin 843828 -> 723540 bytes
mrvl/pcie8997_wlan_v4.bin | Bin 428788 -> 456488 bytes
mrvl/sd8787_uapsta.bin | Bin 463240 -> 466592 bytes
mrvl/sd8801_uapsta.bin | Bin 258680 -> 255988 bytes
mrvl/sd8887_uapsta.bin | Bin 695532 -> 616840 bytes
mrvl/usbusb8997_combo_v4.bin | Bin 604536 -> 610252 bytes
7 files changed, 6 insertions(+), 6 deletions(-)
^ permalink raw reply
* pull-request mwifiex-firmware 2019-02-28
From: Ganapathi Bhat @ 2019-02-28 17:36 UTC (permalink / raw)
To: linux-firmware@kernel.org
Cc: linux-wireless@vger.kernel.org, James Cao, Cathy Luo,
Rakesh Parmar, Zhiyuan Yang
The following changes since commit bd72387b8e49b1b7268ee60c97a131419284fb39:
linux-firmware: update Marvell PCIe-USB8997 firmware image (2019-02-12 11:42:24 +0530)
are available in the git repository at:
git://git.marvell.com/mwifiex-firmware.git
for you to fetch changes up to f0f63ed58fd49c42d06e05d1a6a8a1bd8e8b861c:
linux-firmware: update Marvell 8801/8887 firmware images (2019-02-28 23:02:36 +0530)
----------------------------------------------------------------
Ganapathi Bhat (2):
linux-firmware: update Marvell 8897/8997 firmware images
linux-firmware: update Marvell 8801/8887 firmware images
WHENCE | 10 +++++-----
mrvl/pcie8897_uapsta.bin | Bin 843828 -> 723540 bytes
mrvl/pcie8997_wlan_v4.bin | Bin 428788 -> 456488 bytes
mrvl/sd8801_uapsta.bin | Bin 258680 -> 255988 bytes
mrvl/sd8887_uapsta.bin | Bin 695532 -> 616840 bytes
mrvl/usbusb8997_combo_v4.bin | Bin 604536 -> 610252 bytes
6 files changed, 5 insertions(+), 5 deletions(-)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox