* [RFC PATCH net] netfilter: flowtable: fix offloaded ct timeout never being extended
@ 2026-05-26 6:01 Adrian Bente
2026-05-27 7:34 ` Florian Westphal
0 siblings, 1 reply; 3+ messages in thread
From: Adrian Bente @ 2026-05-26 6:01 UTC (permalink / raw)
To: pablo, kadlec, fw, netfilter-devel
Cc: phil, davem, edumazet, kuba, pabeni, horms, nbd, sean.wang,
lorenzo, andrew+netdev, matthias.bgg, angelogioacchino.delregno,
daniel, coreteam, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek, Adrian Bente
OpenWrt has recently migrated many platforms to kernel 6.18. On the
MediaTek platform, which supports hardware network offloading, WiFi
connections accelerated via the WED path were observed to drop after
roughly 300 seconds.
After several debugging sessions, assisted by the Claude LLM, the
problem was narrowed down as follows:
nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded
flows using:
cmpxchg(&ct->timeout, expires, new_timeout);
'expires' comes from nf_ct_expires(ct) and is a relative value, while
ct->timeout holds an absolute timestamp. The two are never equal, so
the cmpxchg always fails and the timeout is never extended.
This goes unnoticed for most flows, but a long-lived hardware (WED)
offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to
zero, the conntrack entry is reaped and the connection breaks.
Compare against the current ct->timeout value instead.
This patch is sent as RFC: the diagnosis is verified on hardware and
the fix resolves the drop, but review of the chosen approach is
welcome.
Fixes: 03428ca5cee9 ("netfilter: conntrack: rework offload nf_conn timeout extension logic")
Signed-off-by: Adrian Bente <adibente@gmail.com>
---
net/netfilter/nf_flow_table_core.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -541,8 +541,10 @@
* after this -- is fine, datapath is authoritative.
*/
if (new_timeout) {
+ u32 old = READ_ONCE(ct->timeout);
+
new_timeout += nfct_time_stamp;
- cmpxchg(&ct->timeout, expires, new_timeout);
+ cmpxchg(&ct->timeout, old, new_timeout);
}
}
--
2.46.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC PATCH net] netfilter: flowtable: fix offloaded ct timeout never being extended
2026-05-26 6:01 [RFC PATCH net] netfilter: flowtable: fix offloaded ct timeout never being extended Adrian Bente
@ 2026-05-27 7:34 ` Florian Westphal
2026-05-27 16:14 ` Adrian Bențe
0 siblings, 1 reply; 3+ messages in thread
From: Florian Westphal @ 2026-05-27 7:34 UTC (permalink / raw)
To: Adrian Bente
Cc: pablo, netfilter-devel, phil, nbd, sean.wang, lorenzo,
andrew+netdev, matthias.bgg, angelogioacchino.delregno, daniel,
coreteam, linux-mediatek
Adrian Bente <adibente@gmail.com> wrote:
[ trimming CCs .. ]
> OpenWrt has recently migrated many platforms to kernel 6.18. On the
> MediaTek platform, which supports hardware network offloading, WiFi
> connections accelerated via the WED path were observed to drop after
> roughly 300 seconds.
>
> After several debugging sessions, assisted by the Claude LLM, the
> problem was narrowed down as follows:
>
> nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded
> flows using:
>
> cmpxchg(&ct->timeout, expires, new_timeout);
>
> 'expires' comes from nf_ct_expires(ct) and is a relative value, while
> ct->timeout holds an absolute timestamp. The two are never equal, so
> the cmpxchg always fails and the timeout is never extended.
>
> This goes unnoticed for most flows, but a long-lived hardware (WED)
> offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to
> zero, the conntrack entry is reaped and the connection breaks.
>
> Compare against the current ct->timeout value instead.
>
> This patch is sent as RFC: the diagnosis is verified on hardware and
> the fix resolves the drop, but review of the chosen approach is
> welcome.
I guess we need to open-code expires, something like this (not even
compile tested). Also see https://sashiko.dev/#/patchset/20260526060138.3924-1-adibente%40gmail.com
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -506,7 +506,12 @@ static u32 nf_flow_table_tcp_timeout(const struct nf_conn *ct)
static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
{
static const u32 min_timeout = 5 * 60 * HZ;
- u32 expires = nf_ct_expires(ct);
+ u32 ct_timeout = READ_ONCE(ct->timeout);
+ s32 expires;
+
+ expires = ct_timeout - nfct_time_stamp;
+ if (expires <= 0) /* already expired */
+ return;
/* normal case: large enough timeout, nothing to do. */
if (likely(expires >= min_timeout))
@@ -524,7 +529,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
if (nf_ct_is_confirmed(ct) &&
test_bit(IPS_OFFLOAD_BIT, &ct->status)) {
u8 l4proto = nf_ct_protonum(ct);
- u32 new_timeout = true;
+ u32 new_timeout = 1;
switch (l4proto) {
case IPPROTO_UDP:
@@ -549,7 +554,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
*/
if (new_timeout) {
new_timeout += nfct_time_stamp;
- cmpxchg(&ct->timeout, expires, new_timeout);
+ cmpxchg(&ct->timeout, ct_timeout, new_timeout);
}
}
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC PATCH net] netfilter: flowtable: fix offloaded ct timeout never being extended
2026-05-27 7:34 ` Florian Westphal
@ 2026-05-27 16:14 ` Adrian Bențe
0 siblings, 0 replies; 3+ messages in thread
From: Adrian Bențe @ 2026-05-27 16:14 UTC (permalink / raw)
To: Florian Westphal
Cc: pablo, netfilter-devel, phil, nbd, sean.wang, lorenzo,
andrew+netdev, matthias.bgg, angelogioacchino.delregno, daniel,
coreteam, linux-mediatek
> I guess we need to open-code expires, something like this (not even
> compile tested). Also see https://sashiko.dev/#/patchset/20260526060138.3924-1-adibente%40gmail.com
>
> diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
> --- a/net/netfilter/nf_flow_table_core.c
> +++ b/net/netfilter/nf_flow_table_core.c
> @@ -506,7 +506,12 @@ static u32 nf_flow_table_tcp_timeout(const struct nf_conn *ct)
> static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
> {
> static const u32 min_timeout = 5 * 60 * HZ;
> - u32 expires = nf_ct_expires(ct);
> + u32 ct_timeout = READ_ONCE(ct->timeout);
> + s32 expires;
> +
> + expires = ct_timeout - nfct_time_stamp;
> + if (expires <= 0) /* already expired */
> + return;
>
> /* normal case: large enough timeout, nothing to do. */
> if (likely(expires >= min_timeout))
> @@ -524,7 +529,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
> if (nf_ct_is_confirmed(ct) &&
> test_bit(IPS_OFFLOAD_BIT, &ct->status)) {
> u8 l4proto = nf_ct_protonum(ct);
> - u32 new_timeout = true;
> + u32 new_timeout = 1;
>
> switch (l4proto) {
> case IPPROTO_UDP:
> @@ -549,7 +554,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
> */
> if (new_timeout) {
> new_timeout += nfct_time_stamp;
> - cmpxchg(&ct->timeout, expires, new_timeout);
> + cmpxchg(&ct->timeout, ct_timeout, new_timeout);
> }
> }
>
Thanks. Applied your diff to my OpenWrt 6.18 tree with one small
adjustment: changed min_timeout from u32 to s32 so the
"expires >= min_timeout" comparison has both operands signed.
Compiles clean.
Tested on MT7986 with the WED-offloaded flows that originally
reproduced the 300s drop. The flows now stay up well past 300s with
normal offloaded traffic, solution works fine.
I'll send v2 with this diff and Suggested-by: you, unless you'd
rather submit it yourself.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-27 16:15 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-26 6:01 [RFC PATCH net] netfilter: flowtable: fix offloaded ct timeout never being extended Adrian Bente
2026-05-27 7:34 ` Florian Westphal
2026-05-27 16:14 ` Adrian Bențe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox