Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+83 −123
Original line number Original line Diff line number Diff line
@@ -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)
	return false;
	return false;
}
}


static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
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_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 */
	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;
	/* FIXME: Hopefully this is a temporary measure.
		hdrlen = ieee80211_hdrlen(hdr->frame_control);
	 *
		memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
	 * 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;
	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
		/* First frame in an A-MSDU chain has more decapped data. */
			 * to re-insert it. The LLC/SNAP follows A-MSDU
		if (skb == first) {
			 * subframe header. */
			len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
			/* FIXME: Not all LLCs are 8 bytes long */
			len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
			decap_len += 8;
					4);

			decap_hdr += len;
			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)
		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 = skb->next;
			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 = amsdu;
		info->skb = skb;
		info->encrypt_type = enctype;
		info->encrypt_type = enctype;
		skb = skb->next;
		info->skb->next = NULL;


	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)
			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)


	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,
	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,
				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,
			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);
		}
		}
	}
	}


+2 −0
Original line number Original line Diff line number Diff line
@@ -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);
}
}