netfilter: nf_tables: defer gc run if previous batch is still pending
authorSasha Levin <sashal@kernel.org>
Fri, 22 Sep 2023 17:01:13 +0000 (19:01 +0200)
committerSalvatore Bonaccorso <carnil@debian.org>
Fri, 29 Sep 2023 04:25:15 +0000 (05:25 +0100)
commit 8e51830e29e12670b4c10df070a4ea4c9593e961 upstream.

Don't queue more gc work, else we may queue the same elements multiple
times.

If an element is flagged as dead, this can mean that either the previous
gc request was invalidated/discarded by a transaction or that the previous
request is still pending in the system work queue.

The latter will happen if the gc interval is set to a very low value,
e.g. 1ms, and system work queue is backlogged.

The sets refcount is 1 if no previous gc requeusts are queued, so add
a helper for this and skip gc run if old requests are pending.

Add a helper for this and skip the gc run in this case.

Fixes: f6c383b8c31a ("netfilter: nf_tables: adapt set backend to use GC transaction API")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Gbp-Pq: Topic bugfix/all
Gbp-Pq: Name netfilter-nf_tables-defer-gc-run-if-previous-batch-i.patch

include/net/netfilter/nf_tables.h
net/netfilter/nft_set_hash.c
net/netfilter/nft_set_rbtree.c

index 9182b583d429778996022a9eca0aaac543e51c7f..bbe472c07d07eaea46b0ae2213e445481da6517c 100644 (file)
@@ -479,6 +479,11 @@ static inline void *nft_set_priv(const struct nft_set *set)
        return (void *)set->data;
 }
 
+static inline bool nft_set_gc_is_pending(const struct nft_set *s)
+{
+       return refcount_read(&s->refs) != 1;
+}
+
 static inline struct nft_set *nft_set_container_of(const void *priv)
 {
        return (void *)priv - offsetof(struct nft_set, data);
index 9cdf348b048a454ad318e34872ff91a6faf76bbd..68a16ee37b3d017ac90d24b16a14dca86e89ceb8 100644 (file)
@@ -312,6 +312,9 @@ static void nft_rhash_gc(struct work_struct *work)
        nft_net = net_generic(net, nf_tables_net_id);
        gc_seq = READ_ONCE(nft_net->gc_seq);
 
+       if (nft_set_gc_is_pending(set))
+               goto done;
+
        gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL);
        if (!gc)
                goto done;
index ed14849aa47f48f5a308ea4d67db371df4482f44..9b0bdd4216152820da4ca1dbc9579f170b10d819 100644 (file)
@@ -613,6 +613,9 @@ static void nft_rbtree_gc(struct work_struct *work)
        nft_net = net_generic(net, nf_tables_net_id);
        gc_seq  = READ_ONCE(nft_net->gc_seq);
 
+       if (nft_set_gc_is_pending(set))
+               goto done;
+
        gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL);
        if (!gc)
                goto done;