All of lore.kernel.org
 help / color / mirror / Atom feed
From: Valentin Berezhnoy <dreyksmail@gmail.com>
To: netfilter-devel@vger.kernel.org
Cc: Pablo Neira Ayuso <pablo@netfilter.org>,
	Florian Westphal <fw@strlen.de>, Phil Sutter <phil@nwl.cc>,
	coreteam@netfilter.org, Valentin Berezhnoy <dreyksmail@gmail.com>
Subject: [PATCH nf] netfilter: flowtable: fetch hardware counters before removing a flow
Date: Wed, 21 Oct 2026 20:50:30 +0300	[thread overview]
Message-ID: <20261021175030.4800-1-dreyksmail@gmail.com> (raw)

The counters of hardware offloaded flows are added to the conntrack
entry by the stats work, which nf_flow_offload_stats() queues only once
a tenth of the flow timeout has passed since the last update, i.e.
about every 3 seconds with the default nf_flowtable_tcp_timeout of 30
seconds. When a flow is torn down, e.g. after a TCP FIN or RST,
flow_offload_work_del() removes the hardware entries without fetching
their counters, so the traffic since the last update never reaches
conntrack.

At gigabit speeds that can be several hundred megabytes per connection.
On a MediaTek MT7622 router with hardware flow offloading, a 300 MiB
download that closed its connection right after the transfer was
accounted at 69-84% by conntrack, but at 105% (payload plus headers)
when the connection stayed open for 10 seconds before closing, i.e.
after one more stats update.

Fetch the counters once more in the delete work, before the hardware
entries are removed. The flow and its conntrack entry are still
referenced at that point: the garbage collector only releases the flow
once NF_FLOW_HW_DEAD is set at the end of the delete work.

Fixes: ef803b3cf96a ("netfilter: flowtable: add counter support in HW offload")
Assisted-by: LLM
Signed-off-by: Valentin Berezhnoy <dreyksmail@gmail.com>
---

Notes:
    This patch and its changelog were written with an AI coding assistant
    (Claude), which also ran the tests below; I reviewed the change and the
    results.
    
    Testing:
    - Runtime: OpenWrt main (Linux 6.18.55) on a Redmi AX6S (MediaTek
      MT7622) with flowtable hardware offloading and WED, counters read
      through nlbwmon (conntrack). 300 MiB downloads that close their
      connection right after the transfer are now accounted at 105.0 %
      (3 of 3 runs; payload plus TCP/IP headers), 30 downloads of 10 MiB
      on separate connections at 104.9 %, the same as a download whose
      connection stays open 10 s before closing (105.0 %). That kernel also
      carries a fix for OpenWrt's out-of-tree mtk_ppe accounting patch,
      which is needed on that driver for the final poll to read the
      counters of an entry the PPE already unbound after the FIN.
    - Build: net/netfilter/nf_flow_table_offload.o on this tree with W=1
      (x86_64 defconfig plus netfilter), no warnings. Not runtime-tested on
      this tree itself.

 net/netfilter/nf_flow_table_offload.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index 436585922..056c000a6 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -989,15 +989,6 @@ static void flow_offload_work_add(struct flow_offload_work *offload)
 	nf_flow_offload_destroy(flow_rule);
 }
 
-static void flow_offload_work_del(struct flow_offload_work *offload)
-{
-	clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
-	flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
-	if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
-		flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
-	set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
-}
-
 static void flow_offload_tuple_stats(struct flow_offload_work *offload,
 				     enum flow_offload_tuple_dir dir,
 				     struct flow_stats *stats)
@@ -1034,6 +1025,22 @@ static void flow_offload_work_stats(struct flow_offload_work *offload)
 	}
 }
 
+static void flow_offload_work_del(struct flow_offload_work *offload)
+{
+	/* Stats are only polled every few seconds: fetch the counters of the
+	 * hardware entries one last time before they are removed, so that the
+	 * traffic since the last poll is not lost for conntrack accounting.
+	 */
+	if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER)
+		flow_offload_work_stats(offload);
+
+	clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
+	flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
+	if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
+		flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
+	set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
+}
+
 static void flow_offload_work_handler(struct work_struct *work)
 {
 	struct flow_offload_work *offload;
-- 
2.53.0


             reply	other threads:[~2026-10-21 17:50 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 17:50 Valentin Berezhnoy [this message]
2026-10-29 17:22 ` [PATCH nf] netfilter: flowtable: fetch hardware counters before removing a flow Pablo Neira Ayuso

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261021175030.4800-1-dreyksmail@gmail.com \
    --to=dreyksmail@gmail.com \
    --cc=coreteam@netfilter.org \
    --cc=fw@strlen.de \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pablo@netfilter.org \
    --cc=phil@nwl.cc \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.