[PATCH v7 net-next 05/13] tcp: disable RFC3168 fallback identifier for CC modules

2026-01-03 Thread chia-yu . chang
From: Chia-Yu Chang 

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 
Acked-by: Paolo Abeni 

---
v3:
- Add empty line between variable declarations and code.
---
 include/net/tcp.h| 12 +++-
 include/net/tcp_ecn.h| 11 ---
 net/ipv4/tcp_input.c |  2 +-
 net/ipv4/tcp_minisocks.c |  7 ---
 4 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index c4c25f2e0c0d..e0a5cf2f7818 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1207,8 +1207,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;
 
@@ -1354,6 +1357,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);
diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h
index fdde1c342b35..2e1637edf1d3 100644
--- a/include/net/tcp_ecn.h
+++ b/include/net/tcp_ecn.h
@@ -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);
 }
 
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index ccbab5569680..e5c9cf586437 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6861,7 +6861,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, 
struct sk_buff *skb,
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);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index bd5462154f97..9776c921d1bb 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -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_ec

[PATCH v7 net-next 05/13] tcp: disable RFC3168 fallback identifier for CC modules

2025-12-01 Thread chia-yu . chang
From: Chia-Yu Chang 

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 
Acked-by: Paolo Abeni 

---
v3:
- Add empty line between variable declarations and code.
---
 include/net/tcp.h| 12 +++-
 include/net/tcp_ecn.h| 11 ---
 net/ipv4/tcp_input.c |  2 +-
 net/ipv4/tcp_minisocks.c |  7 ---
 4 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index c4c25f2e0c0d..e0a5cf2f7818 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1207,8 +1207,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;
 
@@ -1354,6 +1357,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);
diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h
index fdde1c342b35..2e1637edf1d3 100644
--- a/include/net/tcp_ecn.h
+++ b/include/net/tcp_ecn.h
@@ -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);
 }
 
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index ccbab5569680..e5c9cf586437 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6861,7 +6861,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, 
struct sk_buff *skb,
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);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index bd5462154f97..9776c921d1bb 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -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_ec