summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2015-04-14 14:50:41 +0200
committerJohannes Berg <johannes.berg@intel.com>2015-04-22 11:25:30 +0200
commit80616c0db8a26f030be78fea39f7c880c4263e55 (patch)
tree3c5bba97a4e8774b373f5857231766f67a76aa58
parent680a0daba74fed0bf30530c9b3e7e706cf29855f (diff)
mac80211: allow segmentation offloads
Implement the necessary software segmentation on the normal TX path so that fast-xmit can use segmentation offload if the hardware (or driver) supports it. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/tx.c70
2 files changed, 48 insertions, 25 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 6dd6dd47a9df..ea31f119234b 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -840,7 +840,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* Only HW csum features are currently compatible with mac80211 */
feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
- NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA;
+ NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA |
+ NETIF_F_GSO_SOFTWARE;
if (WARN_ON(hw->netdev_features & ~feature_whitelist))
return -EINVAL;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3a421a04cbec..df62942653ad 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2843,6 +2843,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
+ struct sk_buff *next;
if (unlikely(skb->len < ETH_HLEN)) {
kfree_skb(skb);
@@ -2864,36 +2865,57 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
goto out;
}
- /* we cannot process non-linear frames on this path */
- if (skb_linearize(skb)) {
- kfree_skb(skb);
- goto out;
- }
+ if (skb_is_gso(skb)) {
+ struct sk_buff *segs;
- /* the frame could be fragmented, software-encrypted, and other things
- * so we cannot really handle checksum offload with it - fix it up in
- * software before we handle anything else.
- */
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- if (skb->encapsulation)
- skb_set_inner_transport_header(skb,
- skb_checksum_start_offset(skb));
- else
- skb_set_transport_header(skb,
- skb_checksum_start_offset(skb));
- if (skb_checksum_help(skb))
+ segs = skb_gso_segment(skb, 0);
+ if (IS_ERR(segs)) {
goto out_free;
+ } else if (segs) {
+ consume_skb(skb);
+ skb = segs;
+ }
+ } else {
+ /* we cannot process non-linear frames on this path */
+ if (skb_linearize(skb)) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ /* the frame could be fragmented, software-encrypted, and other
+ * things so we cannot really handle checksum offload with it -
+ * fix it up in software before we handle anything else.
+ */
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (skb->encapsulation)
+ skb_set_inner_transport_header(skb,
+ skb_checksum_start_offset(skb));
+ else
+ skb_set_transport_header(skb,
+ skb_checksum_start_offset(skb));
+ if (skb_checksum_help(skb))
+ goto out_free;
+ }
}
- skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
- if (IS_ERR(skb))
- goto out;
+ next = skb;
+ while (next) {
+ skb = next;
+ next = skb->next;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
- dev->trans_start = jiffies;
+ skb->prev = NULL;
+ skb->next = NULL;
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
+ if (IS_ERR(skb))
+ goto out;
- ieee80211_xmit(sdata, sta, skb);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+
+ ieee80211_xmit(sdata, sta, skb);
+ }
goto out;
out_free:
kfree_skb(skb);