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