@@ -87,7 +87,8 @@ struct ieee80211_local;
#define IEEE80211_SUPPORTED_NETDEV_FEATURES \
(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
NETIF_F_HW_CSUM | NETIF_F_RXCSUM | \
- NETIF_F_SG | NETIF_F_HIGHDMA)
+ NETIF_F_SG | NETIF_F_HIGHDMA | \
+ NETIF_F_GSO_SOFTWARE)
struct ieee80211_fragment_entry {
unsigned long first_frag_time;
@@ -2827,6 +2827,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);
@@ -2848,36 +2849,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 (netif_needs_gso(dev, skb, 0)) {
+ 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);