From: Chia-Yu Chang This patch updates the documentation of ECN‑related GSO flags, it clarifies the limitations of SKB_GSO_TCP_ECN and explains how to preserve the CWR flag (part of the ACE signal) in the Rx path. For Tx, SKB_GSO_TCP_ECN and SKB_GSO_TCP_ACCECN are used respectively for RFC3168 ECN and AccECN (RFC9768). SKB_GSO_TCP_ECN indicates that the first segment has CWR set, while subsequent segments have CWR cleared. In contrast, SKB_GSO_TCP_ACCECN means that the segment uses AccECN and therefore its CWR flag must not be modified durging segmentation. For RX, SKB_GSO_TCP_ECN shall NOT be used, because the stack cannot know whether the connection uses RFC3168 ECN or AccECN, whereas RFC3168 ECN offload may clear CWR flag and thus corrupts the ACE signal. Instead, any segment that arrives with CWR set must use the SKB_GSO_TCP_ACCECN flag to prevent RFC3168 ECN offload logic from clearing the CWR flag. Co-developed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Chia-Yu Chang --- include/linux/skbuff.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 112e48970338..7ece0616dd5d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -671,7 +671,13 @@ enum { /* This indicates the skb is from an untrusted source. */ SKB_GSO_DODGY = 1 << 1, - /* This indicates the tcp segment has CWR set. */ + /* For TX, this indicates that the first TCP segment has CWR set, and + * any subsequent segment in the same skb has CWR cleared. This flag + * must not be used in RX, because the connection to which the segment + * belongs is not tracked to use RFC3168 or AccECN. Using RFC3168 ECN + * offload may clear CWR and corrupt ACE signal (CWR is part of it). + * Instead, SKB_GSO_TCP_ACCECN shall be used to avoid CWR corruption. + */ SKB_GSO_TCP_ECN = 1 << 2, __SKB_GSO_TCP_FIXEDID = 1 << 3, @@ -706,6 +712,13 @@ enum { SKB_GSO_FRAGLIST = 1 << 18, + /* For TX, this indicates that the TCP segment uses the CWR flag as part + * of the ACE signal, and the CWR flag must not be modified in the skb. + * For RX, any incoming segment with CWR set must use this flag so that + * no RFC3168 ECN offload can clear the CWR flag. This is required to + * preserve ACE signal correctness (CWR is part of it) in a forwarding + * scenario, e.g., from one netdevice RX to other netdevice TX + */ SKB_GSO_TCP_ACCECN = 1 << 19, /* These indirectly map onto the same netdev feature. -- 2.34.1 From: Chia-Yu Chang Currently, hns3 and mlx5 Rx paths use SKB_GSO_TCP_ECN flag when a TCP segment with the CWR flag set. This is wrong because SKB_GSO_TCP_ECN is only valid for RFC3168 ECN on Tx, and using it on Rx allows RFC3168 ECN offload to clear the CWR flag. As a result, incoming TCP segments lose their ACE signal integrity required for AccECN (RFC9768), especially when the packet is forwarded and later re-segmented by GSO. Fix this by setting SKB_GSO_TCP_ACCECN for any Rx segment with the CWR flag set. SKB_GSO_TCP_ACCECN ensure that RFC3168 ECN offload will not clear the CWR flag, therefore preserving the ACE signal. Fixes: d474d88f88261 ("net: hns3: add hns3_gro_complete for HW GRO process") Fixes: 92552d3abd329 ("net/mlx5e: HW_GRO cqe handler implementation") Signed-off-by: Chia-Yu Chang --- drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 7a9573dcab74..32993652efa2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -3899,7 +3899,7 @@ static int hns3_gro_complete(struct sk_buff *skb, u32 l234info) skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; if (th->cwr) - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ACCECN; if (l234info & BIT(HNS3_RXD_GRO_FIXID_B)) skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_FIXEDID; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 1f6930c77437..a2ab97d721d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -1307,7 +1307,7 @@ static void mlx5e_shampo_update_ipv4_tcp_hdr(struct mlx5e_rq *rq, struct iphdr * skb->csum_offset = offsetof(struct tcphdr, check); if (tcp->cwr) - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ACCECN; } static void mlx5e_shampo_update_ipv6_tcp_hdr(struct mlx5e_rq *rq, struct ipv6hdr *ipv6, @@ -1328,7 +1328,7 @@ static void mlx5e_shampo_update_ipv6_tcp_hdr(struct mlx5e_rq *rq, struct ipv6hdr skb->csum_offset = offsetof(struct tcphdr, check); if (tcp->cwr) - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ACCECN; } static void mlx5e_shampo_update_hdr(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, bool match) -- 2.34.1