diff options
author | Felix Fietkau <nbd@openwrt.org> | 2016-02-08 08:34:42 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2016-02-24 03:04:37 -0500 |
commit | 2b67f944f88c29e71efdcfc4d0c21ab28fbf23ae (patch) | |
tree | 0b38a091ed34c0418ea3f602075a9e6dab746ea4 /net/wireless | |
parent | 2bf0ccc7095e6cf665bbdb7c32c352d24f1ae033 (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.c | 82 |
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 | } |
645 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 645 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
646 | 646 | ||
647 | static 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 | |||
659 | static 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 | |||
647 | static struct sk_buff * | 701 | static 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 | ||