123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- From: Felix Fietkau <nbd@openwrt.org>
- Date: Mon, 8 Feb 2016 14:33:19 +0100
- Subject: [PATCH] cfg80211: reuse existing page fragments in A-MSDU rx
- This massively reduces data copying and thus improves rx performance
- Signed-off-by: Felix Fietkau <nbd@openwrt.org>
- ---
- --- a/net/wireless/util.c
- +++ b/net/wireless/util.c
- @@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_b
- }
- 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 *
- __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;
- + int cur_len = len;
-
- if (skb->len - offset < len)
- 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
- * 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_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;
- }
- @@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
- u8 *payload;
- int offset = 0, remaining, err;
- struct ethhdr eth;
- + bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
- bool reuse_skb = false;
- bool last = false;
-
- @@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_
- offset += sizeof(struct ethhdr);
- /* reuse skb for the last subframe */
- last = remaining <= subframe_len + padding;
- - if (!skb_is_nonlinear(skb) && last) {
- + if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
- skb_pull(skb, offset);
- frame = skb;
- reuse_skb = true;
- } else {
- - frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
- + frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
- + reuse_frag);
- if (!frame)
- goto purge;
-
|