]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tcp: disable RFC3168 fallback identifier for CC modules
authorChia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Sat, 31 Jan 2026 22:25:05 +0000 (23:25 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 3 Feb 2026 14:13:24 +0000 (15:13 +0100)
When AccECN is not successfully negociated for a TCP flow, it defaults
fallback to classic ECN (RFC3168). However, L4S service will fallback
to non-ECN.

This patch enables congestion control module to control whether it
should not fallback to classic ECN after unsuccessful AccECN negotiation.
A new CA module flag (TCP_CONG_NO_FALLBACK_RFC3168) identifies this
behavior expected by the CA.

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-6-chia-yu.chang@nokia-bell-labs.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/tcp.h
include/net/tcp_ecn.h
net/ipv4/tcp_input.c
net/ipv4/tcp_minisocks.c

index c122ff2f364b3e471285d4fc2a380ab1183ee123..cace4fbe38d07e01a1c9e5cc8e4232edd28160a3 100644 (file)
@@ -1219,8 +1219,11 @@ enum tcp_ca_ack_event_flags {
 #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)
+/* Cannot fallback to RFC3168 during AccECN negotiation */
+#define TCP_CONG_NO_FALLBACK_RFC3168   BIT(4)
 #define TCP_CONG_MASK  (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN | \
-                       TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION)
+                       TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION | \
+                       TCP_CONG_NO_FALLBACK_RFC3168)
 
 union tcp_cc_info;
 
@@ -1375,6 +1378,13 @@ static inline bool tcp_ca_ect_1_negotiation(const struct sock *sk)
        return icsk->icsk_ca_ops->flags & TCP_CONG_ECT_1_NEGOTIATION;
 }
 
+static inline bool tcp_ca_no_fallback_rfc3168(const struct sock *sk)
+{
+       const struct inet_connection_sock *icsk = inet_csk(sk);
+
+       return icsk->icsk_ca_ops->flags & TCP_CONG_NO_FALLBACK_RFC3168;
+}
+
 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 fdde1c342b35cb145ae6dde06949b5255cf33e4b..2e1637edf1d3c7f78b707bddbf124cc100c5f05e 100644 (file)
@@ -507,7 +507,9 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb
                 * | ECN    | AccECN | 0   0   1  | Classic ECN |
                 * +========+========+============+=============+
                 */
-               if (tcp_ecn_mode_pending(tp))
+               if (tcp_ca_no_fallback_rfc3168(sk))
+                       tcp_ecn_mode_set(tp, TCP_ECN_DISABLED);
+               else if (tcp_ecn_mode_pending(tp))
                        /* Downgrade from AccECN, or requested initially */
                        tcp_ecn_mode_set(tp, TCP_ECN_MODE_RFC3168);
                break;
@@ -531,9 +533,11 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb
        }
 }
 
-static inline void tcp_ecn_rcv_syn(struct tcp_sock *tp, const struct tcphdr *th,
+static inline void tcp_ecn_rcv_syn(struct sock *sk, const struct tcphdr *th,
                                   const struct sk_buff *skb)
 {
+       struct tcp_sock *tp = tcp_sk(sk);
+
        if (tcp_ecn_mode_pending(tp)) {
                if (!tcp_accecn_syn_requested(th)) {
                        /* Downgrade to classic ECN feedback */
@@ -545,7 +549,8 @@ static inline void tcp_ecn_rcv_syn(struct tcp_sock *tp, const struct tcphdr *th,
                        tcp_ecn_mode_set(tp, TCP_ECN_MODE_ACCECN);
                }
        }
-       if (tcp_ecn_mode_rfc3168(tp) && (!th->ece || !th->cwr))
+       if (tcp_ecn_mode_rfc3168(tp) &&
+           (!th->ece || !th->cwr || tcp_ca_no_fallback_rfc3168(sk)))
                tcp_ecn_mode_set(tp, TCP_ECN_DISABLED);
 }
 
index e823a3b1ad3ba34791361a476d303574b7c47690..59dafcb45c168c96a04ac79280b0a1ee81dd2615 100644 (file)
@@ -7090,7 +7090,7 @@ consume:
                tp->snd_wl1    = TCP_SKB_CB(skb)->seq;
                tp->max_window = tp->snd_wnd;
 
-               tcp_ecn_rcv_syn(tp, th, skb);
+               tcp_ecn_rcv_syn(sk, th, skb);
 
                tcp_mtup_init(sk);
                tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
index bd5462154f970ba6d244882275a6baadadd283c6..9776c921d1bb4d7d7c0f6f2b17e4e55eb508159e 100644 (file)
@@ -485,9 +485,10 @@ static void tcp_ecn_openreq_child(struct sock *sk,
                tp->accecn_opt_demand = 1;
                tcp_ecn_received_counters_payload(sk, skb);
        } else {
-               tcp_ecn_mode_set(tp, inet_rsk(req)->ecn_ok ?
-                                    TCP_ECN_MODE_RFC3168 :
-                                    TCP_ECN_DISABLED);
+               if (inet_rsk(req)->ecn_ok && !tcp_ca_no_fallback_rfc3168(sk))
+                       tcp_ecn_mode_set(tp, TCP_ECN_MODE_RFC3168);
+               else
+                       tcp_ecn_mode_set(tp, TCP_ECN_DISABLED);
        }
 }