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

Commit 2b67f944 authored by Felix Fietkau's avatar Felix Fietkau Committed by Johannes Berg
Browse files

cfg80211: reuse existing page fragments in A-MSDU rx



This massively reduces data copying and thus improves rx performance

Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 2bf0ccc7
Loading
Loading
Loading
Loading
+77 −5
Original line number Original line Diff line number Diff line
@@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
}
}
EXPORT_SYMBOL(ieee80211_data_from_8023);
EXPORT_SYMBOL(ieee80211_data_from_8023);


static void
__frame_add_frag(struct sk_buff *skb, struct page *page,
		 void *ptr, int len, int size)
{
	struct skb_shared_info *sh = skb_shinfo(skb);
	int page_offset;

	atomic_inc(&page->_count);
	page_offset = ptr - page_address(page);
	skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
}

static void
__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
			    int offset, int len)
{
	struct skb_shared_info *sh = skb_shinfo(skb);
	const skb_frag_t *frag = &sh->frags[-1];
	struct page *frag_page;
	void *frag_ptr;
	int frag_len, frag_size;
	int head_size = skb->len - skb->data_len;
	int cur_len;

	frag_page = virt_to_head_page(skb->head);
	frag_ptr = skb->data;
	frag_size = head_size;

	while (offset >= frag_size) {
		offset -= frag_size;
		frag++;
		frag_page = skb_frag_page(frag);
		frag_ptr = skb_frag_address(frag);
		frag_size = skb_frag_size(frag);
	}

	frag_ptr += offset;
	frag_len = frag_size - offset;

	cur_len = min(len, frag_len);

	__frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
	len -= cur_len;

	while (len > 0) {
		frag++;
		frag_len = skb_frag_size(frag);
		cur_len = min(len, frag_len);
		__frame_add_frag(frame, skb_frag_page(frag),
				 skb_frag_address(frag), cur_len, frag_len);
		len -= cur_len;
	}
}

static struct sk_buff *
static struct sk_buff *
__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
		       int offset, int len)
		       int offset, int len, bool reuse_frag)
{
{
	struct sk_buff *frame;
	struct sk_buff *frame;
	int cur_len = len;


	if (skb->len - offset < len)
	if (skb->len - offset < len)
		return NULL;
		return NULL;


	/*
	 * When reusing framents, copy some data to the head to simplify
	 * ethernet header handling and speed up protocol header processing
	 * in the stack later.
	 */
	if (reuse_frag)
		cur_len = min_t(int, len, 32);

	/*
	/*
	 * Allocate and reserve two bytes more for payload
	 * Allocate and reserve two bytes more for payload
	 * alignment since sizeof(struct ethhdr) is 14.
	 * alignment since sizeof(struct ethhdr) is 14.
	 */
	 */
	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);


	skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
	skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
	skb_copy_bits(skb, offset, skb_put(frame, len), len);
	skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);

	len -= cur_len;
	if (!len)
		return frame;

	offset += cur_len;
	__ieee80211_amsdu_copy_frag(skb, frame, offset, len);


	return frame;
	return frame;
}
}
@@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
	u8 *payload;
	u8 *payload;
	int offset = 0, remaining, err;
	int offset = 0, remaining, err;
	struct ethhdr eth;
	struct ethhdr eth;
	bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
	bool reuse_skb = false;
	bool reuse_skb = false;
	bool last = false;
	bool last = false;


@@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
		offset += sizeof(struct ethhdr);
		offset += sizeof(struct ethhdr);
		/* reuse skb for the last subframe */
		/* reuse skb for the last subframe */
		last = remaining <= subframe_len + padding;
		last = remaining <= subframe_len + padding;
		if (!skb_is_nonlinear(skb) && last) {
		if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
			skb_pull(skb, offset);
			skb_pull(skb, offset);
			frame = skb;
			frame = skb;
			reuse_skb = true;
			reuse_skb = true;
		} else {
		} else {
			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
						       reuse_frag);
			if (!frame)
			if (!frame)
				goto purge;
				goto purge;