1*7ceae9b7SDaniel Gabay /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ 2*7ceae9b7SDaniel Gabay /* 3*7ceae9b7SDaniel Gabay * Copyright (C) 2024 Intel Corporation 4*7ceae9b7SDaniel Gabay */ 5*7ceae9b7SDaniel Gabay #include <net/gso.h> 6*7ceae9b7SDaniel Gabay #include <linux/ieee80211.h> 7*7ceae9b7SDaniel Gabay #include <net/gso.h> 8*7ceae9b7SDaniel Gabay #include <net/ip.h> 9*7ceae9b7SDaniel Gabay 10*7ceae9b7SDaniel Gabay #include "iwl-drv.h" 11*7ceae9b7SDaniel Gabay #include "iwl-utils.h" 12*7ceae9b7SDaniel Gabay 13*7ceae9b7SDaniel Gabay #ifdef CONFIG_INET 14*7ceae9b7SDaniel Gabay int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, 15*7ceae9b7SDaniel Gabay netdev_features_t netdev_flags, 16*7ceae9b7SDaniel Gabay struct sk_buff_head *mpdus_skbs) 17*7ceae9b7SDaniel Gabay { 18*7ceae9b7SDaniel Gabay struct sk_buff *tmp, *next; 19*7ceae9b7SDaniel Gabay struct ieee80211_hdr *hdr = (void *)skb->data; 20*7ceae9b7SDaniel Gabay char cb[sizeof(skb->cb)]; 21*7ceae9b7SDaniel Gabay u16 i = 0; 22*7ceae9b7SDaniel Gabay unsigned int tcp_payload_len; 23*7ceae9b7SDaniel Gabay unsigned int mss = skb_shinfo(skb)->gso_size; 24*7ceae9b7SDaniel Gabay bool ipv4 = (skb->protocol == htons(ETH_P_IP)); 25*7ceae9b7SDaniel Gabay bool qos = ieee80211_is_data_qos(hdr->frame_control); 26*7ceae9b7SDaniel Gabay u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; 27*7ceae9b7SDaniel Gabay 28*7ceae9b7SDaniel Gabay skb_shinfo(skb)->gso_size = num_subframes * mss; 29*7ceae9b7SDaniel Gabay memcpy(cb, skb->cb, sizeof(cb)); 30*7ceae9b7SDaniel Gabay 31*7ceae9b7SDaniel Gabay next = skb_gso_segment(skb, netdev_flags); 32*7ceae9b7SDaniel Gabay skb_shinfo(skb)->gso_size = mss; 33*7ceae9b7SDaniel Gabay skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; 34*7ceae9b7SDaniel Gabay 35*7ceae9b7SDaniel Gabay if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM) 36*7ceae9b7SDaniel Gabay return -ENOMEM; 37*7ceae9b7SDaniel Gabay 38*7ceae9b7SDaniel Gabay if (WARN_ONCE(IS_ERR(next), 39*7ceae9b7SDaniel Gabay "skb_gso_segment error: %d\n", (int)PTR_ERR(next))) 40*7ceae9b7SDaniel Gabay return PTR_ERR(next); 41*7ceae9b7SDaniel Gabay 42*7ceae9b7SDaniel Gabay if (next) 43*7ceae9b7SDaniel Gabay consume_skb(skb); 44*7ceae9b7SDaniel Gabay 45*7ceae9b7SDaniel Gabay skb_list_walk_safe(next, tmp, next) { 46*7ceae9b7SDaniel Gabay memcpy(tmp->cb, cb, sizeof(tmp->cb)); 47*7ceae9b7SDaniel Gabay /* 48*7ceae9b7SDaniel Gabay * Compute the length of all the data added for the A-MSDU. 49*7ceae9b7SDaniel Gabay * This will be used to compute the length to write in the TX 50*7ceae9b7SDaniel Gabay * command. We have: SNAP + IP + TCP for n -1 subframes and 51*7ceae9b7SDaniel Gabay * ETH header for n subframes. 52*7ceae9b7SDaniel Gabay */ 53*7ceae9b7SDaniel Gabay tcp_payload_len = skb_tail_pointer(tmp) - 54*7ceae9b7SDaniel Gabay skb_transport_header(tmp) - 55*7ceae9b7SDaniel Gabay tcp_hdrlen(tmp) + tmp->data_len; 56*7ceae9b7SDaniel Gabay 57*7ceae9b7SDaniel Gabay if (ipv4) 58*7ceae9b7SDaniel Gabay ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes); 59*7ceae9b7SDaniel Gabay 60*7ceae9b7SDaniel Gabay if (tcp_payload_len > mss) { 61*7ceae9b7SDaniel Gabay skb_shinfo(tmp)->gso_size = mss; 62*7ceae9b7SDaniel Gabay skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 : 63*7ceae9b7SDaniel Gabay SKB_GSO_TCPV6; 64*7ceae9b7SDaniel Gabay } else { 65*7ceae9b7SDaniel Gabay if (qos) { 66*7ceae9b7SDaniel Gabay u8 *qc; 67*7ceae9b7SDaniel Gabay 68*7ceae9b7SDaniel Gabay if (ipv4) 69*7ceae9b7SDaniel Gabay ip_send_check(ip_hdr(tmp)); 70*7ceae9b7SDaniel Gabay 71*7ceae9b7SDaniel Gabay qc = ieee80211_get_qos_ctl((void *)tmp->data); 72*7ceae9b7SDaniel Gabay *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; 73*7ceae9b7SDaniel Gabay } 74*7ceae9b7SDaniel Gabay skb_shinfo(tmp)->gso_size = 0; 75*7ceae9b7SDaniel Gabay } 76*7ceae9b7SDaniel Gabay 77*7ceae9b7SDaniel Gabay skb_mark_not_on_list(tmp); 78*7ceae9b7SDaniel Gabay __skb_queue_tail(mpdus_skbs, tmp); 79*7ceae9b7SDaniel Gabay i++; 80*7ceae9b7SDaniel Gabay } 81*7ceae9b7SDaniel Gabay 82*7ceae9b7SDaniel Gabay return 0; 83*7ceae9b7SDaniel Gabay } 84*7ceae9b7SDaniel Gabay IWL_EXPORT_SYMBOL(iwl_tx_tso_segment); 85*7ceae9b7SDaniel Gabay #endif /* CONFIG_INET */ 86