summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-10-13 15:32:49 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-10-13 15:32:50 +0200
commit4e72c178f424e58d58c53949041e061eacafbc5d (patch)
tree37d331f85857051ba3e7d1e8d39bba56b6da9b46 /include
parent59e344304c1a80fb9898223020cf3b10ed3a0da4 (diff)
parenta374c5bb41d1ea72e732153d6a8d17b4e2434d3f (diff)
downloadlinux-next-4e72c178f424e58d58c53949041e061eacafbc5d.tar.gz
linux-next-4e72c178f424e58d58c53949041e061eacafbc5d.zip
Merge branch 'net-re-segment-oversized-tcp-gso-skbs'
Wang Zhan says: ==================== net: re-segment oversized TCP GSO skbs BIG TCP is negotiated per netdevice, so BIG TCP and non-BIG TCP ports can coexist in one path. When an skb exceeds the GSO limits of the port it is sent to, it loses its GSO feature mask and is segmented into individual MSS sized packets, which that port then sends without TSO. This series cuts such a TCP GSO skb into GSO skbs which fit the device limits instead, so the rest of the path keeps using TSO. On a veth -> bridge -> TAP -> guest virtio-net path, with BIG TCP enabled on the veth endpoints and left off in the guest, a single iperf3 TCP flow, six alternating runs per state (-t 15 -O 5, fixed CPU affinity and port tuple): protocol no BIG TCP mixed, no reseg mixed, resegmented TCP/IPv4 51.550 Gbps 15.850 Gbps 52.617 Gbps TCP/IPv6 52.050 Gbps 15.783 Gbps 51.933 Gbps The middle column is the same tree with the re-segmentation disabled. A BIG TCP hop which feeds a 64 KiB hop loses 69% of the throughput of a path which never enables BIG TCP; re-segmentation recovers it. 1/6 moves the GSO size limit lookup into dev.c and makes it follow the packet's L3 protocol, so where the tag sits does not decide which limit applies. 2/6 is the preparation which lets the limit tests be skipped for one caller, and carries no functional change. 3/6 lets the GSO engine group several MSS into one output skb, and 4/6 clears the whole GSO state of the last output of such a group. The new path is taken only when the skb is an unencapsulated TCP GSO skb without a frag_list, it exceeds gso_max_size or gso_max_segs, and the device offloads that GSO type. Everything else keeps today's segmentation. The output obeys the GSO feature and limit contract the device already advertises, so this needs no new UAPI, no device state and no driver change, and it applies automatically. max_segs only says how the output is grouped; an over-limit skb pays one extra ndo_features_check() in exchange for staying a GSO skb. Alternatives considered: - The caller could set skb_shinfo(skb)->gso_size to ~64K and adjust the gso bits in the shared info afterwards, which would need skb_unclone(), a repeat of the grouping logic skb_segment() already has, and a recomputed IPv4 ID for the DF=0 case. Patch layout: [1/6] the GSO size limit follows the packet's L3 protocol [2/6] factor the device limit check out of gso_features_check() [3/6] let the GSO engine group several MSS into one output skb [4/6] clear the whole GSO state of the last output segment [5/6] re-segment oversized TCP GSO skbs in the TX path [6/6] KUnit coverage for re-segmentation --- v5: - patch 3: make max_segs a u8, skb_gso_cb has one byte left - patch 3: only warn about the input segment count without max_segs - patch 4: new, clear the whole GSO state of the last output segment - patch 6: add a plain-tail case, drop the TCP path tests v4: https://lore.kernel.org/20260930111526.2183107-1-wang.zhan@smartx.com/ v3: https://lore.kernel.org/20260928044102.1004310-1-wang.zhan@smartx.com/ v2: https://lore.kernel.org/20260918084651.3022878-1-wang.zhan@smartx.com/ v1: https://lore.kernel.org/20260917063854.2011613-1-wang.zhan@smartx.com/ ==================== Link: https://patch.msgid.link/20261008100651.2534957-1-wang.zhan@smartx.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'include')
-rw-r--r--include/linux/netdevice.h18
-rw-r--r--include/net/gso.h7
-rw-r--r--include/net/udp.h2
3 files changed, 14 insertions, 13 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5b4b92e76b67..8fbe3a5d8169 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -5500,7 +5500,14 @@ void netif_stacked_transfer_operstate(const struct net_device *rootdev,
netdev_features_t passthru_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features);
-netdev_features_t netif_skb_features(struct sk_buff *skb);
+netdev_features_t __netif_skb_features(struct sk_buff *skb,
+ bool check_gso_limits);
+
+static inline netdev_features_t netif_skb_features(struct sk_buff *skb)
+{
+ return __netif_skb_features(skb, true);
+}
+
void skb_warn_bad_offload(const struct sk_buff *skb);
static inline bool net_gso_ok(netdev_features_t features, int gso_type)
@@ -5566,15 +5573,6 @@ netif_get_gro_max_size(const struct net_device *dev, const struct sk_buff *skb)
READ_ONCE(dev->gro_ipv4_max_size);
}
-static inline unsigned int
-netif_get_gso_max_size(const struct net_device *dev, const struct sk_buff *skb)
-{
- /* pairs with WRITE_ONCE() in netif_set_gso(_ipv4)_max_size() */
- return skb->protocol == htons(ETH_P_IPV6) ?
- READ_ONCE(dev->gso_max_size) :
- READ_ONCE(dev->gso_ipv4_max_size);
-}
-
static inline bool netif_is_macsec(const struct net_device *dev)
{
return dev->priv_flags & IFF_MACSEC;
diff --git a/include/net/gso.h b/include/net/gso.h
index 0749230d414e..fe3260b381d4 100644
--- a/include/net/gso.h
+++ b/include/net/gso.h
@@ -21,6 +21,8 @@ struct skb_gso_cb {
__u16 csum_start;
/* Number of IPv4/IPv6 GSO handler entries for this packet. */
u8 recursion_counter;
+ /* Max MSS segs per output skb, 0 = no limit */
+ u8 max_segs;
};
#define SKB_GSO_CB_OFFSET 32
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))
@@ -83,12 +85,13 @@ static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
}
struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
- netdev_features_t features, bool tx_path);
+ netdev_features_t features, bool tx_path,
+ unsigned int max_segs);
static inline struct sk_buff *skb_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
- return __skb_gso_segment(skb, features, true);
+ return __skb_gso_segment(skb, features, true, 0);
}
struct sk_buff *skb_eth_gso_segment(struct sk_buff *skb,
diff --git a/include/net/udp.h b/include/net/udp.h
index 1fee17274745..5bc25dcf25fb 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -613,7 +613,7 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
/* the GSO CB lays after the UDP one, no need to save and restore any
* CB fragment
*/
- segs = __skb_gso_segment(skb, features, false);
+ segs = __skb_gso_segment(skb, features, false, 0);
if (IS_ERR_OR_NULL(segs)) {
drop_count = skb_shinfo(skb)->gso_segs;
goto drop;