Commit f6dc2095 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo

ath10k: report A-MSDU subframes individually

HW reports each A-MSDU subframe as a separate
sk_buff. It is impossible to configure it to
behave differently.

Until now ath10k was reconstructing A-MSDUs from
subframes which involved a lot of memory
operations. This proved to be a significant
contributor to degraded RX performance.
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 1f8bb151
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
/* when under memory pressure rx ring refill may fail and needs a retry */ /* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50 #define HTT_RX_RING_REFILL_RETRY_MS 50
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt) static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
{ {
int size; int size;
...@@ -591,136 +595,111 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr) ...@@ -591,136 +595,111 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
return false; return false;
} }
static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt, struct rfc1042_hdr {
struct htt_rx_info *info) u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctrl;
u8 snap_oui[3];
__be16 snap_type;
} __packed;
struct amsdu_subframe_hdr {
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
__be16 len;
} __packed;
static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
struct htt_rx_info *info)
{ {
struct htt_rx_desc *rxd; struct htt_rx_desc *rxd;
struct sk_buff *amsdu;
struct sk_buff *first; struct sk_buff *first;
struct ieee80211_hdr *hdr;
struct sk_buff *skb = info->skb; struct sk_buff *skb = info->skb;
enum rx_msdu_decap_format fmt; enum rx_msdu_decap_format fmt;
enum htt_rx_mpdu_encrypt_type enctype; enum htt_rx_mpdu_encrypt_type enctype;
struct ieee80211_hdr *hdr;
u8 hdr_buf[64];
unsigned int hdr_len; unsigned int hdr_len;
int crypto_len;
rxd = (void *)skb->data - sizeof(*rxd); rxd = (void *)skb->data - sizeof(*rxd);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE); RX_MPDU_START_INFO0_ENCRYPT_TYPE);
/* FIXME: No idea what assumptions are safe here. Need logs */ hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
if ((fmt == RX_MSDU_DECAP_RAW && skb->next)) { hdr_len = ieee80211_hdrlen(hdr->frame_control);
ath10k_htt_rx_free_msdu_chain(skb->next); memcpy(hdr_buf, hdr, hdr_len);
skb->next = NULL; hdr = (struct ieee80211_hdr *)hdr_buf;
return -ENOTSUPP;
}
/* A-MSDU max is a little less than 8K */ /* FIXME: Hopefully this is a temporary measure.
amsdu = dev_alloc_skb(8*1024); *
if (!amsdu) { * Reporting individual A-MSDU subframes means each reported frame
ath10k_warn("A-MSDU allocation failed\n"); * shares the same sequence number.
ath10k_htt_rx_free_msdu_chain(skb->next); *
skb->next = NULL; * mac80211 drops frames it recognizes as duplicates, i.e.
return -ENOMEM; * retransmission flag is set and sequence number matches sequence
} * number from a previous frame (as per IEEE 802.11-2012: 9.3.2.10
* "Duplicate detection and recovery")
if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) { *
int hdrlen; * To avoid frames being dropped clear retransmission flag for all
* received A-MSDUs.
hdr = (void *)rxd->rx_hdr_status; *
hdrlen = ieee80211_hdrlen(hdr->frame_control); * Worst case: actual duplicate frames will be reported but this should
memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen); * still be handled gracefully by other OSI/ISO layers. */
} hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_RETRY);
first = skb; first = skb;
while (skb) { while (skb) {
void *decap_hdr; void *decap_hdr;
int decap_len = 0; int len;
rxd = (void *)skb->data - sizeof(*rxd); rxd = (void *)skb->data - sizeof(*rxd);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT); RX_MSDU_START_INFO1_DECAP_FORMAT);
decap_hdr = (void *)rxd->rx_hdr_status; decap_hdr = (void *)rxd->rx_hdr_status;
if (skb == first) { skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
/* We receive linked A-MSDU subframe skbuffs. The
* first one contains the original 802.11 header (and
* possible crypto param) in the RX descriptor. The
* A-MSDU subframe header follows that. Each part is
* aligned to 4 byte boundary. */
hdr = (void *)amsdu->data;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
decap_hdr += roundup(hdr_len, 4);
decap_hdr += roundup(crypto_len, 4);
}
/* When fmt == RX_MSDU_DECAP_8023_SNAP_LLC:
*
* SNAP 802.3 consists of:
* [dst:6][src:6][len:2][dsap:1][ssap:1][ctl:1][snap:5]
* [data][fcs:4].
*
* Since this overlaps with A-MSDU header (da, sa, len)
* there's nothing extra to do. */
if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
/* Ethernet2 decap inserts ethernet header in place of
* A-MSDU subframe header. */
skb_pull(skb, 6 + 6 + 2);
/* A-MSDU subframe header length */
decap_len += 6 + 6 + 2;
/* Ethernet2 decap also strips the LLC/SNAP so we need
* to re-insert it. The LLC/SNAP follows A-MSDU
* subframe header. */
/* FIXME: Not all LLCs are 8 bytes long */
decap_len += 8;
memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
}
if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) { /* First frame in an A-MSDU chain has more decapped data. */
/* Native Wifi decap inserts regular 802.11 header if (skb == first) {
* in place of A-MSDU subframe header. */ len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
hdr = (struct ieee80211_hdr *)skb->data; len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
skb_pull(skb, ieee80211_hdrlen(hdr->frame_control)); 4);
decap_hdr += len;
/* A-MSDU subframe header length */
decap_len += 6 + 6 + 2;
memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
} }
if (fmt == RX_MSDU_DECAP_RAW) switch (fmt) {
skb_trim(skb, skb->len - 4); /* remove FCS */ case RX_MSDU_DECAP_RAW:
skb_trim(skb, skb->len - FCS_LEN);
memcpy(skb_put(amsdu, skb->len), skb->data, skb->len); break;
case RX_MSDU_DECAP_NATIVE_WIFI:
/* A-MSDU subframes are padded to 4bytes break;
* but relative to first subframe, not the whole MPDU */ case RX_MSDU_DECAP_ETHERNET2_DIX:
if (skb->next && ((decap_len + skb->len) & 3)) { len = 0;
int padlen = 4 - ((decap_len + skb->len) & 3); len += sizeof(struct rfc1042_hdr);
memset(skb_put(amsdu, padlen), 0, padlen); len += sizeof(struct amsdu_subframe_hdr);
skb_pull(skb, sizeof(struct ethhdr));
memcpy(skb_push(skb, len), decap_hdr, len);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
} }
info->skb = skb;
info->encrypt_type = enctype;
skb = skb->next; skb = skb->next;
} info->skb->next = NULL;
info->skb = amsdu;
info->encrypt_type = enctype;
ath10k_htt_rx_free_msdu_chain(first); ath10k_process_rx(htt->ar, info);
}
return 0; /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
* monitor interface active for sniffing purposes. */
} }
static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
{ {
struct sk_buff *skb = info->skb; struct sk_buff *skb = info->skb;
struct htt_rx_desc *rxd; struct htt_rx_desc *rxd;
...@@ -742,6 +721,8 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) ...@@ -742,6 +721,8 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
RX_MPDU_START_INFO0_ENCRYPT_TYPE); RX_MPDU_START_INFO0_ENCRYPT_TYPE);
hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN; hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
switch (fmt) { switch (fmt) {
case RX_MSDU_DECAP_RAW: case RX_MSDU_DECAP_RAW:
/* remove trailing FCS */ /* remove trailing FCS */
...@@ -782,7 +763,8 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) ...@@ -782,7 +763,8 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
info->skb = skb; info->skb = skb;
info->encrypt_type = enctype; info->encrypt_type = enctype;
return 0;
ath10k_process_rx(htt->ar, info);
} }
static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb) static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
...@@ -854,8 +836,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, ...@@ -854,8 +836,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
int fw_desc_len; int fw_desc_len;
u8 *fw_desc; u8 *fw_desc;
int i, j; int i, j;
int ret;
int ip_summed;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
...@@ -930,11 +910,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, ...@@ -930,11 +910,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
continue; continue;
} }
/* The skb is not yet processed and it may be
* reallocated. Since the offload is in the original
* skb extract the checksum now and assign it later */
ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
info.skb = msdu_head; info.skb = msdu_head;
info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
info.signal = ATH10K_DEFAULT_NOISE_FLOOR; info.signal = ATH10K_DEFAULT_NOISE_FLOOR;
...@@ -947,24 +922,9 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, ...@@ -947,24 +922,9 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
hdr = ath10k_htt_rx_skb_get_hdr(msdu_head); hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
if (ath10k_htt_rx_hdr_is_amsdu(hdr)) if (ath10k_htt_rx_hdr_is_amsdu(hdr))
ret = ath10k_htt_rx_amsdu(htt, &info); ath10k_htt_rx_amsdu(htt, &info);
else else
ret = ath10k_htt_rx_msdu(htt, &info); ath10k_htt_rx_msdu(htt, &info);
if (ret && !info.fcs_err) {
ath10k_warn("error processing msdus %d\n", ret);
dev_kfree_skb_any(info.skb);
continue;
}
if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
info.skb->ip_summed = ip_summed;
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
info.skb->data, info.skb->len);
ath10k_process_rx(htt->ar, &info);
} }
} }
......
...@@ -268,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) ...@@ -268,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
status->vht_nss, status->vht_nss,
status->freq, status->freq,
status->band); status->band);
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
info->skb->data, info->skb->len);
ieee80211_rx(ar->hw, info->skb); ieee80211_rx(ar->hw, info->skb);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment