]> git.itanic.dy.fi Git - linux-stable/commitdiff
netfilter: nf_tables: defer gc run if previous batch is still pending
authorFlorian Westphal <fw@strlen.de>
Tue, 22 Aug 2023 20:03:57 +0000 (22:03 +0200)
committerFlorian Westphal <fw@strlen.de>
Wed, 23 Aug 2023 14:12:59 +0000 (16:12 +0200)
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>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables.h
net/netfilter/nft_set_hash.c
net/netfilter/nft_set_rbtree.c

index ffcbdf08380f8375120c66ba651a428665a82ec5..dd40c75011d25fdf8ce2360c9b291ebc4ee6104b 100644 (file)
@@ -587,6 +587,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 cef5df84600095079f003adff53de8911d6db9ba..524763659f2510c2678e013b19a12a929f7630c5 100644 (file)
@@ -326,6 +326,9 @@ static void nft_rhash_gc(struct work_struct *work)
        nft_net = nft_pernet(net);
        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 f9d4c8fcbbf82745907920032d15b803ae5561bc..c6435e70923197d838d765bad016f7d929c353cc 100644 (file)
@@ -611,6 +611,9 @@ static void nft_rbtree_gc(struct work_struct *work)
        nft_net = nft_pernet(net);
        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;