All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/8] mt76: retry rx polling as long as there is budget left
@ 2018-01-24 15:19 Felix Fietkau
  2018-01-24 15:19 ` [PATCH 2/8] mt76: fix TSF value in probe responses Felix Fietkau
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Sending frames to mac80211 needs time, which could allow for more rx
packets to end up in the DMA ring. Retry polling until there are no more
frames left. Improves rx latency under load.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/dma.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index ecd409a4a89b..e539b3838b94 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -387,17 +387,21 @@ static int
 mt76_dma_rx_poll(struct napi_struct *napi, int budget)
 {
 	struct mt76_dev *dev;
-	int qid, done;
+	int qid, done = 0, cur;
 
 	dev = container_of(napi->dev, struct mt76_dev, napi_dev);
 	qid = napi - dev->napi;
 
-	done = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget);
+	do {
+		cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done);
+		mt76_rx_complete(dev, qid);
+		done += cur;
+	} while (cur && done < budget);
+
 	if (done < budget) {
 		napi_complete(napi);
 		dev->drv->rx_poll_complete(dev, qid);
 	}
-	mt76_rx_complete(dev, qid);
 
 	return done;
 }
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/8] mt76: fix TSF value in probe responses
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:19 ` [PATCH 3/8] mt76: add an intermediate struct for rx status information Felix Fietkau
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Like beacons, probe responses need a hardware-generated TSF value. Set
the flag that causes the hw to generate it

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index a7416a01baa4..98219b971463 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -172,6 +172,7 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_tx_rate *rate = &info->control.rates[0];
 	struct ieee80211_key_conf *key = info->control.hw_key;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	u16 rate_ht_mask = FIELD_PREP(MT_RXWI_RATE_PHY, BIT(1) | BIT(2));
 	u16 txwi_flags = 0;
 	u8 nss;
@@ -248,6 +249,10 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
 				    sta->ht_cap.ampdu_density);
 	}
 
+	if (ieee80211_is_probe_resp(hdr->frame_control) ||
+	    ieee80211_is_beacon(hdr->frame_control))
+		txwi_flags |= MT_TXWI_FLAGS_TS;
+
 	txwi->flags |= cpu_to_le16(txwi_flags);
 	txwi->len_ctl = cpu_to_le16(skb->len);
 }
@@ -634,7 +639,6 @@ mt76_write_beacon(struct mt76x2_dev *dev, int offset, struct sk_buff *skb)
 		return -ENOSPC;
 
 	mt76x2_mac_write_txwi(dev, &txwi, skb, NULL, NULL);
-	txwi.flags |= cpu_to_le16(MT_TXWI_FLAGS_TS);
 
 	mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
 	offset += sizeof(txwi);
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/8] mt76: add an intermediate struct for rx status information
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
  2018-01-24 15:19 ` [PATCH 2/8] mt76: fix TSF value in probe responses Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:19 ` [PATCH 4/8] mt76: get station pointer by wcid and pass it to mac80211 Felix Fietkau
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Preparation for passing in more internal rx data via skb->cb

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mac80211.c   | 29 ++++++++++++++++++++++++-
 drivers/net/wireless/mediatek/mt76/mt76.h       | 13 +++++++++++
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c |  4 ++--
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 3acf0e175d71..258d2623d1bf 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,10 +384,37 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 }
 EXPORT_SYMBOL_GPL(mt76_get_survey);
 
+static void
+mt76_rx_convert(struct sk_buff *skb)
+{
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct mt76_rx_status mstat;
+
+	mstat = *((struct mt76_rx_status *) skb->cb);
+	memset(status, 0, sizeof(*status));
+
+	status->flag = mstat.flag;
+	status->freq = mstat.freq;
+	status->enc_flags = mstat.enc_flags;
+	status->encoding = mstat.encoding;
+	status->bw = mstat.bw;
+	status->rate_idx = mstat.rate_idx;
+	status->nss = mstat.nss;
+	status->band = mstat.band;
+	status->signal = mstat.signal;
+	status->chains = mstat.chains;
+
+	BUILD_BUG_ON(sizeof(mstat) > sizeof(skb->cb));
+	BUILD_BUG_ON(sizeof(status->chain_signal) != sizeof(mstat.chain_signal));
+	memcpy(status->chain_signal, mstat.chain_signal, sizeof(mstat.chain_signal));
+}
+
 void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
 {
 	struct sk_buff *skb;
 
-	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
+	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
+		mt76_rx_convert(skb);
 		ieee80211_rx_napi(dev->hw, NULL, skb, &dev->napi[q]);
+	}
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index f88d9a15210a..be4846ee4828 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -250,6 +250,19 @@ struct mt76_rate_power {
 	};
 };
 
+struct mt76_rx_status {
+	u32 flag;
+	u16 freq;
+	u8 enc_flags;
+	u8 encoding:2, bw:3;
+	u8 rate_idx;
+	u8 nss;
+	u8 band;
+	u8 signal;
+	u8 chains;
+	s8 chain_signal[IEEE80211_MAX_CHAINS];
+};
+
 #define mt76_rr(dev, ...)	(dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__)
 #define mt76_wr(dev, ...)	(dev)->mt76.bus->wr(&((dev)->mt76), __VA_ARGS__)
 #define mt76_rmw(dev, ...)	(dev)->mt76.bus->rmw(&((dev)->mt76), __VA_ARGS__)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index 98219b971463..2e12fc0d5c9e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -29,7 +29,7 @@ void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
 }
 
 static int
-mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
+mt76x2_mac_process_rate(struct mt76_rx_status *status, u16 rate)
 {
 	u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate);
 
@@ -268,7 +268,7 @@ static void mt76x2_remove_hdr_pad(struct sk_buff *skb)
 int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 			  void *rxi)
 {
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
 	struct mt76x2_rxwi *rxwi = rxi;
 	u32 ctl = le32_to_cpu(rxwi->ctl);
 	u16 rate = le16_to_cpu(rxwi->rate);
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/8] mt76: get station pointer by wcid and pass it to mac80211
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
  2018-01-24 15:19 ` [PATCH 2/8] mt76: fix TSF value in probe responses Felix Fietkau
  2018-01-24 15:19 ` [PATCH 3/8] mt76: add an intermediate struct for rx status information Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:19 ` [PATCH 5/8] mt76: implement A-MPDU rx reordering in the driver code Felix Fietkau
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Avoids the rhashtable lookup based on the MAC address inside mac80211

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/dma.c         |  4 ++++
 drivers/net/wireless/mediatek/mt76/mac80211.c    |  9 ++++++---
 drivers/net/wireless/mediatek/mt76/mt76.h        | 14 ++++++++++++++
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 13 +++++++++++++
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c |  1 +
 5 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index e539b3838b94..fd8bea144973 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -392,12 +392,16 @@ mt76_dma_rx_poll(struct napi_struct *napi, int budget)
 	dev = container_of(napi->dev, struct mt76_dev, napi_dev);
 	qid = napi - dev->napi;
 
+	rcu_read_lock();
+
 	do {
 		cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done);
 		mt76_rx_complete(dev, qid);
 		done += cur;
 	} while (cur && done < budget);
 
+	rcu_read_unlock();
+
 	if (done < budget) {
 		napi_complete(napi);
 		dev->drv->rx_poll_complete(dev, qid);
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 258d2623d1bf..c1982211d658 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,7 +384,7 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 }
 EXPORT_SYMBOL_GPL(mt76_get_survey);
 
-static void
+static struct ieee80211_sta *
 mt76_rx_convert(struct sk_buff *skb)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -407,14 +407,17 @@ mt76_rx_convert(struct sk_buff *skb)
 	BUILD_BUG_ON(sizeof(mstat) > sizeof(skb->cb));
 	BUILD_BUG_ON(sizeof(status->chain_signal) != sizeof(mstat.chain_signal));
 	memcpy(status->chain_signal, mstat.chain_signal, sizeof(mstat.chain_signal));
+
+	return wcid_to_sta(mstat.wcid);
 }
 
 void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
 {
+	struct ieee80211_sta *sta;
 	struct sk_buff *skb;
 
 	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
-		mt76_rx_convert(skb);
-		ieee80211_rx_napi(dev->hw, NULL, skb, &dev->napi[q]);
+		sta = mt76_rx_convert(skb);
+		ieee80211_rx_napi(dev->hw, sta, skb, &dev->napi[q]);
 	}
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index be4846ee4828..e20c52607c66 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -125,6 +125,8 @@ struct mt76_wcid {
 	u8 idx;
 	u8 hw_key_idx;
 
+	u8 sta:1;
+
 	__le16 tx_rate;
 	bool tx_rate_set;
 	u8 tx_rate_nss;
@@ -251,6 +253,7 @@ struct mt76_rate_power {
 };
 
 struct mt76_rx_status {
+	struct mt76_wcid *wcid;
 	u32 flag;
 	u16 freq;
 	u8 enc_flags;
@@ -343,6 +346,17 @@ mtxq_to_txq(struct mt76_txq *mtxq)
 	return container_of(ptr, struct ieee80211_txq, drv_priv);
 }
 
+static inline struct ieee80211_sta *
+wcid_to_sta(struct mt76_wcid *wcid)
+{
+	void *ptr = wcid;
+
+	if (!wcid || !wcid->sta)
+		return NULL;
+
+	return container_of(ptr, struct ieee80211_sta, drv_priv);
+}
+
 int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
 		      struct sk_buff *skb, struct mt76_wcid *wcid,
 		      struct ieee80211_sta *sta);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index 2e12fc0d5c9e..75f2843847d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -265,6 +265,15 @@ static void mt76x2_remove_hdr_pad(struct sk_buff *skb)
 	skb_pull(skb, 2);
 }
 
+static struct mt76_wcid *
+mt76x2_rx_get_wcid(struct mt76x2_dev *dev, u8 idx)
+{
+	if (idx >= ARRAY_SIZE(dev->wcid))
+		return NULL;
+
+	return rcu_dereference(dev->wcid[idx]);
+}
+
 int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 			  void *rxi)
 {
@@ -272,8 +281,12 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 	struct mt76x2_rxwi *rxwi = rxi;
 	u32 ctl = le32_to_cpu(rxwi->ctl);
 	u16 rate = le16_to_cpu(rxwi->rate);
+	u8 wcid;
 	int len;
 
+	wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl);
+	status->wcid = mt76x2_rx_get_wcid(dev, wcid);
+
 	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
 		mt76x2_remove_hdr_pad(skb);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index aa5fbb64e218..2a1cb65c5edb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -273,6 +273,7 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		goto out;
 	}
 
+	msta->wcid.sta = 1;
 	msta->wcid.idx = idx;
 	msta->wcid.hw_key_idx = -1;
 	mt76x2_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/8] mt76: implement A-MPDU rx reordering in the driver code
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
                   ` (2 preceding siblings ...)
  2018-01-24 15:19 ` [PATCH 4/8] mt76: get station pointer by wcid and pass it to mac80211 Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:19 ` [PATCH 6/8] mt76: split mt76_rx_complete Felix Fietkau
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

This is required for performing CCMP PN validation in software

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/Makefile      |   2 +-
 drivers/net/wireless/mediatek/mt76/agg-rx.c      | 264 +++++++++++++++++++++++
 drivers/net/wireless/mediatek/mt76/mac80211.c    |  11 +-
 drivers/net/wireless/mediatek/mt76/mt76.h        |  35 +++
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c |   2 +
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  |   7 +
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c |   2 +
 7 files changed, 319 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/wireless/mediatek/mt76/agg-rx.c

diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile
index 2bb919863616..a0156bc01dea 100644
--- a/drivers/net/wireless/mediatek/mt76/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_MT76_CORE) += mt76.o
 obj-$(CONFIG_MT76x2E) += mt76x2e.o
 
 mt76-y := \
-	mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o
+	mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o agg-rx.o
 
 CFLAGS_trace.o := -I$(src)
 
diff --git a/drivers/net/wireless/mediatek/mt76/agg-rx.c b/drivers/net/wireless/mediatek/mt76/agg-rx.c
new file mode 100644
index 000000000000..ecd0bcd149e5
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/agg-rx.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "mt76.h"
+
+#define REORDER_TIMEOUT (HZ / 10)
+
+static void
+mt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx)
+{
+	struct sk_buff *skb;
+
+	tid->head = ieee80211_sn_inc(tid->head);
+
+	skb = tid->reorder_buf[idx];
+	if (!skb)
+		return;
+
+	tid->reorder_buf[idx] = NULL;
+	tid->nframes--;
+	__skb_queue_tail(frames, skb);
+}
+
+static void
+mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid, struct sk_buff_head *frames,
+			 u16 head)
+{
+	int idx;
+
+	while (ieee80211_sn_less(tid->head, head)) {
+		idx = tid->head % tid->size;
+		mt76_aggr_release(tid, frames, idx);
+	}
+}
+
+static void
+mt76_rx_aggr_release_head(struct mt76_rx_tid *tid, struct sk_buff_head *frames)
+{
+	int idx = tid->head % tid->size;
+
+	while (tid->reorder_buf[idx]) {
+		mt76_aggr_release(tid, frames, idx);
+		idx = tid->head % tid->size;
+	}
+}
+
+static void
+mt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames)
+{
+	struct mt76_rx_status *status;
+	struct sk_buff *skb;
+	int start, idx, nframes;
+
+	if (!tid->nframes)
+		return;
+
+	mt76_rx_aggr_release_head(tid, frames);
+
+	start = tid->head % tid->size;
+	nframes = tid->nframes;
+
+	for (idx = (tid->head + 1) % tid->size;
+	     idx != start && nframes;
+	     idx = (idx + 1) % tid->size) {
+
+		skb = tid->reorder_buf[idx];
+		if (!skb)
+			continue;
+
+		nframes--;
+		status = (struct mt76_rx_status *) skb->cb;
+		if (!time_after(jiffies, status->reorder_time +
+					 REORDER_TIMEOUT))
+			continue;
+
+		mt76_rx_aggr_release_frames(tid, frames, status->seqno);
+	}
+
+	mt76_rx_aggr_release_head(tid, frames);
+}
+
+static void
+mt76_rx_aggr_reorder_work(struct work_struct *work)
+{
+	struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid,
+					       reorder_work.work);
+	struct mt76_dev *dev = tid->dev;
+	struct ieee80211_sta *sta;
+	struct sk_buff_head frames;
+	struct sk_buff *skb;
+
+	__skb_queue_head_init(&frames);
+
+	local_bh_disable();
+
+	spin_lock(&tid->lock);
+	mt76_rx_aggr_check_release(tid, &frames);
+	spin_unlock(&tid->lock);
+
+	ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, REORDER_TIMEOUT);
+
+	while ((skb = __skb_dequeue(&frames)) != NULL) {
+		sta = mt76_rx_convert(skb);
+		ieee80211_rx_napi(dev->hw, sta, skb, NULL);
+	}
+
+	local_bh_enable();
+}
+
+void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
+{
+	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
+	struct mt76_wcid *wcid = status->wcid;
+	struct ieee80211_sta *sta;
+	struct mt76_rx_tid *tid;
+	bool sn_less;
+	u16 seqno, head, size;
+	u8 idx;
+
+	__skb_queue_tail(frames, skb);
+
+	sta = wcid_to_sta(wcid);
+	if (!sta || !status->aggr)
+		return;
+
+	tid = rcu_dereference(wcid->aggr[status->tid]);
+	if (!tid)
+		return;
+
+	spin_lock_bh(&tid->lock);
+
+	if (tid->stopped)
+		goto out;
+
+	head = tid->head;
+	seqno = status->seqno;
+	size = tid->size;
+	sn_less = ieee80211_sn_less(seqno, head);
+
+	if (!tid->started) {
+		if (sn_less)
+			goto out;
+
+		tid->started = true;
+	}
+
+	if (sn_less) {
+		__skb_unlink(skb, frames);
+		dev_kfree_skb(skb);
+		goto out;
+	}
+
+	if (seqno == head) {
+		tid->head = ieee80211_sn_inc(head);
+		if (tid->nframes)
+			mt76_rx_aggr_release_head(tid, frames);
+		goto out;
+	}
+
+	__skb_unlink(skb, frames);
+
+	/*
+	 * Frame sequence number exceeds buffering window, free up some space
+	 * by releasing previous frames
+	 */
+	if (!ieee80211_sn_less(seqno, head + size)) {
+		head = ieee80211_sn_inc(ieee80211_sn_sub(seqno, size));
+		mt76_rx_aggr_release_frames(tid, frames, head);
+	}
+
+	idx = seqno % size;
+
+	/* Discard if the current slot is already in use */
+	if (tid->reorder_buf[idx]) {
+		dev_kfree_skb(skb);
+		goto out;
+	}
+
+	status->reorder_time = jiffies;
+	tid->reorder_buf[idx] = skb;
+	tid->nframes++;
+	mt76_rx_aggr_release_head(tid, frames);
+
+	ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, REORDER_TIMEOUT);
+
+out:
+	spin_unlock_bh(&tid->lock);
+}
+
+int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,
+		       u16 ssn, u8 size)
+{
+	struct mt76_rx_tid *tid;
+
+	mt76_rx_aggr_stop(dev, wcid, tidno);
+
+	tid = kzalloc(sizeof(*tid) + size * sizeof(tid->reorder_buf[0]),
+		      GFP_KERNEL);
+	if (!tid)
+		return -ENOMEM;
+
+	tid->dev = dev;
+	tid->head = ssn;
+	tid->size = size;
+	INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);
+	spin_lock_init(&tid->lock);
+
+	rcu_assign_pointer(wcid->aggr[tidno], tid);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_rx_aggr_start);
+
+static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid)
+{
+	u8 size = tid->size;
+	int i;
+
+	spin_lock_bh(&tid->lock);
+
+	tid->stopped = true;
+	for (i = 0; tid->nframes && i < size; i++) {
+		struct sk_buff *skb = tid->reorder_buf[i];
+
+		if (!skb)
+			continue;
+
+		tid->nframes--;
+		dev_kfree_skb(skb);
+	}
+
+	spin_unlock_bh(&tid->lock);
+
+	cancel_delayed_work_sync(&tid->reorder_work);
+}
+
+void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno)
+{
+	struct mt76_rx_tid *tid;
+
+	rcu_read_lock();
+
+	tid = rcu_dereference(wcid->aggr[tidno]);
+	if (tid) {
+		rcu_assign_pointer(wcid->aggr[tidno], NULL);
+		mt76_rx_aggr_shutdown(dev, tid);
+		kfree_rcu(tid, rcu_head);
+	}
+
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(mt76_rx_aggr_stop);
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index c1982211d658..5978e5b0a2fe 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,8 +384,7 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 }
 EXPORT_SYMBOL_GPL(mt76_get_survey);
 
-static struct ieee80211_sta *
-mt76_rx_convert(struct sk_buff *skb)
+struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct mt76_rx_status mstat;
@@ -414,9 +413,15 @@ mt76_rx_convert(struct sk_buff *skb)
 void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
 {
 	struct ieee80211_sta *sta;
+	struct sk_buff_head frames;
 	struct sk_buff *skb;
 
-	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
+	__skb_queue_head_init(&frames);
+
+	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
+		mt76_rx_aggr_reorder(skb, &frames);
+
+	while ((skb = __skb_dequeue(&frames)) != NULL) {
 		sta = mt76_rx_convert(skb);
 		ieee80211_rx_napi(dev->hw, sta, skb, &dev->napi[q]);
 	}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index e20c52607c66..cde199ed3f57 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -122,6 +122,10 @@ struct mt76_queue_ops {
 };
 
 struct mt76_wcid {
+	struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS];
+
+	struct work_struct aggr_work;
+
 	u8 idx;
 	u8 hw_key_idx;
 
@@ -152,6 +156,24 @@ struct mt76_txwi_cache {
 	struct list_head list;
 };
 
+
+struct mt76_rx_tid {
+	struct rcu_head rcu_head;
+
+	struct mt76_dev *dev;
+
+	spinlock_t lock;
+	struct delayed_work reorder_work;
+
+	u16 head;
+	u8 size;
+	u8 nframes;
+
+	u8 started:1, stopped:1, timer_pending:1;
+
+	struct sk_buff *reorder_buf[];
+};
+
 enum {
 	MT76_STATE_INITIALIZED,
 	MT76_STATE_RUNNING,
@@ -254,6 +276,13 @@ struct mt76_rate_power {
 
 struct mt76_rx_status {
 	struct mt76_wcid *wcid;
+
+	unsigned long reorder_time;
+
+	u8 aggr;
+	u8 tid;
+	u16 seqno;
+
 	u32 flag;
 	u16 freq;
 	u8 enc_flags;
@@ -380,9 +409,15 @@ void mt76_set_channel(struct mt76_dev *dev);
 int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 		    struct survey_info *survey);
 
+int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
+		       u16 ssn, u8 size);
+void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
+
 /* internal */
 void mt76_tx_free(struct mt76_dev *dev);
 void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
 void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q);
+void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames);
+struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb);
 
 #endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 7b4851481dd6..1e34b578b151 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -842,6 +842,8 @@ int mt76x2_register_device(struct mt76x2_dev *dev)
 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
 
 	ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES);
+	ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
+
 	INIT_DELAYED_WORK(&dev->cal_work, mt76x2_phy_calibrate);
 	INIT_DELAYED_WORK(&dev->mac_work, mt76x2_mac_work);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index 75f2843847d0..b96d8a88656c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -281,6 +281,7 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 	struct mt76x2_rxwi *rxwi = rxi;
 	u32 ctl = le32_to_cpu(rxwi->ctl);
 	u16 rate = le16_to_cpu(rxwi->rate);
+	u16 tid_sn = le16_to_cpu(rxwi->tid_sn);
 	u8 wcid;
 	int len;
 
@@ -290,6 +291,9 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
 		mt76x2_remove_hdr_pad(skb);
 
+	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_BA))
+		status->aggr = true;
+
 	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
 		status->flag |= RX_FLAG_DECRYPTED;
 		status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
@@ -307,6 +311,9 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 	status->freq = dev->mt76.chandef.chan->center_freq;
 	status->band = dev->mt76.chandef.chan->band;
 
+	status->tid = FIELD_GET(MT_RXWI_TID, tid_sn);
+	status->seqno = FIELD_GET(MT_RXWI_SN, tid_sn);
+
 	return mt76x2_mac_process_rate(status, rate);
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 2a1cb65c5edb..bc08040123b9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -487,9 +487,11 @@ mt76x2_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
 	switch (action) {
 	case IEEE80211_AMPDU_RX_START:
+		mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, *ssn, params->buf_size);
 		mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid));
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
+		mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
 		mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4,
 			   BIT(16 + tid));
 		break;
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 6/8] mt76: split mt76_rx_complete
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
                   ` (3 preceding siblings ...)
  2018-01-24 15:19 ` [PATCH 5/8] mt76: implement A-MPDU rx reordering in the driver code Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:19 ` [PATCH 7/8] mt76: pass the per-vif wcid to the core for multicast rx Felix Fietkau
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Add a separate function for processing frames after A-MPDU reordering,
reduce code duplication

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/agg-rx.c   |  8 +-------
 drivers/net/wireless/mediatek/mt76/dma.c      |  2 +-
 drivers/net/wireless/mediatek/mt76/mac80211.c | 24 ++++++++++++++++++------
 drivers/net/wireless/mediatek/mt76/mt76.h     |  5 +++--
 4 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/agg-rx.c b/drivers/net/wireless/mediatek/mt76/agg-rx.c
index ecd0bcd149e5..8027bb7c03c2 100644
--- a/drivers/net/wireless/mediatek/mt76/agg-rx.c
+++ b/drivers/net/wireless/mediatek/mt76/agg-rx.c
@@ -97,9 +97,7 @@ mt76_rx_aggr_reorder_work(struct work_struct *work)
 	struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid,
 					       reorder_work.work);
 	struct mt76_dev *dev = tid->dev;
-	struct ieee80211_sta *sta;
 	struct sk_buff_head frames;
-	struct sk_buff *skb;
 
 	__skb_queue_head_init(&frames);
 
@@ -110,11 +108,7 @@ mt76_rx_aggr_reorder_work(struct work_struct *work)
 	spin_unlock(&tid->lock);
 
 	ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, REORDER_TIMEOUT);
-
-	while ((skb = __skb_dequeue(&frames)) != NULL) {
-		sta = mt76_rx_convert(skb);
-		ieee80211_rx_napi(dev->hw, sta, skb, NULL);
-	}
+	mt76_rx_complete(dev, &frames, -1);
 
 	local_bh_enable();
 }
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index fd8bea144973..3518703524e7 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -396,7 +396,7 @@ mt76_dma_rx_poll(struct napi_struct *napi, int budget)
 
 	do {
 		cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done);
-		mt76_rx_complete(dev, qid);
+		mt76_rx_poll_complete(dev, qid);
 		done += cur;
 	} while (cur && done < budget);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 5978e5b0a2fe..77f1be161009 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,7 +384,7 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 }
 EXPORT_SYMBOL_GPL(mt76_get_survey);
 
-struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
+static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct mt76_rx_status mstat;
@@ -410,9 +410,24 @@ struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 	return wcid_to_sta(mstat.wcid);
 }
 
-void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
+void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+		      int queue)
 {
+	struct napi_struct *napi = NULL;
 	struct ieee80211_sta *sta;
+	struct sk_buff *skb;
+
+	if (queue >= 0)
+	    napi = &dev->napi[queue];
+
+	while ((skb = __skb_dequeue(frames)) != NULL) {
+		sta = mt76_rx_convert(skb);
+		ieee80211_rx_napi(dev->hw, sta, skb, napi);
+	}
+}
+
+void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
+{
 	struct sk_buff_head frames;
 	struct sk_buff *skb;
 
@@ -421,8 +436,5 @@ void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
 	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
 		mt76_rx_aggr_reorder(skb, &frames);
 
-	while ((skb = __skb_dequeue(&frames)) != NULL) {
-		sta = mt76_rx_convert(skb);
-		ieee80211_rx_napi(dev->hw, sta, skb, &dev->napi[q]);
-	}
+	mt76_rx_complete(dev, &frames, q);
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index cde199ed3f57..af98bc65c2e1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -416,8 +416,9 @@ void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
 /* internal */
 void mt76_tx_free(struct mt76_dev *dev);
 void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
-void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q);
+void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+		      int queue);
+void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q);
 void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames);
-struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb);
 
 #endif
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 7/8] mt76: pass the per-vif wcid to the core for multicast rx
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
                   ` (4 preceding siblings ...)
  2018-01-24 15:19 ` [PATCH 6/8] mt76: split mt76_rx_complete Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:19 ` [PATCH 8/8] mt76: validate rx CCMP PN Felix Fietkau
  2018-01-26  9:20 ` [1/8] mt76: retry rx polling as long as there is budget left Kalle Valo
  7 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Preparation for adding software rx CCMP PN validation

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2.h      |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 15 ++++++++++++---
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c |  1 +
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index a12dfce8c0d1..17df17afd9bf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -144,6 +144,7 @@ struct mt76x2_vif {
 struct mt76x2_sta {
 	struct mt76_wcid wcid; /* must be first */
 
+	struct mt76x2_vif *vif;
 	struct mt76x2_tx_status status;
 	int n_frames;
 };
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index b96d8a88656c..f56a8f459fe6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -266,12 +266,20 @@ static void mt76x2_remove_hdr_pad(struct sk_buff *skb)
 }
 
 static struct mt76_wcid *
-mt76x2_rx_get_wcid(struct mt76x2_dev *dev, u8 idx)
+mt76x2_rx_get_sta_wcid(struct mt76x2_dev *dev, u8 idx, bool unicast)
 {
+	struct mt76x2_sta *sta;
+	struct mt76_wcid *wcid;
+
 	if (idx >= ARRAY_SIZE(dev->wcid))
 		return NULL;
 
-	return rcu_dereference(dev->wcid[idx]);
+	wcid = rcu_dereference(dev->wcid[idx]);
+	if (unicast || !wcid)
+		return wcid;
+
+	sta = container_of(wcid, struct mt76x2_sta, wcid);
+	return &sta->vif->group_wcid;
 }
 
 int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
@@ -282,11 +290,12 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 	u32 ctl = le32_to_cpu(rxwi->ctl);
 	u16 rate = le16_to_cpu(rxwi->rate);
 	u16 tid_sn = le16_to_cpu(rxwi->tid_sn);
+	bool unicast = rxwi->rxinfo & cpu_to_le32(MT_RXINFO_UNICAST);
 	u8 wcid;
 	int len;
 
 	wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl);
-	status->wcid = mt76x2_rx_get_wcid(dev, wcid);
+	status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast);
 
 	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
 		mt76x2_remove_hdr_pad(skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index bc08040123b9..08fe804c6a43 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -273,6 +273,7 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		goto out;
 	}
 
+	msta->vif = mvif;
 	msta->wcid.sta = 1;
 	msta->wcid.idx = idx;
 	msta->wcid.hw_key_idx = -1;
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 8/8] mt76: validate rx CCMP PN
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
                   ` (5 preceding siblings ...)
  2018-01-24 15:19 ` [PATCH 7/8] mt76: pass the per-vif wcid to the core for multicast rx Felix Fietkau
@ 2018-01-24 15:19 ` Felix Fietkau
  2018-01-24 15:56   ` Johannes Berg
  2018-01-25 10:44   ` [PATCH v2 " Felix Fietkau
  2018-01-26  9:20 ` [1/8] mt76: retry rx polling as long as there is budget left Kalle Valo
  7 siblings, 2 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 15:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Apparently hardware does not perform CCMP PN validation in hardware, so
we need to take care of this in the driver. This is important for
protecting against replay attacks

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mac80211.c    | 51 ++++++++++++++++++++++++
 drivers/net/wireless/mediatek/mt76/mt76.h        | 12 +++++-
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c |  2 +-
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 45 ++++++++++++++++-----
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c |  1 +
 5 files changed, 98 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 77f1be161009..c203e7b48c58 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,6 +384,27 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 }
 EXPORT_SYMBOL_GPL(mt76_get_survey);
 
+void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
+			 struct ieee80211_key_conf *key)
+{
+	struct ieee80211_key_seq seq;
+	int i;
+
+	wcid->rx_check_pn = false;
+
+	if (!key)
+		return;
+
+	if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+		wcid->rx_check_pn = true;
+
+	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+		ieee80211_get_key_rx_seq(key, i, &seq);
+		memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn));
+	}
+}
+EXPORT_SYMBOL(mt76_wcid_key_setup);
+
 static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -410,6 +431,31 @@ static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 	return wcid_to_sta(mstat.wcid);
 }
 
+static int
+mt76_check_ccmp_pn(struct sk_buff *skb)
+{
+	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
+	struct mt76_wcid *wcid = status->wcid;
+	int ret;
+
+	if (!(status->flag & RX_FLAG_DECRYPTED))
+		return 0;
+
+	if (!wcid || !wcid->rx_check_pn)
+		return 0;
+
+	BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
+	ret = memcmp(status->iv, wcid->rx_key_pn[status->tid],
+		     sizeof(status->iv));
+	if (ret <= 0)
+		return -EINVAL; /* replay */
+
+	memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv));
+	status->flag |= RX_FLAG_PN_VALIDATED;
+
+	return 0;
+}
+
 void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 		      int queue)
 {
@@ -421,6 +467,11 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 	    napi = &dev->napi[queue];
 
 	while ((skb = __skb_dequeue(frames)) != NULL) {
+		if (mt76_check_ccmp_pn(skb)) {
+			dev_kfree_skb(skb);
+			continue;
+		}
+
 		sta = mt76_rx_convert(skb);
 		ieee80211_rx_napi(dev->hw, sta, skb, napi);
 	}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index af98bc65c2e1..129015c9d116 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -131,6 +131,9 @@ struct mt76_wcid {
 
 	u8 sta:1;
 
+	u8 rx_check_pn;
+	u8 rx_key_pn[IEEE80211_NUM_TIDS][6];
+
 	__le16 tx_rate;
 	bool tx_rate_set;
 	u8 tx_rate_nss;
@@ -279,12 +282,14 @@ struct mt76_rx_status {
 
 	unsigned long reorder_time;
 
-	u8 aggr;
+	u8 iv[6];
+
+	u8 aggr:1;
 	u8 tid;
 	u16 seqno;
 
-	u32 flag;
 	u16 freq;
+	u32 flag;
 	u8 enc_flags;
 	u8 encoding:2, bw:3;
 	u8 rate_idx;
@@ -413,6 +418,9 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
 		       u16 ssn, u8 size);
 void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
 
+void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
+			 struct ieee80211_key_conf *key);
+
 /* internal */
 void mt76_tx_free(struct mt76_dev *dev);
 void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 1e34b578b151..1b00ae4465a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -131,7 +131,7 @@ mt76_write_mac_initvals(struct mt76x2_dev *dev)
 		{ MT_RX_FILTR_CFG,		0x00015f97 },
 		{ MT_LEGACY_BASIC_RATE,		0x0000017f },
 		{ MT_HT_BASIC_RATE,		0x00004003 },
-		{ MT_PN_PAD_MODE,		0x00000002 },
+		{ MT_PN_PAD_MODE,		0x00000003 },
 		{ MT_TXOP_HLDR_ET,		0x00000002 },
 		{ 0xa44,			0x00000000 },
 		{ MT_HEADER_TRANS_CTRL_REG,	0x00000000 },
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index f56a8f459fe6..701e90a2858a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -257,12 +257,16 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
 	txwi->len_ctl = cpu_to_le16(skb->len);
 }
 
-static void mt76x2_remove_hdr_pad(struct sk_buff *skb)
+static void mt76x2_remove_hdr_pad(struct sk_buff *skb, int len)
 {
-	int len = ieee80211_get_hdrlen_from_skb(skb);
+	int hdrlen;
 
-	memmove(skb->data + 2, skb->data, len);
-	skb_pull(skb, 2);
+	if (!len)
+		return;
+
+	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+	memmove(skb->data + len, skb->data, hdrlen);
+	skb_pull(skb, len);
 }
 
 static struct mt76_wcid *
@@ -287,28 +291,49 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 {
 	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
 	struct mt76x2_rxwi *rxwi = rxi;
+	u32 rxinfo = le32_to_cpu(rxwi->rxinfo);
 	u32 ctl = le32_to_cpu(rxwi->ctl);
 	u16 rate = le16_to_cpu(rxwi->rate);
 	u16 tid_sn = le16_to_cpu(rxwi->tid_sn);
 	bool unicast = rxwi->rxinfo & cpu_to_le32(MT_RXINFO_UNICAST);
+	int pad_len = 0;
+	u8 pn_len;
 	u8 wcid;
 	int len;
 
+	if (rxinfo & MT_RXINFO_L2PAD)
+		pad_len += 2;
+
+	len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
+	pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo);
+	if (pn_len) {
+		int offset = ieee80211_get_hdrlen_from_skb(skb) + pad_len;
+		u8 *data = skb->data + offset;
+
+		status->iv[0] = data[7];
+		status->iv[1] = data[6];
+		status->iv[2] = data[5];
+		status->iv[3] = data[4];
+		status->iv[4] = data[1];
+		status->iv[5] = data[0];
+
+		pad_len += pn_len << 2;
+		len -= pn_len << 2;
+	}
+
+	mt76x2_remove_hdr_pad(skb, pad_len);
+
 	wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl);
 	status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast);
 
-	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
-		mt76x2_remove_hdr_pad(skb);
-
-	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_BA))
+	if (rxinfo & MT_RXINFO_BA)
 		status->aggr = true;
 
-	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
+	if (rxinfo & MT_RXINFO_DECRYPT) {
 		status->flag |= RX_FLAG_DECRYPTED;
 		status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
 	}
 
-	len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
 	if (WARN_ON_ONCE(len > skb->len))
 		return -EINVAL;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 08fe804c6a43..bf26284b9989 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -371,6 +371,7 @@ mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
 		key = NULL;
 	}
+	mt76_wcid_key_setup(&dev->mt76, wcid, key);
 
 	if (!msta) {
 		if (key || wcid->hw_key_idx == idx) {
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 8/8] mt76: validate rx CCMP PN
  2018-01-24 15:19 ` [PATCH 8/8] mt76: validate rx CCMP PN Felix Fietkau
@ 2018-01-24 15:56   ` Johannes Berg
  2018-01-24 16:11     ` Felix Fietkau
  2018-01-25 10:44   ` [PATCH v2 " Felix Fietkau
  1 sibling, 1 reply; 15+ messages in thread
From: Johannes Berg @ 2018-01-24 15:56 UTC (permalink / raw)
  To: Felix Fietkau, linux-wireless; +Cc: kvalo

On Wed, 2018-01-24 at 16:19 +0100, Felix Fietkau wrote:
> Apparently hardware does not perform CCMP PN validation in hardware, so
> we need to take care of this in the driver. This is important for
> protecting against replay attacks
> 
> +static int
> +mt76_check_ccmp_pn(struct sk_buff *skb)
> +{
> +	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
> +	struct mt76_wcid *wcid = status->wcid;
> +	int ret;
> +
> +	if (!(status->flag & RX_FLAG_DECRYPTED))
> +		return 0;
> +
> +	if (!wcid || !wcid->rx_check_pn)
> +		return 0;
> +
> +	BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
> +	ret = memcmp(status->iv, wcid->rx_key_pn[status->tid],
> +		     sizeof(status->iv));
> +	if (ret <= 0)
> +		return -EINVAL; /* replay */
> +
> +	memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv));
> +	status->flag |= RX_FLAG_PN_VALIDATED;

You shouldn't do this, try to somehow make it rely on mac80211 instead.

Otherwise, you really have to handle CCMP vs. fragmentation.

johannes

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 8/8] mt76: validate rx CCMP PN
  2018-01-24 15:56   ` Johannes Berg
@ 2018-01-24 16:11     ` Felix Fietkau
  2018-01-24 16:20       ` Johannes Berg
  0 siblings, 1 reply; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 16:11 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: kvalo

On 2018-01-24 16:56, Johannes Berg wrote:
> On Wed, 2018-01-24 at 16:19 +0100, Felix Fietkau wrote:
>> Apparently hardware does not perform CCMP PN validation in hardware, so
>> we need to take care of this in the driver. This is important for
>> protecting against replay attacks
>> 
>> +static int
>> +mt76_check_ccmp_pn(struct sk_buff *skb)
>> +{
>> +	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
>> +	struct mt76_wcid *wcid = status->wcid;
>> +	int ret;
>> +
>> +	if (!(status->flag & RX_FLAG_DECRYPTED))
>> +		return 0;
>> +
>> +	if (!wcid || !wcid->rx_check_pn)
>> +		return 0;
>> +
>> +	BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
>> +	ret = memcmp(status->iv, wcid->rx_key_pn[status->tid],
>> +		     sizeof(status->iv));
>> +	if (ret <= 0)
>> +		return -EINVAL; /* replay */
>> +
>> +	memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv));
>> +	status->flag |= RX_FLAG_PN_VALIDATED;
> 
> You shouldn't do this, try to somehow make it rely on mac80211 instead.
> 
> Otherwise, you really have to handle CCMP vs. fragmentation.
I guess I will have to look into fragmentation. I have a second driver
pending that only reports the CCMP PN outside of the packet, and for
performance reasons I really don't want to translate it and move it to a
place where mac80211 can parse it.
I'm also looking into doing parallel rx in software to see if I can get
more performance that way. I think for that I would also need CCMP PN
validation in the driver.

- Felix

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 8/8] mt76: validate rx CCMP PN
  2018-01-24 16:11     ` Felix Fietkau
@ 2018-01-24 16:20       ` Johannes Berg
  2018-01-24 16:23         ` Felix Fietkau
  0 siblings, 1 reply; 15+ messages in thread
From: Johannes Berg @ 2018-01-24 16:20 UTC (permalink / raw)
  To: Felix Fietkau, linux-wireless; +Cc: kvalo

On Wed, 2018-01-24 at 17:11 +0100, Felix Fietkau wrote:
> 
> I guess I will have to look into fragmentation. I have a second driver
> pending that only reports the CCMP PN outside of the packet, and for
> performance reasons I really don't want to translate it and move it to a
> place where mac80211 can parse it.
> I'm also looking into doing parallel rx in software to see if I can get
> more performance that way. I think for that I would also need CCMP PN
> validation in the driver.

Fair enough. We also do it, except we just decide that fragmented
packets we don't care and can use mac80211 - but we get the PN in-
frame, just can't always use mac80211 due to parallel RX.

johannes

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 8/8] mt76: validate rx CCMP PN
  2018-01-24 16:20       ` Johannes Berg
@ 2018-01-24 16:23         ` Felix Fietkau
  0 siblings, 0 replies; 15+ messages in thread
From: Felix Fietkau @ 2018-01-24 16:23 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: kvalo

On 2018-01-24 17:20, Johannes Berg wrote:
> On Wed, 2018-01-24 at 17:11 +0100, Felix Fietkau wrote:
>> 
>> I guess I will have to look into fragmentation. I have a second driver
>> pending that only reports the CCMP PN outside of the packet, and for
>> performance reasons I really don't want to translate it and move it to a
>> place where mac80211 can parse it.
>> I'm also looking into doing parallel rx in software to see if I can get
>> more performance that way. I think for that I would also need CCMP PN
>> validation in the driver.
> 
> Fair enough. We also do it, except we just decide that fragmented
> packets we don't care and can use mac80211 - but we get the PN in-
> frame, just can't always use mac80211 due to parallel RX.
I guess I could make the driver translate the header for fragmented
packets only.

- Felix

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 8/8] mt76: validate rx CCMP PN
  2018-01-24 15:19 ` [PATCH 8/8] mt76: validate rx CCMP PN Felix Fietkau
  2018-01-24 15:56   ` Johannes Berg
@ 2018-01-25 10:44   ` Felix Fietkau
  2018-01-26  9:21     ` [v2,8/8] " Kalle Valo
  1 sibling, 1 reply; 15+ messages in thread
From: Felix Fietkau @ 2018-01-25 10:44 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo, johannes

Apparently hardware does not perform CCMP PN validation in hardware, so
we need to take care of this in the driver. This is important for
protecting against replay attacks.

Since validation of fragmented frames is more complex, the CCMP header
for those is preserved. To keep the counter in sync, the first fragment
is verified by both mt76 and mac80211, and all other fragments only by
mac80211.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mac80211.c    | 65 ++++++++++++++++++++++++
 drivers/net/wireless/mediatek/mt76/mt76.h        | 12 ++++-
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c |  2 +-
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 61 +++++++++++++++++-----
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c |  1 +
 5 files changed, 125 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 77f1be161009..5fcb2deb89a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -384,6 +384,27 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
 }
 EXPORT_SYMBOL_GPL(mt76_get_survey);
 
+void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
+			 struct ieee80211_key_conf *key)
+{
+	struct ieee80211_key_seq seq;
+	int i;
+
+	wcid->rx_check_pn = false;
+
+	if (!key)
+		return;
+
+	if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+		wcid->rx_check_pn = true;
+
+	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+		ieee80211_get_key_rx_seq(key, i, &seq);
+		memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn));
+	}
+}
+EXPORT_SYMBOL(mt76_wcid_key_setup);
+
 static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -410,6 +431,45 @@ static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
 	return wcid_to_sta(mstat.wcid);
 }
 
+static int
+mt76_check_ccmp_pn(struct sk_buff *skb)
+{
+	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
+	struct mt76_wcid *wcid = status->wcid;
+	struct ieee80211_hdr *hdr;
+	int ret;
+
+	if (!(status->flag & RX_FLAG_DECRYPTED))
+		return 0;
+
+	if (!wcid || !wcid->rx_check_pn)
+		return 0;
+
+	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+		/*
+		 * Validate the first fragment both here and in mac80211
+		 * All further fragments will be validated by mac80211 only.
+		 */
+		hdr = (struct ieee80211_hdr *) skb->data;
+		if (ieee80211_is_frag(hdr) &&
+		    !ieee80211_is_first_frag(hdr->frame_control))
+			return 0;
+	}
+
+	BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
+	ret = memcmp(status->iv, wcid->rx_key_pn[status->tid],
+		     sizeof(status->iv));
+	if (ret <= 0)
+		return -EINVAL; /* replay */
+
+	memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv));
+
+	if (status->flag & RX_FLAG_IV_STRIPPED)
+		status->flag |= RX_FLAG_PN_VALIDATED;
+
+	return 0;
+}
+
 void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 		      int queue)
 {
@@ -421,6 +481,11 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 	    napi = &dev->napi[queue];
 
 	while ((skb = __skb_dequeue(frames)) != NULL) {
+		if (mt76_check_ccmp_pn(skb)) {
+			dev_kfree_skb(skb);
+			continue;
+		}
+
 		sta = mt76_rx_convert(skb);
 		ieee80211_rx_napi(dev->hw, sta, skb, napi);
 	}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index af98bc65c2e1..129015c9d116 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -131,6 +131,9 @@ struct mt76_wcid {
 
 	u8 sta:1;
 
+	u8 rx_check_pn;
+	u8 rx_key_pn[IEEE80211_NUM_TIDS][6];
+
 	__le16 tx_rate;
 	bool tx_rate_set;
 	u8 tx_rate_nss;
@@ -279,12 +282,14 @@ struct mt76_rx_status {
 
 	unsigned long reorder_time;
 
-	u8 aggr;
+	u8 iv[6];
+
+	u8 aggr:1;
 	u8 tid;
 	u16 seqno;
 
-	u32 flag;
 	u16 freq;
+	u32 flag;
 	u8 enc_flags;
 	u8 encoding:2, bw:3;
 	u8 rate_idx;
@@ -413,6 +418,9 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
 		       u16 ssn, u8 size);
 void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
 
+void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
+			 struct ieee80211_key_conf *key);
+
 /* internal */
 void mt76_tx_free(struct mt76_dev *dev);
 void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 1e34b578b151..1b00ae4465a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -131,7 +131,7 @@ mt76_write_mac_initvals(struct mt76x2_dev *dev)
 		{ MT_RX_FILTR_CFG,		0x00015f97 },
 		{ MT_LEGACY_BASIC_RATE,		0x0000017f },
 		{ MT_HT_BASIC_RATE,		0x00004003 },
-		{ MT_PN_PAD_MODE,		0x00000002 },
+		{ MT_PN_PAD_MODE,		0x00000003 },
 		{ MT_TXOP_HLDR_ET,		0x00000002 },
 		{ 0xa44,			0x00000000 },
 		{ MT_HEADER_TRANS_CTRL_REG,	0x00000000 },
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index f56a8f459fe6..6c30b5eaa9ca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -257,12 +257,16 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
 	txwi->len_ctl = cpu_to_le16(skb->len);
 }
 
-static void mt76x2_remove_hdr_pad(struct sk_buff *skb)
+static void mt76x2_remove_hdr_pad(struct sk_buff *skb, int len)
 {
-	int len = ieee80211_get_hdrlen_from_skb(skb);
+	int hdrlen;
 
-	memmove(skb->data + 2, skb->data, len);
-	skb_pull(skb, 2);
+	if (!len)
+		return;
+
+	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+	memmove(skb->data + len, skb->data, hdrlen);
+	skb_pull(skb, len);
 }
 
 static struct mt76_wcid *
@@ -287,28 +291,59 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 {
 	struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
 	struct mt76x2_rxwi *rxwi = rxi;
+	u32 rxinfo = le32_to_cpu(rxwi->rxinfo);
 	u32 ctl = le32_to_cpu(rxwi->ctl);
 	u16 rate = le16_to_cpu(rxwi->rate);
 	u16 tid_sn = le16_to_cpu(rxwi->tid_sn);
 	bool unicast = rxwi->rxinfo & cpu_to_le32(MT_RXINFO_UNICAST);
+	int pad_len = 0;
+	u8 pn_len;
 	u8 wcid;
 	int len;
 
+	if (rxinfo & MT_RXINFO_L2PAD)
+		pad_len += 2;
+
+	if (rxinfo & MT_RXINFO_DECRYPT) {
+		status->flag |= RX_FLAG_DECRYPTED;
+		status->flag |= RX_FLAG_MMIC_STRIPPED;
+		status->flag |= RX_FLAG_MIC_STRIPPED;
+		status->flag |= RX_FLAG_IV_STRIPPED;
+	}
+
 	wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl);
 	status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast);
 
-	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
-		mt76x2_remove_hdr_pad(skb);
+	len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
+	pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo);
+	if (pn_len) {
+		int offset = ieee80211_get_hdrlen_from_skb(skb) + pad_len;
+		u8 *data = skb->data + offset;
+
+		status->iv[0] = data[7];
+		status->iv[1] = data[6];
+		status->iv[2] = data[5];
+		status->iv[3] = data[4];
+		status->iv[4] = data[1];
+		status->iv[5] = data[0];
+
+		/*
+		 * Driver CCMP validation can't deal with fragments.
+		 * Let mac80211 take care of it.
+		 */
+		if (rxinfo & MT_RXINFO_FRAG) {
+			status->flag &= ~RX_FLAG_IV_STRIPPED;
+		} else {
+			pad_len += pn_len << 2;
+			len -= pn_len << 2;
+		}
+	}
+
+	mt76x2_remove_hdr_pad(skb, pad_len);
 
-	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_BA))
+	if (rxinfo & MT_RXINFO_BA)
 		status->aggr = true;
 
-	if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
-		status->flag |= RX_FLAG_DECRYPTED;
-		status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
-	}
-
-	len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
 	if (WARN_ON_ONCE(len > skb->len))
 		return -EINVAL;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 08fe804c6a43..bf26284b9989 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -371,6 +371,7 @@ mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
 		key = NULL;
 	}
+	mt76_wcid_key_setup(&dev->mt76, wcid, key);
 
 	if (!msta) {
 		if (key || wcid->hw_key_idx == idx) {
-- 
2.14.2

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [1/8] mt76: retry rx polling as long as there is budget left
  2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
                   ` (6 preceding siblings ...)
  2018-01-24 15:19 ` [PATCH 8/8] mt76: validate rx CCMP PN Felix Fietkau
@ 2018-01-26  9:20 ` Kalle Valo
  7 siblings, 0 replies; 15+ messages in thread
From: Kalle Valo @ 2018-01-26  9:20 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-wireless

Felix Fietkau <nbd@nbd.name> wrote:

> Sending frames to mac80211 needs time, which could allow for more rx
> packets to end up in the DMA ring. Retry polling until there are no more
> frames left. Improves rx latency under load.
> 
> Signed-off-by: Felix Fietkau <nbd@nbd.name>

7 patches applied to wireless-drivers-next.git, thanks.

2b4307f538ab mt76: retry rx polling as long as there is budget left
1342cfea315a mt76: fix TSF value in probe responses
4e34249e9565 mt76: add an intermediate struct for rx status information
9c68a57bc22d mt76: get station pointer by wcid and pass it to mac80211
aee5b8cf2477 mt76: implement A-MPDU rx reordering in the driver code
9d9d738b4b54 mt76: split mt76_rx_complete
3675302de8e9 mt76: pass the per-vif wcid to the core for multicast rx

-- 
https://patchwork.kernel.org/patch/10182707/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [v2,8/8] mt76: validate rx CCMP PN
  2018-01-25 10:44   ` [PATCH v2 " Felix Fietkau
@ 2018-01-26  9:21     ` Kalle Valo
  0 siblings, 0 replies; 15+ messages in thread
From: Kalle Valo @ 2018-01-26  9:21 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-wireless, johannes

Felix Fietkau <nbd@nbd.name> wrote:

> Apparently hardware does not perform CCMP PN validation in hardware, so
> we need to take care of this in the driver. This is important for
> protecting against replay attacks.
> 
> Since validation of fragmented frames is more complex, the CCMP header
> for those is preserved. To keep the counter in sync, the first fragment
> is verified by both mt76 and mac80211, and all other fragments only by
> mac80211.
> 
> Signed-off-by: Felix Fietkau <nbd@nbd.name>

Patch applied to wireless-drivers-next.git, thanks.

30ce7f4456ae mt76: validate rx CCMP PN

-- 
https://patchwork.kernel.org/patch/10183891/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2018-01-26  9:21 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-24 15:19 [PATCH 1/8] mt76: retry rx polling as long as there is budget left Felix Fietkau
2018-01-24 15:19 ` [PATCH 2/8] mt76: fix TSF value in probe responses Felix Fietkau
2018-01-24 15:19 ` [PATCH 3/8] mt76: add an intermediate struct for rx status information Felix Fietkau
2018-01-24 15:19 ` [PATCH 4/8] mt76: get station pointer by wcid and pass it to mac80211 Felix Fietkau
2018-01-24 15:19 ` [PATCH 5/8] mt76: implement A-MPDU rx reordering in the driver code Felix Fietkau
2018-01-24 15:19 ` [PATCH 6/8] mt76: split mt76_rx_complete Felix Fietkau
2018-01-24 15:19 ` [PATCH 7/8] mt76: pass the per-vif wcid to the core for multicast rx Felix Fietkau
2018-01-24 15:19 ` [PATCH 8/8] mt76: validate rx CCMP PN Felix Fietkau
2018-01-24 15:56   ` Johannes Berg
2018-01-24 16:11     ` Felix Fietkau
2018-01-24 16:20       ` Johannes Berg
2018-01-24 16:23         ` Felix Fietkau
2018-01-25 10:44   ` [PATCH v2 " Felix Fietkau
2018-01-26  9:21     ` [v2,8/8] " Kalle Valo
2018-01-26  9:20 ` [1/8] mt76: retry rx polling as long as there is budget left Kalle Valo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.