mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-07 21:24:00 +08:00
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: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
1f8bb1518e
commit
f6dc2095a0
@ -41,6 +41,10 @@
|
||||
/* when under memory pressure rx ring refill may fail and needs a retry */
|
||||
#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)
|
||||
{
|
||||
int size;
|
||||
@ -591,136 +595,111 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
|
||||
struct htt_rx_info *info)
|
||||
struct rfc1042_hdr {
|
||||
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 sk_buff *amsdu;
|
||||
struct sk_buff *first;
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct sk_buff *skb = info->skb;
|
||||
enum rx_msdu_decap_format fmt;
|
||||
enum htt_rx_mpdu_encrypt_type enctype;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 hdr_buf[64];
|
||||
unsigned int hdr_len;
|
||||
int crypto_len;
|
||||
|
||||
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),
|
||||
RX_MPDU_START_INFO0_ENCRYPT_TYPE);
|
||||
|
||||
/* FIXME: No idea what assumptions are safe here. Need logs */
|
||||
if ((fmt == RX_MSDU_DECAP_RAW && skb->next)) {
|
||||
ath10k_htt_rx_free_msdu_chain(skb->next);
|
||||
skb->next = NULL;
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
|
||||
hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
||||
memcpy(hdr_buf, hdr, hdr_len);
|
||||
hdr = (struct ieee80211_hdr *)hdr_buf;
|
||||
|
||||
/* A-MSDU max is a little less than 8K */
|
||||
amsdu = dev_alloc_skb(8*1024);
|
||||
if (!amsdu) {
|
||||
ath10k_warn("A-MSDU allocation failed\n");
|
||||
ath10k_htt_rx_free_msdu_chain(skb->next);
|
||||
skb->next = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
|
||||
int hdrlen;
|
||||
|
||||
hdr = (void *)rxd->rx_hdr_status;
|
||||
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
|
||||
}
|
||||
/* FIXME: Hopefully this is a temporary measure.
|
||||
*
|
||||
* Reporting individual A-MSDU subframes means each reported frame
|
||||
* shares the same sequence number.
|
||||
*
|
||||
* mac80211 drops frames it recognizes as duplicates, i.e.
|
||||
* 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")
|
||||
*
|
||||
* To avoid frames being dropped clear retransmission flag for all
|
||||
* received A-MSDUs.
|
||||
*
|
||||
* Worst case: actual duplicate frames will be reported but this should
|
||||
* still be handled gracefully by other OSI/ISO layers. */
|
||||
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_RETRY);
|
||||
|
||||
first = skb;
|
||||
while (skb) {
|
||||
void *decap_hdr;
|
||||
int decap_len = 0;
|
||||
int len;
|
||||
|
||||
rxd = (void *)skb->data - sizeof(*rxd);
|
||||
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;
|
||||
|
||||
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
|
||||
|
||||
/* First frame in an A-MSDU chain has more decapped data. */
|
||||
if (skb == first) {
|
||||
/* 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);
|
||||
len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
|
||||
len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
|
||||
4);
|
||||
decap_hdr += len;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
switch (fmt) {
|
||||
case RX_MSDU_DECAP_RAW:
|
||||
skb_trim(skb, skb->len - FCS_LEN);
|
||||
break;
|
||||
case RX_MSDU_DECAP_NATIVE_WIFI:
|
||||
break;
|
||||
case RX_MSDU_DECAP_ETHERNET2_DIX:
|
||||
len = 0;
|
||||
len += sizeof(struct rfc1042_hdr);
|
||||
len += sizeof(struct amsdu_subframe_hdr);
|
||||
|
||||
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) {
|
||||
/* Native Wifi decap inserts regular 802.11 header
|
||||
* in place of A-MSDU subframe header. */
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
|
||||
|
||||
/* 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)
|
||||
skb_trim(skb, skb->len - 4); /* remove FCS */
|
||||
|
||||
memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
|
||||
|
||||
/* A-MSDU subframes are padded to 4bytes
|
||||
* but relative to first subframe, not the whole MPDU */
|
||||
if (skb->next && ((decap_len + skb->len) & 3)) {
|
||||
int padlen = 4 - ((decap_len + skb->len) & 3);
|
||||
memset(skb_put(amsdu, padlen), 0, padlen);
|
||||
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;
|
||||
info->skb->next = NULL;
|
||||
|
||||
ath10k_process_rx(htt->ar, info);
|
||||
}
|
||||
|
||||
info->skb = amsdu;
|
||||
info->encrypt_type = enctype;
|
||||
|
||||
ath10k_htt_rx_free_msdu_chain(first);
|
||||
|
||||
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 htt_rx_desc *rxd;
|
||||
@ -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);
|
||||
hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
|
||||
|
||||
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
|
||||
|
||||
switch (fmt) {
|
||||
case RX_MSDU_DECAP_RAW:
|
||||
/* remove trailing FCS */
|
||||
@ -782,7 +763,8 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
|
||||
|
||||
info->skb = skb;
|
||||
info->encrypt_type = enctype;
|
||||
return 0;
|
||||
|
||||
ath10k_process_rx(htt->ar, info);
|
||||
}
|
||||
|
||||
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,
|
||||
int fw_desc_len;
|
||||
u8 *fw_desc;
|
||||
int i, j;
|
||||
int ret;
|
||||
int ip_summed;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
@ -930,11 +910,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
|
||||
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.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
|
||||
info.signal = ATH10K_DEFAULT_NOISE_FLOOR;
|
||||
@ -947,24 +922,9 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
|
||||
hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
|
||||
|
||||
if (ath10k_htt_rx_hdr_is_amsdu(hdr))
|
||||
ret = ath10k_htt_rx_amsdu(htt, &info);
|
||||
ath10k_htt_rx_amsdu(htt, &info);
|
||||
else
|
||||
ret = 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);
|
||||
ath10k_htt_rx_msdu(htt, &info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
|
||||
status->vht_nss,
|
||||
status->freq,
|
||||
status->band);
|
||||
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
|
||||
info->skb->data, info->skb->len);
|
||||
|
||||
ieee80211_rx(ar->hw, info->skb);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user