2020-06-10 04:33:18

by Sriram R

[permalink] [raw]
Subject: [PATCH 0/2] mac80211: add 802.11 decapsulation offload support

Adding support for offloading 802.11 decapsulation to the HW.
This helps in RX path optmization to reduces the CPU cycles
spent on host CPU for doing the conversion from 802.11 format
to ethernet format and thereby improving the performance of the device.

Sriram R (2):
mac80211: add receive path for ethernet frame format
ath11k: add rx path 802.11 decapsulation offloading support

drivers/net/wireless/ath/ath11k/core.h | 3 +
drivers/net/wireless/ath/ath11k/dp_rx.c | 185 ++++++++++++++----------
drivers/net/wireless/ath/ath11k/hal_desc.h | 2 +
drivers/net/wireless/ath/ath11k/mac.c | 20 +++
include/net/mac80211.h | 20 +++
net/mac80211/rx.c | 222 ++++++++++++++++++++++++++++-
6 files changed, 373 insertions(+), 79 deletions(-)

--
2.7.4


2020-06-10 04:33:18

by Sriram R

[permalink] [raw]
Subject: [PATCH 1/2] mac80211: add receive path for ethernet frame format

Implement rx path which does fewer processing on the received data
frame which has already gone through 802.11 header decapsulation
and other functionalities which require 802.11 header in the low
level driver or hardware. Currently this rx path is restricted
to AP and STA mode, but can be extended for Adhoc mode as well.

It is upto to the low level driver to invoke the correct API and
make sure if the frame that it passes is in ethernet format and
the sta pointer is valid.

Co-developed-by: Manikanta Pubbisetty <[email protected]>
Signed-off-by: Manikanta Pubbisetty <[email protected]>
Co-developed-by: Vasanthakumar Thiagarajan <[email protected]>
Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
Signed-off-by: Sriram R <[email protected]>
---
include/net/mac80211.h | 20 +++++
net/mac80211/rx.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 237 insertions(+), 5 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7cb7124..9233a3b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1258,6 +1258,9 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* the "0-length PSDU" field included there. The value for it is
* in &struct ieee80211_rx_status. Note that if this value isn't
* known the frame shouldn't be reported.
+ * @RX_FLAG_80211_MCAST: If the receiver address (addr1) in the frame is
+ * multicast. This is used with the data frames by the drivers
+ * supporting 802.11 hdr decap offload.
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -1290,6 +1293,7 @@ enum mac80211_rx_flags {
RX_FLAG_RADIOTAP_HE_MU = BIT(27),
RX_FLAG_RADIOTAP_LSIG = BIT(28),
RX_FLAG_NO_PSDU = BIT(29),
+ RX_FLAG_80211_MCAST = BIT(30),
};

/**
@@ -4442,6 +4446,22 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw,
}

/**
+ * ieee80211_rx_8023 - Receive frames in 802.11 decapsulated format
+ *
+ * Low level driver capable of 802.11 header decap uses this function. The frame
+ * will be in ethernet format.
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other.
+ *
+ * @hw: the hardware this frame came in on
+ * @sta : the station the frame was received from, must not be %NULL
+ * @skb: the buffer to receive, owned by mac80211 after this call
+ * @napi: the NAPI context
+ */
+void ieee80211_rx_8023(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct sk_buff *skb, struct napi_struct *napi);
+
+/**
* ieee80211_sta_ps_transition - PS transition for connected sta
*
* When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 21854a61..d07281c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2508,13 +2508,14 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control)
return 0;
}

+static const u8 pae_group_addr[ETH_ALEN] __aligned(2) = {0x01, 0x80, 0xC2, 0x00,
+ 0x00, 0x03};
+
/*
* requires that rx->skb is a frame with ethernet header
*/
static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
{
- static const u8 pae_group_addr[ETH_ALEN] __aligned(2)
- = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;

/*
@@ -2570,6 +2571,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
struct sk_buff *skb, *xmit_skb;
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
struct sta_info *dsta;
+ struct ieee80211_sta_rx_stats *rx_stats;

skb = rx->skb;
xmit_skb = NULL;
@@ -2582,9 +2584,12 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
* for non-QoS-data frames. Here we know it's a data
* frame, so count MSDUs.
*/
- u64_stats_update_begin(&rx->sta->rx_stats.syncp);
- rx->sta->rx_stats.msdu[rx->seqno_idx]++;
- u64_stats_update_end(&rx->sta->rx_stats.syncp);
+ rx_stats = &rx->sta->rx_stats;
+ if (ieee80211_hw_check(&rx->local->hw, USES_RSS))
+ rx_stats = this_cpu_ptr(rx->sta->pcpu_rx_stats);
+ u64_stats_update_begin(&rx_stats->syncp);
+ rx_stats->msdu[rx->seqno_idx]++;
+ u64_stats_update_end(&rx_stats->syncp);
}

if ((sdata->vif.type == NL80211_IFTYPE_AP ||
@@ -4782,3 +4787,210 @@ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee80211_rx_irqsafe);
+
+/* Receive path for 80211 header decap offloaded data frames */
+
+static void
+ieee80211_rx_handle_8023(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, struct sk_buff *skb,
+ struct napi_struct *napi)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_vif *vif = &sdata->vif;
+ struct net_device *dev = sdata->dev;
+ struct ieee80211_rx_status *status;
+ struct ieee80211_key *key = NULL;
+ struct ieee80211_rx_data rx;
+ int i;
+ struct ethhdr *ehdr;
+ struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
+ struct ieee80211_supported_band *sband;
+
+ ehdr = (struct ethhdr *)skb->data;
+ status = IEEE80211_SKB_RXCB(skb);
+
+ if (WARN_ON(status->band >= NUM_NL80211_BANDS))
+ goto drop;
+
+ sband = local->hw.wiphy->bands[status->band];
+ if (WARN_ON(!sband))
+ goto drop;
+
+ if (ieee80211_hw_check(&local->hw, USES_RSS))
+ stats = this_cpu_ptr(sta->pcpu_rx_stats);
+
+ /* TODO: Extend ieee80211_rx_8023() with bssid so that Ethernet
+ * encap/decap can be supported in Adhoc interface type as well.
+ * Adhoc interface depends on bssid to update last_rx.
+ */
+ if (vif->type != NL80211_IFTYPE_STATION &&
+ vif->type != NL80211_IFTYPE_AP_VLAN &&
+ vif->type != NL80211_IFTYPE_AP)
+ goto drop;
+
+ if (unlikely(!test_sta_flag(sta, WLAN_STA_AUTHORIZED))) {
+ if (ehdr->h_proto != sdata->control_port_protocol)
+ goto drop;
+ else if (!ether_addr_equal(ehdr->h_dest, vif->addr) &&
+ !ether_addr_equal(ehdr->h_dest, pae_group_addr))
+ goto drop;
+ }
+
+ if (status->flag & RX_FLAG_80211_MCAST) {
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ key = rcu_dereference(sta->gtk[i]);
+ if (key)
+ break;
+ }
+ } else {
+ key = rcu_dereference(sta->ptk[sta->ptk_idx]);
+ }
+
+ if (key && unlikely(key->flags & KEY_FLAG_TAINTED))
+ goto drop;
+
+ if (status->flag & RX_FLAG_MMIC_ERROR) {
+ if (key)
+ key->u.tkip.mic_failures++;
+ goto mic_fail;
+ }
+
+#define ETH_RX_CRYPT_FLAGS (RX_FLAG_PN_VALIDATED | RX_FLAG_DECRYPTED)
+
+ if (key && (status->flag & ETH_RX_CRYPT_FLAGS) != ETH_RX_CRYPT_FLAGS)
+ goto drop;
+
+ if (!(status->flag & RX_FLAG_DUP_VALIDATED))
+ goto drop;
+
+ I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
+
+ if (!(status->flag & RX_FLAG_80211_MCAST)) {
+ stats->last_rx = jiffies;
+ stats->last_rate = sta_stats_encode_rate(status);
+ }
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !(status->flag & RX_FLAG_80211_MCAST))
+ ieee80211_sta_reset_conn_monitor(sdata);
+
+ u64_stats_update_begin(&stats->syncp);
+ stats->fragments++;
+ stats->packets++;
+ stats->bytes += skb->len;
+ u64_stats_update_end(&stats->syncp);
+
+ if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
+ stats->last_signal = status->signal;
+ if (!ieee80211_hw_check(&local->hw, USES_RSS))
+ ewma_signal_add(&sta->rx_stats_avg.signal,
+ -status->signal);
+ }
+
+ if (status->chains) {
+ stats->chains = status->chains;
+ for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
+ int signal = status->chain_signal[i];
+
+ if (!(status->chains & BIT(i)))
+ continue;
+
+ stats->chain_signal_last[i] = signal;
+ if (!ieee80211_hw_check(&local->hw, USES_RSS))
+ ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
+ -signal);
+ }
+ }
+
+ if (unlikely(ehdr->h_proto == cpu_to_be16(ETH_P_TDLS))) {
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+
+ if (pskb_may_pull(skb,
+ offsetof(struct ieee80211_tdls_data, u)) &&
+ tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
+ tf->category == WLAN_CATEGORY_TDLS &&
+ (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
+ tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
+ skb_queue_tail(&local->skb_queue_tdls_chsw, skb);
+ schedule_work(&local->tdls_chsw_work);
+ return;
+ }
+ }
+
+ memset(&rx, 0, sizeof(rx));
+ rx.skb = skb;
+ rx.sdata = sdata;
+ rx.local = local;
+ rx.sta = sta;
+ rx.napi = napi;
+
+ if (vif->type == NL80211_IFTYPE_AP_VLAN && sdata->bss &&
+ unlikely(ehdr->h_proto == sdata->control_port_protocol)) {
+ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+ u.ap);
+ dev = sdata->dev;
+ rx.sdata = sdata;
+ }
+
+ rx.skb->dev = dev;
+
+ /* FIXME: Since rx.seqno_idx is not available for decap offloaded frames
+ * rx msdu stats update at the seqno_idx in ieee80211_deliver_skb()
+ * will always be updated at index 0 and will not be very useful.
+ */
+ ieee80211_deliver_skb(&rx);
+
+ return;
+
+mic_fail:
+
+ cfg80211_michael_mic_failure(sdata->dev, sta->addr,
+ is_multicast_ether_addr(ehdr->h_dest) ?
+ NL80211_KEYTYPE_GROUP :
+ NL80211_KEYTYPE_PAIRWISE,
+ key ? key->conf.keyidx : -1,
+ NULL, GFP_ATOMIC);
+
+drop:
+ stats->dropped++;
+ dev_kfree_skb(skb);
+}
+
+/* Receive path handler that a low level driver supporting 802.11 hdr decap
+ * offload can call. The frame is in ethernet format and the assumption is
+ * all necessary operations like decryption, defrag, deaggregation, etc.
+ * requiring 802.11 headers are already performed in the low level driver
+ * or hardware.
+ */
+void ieee80211_rx_8023(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta, struct sk_buff *skb,
+ struct napi_struct *napi)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+
+ if (unlikely(local->quiescing || local->suspended))
+ goto drop;
+
+ if (unlikely(local->in_reconfig))
+ goto drop;
+
+ if (WARN_ON(!local->started))
+ goto drop;
+
+ if (!pubsta)
+ goto drop;
+
+ sta = container_of(pubsta, struct sta_info, sta);
+
+ /* TODO: Toggle Rx throughput LED */
+
+ rcu_read_lock();
+ ieee80211_rx_handle_8023(sta->sdata, sta, skb, napi);
+ rcu_read_unlock();
+
+ return;
+drop:
+ kfree_skb(skb);
+}
+EXPORT_SYMBOL(ieee80211_rx_8023);
--
2.7.4

2020-06-10 04:33:18

by Sriram R

[permalink] [raw]
Subject: [PATCH 2/2] ath11k: add rx path 802.11 decapsulation offloading support

Adding 802.11 decapsulation offloading support to the driver.
Since the packets delivered to the driver are in 802.3 format, these
can be sent to the network core with minimal processing in mac80211.
This helps in releasing some CPU cycles in the host processor and
thereby improving the performance.

Ethernet decap can be enabled by using a modparam as shown below:
insmod ath11k frame_mode=2

Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01213-QCAHKSWPL_SILICONZ-1

Signed-off-by: Manikanta Pubbisetty <[email protected]>
Signed-off-by: Sriram R <[email protected]>
---
drivers/net/wireless/ath/ath11k/core.h | 3 +
drivers/net/wireless/ath/ath11k/dp_rx.c | 185 +++++++++++++++++------------
drivers/net/wireless/ath/ath11k/hal_desc.h | 2 +
drivers/net/wireless/ath/ath11k/mac.c | 20 ++++
4 files changed, 136 insertions(+), 74 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index e04f0e7..f9e0f30 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -77,6 +77,7 @@ struct ath11k_skb_rxcb {
bool is_first_msdu;
bool is_last_msdu;
bool is_continuation;
+ bool is_mcbc;
struct hal_rx_desc *rx_desc;
u8 err_rel_src;
u8 err_code;
@@ -84,6 +85,8 @@ struct ath11k_skb_rxcb {
u8 unmapped;
u8 is_frag;
u8 tid;
+ u16 peer_id;
+ u16 seq_no;
};

enum ath11k_hw_rev {
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index a54610d..d5cf15c 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -260,6 +260,13 @@ static u32 ath11k_dp_rxdesc_get_ppduid(struct hal_rx_desc *rx_desc)
return __le16_to_cpu(rx_desc->mpdu_start.phy_ppdu_id);
}

+static bool ath11k_dp_rx_h_attn_is_mcbc(struct hal_rx_desc *desc)
+{
+ return ath11k_dp_rx_h_msdu_end_first_msdu(desc) &&
+ (!!FIELD_GET(RX_ATTENTION_INFO1_MCAST_BCAST,
+ __le32_to_cpu(desc->attention.info1)));
+}
+
/* Returns number of Rx buffers replenished */
int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
struct dp_rxdma_ring *rx_ring,
@@ -2012,9 +2019,15 @@ static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu,
decrypted);
break;
case DP_RX_DECAP_TYPE_ETHERNET2_DIX:
- /* TODO undecap support for middle/last msdu's of amsdu */
- ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr,
- enctype, status);
+ /* PN for mcast packets will be validated in mac80211;
+ * remove eth header and add 802.11 header.
+ */
+ if (ATH11K_SKB_RXCB(msdu)->is_mcbc) {
+ status->flag |= RX_FLAG_80211_MCAST;
+ if (decrypted)
+ ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr,
+ enctype, status);
+ }
break;
case DP_RX_DECAP_TYPE_8023:
/* TODO: Handle undecap for these formats */
@@ -2022,39 +2035,65 @@ static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu,
}
}

+static struct ath11k_peer *
+ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu)
+{
+ struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
+ struct hal_rx_desc *rx_desc = rxcb->rx_desc;
+ struct ath11k_peer *peer = NULL;
+
+ lockdep_assert_held(&ab->base_lock);
+
+ if (rxcb->peer_id)
+ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
+ if (peer)
+ return peer;
+
+ if (!rx_desc ||
+ !(rx_desc->mpdu_start.info1 & RX_MPDU_START_INFO1_MAC_ADDR2_VALID))
+ return NULL;
+
+ peer = ath11k_peer_find_by_addr(ab, rx_desc->mpdu_start.addr2);
+
+ return peer;
+}
+
static void ath11k_dp_rx_h_mpdu(struct ath11k *ar,
struct sk_buff *msdu,
struct hal_rx_desc *rx_desc,
struct ieee80211_rx_status *rx_status)
{
- bool fill_crypto_hdr, mcast;
+ bool fill_crypto_hdr;
enum hal_encrypt_type enctype;
bool is_decrypted = false;
+ struct ath11k_skb_rxcb *rxcb;
struct ieee80211_hdr *hdr;
struct ath11k_peer *peer;
u32 err_bitmap;

- hdr = (struct ieee80211_hdr *)msdu->data;
-
/* PN for multicast packets will be checked in mac80211 */
+ rxcb = ATH11K_SKB_RXCB(msdu);
+ rxcb->is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(rx_desc);
+ fill_crypto_hdr = rxcb->is_mcbc;

- mcast = is_multicast_ether_addr(hdr->addr1);
- fill_crypto_hdr = mcast;
-
- is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc);
+ if (rxcb->is_mcbc) {
+ rxcb->peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(rx_desc);
+ rxcb->seq_no = ath11k_dp_rx_h_mpdu_start_seq_no(rx_desc);
+ }

spin_lock_bh(&ar->ab->base_lock);
- peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr2);
+ peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu);
if (peer) {
- if (mcast)
+ if (rxcb->is_mcbc)
enctype = peer->sec_type_grp;
else
enctype = peer->sec_type;
} else {
- enctype = HAL_ENCRYPT_TYPE_OPEN;
+ enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc);
}
spin_unlock_bh(&ar->ab->base_lock);

+ is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc);
err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_desc);

/* Clear per-MPDU flags while leaving per-PPDU flags intact */
@@ -2087,8 +2126,11 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar,
if (!is_decrypted || fill_crypto_hdr)
return;

- hdr = (void *)msdu->data;
- hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ if (ath11k_dp_rx_h_msdu_start_decap_type(rx_desc) !=
+ DP_RX_DECAP_TYPE_ETHERNET2_DIX) {
+ hdr = (void *)msdu->data;
+ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ }
}

static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc,
@@ -2193,51 +2235,48 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
ath11k_dp_rx_h_rate(ar, rx_desc, rx_status);
}

-static char *ath11k_print_get_tid(struct ieee80211_hdr *hdr, char *out,
- size_t size)
-{
- u8 *qc;
- int tid;
-
- if (!ieee80211_is_data_qos(hdr->frame_control))
- return "";
-
- qc = ieee80211_get_qos_ctl(hdr);
- tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
- snprintf(out, size, "tid %d", tid);
-
- return out;
-}
-
static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *napi,
- struct sk_buff *msdu)
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status)
{
static const struct ieee80211_radiotap_he known = {
- .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
+ .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN),
.data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
};
- struct ieee80211_rx_status *status;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+ struct ieee80211_rx_status *rx_status;
struct ieee80211_radiotap_he *he = NULL;
- char tid[32];
+ struct ieee80211_sta *pubsta = NULL;
+ struct ath11k_peer *peer;
+ struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
+ bool is_mcbc = rxcb->is_mcbc;
+ u8 decap;

- status = IEEE80211_SKB_RXCB(msdu);
- if (status->encoding == RX_ENC_HE) {
+ if (status->encoding == RX_ENC_HE && status->flag & RX_FLAG_ONLY_MONITOR) {
he = skb_push(msdu, sizeof(known));
memcpy(he, &known, sizeof(known));
status->flag |= RX_FLAG_RADIOTAP_HE;
}

+ if (!(status->flag & RX_FLAG_ONLY_MONITOR))
+ decap = ath11k_dp_rx_h_msdu_start_decap_type(rxcb->rx_desc);
+ else
+ decap = DP_RX_DECAP_TYPE_RAW;
+
+ spin_lock_bh(&ar->ab->base_lock);
+ peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu);
+ if (peer && peer->sta)
+ pubsta = peer->sta;
+ spin_unlock_bh(&ar->ab->base_lock);
+
ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
- "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
+ "rx skb %pK len %u peer %pM tid %u %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
msdu,
msdu->len,
- ieee80211_get_SA(hdr),
- ath11k_print_get_tid(hdr, tid, sizeof(tid)),
- is_multicast_ether_addr(ieee80211_get_DA(hdr)) ?
- "mcast" : "ucast",
- (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4,
+ peer ? peer->addr : NULL,
+ rxcb->tid,
+ (rxcb->rx_desc->msdu_end.info2 &
+ RX_MSDU_END_INFO2_DA_IS_MCBC) ? "mcast" : "ucast",
+ rxcb->seq_no,
(status->encoding == RX_ENC_LEGACY) ? "legacy" : "",
(status->encoding == RX_ENC_HT) ? "ht" : "",
(status->encoding == RX_ENC_VHT) ? "vht" : "",
@@ -2254,20 +2293,28 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap
!!(status->flag & RX_FLAG_MMIC_ERROR),
!!(status->flag & RX_FLAG_AMSDU_MORE));

+ rx_status = IEEE80211_SKB_RXCB(msdu);
+ *rx_status = *status;
+
/* TODO: trace rx packet */

- ieee80211_rx_napi(ar->hw, NULL, msdu, napi);
+ /* PN for multicast packets are validated in mac80211,
+ * so send them through ieee80211_rx_napi().
+ */
+ if (decap != DP_RX_DECAP_TYPE_ETHERNET2_DIX ||
+ (is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
+ ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+ else
+ ieee80211_rx_8023(ar->hw, pubsta, msdu, napi);
}

static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
struct sk_buff *msdu,
- struct sk_buff_head *msdu_list)
+ struct sk_buff_head *msdu_list,
+ struct ieee80211_rx_status *rx_status)
{
struct hal_rx_desc *rx_desc, *lrx_desc;
- struct ieee80211_rx_status rx_status = {0};
- struct ieee80211_rx_status *status;
struct ath11k_skb_rxcb *rxcb;
- struct ieee80211_hdr *hdr;
struct sk_buff *last_buf;
u8 l3_pad_bytes;
u8 *hdr_status;
@@ -2321,19 +2368,11 @@ static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
}
}

- hdr = (struct ieee80211_hdr *)msdu->data;
-
- /* Process only data frames */
- if (!ieee80211_is_data(hdr->frame_control))
- return -EINVAL;
+ ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status);
+ ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status);

- ath11k_dp_rx_h_ppdu(ar, rx_desc, &rx_status);
- ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, &rx_status);
+ rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED;

- rx_status.flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED;
-
- status = IEEE80211_SKB_RXCB(msdu);
- *status = rx_status;
return 0;

free_out:
@@ -2348,6 +2387,7 @@ static void ath11k_dp_rx_process_received_packets(struct ath11k_base *ab,
struct ath11k_skb_rxcb *rxcb;
struct sk_buff *msdu;
struct ath11k *ar;
+ struct ieee80211_rx_status rx_status = {0};
u8 mac_id;
int ret;

@@ -2370,7 +2410,7 @@ static void ath11k_dp_rx_process_received_packets(struct ath11k_base *ab,
continue;
}

- ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list);
+ ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status);
if (ret) {
ath11k_dbg(ab, ATH11K_DBG_DATA,
"Unable to process msdu %d", ret);
@@ -2378,7 +2418,7 @@ static void ath11k_dp_rx_process_received_packets(struct ath11k_base *ab,
continue;
}

- ath11k_dp_rx_deliver_msdu(ar, napi, msdu);
+ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status);
(*quota)--;
}

@@ -2460,9 +2500,13 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id,
RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU);
rxcb->is_continuation = !!(desc.rx_msdu_info.info0 &
RX_MSDU_DESC_INFO0_MSDU_CONTINUATION);
- rxcb->mac_id = mac_id;
+ rxcb->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID,
+ desc.rx_mpdu_info.meta_data);
+ rxcb->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM,
+ desc.rx_mpdu_info.info0);
rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM,
desc.info0);
+ rxcb->mac_id = mac_id;

__skb_queue_tail(&msdu_list, msdu);

@@ -3749,7 +3793,6 @@ static void ath11k_dp_rx_wbm_err(struct ath11k *ar,
{
struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
struct ieee80211_rx_status rxs = {0};
- struct ieee80211_rx_status *status;
bool drop = true;

switch (rxcb->err_rel_src) {
@@ -3769,10 +3812,7 @@ static void ath11k_dp_rx_wbm_err(struct ath11k *ar,
return;
}

- status = IEEE80211_SKB_RXCB(msdu);
- *status = rxs;
-
- ath11k_dp_rx_deliver_msdu(ar, napi, msdu);
+ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rxs);
}

int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab,
@@ -4599,7 +4639,7 @@ static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id,
{
struct ath11k_pdev_dp *dp = &ar->dp;
struct sk_buff *mon_skb, *skb_next, *header;
- struct ieee80211_rx_status *rxs = &dp->rx_status, *status;
+ struct ieee80211_rx_status *rxs = &dp->rx_status;

mon_skb = ath11k_dp_rx_mon_merg_msdus(ar, mac_id, head_msdu,
tail_msdu, rxs);
@@ -4625,10 +4665,7 @@ static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id,
}
rxs->flag |= RX_FLAG_ONLY_MONITOR;

- status = IEEE80211_SKB_RXCB(mon_skb);
- *status = *rxs;
-
- ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb);
+ ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb, rxs);
mon_skb = skb_next;
} while (mon_skb);
rxs->flag = 0;
diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h
index 8a59281..a47c288 100644
--- a/drivers/net/wireless/ath/ath11k/hal_desc.h
+++ b/drivers/net/wireless/ath/ath11k/hal_desc.h
@@ -496,6 +496,8 @@ struct hal_tlv_hdr {
#define RX_MPDU_DESC_INFO0_DA_IDX_TIMEOUT BIT(29)
#define RX_MPDU_DESC_INFO0_RAW_MPDU BIT(30)

+#define RX_MPDU_DESC_META_DATA_PEER_ID GENMASK(15, 0)
+
struct rx_mpdu_desc {
u32 info0; /* %RX_MPDU_DESC_INFO */
u32 meta_data;
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 2836a0f..de6c132 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -4278,6 +4278,26 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
goto err_vdev_del;
}

+ /* Do not enable ethernet mode for mesh vifs, packets in mesh network
+ * can be forwarded to other mesh nodes and mac80211 expects the packet
+ * in 802.11 format. Also, please note that ethernet mode shall not be
+ * used on interface where 4addr mode is enabled as we might not have
+ * 4addr related config during add_interface().
+ */
+ if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET &&
+ vif->type != NL80211_IFTYPE_MESH_POINT) {
+ param_id = WMI_VDEV_PARAM_RX_DECAP_TYPE;
+ param_value = ATH11K_HW_TXRX_ETHERNET;
+
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+ param_id, param_value);
+ if (ret) {
+ ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
+ arvif->vdev_id, ret);
+ goto err_vdev_del;
+ }
+ }
+
nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_NSS, nss);
--
2.7.4

2020-08-03 10:09:59

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/2] mac80211: add receive path for ethernet frame format

Hi,

sorry for the delay.

> +static const u8 pae_group_addr[ETH_ALEN] __aligned(2) = {0x01, 0x80, 0xC2, 0x00,
> + 0x00, 0x03};

Why the strange line breaking, and not preserve like it was:

> static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
> {
> - static const u8 pae_group_addr[ETH_ALEN] __aligned(2)
> - = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
> struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;


> @@ -2582,9 +2584,12 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
> * for non-QoS-data frames. Here we know it's a data
> * frame, so count MSDUs.
> */
> - u64_stats_update_begin(&rx->sta->rx_stats.syncp);
> - rx->sta->rx_stats.msdu[rx->seqno_idx]++;
> - u64_stats_update_end(&rx->sta->rx_stats.syncp);
> + rx_stats = &rx->sta->rx_stats;
> + if (ieee80211_hw_check(&rx->local->hw, USES_RSS))
> + rx_stats = this_cpu_ptr(rx->sta->pcpu_rx_stats);
> + u64_stats_update_begin(&rx_stats->syncp);
> + rx_stats->msdu[rx->seqno_idx]++;
> + u64_stats_update_end(&rx_stats->syncp);
> }

This seems like an unrelated fix?

> + if (ieee80211_hw_check(&local->hw, USES_RSS))
> + stats = this_cpu_ptr(sta->pcpu_rx_stats);
> +
> + /* TODO: Extend ieee80211_rx_8023() with bssid so that Ethernet
> + * encap/decap can be supported in Adhoc interface type as well.
> + * Adhoc interface depends on bssid to update last_rx.

What would the plan for this be? And I guess by that I mostly mean
"where's the space for it"? In a function argument?

Any reason not to do this from the start, to avoid having to change the
API again later? Though I guess the bssid argument could then be allowed
to be NULL for the modes listed:

> + if (vif->type != NL80211_IFTYPE_STATION &&
> + vif->type != NL80211_IFTYPE_AP_VLAN &&
> + vif->type != NL80211_IFTYPE_AP)
> + goto drop;


> +
> + if (unlikely(!test_sta_flag(sta, WLAN_STA_AUTHORIZED))) {
> + if (ehdr->h_proto != sdata->control_port_protocol)
> + goto drop;
> + else if (!ether_addr_equal(ehdr->h_dest, vif->addr) &&
> + !ether_addr_equal(ehdr->h_dest, pae_group_addr))
> + goto drop;
> + }

Maybe refactor ieee80211_frame_allowed() a bit? This is basically the
same as the first condition in there, after all. Then you'd also not
have to pull pae_group_addr to the file scope.

> + if (status->flag & RX_FLAG_80211_MCAST) {
> + for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
> + key = rcu_dereference(sta->gtk[i]);
> + if (key)
> + break;

What? This doesn't seem right at all.

Either you cannot support multiple GTKs (umm?) or you should probably
have the right one somehow? Or maybe you don't need to do anything per
key?

> + }
> + } else {
> + key = rcu_dereference(sta->ptk[sta->ptk_idx]);
> + }
> +
> + if (key && unlikely(key->flags & KEY_FLAG_TAINTED))
> + goto drop;
> +
> + if (status->flag & RX_FLAG_MMIC_ERROR) {
> + if (key)
> + key->u.tkip.mic_failures++;
> + goto mic_fail;
> + }

But then you do per-key things.

> + if (!(status->flag & RX_FLAG_DUP_VALIDATED))
> + goto drop;
> +
> + I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
> +
> + if (!(status->flag & RX_FLAG_80211_MCAST)) {
> + stats->last_rx = jiffies;
> + stats->last_rate = sta_stats_encode_rate(status);
> + }
> +
> + if (sdata->vif.type == NL80211_IFTYPE_STATION &&
> + !(status->flag & RX_FLAG_80211_MCAST))
> + ieee80211_sta_reset_conn_monitor(sdata);
> +
> + u64_stats_update_begin(&stats->syncp);
> + stats->fragments++;
> + stats->packets++;

fragments and packets don't need the begin/end, do they?

> + stats->bytes += skb->len;
> + u64_stats_update_end(&stats->syncp);
> +
> + if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
> + stats->last_signal = status->signal;
> + if (!ieee80211_hw_check(&local->hw, USES_RSS))
> + ewma_signal_add(&sta->rx_stats_avg.signal,
> + -status->signal);
> + }
> +
> + if (status->chains) {
> + stats->chains = status->chains;
> + for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
> + int signal = status->chain_signal[i];
> +
> + if (!(status->chains & BIT(i)))
> + continue;
> +
> + stats->chain_signal_last[i] = signal;
> + if (!ieee80211_hw_check(&local->hw, USES_RSS))
> + ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
> + -signal);
> + }
> + }

This seems very duplicated - can you refactor it?

> + if (unlikely(ehdr->h_proto == cpu_to_be16(ETH_P_TDLS))) {
> + struct ieee80211_tdls_data *tf = (void *)skb->data;
> +
> + if (pskb_may_pull(skb,
> + offsetof(struct ieee80211_tdls_data, u)) &&
> + tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
> + tf->category == WLAN_CATEGORY_TDLS &&
> + (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
> + tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
> + skb_queue_tail(&local->skb_queue_tdls_chsw, skb);
> + schedule_work(&local->tdls_chsw_work);
> + return;
> + }
> + }

same here.

Ultimately, it seems that the parts of the RX path _after_ conversion
really ought to be the same between the normal and offloaded conversion?
So why wouldn't those be (essentially) the same code?

> + /* FIXME: Since rx.seqno_idx is not available for decap offloaded frames
> + * rx msdu stats update at the seqno_idx in ieee80211_deliver_skb()
> + * will always be updated at index 0 and will not be very useful.
> + */
> + ieee80211_deliver_skb(&rx);

That's an issue. You should either fix that or prevent the bad counters
being shown to userspace - IMHO lack of stats is better than broken
stats.

johannes

2020-08-03 10:48:03

by Sriram R

[permalink] [raw]
Subject: Re: [PATCH 1/2] mac80211: add receive path for ethernet frame format

Hi,
On 2020-08-03 15:36, Johannes Berg wrote:
> Hi,
>
> sorry for the delay.
>
>> +static const u8 pae_group_addr[ETH_ALEN] __aligned(2) = {0x01, 0x80,
>> 0xC2, 0x00,
>> + 0x00, 0x03};
>
> Why the strange line breaking, and not preserve like it was:
I'll fix in next revision.
>
>> static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx,
>> __le16 fc)
>> {
>> - static const u8 pae_group_addr[ETH_ALEN] __aligned(2)
>> - = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
>> struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
>
>
>> @@ -2582,9 +2584,12 @@ ieee80211_deliver_skb(struct ieee80211_rx_data
>> *rx)
>> * for non-QoS-data frames. Here we know it's a data
>> * frame, so count MSDUs.
>> */
>> - u64_stats_update_begin(&rx->sta->rx_stats.syncp);
>> - rx->sta->rx_stats.msdu[rx->seqno_idx]++;
>> - u64_stats_update_end(&rx->sta->rx_stats.syncp);
>> + rx_stats = &rx->sta->rx_stats;
>> + if (ieee80211_hw_check(&rx->local->hw, USES_RSS))
>> + rx_stats = this_cpu_ptr(rx->sta->pcpu_rx_stats);
>> + u64_stats_update_begin(&rx_stats->syncp);
>> + rx_stats->msdu[rx->seqno_idx]++;
>> + u64_stats_update_end(&rx_stats->syncp);
>> }
>
> This seems like an unrelated fix?
Yes, I'll move this separately.
>
>> + if (ieee80211_hw_check(&local->hw, USES_RSS))
>> + stats = this_cpu_ptr(sta->pcpu_rx_stats);
>> +
>> + /* TODO: Extend ieee80211_rx_8023() with bssid so that Ethernet
>> + * encap/decap can be supported in Adhoc interface type as well.
>> + * Adhoc interface depends on bssid to update last_rx.
>
> What would the plan for this be? And I guess by that I mostly mean
> "where's the space for it"? In a function argument?
>
> Any reason not to do this from the start, to avoid having to change the
> API again later? Though I guess the bssid argument could then be
> allowed
> to be NULL for the modes listed:
Sure, i'll extend the api in next revision.
>
>> + if (vif->type != NL80211_IFTYPE_STATION &&
>> + vif->type != NL80211_IFTYPE_AP_VLAN &&
>> + vif->type != NL80211_IFTYPE_AP)
>> + goto drop;
>
>
>> +
>> + if (unlikely(!test_sta_flag(sta, WLAN_STA_AUTHORIZED))) {
>> + if (ehdr->h_proto != sdata->control_port_protocol)
>> + goto drop;
>> + else if (!ether_addr_equal(ehdr->h_dest, vif->addr) &&
>> + !ether_addr_equal(ehdr->h_dest, pae_group_addr))
>> + goto drop;
>> + }
>
> Maybe refactor ieee80211_frame_allowed() a bit? This is basically the
> same as the first condition in there, after all. Then you'd also not
> have to pull pae_group_addr to the file scope.
>
Sure. I'll refactor this and similar duplicate code below in next
revision.
>> + if (status->flag & RX_FLAG_80211_MCAST) {
>> + for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
>> + key = rcu_dereference(sta->gtk[i]);
>> + if (key)
>> + break;
>
> What? This doesn't seem right at all.
I'll update ieee80211_rx_8023() to get some meta data of the received
packet
such as key id and tid from the driver, so that this key id selection
and
tid stats issue pointed below is also resolved.
>
> Either you cannot support multiple GTKs (umm?) or you should probably
> have the right one somehow? Or maybe you don't need to do anything per
> key?
>
>> + }
>> + } else {
>> + key = rcu_dereference(sta->ptk[sta->ptk_idx]);
>> + }
>> +
>> + if (key && unlikely(key->flags & KEY_FLAG_TAINTED))
>> + goto drop;
>> +
>> + if (status->flag & RX_FLAG_MMIC_ERROR) {
>> + if (key)
>> + key->u.tkip.mic_failures++;
>> + goto mic_fail;
>> + }
>
> But then you do per-key things.
>
>> + if (!(status->flag & RX_FLAG_DUP_VALIDATED))
>> + goto drop;
>> +
>> + I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
>> +
>> + if (!(status->flag & RX_FLAG_80211_MCAST)) {
>> + stats->last_rx = jiffies;
>> + stats->last_rate = sta_stats_encode_rate(status);
>> + }
>> +
>> + if (sdata->vif.type == NL80211_IFTYPE_STATION &&
>> + !(status->flag & RX_FLAG_80211_MCAST))
>> + ieee80211_sta_reset_conn_monitor(sdata);
>> +
>> + u64_stats_update_begin(&stats->syncp);
>> + stats->fragments++;
>> + stats->packets++;
>
> fragments and packets don't need the begin/end, do they?
I'll remove it.
>
>> + stats->bytes += skb->len;
>> + u64_stats_update_end(&stats->syncp);
>> +
>> + if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
>> + stats->last_signal = status->signal;
>> + if (!ieee80211_hw_check(&local->hw, USES_RSS))
>> + ewma_signal_add(&sta->rx_stats_avg.signal,
>> + -status->signal);
>> + }
>> +
>> + if (status->chains) {
>> + stats->chains = status->chains;
>> + for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
>> + int signal = status->chain_signal[i];
>> +
>> + if (!(status->chains & BIT(i)))
>> + continue;
>> +
>> + stats->chain_signal_last[i] = signal;
>> + if (!ieee80211_hw_check(&local->hw, USES_RSS))
>> + ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
>> + -signal);
>> + }
>> + }
>
> This seems very duplicated - can you refactor it?
Sure.
>
>> + if (unlikely(ehdr->h_proto == cpu_to_be16(ETH_P_TDLS))) {
>> + struct ieee80211_tdls_data *tf = (void *)skb->data;
>> +
>> + if (pskb_may_pull(skb,
>> + offsetof(struct ieee80211_tdls_data, u)) &&
>> + tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
>> + tf->category == WLAN_CATEGORY_TDLS &&
>> + (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
>> + tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
>> + skb_queue_tail(&local->skb_queue_tdls_chsw, skb);
>> + schedule_work(&local->tdls_chsw_work);
>> + return;
>> + }
>> + }
>
> same here.
>
> Ultimately, it seems that the parts of the RX path _after_ conversion
> really ought to be the same between the normal and offloaded
> conversion?
> So why wouldn't those be (essentially) the same code?
Yes, i'll remove these duplicate code in next revision.
>
>> + /* FIXME: Since rx.seqno_idx is not available for decap offloaded
>> frames
>> + * rx msdu stats update at the seqno_idx in ieee80211_deliver_skb()
>> + * will always be updated at index 0 and will not be very useful.
>> + */
>> + ieee80211_deliver_skb(&rx);
>
> That's an issue. You should either fix that or prevent the bad counters
> being shown to userspace - IMHO lack of stats is better than broken
> stats.
As mentioned, we can extend the api to allow the driver to pass tid as
part of rx metadata and have the
stats updated.

Thanks for review.

Regards,
Sriram.R
>
> johannes