aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2016-02-08 08:34:42 -0500
committerJohannes Berg <johannes.berg@intel.com>2016-02-24 03:04:37 -0500
commit2b67f944f88c29e71efdcfc4d0c21ab28fbf23ae (patch)
tree0b38a091ed34c0418ea3f602075a9e6dab746ea4 /net/wireless
parent2bf0ccc7095e6cf665bbdb7c32c352d24f1ae033 (diff)
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> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/util.c82
1 files changed, 77 insertions, 5 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 9880c894c58d..c7f6820bb258 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
644} 644}
645EXPORT_SYMBOL(ieee80211_data_from_8023); 645EXPORT_SYMBOL(ieee80211_data_from_8023);
646 646
647static void
648__frame_add_frag(struct sk_buff *skb, struct page *page,
649 void *ptr, int len, int size)
650{
651 struct skb_shared_info *sh = skb_shinfo(skb);
652 int page_offset;
653
654 atomic_inc(&page->_count);
655 page_offset = ptr - page_address(page);
656 skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
657}
658
659static void
660__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
661 int offset, int len)
662{
663 struct skb_shared_info *sh = skb_shinfo(skb);
664 const skb_frag_t *frag = &sh->frags[-1];
665 struct page *frag_page;
666 void *frag_ptr;
667 int frag_len, frag_size;
668 int head_size = skb->len - skb->data_len;
669 int cur_len;
670
671 frag_page = virt_to_head_page(skb->head);
672 frag_ptr = skb->data;
673 frag_size = head_size;
674
675 while (offset >= frag_size) {
676 offset -= frag_size;
677 frag++;
678 frag_page = skb_frag_page(frag);
679 frag_ptr = skb_frag_address(frag);
680 frag_size = skb_frag_size(frag);
681 }
682
683 frag_ptr += offset;
684 frag_len = frag_size - offset;
685
686 cur_len = min(len, frag_len);
687
688 __frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
689 len -= cur_len;
690
691 while (len > 0) {
692 frag++;
693 frag_len = skb_frag_size(frag);
694 cur_len = min(len, frag_len);
695 __frame_add_frag(frame, skb_frag_page(frag),
696 skb_frag_address(frag), cur_len, frag_len);
697 len -= cur_len;
698 }
699}
700
647static struct sk_buff * 701static struct sk_buff *
648__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen, 702__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
649 int offset, int len) 703 int offset, int len, bool reuse_frag)
650{ 704{
651 struct sk_buff *frame; 705 struct sk_buff *frame;
706 int cur_len = len;
652 707
653 if (skb->len - offset < len) 708 if (skb->len - offset < len)
654 return NULL; 709 return NULL;
655 710
656 /* 711 /*
712 * When reusing framents, copy some data to the head to simplify
713 * ethernet header handling and speed up protocol header processing
714 * in the stack later.
715 */
716 if (reuse_frag)
717 cur_len = min_t(int, len, 32);
718
719 /*
657 * Allocate and reserve two bytes more for payload 720 * Allocate and reserve two bytes more for payload
658 * alignment since sizeof(struct ethhdr) is 14. 721 * alignment since sizeof(struct ethhdr) is 14.
659 */ 722 */
660 frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len); 723 frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
661 724
662 skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); 725 skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
663 skb_copy_bits(skb, offset, skb_put(frame, len), len); 726 skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
727
728 len -= cur_len;
729 if (!len)
730 return frame;
731
732 offset += cur_len;
733 __ieee80211_amsdu_copy_frag(skb, frame, offset, len);
664 734
665 return frame; 735 return frame;
666} 736}
@@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
676 u8 *payload; 746 u8 *payload;
677 int offset = 0, remaining, err; 747 int offset = 0, remaining, err;
678 struct ethhdr eth; 748 struct ethhdr eth;
749 bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
679 bool reuse_skb = false; 750 bool reuse_skb = false;
680 bool last = false; 751 bool last = false;
681 752
@@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
703 offset += sizeof(struct ethhdr); 774 offset += sizeof(struct ethhdr);
704 /* reuse skb for the last subframe */ 775 /* reuse skb for the last subframe */
705 last = remaining <= subframe_len + padding; 776 last = remaining <= subframe_len + padding;
706 if (!skb_is_nonlinear(skb) && last) { 777 if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
707 skb_pull(skb, offset); 778 skb_pull(skb, offset);
708 frame = skb; 779 frame = skb;
709 reuse_skb = true; 780 reuse_skb = true;
710 } else { 781 } else {
711 frame = __ieee80211_amsdu_copy(skb, hlen, offset, len); 782 frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
783 reuse_frag);
712 if (!frame) 784 if (!frame)
713 goto purge; 785 goto purge;
714 786