]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tcp: ECT_1_NEGOTIATION and NEEDS_ACCECN identifiers
authorChia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Sat, 31 Jan 2026 22:25:04 +0000 (23:25 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 3 Feb 2026 14:13:24 +0000 (15:13 +0100)
Two flags for congestion control (CC) module are added in this patch
related to AccECN negotiation. First, a new flag (TCP_CONG_NEEDS_ACCECN)
defines that the CC expects to negotiate AccECN functionality using the
ECE, CWR and AE flags in the TCP header.

Second, during ECN negotiation, ECT(0) in the IP header is used. This
patch enables CC to control whether ECT(0) or ECT(1) should be used on
a per-segment basis. A new flag (TCP_CONG_ECT_1_NEGOTIATION) defines the
expected ECT value in the IP header by the CA when not-yet initialized
for the connection.

The detailed AccECN negotiaotn can be found in IETF RFC9768.

Co-developed-by: Olivier Tilmans <olivier.tilmans@nokia.com>
Signed-off-by: Olivier Tilmans <olivier.tilmans@nokia.com>
Signed-off-by: Ilpo Järvinen <ij@kernel.org>
Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260131222515.8485-5-chia-yu.chang@nokia-bell-labs.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/inet_ecn.h
include/net/tcp.h
include/net/tcp_ecn.h
net/ipv4/tcp_cong.c
net/ipv4/tcp_input.c

index ea32393464a291aad77400b34fcdcb5031f01676..827b87a95dab3f1f02f966fa7f9346da73ff70a3 100644 (file)
@@ -51,11 +51,25 @@ static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
        return outer;
 }
 
+/* Apply either ECT(0) or ECT(1) */
+static inline void __INET_ECN_xmit(struct sock *sk, bool use_ect_1)
+{
+       __u8 ect = use_ect_1 ? INET_ECN_ECT_1 : INET_ECN_ECT_0;
+
+       /* Mask the complete byte in case the connection alternates between
+        * ECT(0) and ECT(1).
+        */
+       inet_sk(sk)->tos &= ~INET_ECN_MASK;
+       inet_sk(sk)->tos |= ect;
+       if (inet6_sk(sk)) {
+               inet6_sk(sk)->tclass &= ~INET_ECN_MASK;
+               inet6_sk(sk)->tclass |= ect;
+       }
+}
+
 static inline void INET_ECN_xmit(struct sock *sk)
 {
-       inet_sk(sk)->tos |= INET_ECN_ECT_0;
-       if (inet6_sk(sk) != NULL)
-               inet6_sk(sk)->tclass |= INET_ECN_ECT_0;
+       __INET_ECN_xmit(sk, false);
 }
 
 static inline void INET_ECN_dontxmit(struct sock *sk)
index cecec1a92d5e6e4b298e18fa340014aabcbc1aff..c122ff2f364b3e471285d4fc2a380ab1183ee123 100644 (file)
@@ -1215,7 +1215,12 @@ enum tcp_ca_ack_event_flags {
 #define TCP_CONG_NON_RESTRICTED                BIT(0)
 /* Requires ECN/ECT set on all packets */
 #define TCP_CONG_NEEDS_ECN             BIT(1)
-#define TCP_CONG_MASK  (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN)
+/* Require successfully negotiated AccECN capability */
+#define TCP_CONG_NEEDS_ACCECN          BIT(2)
+/* Use ECT(1) instead of ECT(0) while the CA is uninitialized */
+#define TCP_CONG_ECT_1_NEGOTIATION     BIT(3)
+#define TCP_CONG_MASK  (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN | \
+                       TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION)
 
 union tcp_cc_info;
 
@@ -1356,6 +1361,20 @@ static inline bool tcp_ca_needs_ecn(const struct sock *sk)
        return icsk->icsk_ca_ops->flags & TCP_CONG_NEEDS_ECN;
 }
 
+static inline bool tcp_ca_needs_accecn(const struct sock *sk)
+{
+       const struct inet_connection_sock *icsk = inet_csk(sk);
+
+       return icsk->icsk_ca_ops->flags & TCP_CONG_NEEDS_ACCECN;
+}
+
+static inline bool tcp_ca_ect_1_negotiation(const struct sock *sk)
+{
+       const struct inet_connection_sock *icsk = inet_csk(sk);
+
+       return icsk->icsk_ca_ops->flags & TCP_CONG_ECT_1_NEGOTIATION;
+}
+
 static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
index f13e5cd2b1ac3cd7d4e74366d3fd33e74ce1b92d..fdde1c342b35cb145ae6dde06949b5255cf33e4b 100644 (file)
@@ -31,6 +31,12 @@ enum tcp_accecn_option {
        TCP_ACCECN_OPTION_FULL = 2,
 };
 
+/* Apply either ECT(0) or ECT(1) based on TCP_CONG_ECT_1_NEGOTIATION flag */
+static inline void INET_ECN_xmit_ect_1_negotiation(struct sock *sk)
+{
+       __INET_ECN_xmit(sk, tcp_ca_ect_1_negotiation(sk));
+}
+
 static inline void tcp_ecn_queue_cwr(struct tcp_sock *tp)
 {
        /* Do not set CWR if in AccECN mode! */
@@ -561,7 +567,7 @@ static inline void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb)
                TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_ECE;
        else if (tcp_ca_needs_ecn(sk) ||
                 tcp_bpf_ca_needs_ecn(sk))
-               INET_ECN_xmit(sk);
+               INET_ECN_xmit_ect_1_negotiation(sk);
 
        if (tp->ecn_flags & TCP_ECN_MODE_ACCECN) {
                TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_ACE;
@@ -579,7 +585,8 @@ static inline void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)
        bool use_ecn, use_accecn;
        u8 tcp_ecn = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn);
 
-       use_accecn = tcp_ecn == TCP_ECN_IN_ACCECN_OUT_ACCECN;
+       use_accecn = tcp_ecn == TCP_ECN_IN_ACCECN_OUT_ACCECN ||
+                    tcp_ca_needs_accecn(sk);
        use_ecn = tcp_ecn == TCP_ECN_IN_ECN_OUT_ECN ||
                  tcp_ecn == TCP_ECN_IN_ACCECN_OUT_ECN ||
                  tcp_ca_needs_ecn(sk) || bpf_needs_ecn || use_accecn;
@@ -595,7 +602,7 @@ static inline void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)
 
        if (use_ecn) {
                if (tcp_ca_needs_ecn(sk) || bpf_needs_ecn)
-                       INET_ECN_xmit(sk);
+                       INET_ECN_xmit_ect_1_negotiation(sk);
 
                TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR;
                if (use_accecn) {
index df758adbb445f2a8e60fdc56fe9fadbf0b93941b..e9f6c77e0631631a09f3099fd5af08a041d571df 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/gfp.h>
 #include <linux/jhash.h>
 #include <net/tcp.h>
+#include <net/tcp_ecn.h>
 #include <trace/events/tcp.h>
 
 static DEFINE_SPINLOCK(tcp_cong_list_lock);
@@ -227,7 +228,7 @@ void tcp_assign_congestion_control(struct sock *sk)
 
        memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
        if (ca->flags & TCP_CONG_NEEDS_ECN)
-               INET_ECN_xmit(sk);
+               INET_ECN_xmit_ect_1_negotiation(sk);
        else
                INET_ECN_dontxmit(sk);
 }
@@ -257,7 +258,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,
        memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
 
        if (ca->flags & TCP_CONG_NEEDS_ECN)
-               INET_ECN_xmit(sk);
+               INET_ECN_xmit_ect_1_negotiation(sk);
        else
                INET_ECN_dontxmit(sk);
 
index 38852e04229af584499af1b6bdff01e10f23238e..e823a3b1ad3ba34791361a476d303574b7c47690 100644 (file)
@@ -7495,7 +7495,8 @@ static void tcp_ecn_create_request(struct request_sock *req,
        u32 ecn_ok_dst;
 
        if (tcp_accecn_syn_requested(th) &&
-           READ_ONCE(net->ipv4.sysctl_tcp_ecn) >= 3) {
+           (READ_ONCE(net->ipv4.sysctl_tcp_ecn) >= 3 ||
+            tcp_ca_needs_accecn(listen_sk))) {
                inet_rsk(req)->ecn_ok = 1;
                tcp_rsk(req)->accecn_ok = 1;
                tcp_rsk(req)->syn_ect_rcv = TCP_SKB_CB(skb)->ip_dsfield &