All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Qihang <q.h.hack.winter@gmail.com>
Cc: fw@strlen.de, phil@nwl.cc, vladbu@nvidia.com, ozsh@nvidia.com,
	netfilter-devel@vger.kernel.org, coreteam@netfilter.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH net] netfilter: nf_flow_table: defer pernet stats release to last flowtable teardown
Date: Tue, 10 Nov 2026 01:50:25 +0100	[thread overview]
Message-ID: <avJqUf4_aBq_zkRo@chamomile> (raw)
In-Reply-To: <20261105065509.94627-1-q.h.hack.winter@gmail.com>

On Thu, Nov 05, 2026 at 02:55:09PM +0800, Qihang wrote:
> The pernet flowtable statistics (net->ft.stat) are released when the
> netns exits, but the flowtables attached to a dying netns are torn down
> asynchronously afterwards: the tc conntrack action frees its flowtable
> from an RCU-delayed workqueue item (tcf_ct_flow_table_cleanup_work()),
> and nf_tables releases flowtables from the transaction release work.
> 
> nf_flow_table_free() ends with a garbage-collection pass that queues
> offload works; flow_offload_queue_work() increments the pernet counters
> when scheduling them and the handlers decrement the counters again once
> they complete.  The flushes in nf_flow_table_free() only drain those
> workqueues when hardware offload is in use, so after free_percpu() the
> stray workers still this_cpu_inc()/this_cpu_dec() into freed percpu
> memory.
> 
> Account flowtables at init time and defer the release of the statistics
> to the last nf_flow_table_free() of a dismantled netns, after draining
> the offload workqueues.  A netns without flowtables keeps the
> synchronous release.

Looks like a duplicate of:

https://patchwork.ozlabs.org/project/netfilter-devel/patch/20261029230657.302417-1-pablo@netfilter.org/

> Fixes: b038177636f8 ("netfilter: nf_flow_table: count pending offload workqueue tasks")
> Cc: stable@vger.kernel.org
> Signed-off-by: Qihang <q.h.hack.winter@gmail.com>
> ---
>  include/net/netfilter/nf_flow_table.h |  1 +
>  include/net/netns/flow_table.h        | 11 +++++++
>  net/netfilter/nf_flow_table_core.c    | 44 ++++++++++++++++++++++++++-
>  net/netfilter/nf_flow_table_offload.c |  7 +++++
>  4 files changed, 62 insertions(+), 1 deletion(-)
> 
> diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
> index 5b22d31cb1407..91ffa3072a0cc 100644
> --- a/include/net/netfilter/nf_flow_table.h
> +++ b/include/net/netfilter/nf_flow_table.h
> @@ -376,6 +376,7 @@ void nf_flow_offload_stats(struct nf_flowtable *flowtable,
>  
>  void nf_flow_table_offload_flush(struct nf_flowtable *flowtable);
>  void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable);
> +void nf_flow_table_offload_drain(void);
>  
>  int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
>  				struct net_device *dev,
> diff --git a/include/net/netns/flow_table.h b/include/net/netns/flow_table.h
> index 1c5fc657e2675..6d2dc0d1f06c7 100644
> --- a/include/net/netns/flow_table.h
> +++ b/include/net/netns/flow_table.h
> @@ -2,6 +2,9 @@
>  #ifndef __NETNS_FLOW_TABLE_H
>  #define __NETNS_FLOW_TABLE_H
>  
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
>  struct nf_flow_table_stat {
>  	unsigned int count_wq_add;
>  	unsigned int count_wq_del;
> @@ -9,6 +12,14 @@ struct nf_flow_table_stat {
>  };
>  
>  struct netns_ft {
> +	/* Protects nr_ft and exiting. */
> +	spinlock_t		lock;
> +	/* Number of flowtables attached to this netns. */
> +	int			nr_ft;
> +	/* Set at netns exit; the statistics are then released by the
> +	 * last flowtable teardown.
> +	 */
> +	bool			exiting;
>  	struct nf_flow_table_stat __percpu *stat;
>  };
>  #endif
> diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
> index 0d72dc8ae47a1..de0a9f0e4f7b9 100644
> --- a/net/netfilter/nf_flow_table_core.c
> +++ b/net/netfilter/nf_flow_table_core.c
> @@ -715,6 +715,7 @@ EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
>  
>  int nf_flow_table_init(struct nf_flowtable *flowtable)
>  {
> +	struct net *net;
>  	int err;
>  
>  	INIT_DELAYED_WORK(&flowtable->gc_work, nf_flow_offload_work_gc);
> @@ -733,6 +734,13 @@ int nf_flow_table_init(struct nf_flowtable *flowtable)
>  	list_add(&flowtable->list, &flowtables);
>  	mutex_unlock(&flowtable_lock);
>  
> +	net = read_pnet(&flowtable->net);
> +	if (net) {
> +		spin_lock(&net->ft.lock);
> +		net->ft.nr_ft++;
> +		spin_unlock(&net->ft.lock);
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(nf_flow_table_init);
> @@ -771,6 +779,8 @@ EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);
>  
>  void nf_flow_table_free(struct nf_flowtable *flow_table)
>  {
> +	struct net *net;
> +
>  	mutex_lock(&flowtable_lock);
>  	list_del(&flow_table->list);
>  	mutex_unlock(&flowtable_lock);
> @@ -781,19 +791,51 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
>  	nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
>  	nf_flow_table_gc_run(flow_table);
>  	nf_flow_table_offload_flush_cleanup(flow_table);
> +
> +	/* The gc pass above can still queue offload works whose handlers
> +	 * decrement the pernet statistics.  Drain them all.
> +	 */
> +	nf_flow_table_offload_drain();
>  	rhashtable_destroy(&flow_table->rhashtable);
> +
> +	net = read_pnet(&flow_table->net);
> +	if (net) {
> +		bool release;
> +
> +		spin_lock(&net->ft.lock);
> +		release = net->ft.exiting && !--net->ft.nr_ft;
> +		spin_unlock(&net->ft.lock);
> +
> +		/* Released by the last flowtable teardown, see
> +		 * nf_flow_table_fini_net().
> +		 */
> +		if (release)
> +			free_percpu(net->ft.stat);
> +	}
>  }
>  EXPORT_SYMBOL_GPL(nf_flow_table_free);
>  
>  static int nf_flow_table_init_net(struct net *net)
>  {
> +	spin_lock_init(&net->ft.lock);
>  	net->ft.stat = alloc_percpu(struct nf_flow_table_stat);
>  	return net->ft.stat ? 0 : -ENOMEM;
>  }
>  
>  static void nf_flow_table_fini_net(struct net *net)
>  {
> -	free_percpu(net->ft.stat);
> +	bool release;
> +
> +	spin_lock(&net->ft.lock);
> +	net->ft.exiting = true;
> +	release = !net->ft.nr_ft;
> +	spin_unlock(&net->ft.lock);
> +
> +	/* Pending flowtables still update the statistics from workqueue
> +	 * context; the last one releases them, see nf_flow_table_free().
> +	 */
> +	if (release)
> +		free_percpu(net->ft.stat);
>  }
>  
>  static int nf_flow_table_pernet_init(struct net *net)
> diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
> index 4365859220e6a..6ac28802ef632 100644
> --- a/net/netfilter/nf_flow_table_offload.c
> +++ b/net/netfilter/nf_flow_table_offload.c
> @@ -1180,6 +1180,13 @@ void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
>  	}
>  }
>  
> +void nf_flow_table_offload_drain(void)
> +{
> +	flush_workqueue(nf_flow_offload_add_wq);
> +	flush_workqueue(nf_flow_offload_del_wq);
> +	flush_workqueue(nf_flow_offload_stats_wq);
> +}
> +
>  static int nf_flow_table_block_setup(struct nf_flowtable *flowtable,
>  				     struct flow_block_offload *bo,
>  				     enum flow_block_command cmd)
> -- 
> 2.54.0 (Apple Git-157)
> 

      parent reply	other threads:[~2026-11-10  0:50 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05  6:55 [PATCH net] netfilter: nf_flow_table: defer pernet stats release to last flowtable teardown Qihang
2026-11-05  7:03 ` netdev-bot+sinfo
2026-11-09  7:27 ` netdev-bot+sashiko
2026-11-10  0:50 ` Pablo Neira Ayuso [this message]

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=avJqUf4_aBq_zkRo@chamomile \
    --to=pablo@netfilter.org \
    --cc=coreteam@netfilter.org \
    --cc=fw@strlen.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=ozsh@nvidia.com \
    --cc=phil@nwl.cc \
    --cc=q.h.hack.winter@gmail.com \
    --cc=stable@vger.kernel.org \
    --cc=vladbu@nvidia.com \
    /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.