+++ /dev/null
-From cb0eb22a987fceee0994a50d1a4d237060c1e422 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 9 Aug 2023 14:54:23 +0200
-Subject: netfilter: nf_tables: adapt set backend to use GC transaction API
-
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-
-[ Upstream commit f6c383b8c31a93752a52697f8430a71dcbc46adf ]
-
-Use the GC transaction API to replace the old and buggy gc API and the
-busy mark approach.
-
-No set elements are removed from async garbage collection anymore,
-instead the _DEAD bit is set on so the set element is not visible from
-lookup path anymore. Async GC enqueues transaction work that might be
-aborted and retried later.
-
-rbtree and pipapo set backends does not set on the _DEAD bit from the
-sync GC path since this runs in control plane path where mutex is held.
-In this case, set elements are deactivated, removed and then released
-via RCU callback, sync GC never fails.
-
-Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges")
-Fixes: 8d8540c4f5e0 ("netfilter: nft_set_rbtree: add timeout support")
-Fixes: 9d0982927e79 ("netfilter: nft_hash: add support for timeouts")
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- net/netfilter/nf_tables_api.c | 7 +-
- net/netfilter/nft_set_hash.c | 77 +++++++++++-------
- net/netfilter/nft_set_pipapo.c | 48 ++++++++---
- net/netfilter/nft_set_rbtree.c | 144 ++++++++++++++++++++-------------
- 4 files changed, 173 insertions(+), 103 deletions(-)
-
-diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
-index 4d827177aaebe..35d84e8b79524 100644
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -6148,7 +6148,6 @@ static void nft_setelem_activate(struct net *net, struct nft_set *set,
-
- if (nft_setelem_is_catchall(set, elem)) {
- nft_set_elem_change_active(net, set, ext);
-- nft_set_elem_clear_busy(ext);
- } else {
- set->ops->activate(net, set, elem);
- }
-@@ -6163,8 +6162,7 @@ static int nft_setelem_catchall_deactivate(const struct net *net,
-
- list_for_each_entry(catchall, &set->catchall_list, list) {
- ext = nft_set_elem_ext(set, catchall->elem);
-- if (!nft_is_active(net, ext) ||
-- nft_set_elem_mark_busy(ext))
-+ if (!nft_is_active(net, ext))
- continue;
-
- kfree(elem->priv);
-@@ -6874,8 +6872,7 @@ static int nft_set_catchall_flush(const struct nft_ctx *ctx,
-
- list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
- ext = nft_set_elem_ext(set, catchall->elem);
-- if (!nft_set_elem_active(ext, genmask) ||
-- nft_set_elem_mark_busy(ext))
-+ if (!nft_set_elem_active(ext, genmask))
- continue;
-
- elem.priv = catchall->elem;
-diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c
-index 0b73cb0e752f7..960cbd56c0406 100644
---- a/net/netfilter/nft_set_hash.c
-+++ b/net/netfilter/nft_set_hash.c
-@@ -59,6 +59,8 @@ static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg,
-
- if (memcmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
- return 1;
-+ if (nft_set_elem_is_dead(&he->ext))
-+ return 1;
- if (nft_set_elem_expired(&he->ext))
- return 1;
- if (!nft_set_elem_active(&he->ext, x->genmask))
-@@ -188,7 +190,6 @@ static void nft_rhash_activate(const struct net *net, const struct nft_set *set,
- struct nft_rhash_elem *he = elem->priv;
-
- nft_set_elem_change_active(net, set, &he->ext);
-- nft_set_elem_clear_busy(&he->ext);
- }
-
- static bool nft_rhash_flush(const struct net *net,
-@@ -196,12 +197,9 @@ static bool nft_rhash_flush(const struct net *net,
- {
- struct nft_rhash_elem *he = priv;
-
-- if (!nft_set_elem_mark_busy(&he->ext) ||
-- !nft_is_active(net, &he->ext)) {
-- nft_set_elem_change_active(net, set, &he->ext);
-- return true;
-- }
-- return false;
-+ nft_set_elem_change_active(net, set, &he->ext);
-+
-+ return true;
- }
-
- static void *nft_rhash_deactivate(const struct net *net,
-@@ -218,9 +216,8 @@ static void *nft_rhash_deactivate(const struct net *net,
-
- rcu_read_lock();
- he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
-- if (he != NULL &&
-- !nft_rhash_flush(net, set, he))
-- he = NULL;
-+ if (he)
-+ nft_set_elem_change_active(net, set, &he->ext);
-
- rcu_read_unlock();
-
-@@ -314,25 +311,48 @@ static bool nft_rhash_expr_needs_gc_run(const struct nft_set *set,
-
- static void nft_rhash_gc(struct work_struct *work)
- {
-+ struct nftables_pernet *nft_net;
- struct nft_set *set;
- struct nft_rhash_elem *he;
- struct nft_rhash *priv;
-- struct nft_set_gc_batch *gcb = NULL;
- struct rhashtable_iter hti;
-+ struct nft_trans_gc *gc;
-+ struct net *net;
-+ u32 gc_seq;
-
- priv = container_of(work, struct nft_rhash, gc_work.work);
- set = nft_set_container_of(priv);
-+ net = read_pnet(&set->net);
-+ nft_net = nft_pernet(net);
-+ gc_seq = READ_ONCE(nft_net->gc_seq);
-+
-+ gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL);
-+ if (!gc)
-+ goto done;
-
- rhashtable_walk_enter(&priv->ht, &hti);
- rhashtable_walk_start(&hti);
-
- while ((he = rhashtable_walk_next(&hti))) {
- if (IS_ERR(he)) {
-- if (PTR_ERR(he) != -EAGAIN)
-- break;
-+ if (PTR_ERR(he) != -EAGAIN) {
-+ nft_trans_gc_destroy(gc);
-+ gc = NULL;
-+ goto try_later;
-+ }
- continue;
- }
-
-+ /* Ruleset has been updated, try later. */
-+ if (READ_ONCE(nft_net->gc_seq) != gc_seq) {
-+ nft_trans_gc_destroy(gc);
-+ gc = NULL;
-+ goto try_later;
-+ }
-+
-+ if (nft_set_elem_is_dead(&he->ext))
-+ goto dead_elem;
-+
- if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPRESSIONS) &&
- nft_rhash_expr_needs_gc_run(set, &he->ext))
- goto needs_gc_run;
-@@ -340,26 +360,26 @@ static void nft_rhash_gc(struct work_struct *work)
- if (!nft_set_elem_expired(&he->ext))
- continue;
- needs_gc_run:
-- if (nft_set_elem_mark_busy(&he->ext))
-- continue;
-+ nft_set_elem_dead(&he->ext);
-+dead_elem:
-+ gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
-+ if (!gc)
-+ goto try_later;
-
-- gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
-- if (gcb == NULL)
-- break;
-- rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params);
-- atomic_dec(&set->nelems);
-- nft_set_gc_batch_add(gcb, he);
-+ nft_trans_gc_elem_add(gc, he);
- }
-+
-+ gc = nft_trans_gc_catchall(gc, gc_seq);
-+
-+try_later:
-+ /* catchall list iteration requires rcu read side lock. */
- rhashtable_walk_stop(&hti);
- rhashtable_walk_exit(&hti);
-
-- he = nft_set_catchall_gc(set);
-- if (he) {
-- gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
-- if (gcb)
-- nft_set_gc_batch_add(gcb, he);
-- }
-- nft_set_gc_batch_complete(gcb);
-+ if (gc)
-+ nft_trans_gc_queue_async_done(gc);
-+
-+done:
- queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
- nft_set_gc_interval(set));
- }
-@@ -422,7 +442,6 @@ static void nft_rhash_destroy(const struct nft_ctx *ctx,
- };
-
- cancel_delayed_work_sync(&priv->gc_work);
-- rcu_barrier();
- rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy,
- (void *)&rhash_ctx);
- }
-diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c
-index 8c16681884b7e..0a49c59a22dbd 100644
---- a/net/netfilter/nft_set_pipapo.c
-+++ b/net/netfilter/nft_set_pipapo.c
-@@ -1536,16 +1536,34 @@ static void pipapo_drop(struct nft_pipapo_match *m,
- }
- }
-
-+static void nft_pipapo_gc_deactivate(struct net *net, struct nft_set *set,
-+ struct nft_pipapo_elem *e)
-+
-+{
-+ struct nft_set_elem elem = {
-+ .priv = e,
-+ };
-+
-+ nft_setelem_data_deactivate(net, set, &elem);
-+}
-+
- /**
- * pipapo_gc() - Drop expired entries from set, destroy start and end elements
- * @set: nftables API set representation
- * @m: Matching data
- */
--static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m)
-+static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
- {
-+ struct nft_set *set = (struct nft_set *) _set;
- struct nft_pipapo *priv = nft_set_priv(set);
-+ struct net *net = read_pnet(&set->net);
- int rules_f0, first_rule = 0;
- struct nft_pipapo_elem *e;
-+ struct nft_trans_gc *gc;
-+
-+ gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
-+ if (!gc)
-+ return;
-
- while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) {
- union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
-@@ -1569,13 +1587,20 @@ static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m)
- f--;
- i--;
- e = f->mt[rulemap[i].to].e;
-- if (nft_set_elem_expired(&e->ext) &&
-- !nft_set_elem_mark_busy(&e->ext)) {
-+
-+ /* synchronous gc never fails, there is no need to set on
-+ * NFT_SET_ELEM_DEAD_BIT.
-+ */
-+ if (nft_set_elem_expired(&e->ext)) {
- priv->dirty = true;
-- pipapo_drop(m, rulemap);
-
-- rcu_barrier();
-- nft_set_elem_destroy(set, e, true);
-+ gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
-+ if (!gc)
-+ break;
-+
-+ nft_pipapo_gc_deactivate(net, set, e);
-+ pipapo_drop(m, rulemap);
-+ nft_trans_gc_elem_add(gc, e);
-
- /* And check again current first rule, which is now the
- * first we haven't checked.
-@@ -1585,11 +1610,11 @@ static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m)
- }
- }
-
-- e = nft_set_catchall_gc(set);
-- if (e)
-- nft_set_elem_destroy(set, e, true);
--
-- priv->last_gc = jiffies;
-+ gc = nft_trans_gc_catchall(gc, 0);
-+ if (gc) {
-+ nft_trans_gc_queue_sync_done(gc);
-+ priv->last_gc = jiffies;
-+ }
- }
-
- /**
-@@ -1725,7 +1750,6 @@ static void nft_pipapo_activate(const struct net *net,
- return;
-
- nft_set_elem_change_active(net, set, &e->ext);
-- nft_set_elem_clear_busy(&e->ext);
- }
-
- /**
-diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
-index 8d73fffd2d09d..7015153b7be18 100644
---- a/net/netfilter/nft_set_rbtree.c
-+++ b/net/netfilter/nft_set_rbtree.c
-@@ -46,6 +46,12 @@ static int nft_rbtree_cmp(const struct nft_set *set,
- set->klen);
- }
-
-+static bool nft_rbtree_elem_expired(const struct nft_rbtree_elem *rbe)
-+{
-+ return nft_set_elem_expired(&rbe->ext) ||
-+ nft_set_elem_is_dead(&rbe->ext);
-+}
-+
- static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set,
- const u32 *key, const struct nft_set_ext **ext,
- unsigned int seq)
-@@ -80,7 +86,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
- continue;
- }
-
-- if (nft_set_elem_expired(&rbe->ext))
-+ if (nft_rbtree_elem_expired(rbe))
- return false;
-
- if (nft_rbtree_interval_end(rbe)) {
-@@ -98,7 +104,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
-
- if (set->flags & NFT_SET_INTERVAL && interval != NULL &&
- nft_set_elem_active(&interval->ext, genmask) &&
-- !nft_set_elem_expired(&interval->ext) &&
-+ !nft_rbtree_elem_expired(interval) &&
- nft_rbtree_interval_start(interval)) {
- *ext = &interval->ext;
- return true;
-@@ -215,6 +221,18 @@ static void *nft_rbtree_get(const struct net *net, const struct nft_set *set,
- return rbe;
- }
-
-+static void nft_rbtree_gc_remove(struct net *net, struct nft_set *set,
-+ struct nft_rbtree *priv,
-+ struct nft_rbtree_elem *rbe)
-+{
-+ struct nft_set_elem elem = {
-+ .priv = rbe,
-+ };
-+
-+ nft_setelem_data_deactivate(net, set, &elem);
-+ rb_erase(&rbe->node, &priv->root);
-+}
-+
- static int nft_rbtree_gc_elem(const struct nft_set *__set,
- struct nft_rbtree *priv,
- struct nft_rbtree_elem *rbe,
-@@ -222,11 +240,12 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set,
- {
- struct nft_set *set = (struct nft_set *)__set;
- struct rb_node *prev = rb_prev(&rbe->node);
-+ struct net *net = read_pnet(&set->net);
- struct nft_rbtree_elem *rbe_prev;
-- struct nft_set_gc_batch *gcb;
-+ struct nft_trans_gc *gc;
-
-- gcb = nft_set_gc_batch_check(set, NULL, GFP_ATOMIC);
-- if (!gcb)
-+ gc = nft_trans_gc_alloc(set, 0, GFP_ATOMIC);
-+ if (!gc)
- return -ENOMEM;
-
- /* search for end interval coming before this element.
-@@ -244,17 +263,28 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set,
-
- if (prev) {
- rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node);
-+ nft_rbtree_gc_remove(net, set, priv, rbe_prev);
-
-- rb_erase(&rbe_prev->node, &priv->root);
-- atomic_dec(&set->nelems);
-- nft_set_gc_batch_add(gcb, rbe_prev);
-+ /* There is always room in this trans gc for this element,
-+ * memory allocation never actually happens, hence, the warning
-+ * splat in such case. No need to set NFT_SET_ELEM_DEAD_BIT,
-+ * this is synchronous gc which never fails.
-+ */
-+ gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
-+ if (WARN_ON_ONCE(!gc))
-+ return -ENOMEM;
-+
-+ nft_trans_gc_elem_add(gc, rbe_prev);
- }
-
-- rb_erase(&rbe->node, &priv->root);
-- atomic_dec(&set->nelems);
-+ nft_rbtree_gc_remove(net, set, priv, rbe);
-+ gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
-+ if (WARN_ON_ONCE(!gc))
-+ return -ENOMEM;
-+
-+ nft_trans_gc_elem_add(gc, rbe);
-
-- nft_set_gc_batch_add(gcb, rbe);
-- nft_set_gc_batch_complete(gcb);
-+ nft_trans_gc_queue_sync_done(gc);
-
- return 0;
- }
-@@ -482,7 +512,6 @@ static void nft_rbtree_activate(const struct net *net,
- struct nft_rbtree_elem *rbe = elem->priv;
-
- nft_set_elem_change_active(net, set, &rbe->ext);
-- nft_set_elem_clear_busy(&rbe->ext);
- }
-
- static bool nft_rbtree_flush(const struct net *net,
-@@ -490,12 +519,9 @@ static bool nft_rbtree_flush(const struct net *net,
- {
- struct nft_rbtree_elem *rbe = priv;
-
-- if (!nft_set_elem_mark_busy(&rbe->ext) ||
-- !nft_is_active(net, &rbe->ext)) {
-- nft_set_elem_change_active(net, set, &rbe->ext);
-- return true;
-- }
-- return false;
-+ nft_set_elem_change_active(net, set, &rbe->ext);
-+
-+ return true;
- }
-
- static void *nft_rbtree_deactivate(const struct net *net,
-@@ -572,26 +598,40 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
-
- static void nft_rbtree_gc(struct work_struct *work)
- {
-- struct nft_rbtree_elem *rbe, *rbe_end = NULL, *rbe_prev = NULL;
-- struct nft_set_gc_batch *gcb = NULL;
-+ struct nft_rbtree_elem *rbe, *rbe_end = NULL;
-+ struct nftables_pernet *nft_net;
- struct nft_rbtree *priv;
-+ struct nft_trans_gc *gc;
- struct rb_node *node;
- struct nft_set *set;
-+ unsigned int gc_seq;
- struct net *net;
-- u8 genmask;
-
- priv = container_of(work, struct nft_rbtree, gc_work.work);
- set = nft_set_container_of(priv);
- net = read_pnet(&set->net);
-- genmask = nft_genmask_cur(net);
-+ nft_net = nft_pernet(net);
-+ gc_seq = READ_ONCE(nft_net->gc_seq);
-+
-+ gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL);
-+ if (!gc)
-+ goto done;
-
- write_lock_bh(&priv->lock);
- write_seqcount_begin(&priv->count);
- for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
-+
-+ /* Ruleset has been updated, try later. */
-+ if (READ_ONCE(nft_net->gc_seq) != gc_seq) {
-+ nft_trans_gc_destroy(gc);
-+ gc = NULL;
-+ goto try_later;
-+ }
-+
- rbe = rb_entry(node, struct nft_rbtree_elem, node);
-
-- if (!nft_set_elem_active(&rbe->ext, genmask))
-- continue;
-+ if (nft_set_elem_is_dead(&rbe->ext))
-+ goto dead_elem;
-
- /* elements are reversed in the rbtree for historical reasons,
- * from highest to lowest value, that is why end element is
-@@ -604,46 +644,36 @@ static void nft_rbtree_gc(struct work_struct *work)
- if (!nft_set_elem_expired(&rbe->ext))
- continue;
-
-- if (nft_set_elem_mark_busy(&rbe->ext)) {
-- rbe_end = NULL;
-+ nft_set_elem_dead(&rbe->ext);
-+
-+ if (!rbe_end)
- continue;
-- }
-
-- if (rbe_prev) {
-- rb_erase(&rbe_prev->node, &priv->root);
-- rbe_prev = NULL;
-- }
-- gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
-- if (!gcb)
-- break;
-+ nft_set_elem_dead(&rbe_end->ext);
-
-- atomic_dec(&set->nelems);
-- nft_set_gc_batch_add(gcb, rbe);
-- rbe_prev = rbe;
-+ gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
-+ if (!gc)
-+ goto try_later;
-
-- if (rbe_end) {
-- atomic_dec(&set->nelems);
-- nft_set_gc_batch_add(gcb, rbe_end);
-- rb_erase(&rbe_end->node, &priv->root);
-- rbe_end = NULL;
-- }
-- node = rb_next(node);
-- if (!node)
-- break;
-+ nft_trans_gc_elem_add(gc, rbe_end);
-+ rbe_end = NULL;
-+dead_elem:
-+ gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
-+ if (!gc)
-+ goto try_later;
-+
-+ nft_trans_gc_elem_add(gc, rbe);
- }
-- if (rbe_prev)
-- rb_erase(&rbe_prev->node, &priv->root);
-+
-+ gc = nft_trans_gc_catchall(gc, gc_seq);
-+
-+try_later:
- write_seqcount_end(&priv->count);
- write_unlock_bh(&priv->lock);
-
-- rbe = nft_set_catchall_gc(set);
-- if (rbe) {
-- gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
-- if (gcb)
-- nft_set_gc_batch_add(gcb, rbe);
-- }
-- nft_set_gc_batch_complete(gcb);
--
-+ if (gc)
-+ nft_trans_gc_queue_async_done(gc);
-+done:
- queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
- nft_set_gc_interval(set));
- }
---
-2.40.1
-
+++ /dev/null
-From ab4d2fdc1d651fc87fbeec81aa3d9172e5d04851 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Tue, 15 Aug 2023 15:39:00 +0200
-Subject: netfilter: nf_tables: fix GC transaction races with netns and netlink
- event exit path
-
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-
-[ Upstream commit 6a33d8b73dfac0a41f3877894b38082bd0c9a5bc ]
-
-Netlink event path is missing a synchronization point with GC
-transactions. Add GC sequence number update to netns release path and
-netlink event path, any GC transaction losing race will be discarded.
-
-Fixes: 5f68718b34a5 ("netfilter: nf_tables: GC transaction API to avoid race with control plane")
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Signed-off-by: Florian Westphal <fw@strlen.de>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- net/netfilter/nf_tables_api.c | 36 +++++++++++++++++++++++++++++++----
- 1 file changed, 32 insertions(+), 4 deletions(-)
-
-diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
-index 2f8290e175ad7..c0f1eccf2283d 100644
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -9408,6 +9408,22 @@ static void nft_set_commit_update(struct list_head *set_update_list)
- }
- }
-
-+static unsigned int nft_gc_seq_begin(struct nftables_pernet *nft_net)
-+{
-+ unsigned int gc_seq;
-+
-+ /* Bump gc counter, it becomes odd, this is the busy mark. */
-+ gc_seq = READ_ONCE(nft_net->gc_seq);
-+ WRITE_ONCE(nft_net->gc_seq, ++gc_seq);
-+
-+ return gc_seq;
-+}
-+
-+static void nft_gc_seq_end(struct nftables_pernet *nft_net, unsigned int gc_seq)
-+{
-+ WRITE_ONCE(nft_net->gc_seq, ++gc_seq);
-+}
-+
- static int nf_tables_commit(struct net *net, struct sk_buff *skb)
- {
- struct nftables_pernet *nft_net = nft_pernet(net);
-@@ -9493,9 +9509,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
-
- WRITE_ONCE(nft_net->base_seq, base_seq);
-
-- /* Bump gc counter, it becomes odd, this is the busy mark. */
-- gc_seq = READ_ONCE(nft_net->gc_seq);
-- WRITE_ONCE(nft_net->gc_seq, ++gc_seq);
-+ gc_seq = nft_gc_seq_begin(nft_net);
-
- /* step 3. Start new generation, rules_gen_X now in use. */
- net->nft.gencursor = nft_gencursor_next(net);
-@@ -9686,7 +9700,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
- nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
- nf_tables_commit_audit_log(&adl, nft_net->base_seq);
-
-- WRITE_ONCE(nft_net->gc_seq, ++gc_seq);
-+ nft_gc_seq_end(nft_net, gc_seq);
- nf_tables_commit_release(net);
-
- return 0;
-@@ -10669,6 +10683,7 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
- struct net *net = n->net;
- unsigned int deleted;
- bool restart = false;
-+ unsigned int gc_seq;
-
- if (event != NETLINK_URELEASE || n->protocol != NETLINK_NETFILTER)
- return NOTIFY_DONE;
-@@ -10676,6 +10691,9 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
- nft_net = nft_pernet(net);
- deleted = 0;
- mutex_lock(&nft_net->commit_mutex);
-+
-+ gc_seq = nft_gc_seq_begin(nft_net);
-+
- if (!list_empty(&nf_tables_destroy_list))
- nf_tables_trans_destroy_flush_work();
- again:
-@@ -10698,6 +10716,8 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
- if (restart)
- goto again;
- }
-+ nft_gc_seq_end(nft_net, gc_seq);
-+
- mutex_unlock(&nft_net->commit_mutex);
-
- return NOTIFY_DONE;
-@@ -10735,12 +10755,20 @@ static void __net_exit nf_tables_pre_exit_net(struct net *net)
- static void __net_exit nf_tables_exit_net(struct net *net)
- {
- struct nftables_pernet *nft_net = nft_pernet(net);
-+ unsigned int gc_seq;
-
- mutex_lock(&nft_net->commit_mutex);
-+
-+ gc_seq = nft_gc_seq_begin(nft_net);
-+
- if (!list_empty(&nft_net->commit_list) ||
- !list_empty(&nft_net->module_list))
- __nf_tables_abort(net, NFNL_ABORT_NONE);
-+
- __nft_release_tables(net);
-+
-+ nft_gc_seq_end(nft_net, gc_seq);
-+
- mutex_unlock(&nft_net->commit_mutex);
- WARN_ON_ONCE(!list_empty(&nft_net->tables));
- WARN_ON_ONCE(!list_empty(&nft_net->module_list));
---
-2.40.1
-
+++ /dev/null
-From d5ae1c479fa44a220754d6d7a512b8a44d71feea Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Thu, 10 Aug 2023 23:59:03 +0200
-Subject: netfilter: nf_tables: fix kdoc warnings after gc rework
-
-From: Florian Westphal <fw@strlen.de>
-
-[ Upstream commit 08713cb006b6f07434f276c5ee214fb20c7fd965 ]
-
-Jakub Kicinski says:
- We've got some new kdoc warnings here:
- net/netfilter/nft_set_pipapo.c:1557: warning: Function parameter or member '_set' not described in 'pipapo_gc'
- net/netfilter/nft_set_pipapo.c:1557: warning: Excess function parameter 'set' description in 'pipapo_gc'
- include/net/netfilter/nf_tables.h:577: warning: Function parameter or member 'dead' not described in 'nft_set'
-
-Fixes: 5f68718b34a5 ("netfilter: nf_tables: GC transaction API to avoid race with control plane")
-Fixes: f6c383b8c31a ("netfilter: nf_tables: adapt set backend to use GC transaction API")
-Reported-by: Jakub Kicinski <kuba@kernel.org>
-Closes: https://lore.kernel.org/netdev/20230810104638.746e46f1@kernel.org/
-Signed-off-by: Florian Westphal <fw@strlen.de>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- include/net/netfilter/nf_tables.h | 1 +
- net/netfilter/nft_set_pipapo.c | 2 +-
- 2 files changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
-index 143c6db05be52..31b22c541cf22 100644
---- a/include/net/netfilter/nf_tables.h
-+++ b/include/net/netfilter/nf_tables.h
-@@ -529,6 +529,7 @@ struct nft_set_elem_expr {
- * @expr: stateful expression
- * @ops: set ops
- * @flags: set flags
-+ * @dead: set will be freed, never cleared
- * @genmask: generation mask
- * @klen: key length
- * @dlen: data length
-diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c
-index 0a49c59a22dbd..03472c1d9d548 100644
---- a/net/netfilter/nft_set_pipapo.c
-+++ b/net/netfilter/nft_set_pipapo.c
-@@ -1549,7 +1549,7 @@ static void nft_pipapo_gc_deactivate(struct net *net, struct nft_set *set,
-
- /**
- * pipapo_gc() - Drop expired entries from set, destroy start and end elements
-- * @set: nftables API set representation
-+ * @_set: nftables API set representation
- * @m: Matching data
- */
- static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
---
-2.40.1
-
+++ /dev/null
-From 4f598f962a1ca0d5412f5e14001b99b0adb43bcc Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 9 Aug 2023 14:31:54 +0200
-Subject: netfilter: nf_tables: GC transaction API to avoid race with control
- plane
-
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-
-[ Upstream commit 5f68718b34a531a556f2f50300ead2862278da26 ]
-
-The set types rhashtable and rbtree use a GC worker to reclaim memory.
-From system work queue, in periodic intervals, a scan of the table is
-done.
-
-The major caveat here is that the nft transaction mutex is not held.
-This causes a race between control plane and GC when they attempt to
-delete the same element.
-
-We cannot grab the netlink mutex from the work queue, because the
-control plane has to wait for the GC work queue in case the set is to be
-removed, so we get following deadlock:
-
- cpu 1 cpu2
- GC work transaction comes in , lock nft mutex
- `acquire nft mutex // BLOCKS
- transaction asks to remove the set
- set destruction calls cancel_work_sync()
-
-cancel_work_sync will now block forever, because it is waiting for the
-mutex the caller already owns.
-
-This patch adds a new API that deals with garbage collection in two
-steps:
-
-1) Lockless GC of expired elements sets on the NFT_SET_ELEM_DEAD_BIT
- so they are not visible via lookup. Annotate current GC sequence in
- the GC transaction. Enqueue GC transaction work as soon as it is
- full. If ruleset is updated, then GC transaction is aborted and
- retried later.
-
-2) GC work grabs the mutex. If GC sequence has changed then this GC
- transaction lost race with control plane, abort it as it contains
- stale references to objects and let GC try again later. If the
- ruleset is intact, then this GC transaction deactivates and removes
- the elements and it uses call_rcu() to destroy elements.
-
-Note that no elements are removed from GC lockless path, the _DEAD bit
-is set and pointers are collected. GC catchall does not remove the
-elements anymore too. There is a new set->dead flag that is set on to
-abort the GC transaction to deal with set->ops->destroy() path which
-removes the remaining elements in the set from commit_release, where no
-mutex is held.
-
-To deal with GC when mutex is held, which allows safe deactivate and
-removal, add sync GC API which releases the set element object via
-call_rcu(). This is used by rbtree and pipapo backends which also
-perform garbage collection from control plane path.
-
-Since element removal from sets can happen from control plane and
-element garbage collection/timeout, it is necessary to keep the set
-structure alive until all elements have been deactivated and destroyed.
-
-We cannot do a cancel_work_sync or flush_work in nft_set_destroy because
-its called with the transaction mutex held, but the aforementioned async
-work queue might be blocked on the very mutex that nft_set_destroy()
-callchain is sitting on.
-
-This gives us the choice of ABBA deadlock or UaF.
-
-To avoid both, add set->refs refcount_t member. The GC API can then
-increment the set refcount and release it once the elements have been
-free'd.
-
-Set backends are adapted to use the GC transaction API in a follow up
-patch entitled:
-
- ("netfilter: nf_tables: use gc transaction API in set backends")
-
-This is joint work with Florian Westphal.
-
-Fixes: cfed7e1b1f8e ("netfilter: nf_tables: add set garbage collection helpers")
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- include/net/netfilter/nf_tables.h | 64 +++++++-
- net/netfilter/nf_tables_api.c | 248 ++++++++++++++++++++++++++++--
- 2 files changed, 300 insertions(+), 12 deletions(-)
-
-diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
-index 120be22ef6aa2..483b106466f3d 100644
---- a/include/net/netfilter/nf_tables.h
-+++ b/include/net/netfilter/nf_tables.h
-@@ -507,6 +507,7 @@ struct nft_set_elem_expr {
- *
- * @list: table set list node
- * @bindings: list of set bindings
-+ * @refs: internal refcounting for async set destruction
- * @table: table this set belongs to
- * @net: netnamespace this set belongs to
- * @name: name of the set
-@@ -536,6 +537,7 @@ struct nft_set_elem_expr {
- struct nft_set {
- struct list_head list;
- struct list_head bindings;
-+ refcount_t refs;
- struct nft_table *table;
- possible_net_t net;
- char *name;
-@@ -557,7 +559,8 @@ struct nft_set {
- struct list_head pending_update;
- /* runtime data below here */
- const struct nft_set_ops *ops ____cacheline_aligned;
-- u16 flags:14,
-+ u16 flags:13,
-+ dead:1,
- genmask:2;
- u8 klen;
- u8 dlen;
-@@ -1579,6 +1582,32 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
- clear_bit(NFT_SET_ELEM_BUSY_BIT, word);
- }
-
-+#define NFT_SET_ELEM_DEAD_MASK (1 << 3)
-+
-+#if defined(__LITTLE_ENDIAN_BITFIELD)
-+#define NFT_SET_ELEM_DEAD_BIT 3
-+#elif defined(__BIG_ENDIAN_BITFIELD)
-+#define NFT_SET_ELEM_DEAD_BIT (BITS_PER_LONG - BITS_PER_BYTE + 3)
-+#else
-+#error
-+#endif
-+
-+static inline void nft_set_elem_dead(struct nft_set_ext *ext)
-+{
-+ unsigned long *word = (unsigned long *)ext;
-+
-+ BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0);
-+ set_bit(NFT_SET_ELEM_DEAD_BIT, word);
-+}
-+
-+static inline int nft_set_elem_is_dead(const struct nft_set_ext *ext)
-+{
-+ unsigned long *word = (unsigned long *)ext;
-+
-+ BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0);
-+ return test_bit(NFT_SET_ELEM_DEAD_BIT, word);
-+}
-+
- /**
- * struct nft_trans - nf_tables object update in transaction
- *
-@@ -1710,6 +1739,38 @@ struct nft_trans_flowtable {
- #define nft_trans_flowtable_flags(trans) \
- (((struct nft_trans_flowtable *)trans->data)->flags)
-
-+#define NFT_TRANS_GC_BATCHCOUNT 256
-+
-+struct nft_trans_gc {
-+ struct list_head list;
-+ struct net *net;
-+ struct nft_set *set;
-+ u32 seq;
-+ u8 count;
-+ void *priv[NFT_TRANS_GC_BATCHCOUNT];
-+ struct rcu_head rcu;
-+};
-+
-+struct nft_trans_gc *nft_trans_gc_alloc(struct nft_set *set,
-+ unsigned int gc_seq, gfp_t gfp);
-+void nft_trans_gc_destroy(struct nft_trans_gc *trans);
-+
-+struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc,
-+ unsigned int gc_seq, gfp_t gfp);
-+void nft_trans_gc_queue_async_done(struct nft_trans_gc *gc);
-+
-+struct nft_trans_gc *nft_trans_gc_queue_sync(struct nft_trans_gc *gc, gfp_t gfp);
-+void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans);
-+
-+void nft_trans_gc_elem_add(struct nft_trans_gc *gc, void *priv);
-+
-+struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
-+ unsigned int gc_seq);
-+
-+void nft_setelem_data_deactivate(const struct net *net,
-+ const struct nft_set *set,
-+ struct nft_set_elem *elem);
-+
- int __init nft_chain_filter_init(void);
- void nft_chain_filter_fini(void);
-
-@@ -1736,6 +1797,7 @@ struct nftables_pernet {
- struct mutex commit_mutex;
- u64 table_handle;
- unsigned int base_seq;
-+ unsigned int gc_seq;
- };
-
- extern unsigned int nf_tables_net_id;
-diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
-index a013d2a31f0b8..4d827177aaebe 100644
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -31,7 +31,9 @@ static LIST_HEAD(nf_tables_expressions);
- static LIST_HEAD(nf_tables_objects);
- static LIST_HEAD(nf_tables_flowtables);
- static LIST_HEAD(nf_tables_destroy_list);
-+static LIST_HEAD(nf_tables_gc_list);
- static DEFINE_SPINLOCK(nf_tables_destroy_list_lock);
-+static DEFINE_SPINLOCK(nf_tables_gc_list_lock);
-
- enum {
- NFT_VALIDATE_SKIP = 0,
-@@ -120,6 +122,9 @@ static void nft_validate_state_update(struct nft_table *table, u8 new_validate_s
- static void nf_tables_trans_destroy_work(struct work_struct *w);
- static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work);
-
-+static void nft_trans_gc_work(struct work_struct *work);
-+static DECLARE_WORK(trans_gc_work, nft_trans_gc_work);
-+
- static void nft_ctx_init(struct nft_ctx *ctx,
- struct net *net,
- const struct sk_buff *skb,
-@@ -581,10 +586,6 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
- return __nft_trans_set_add(ctx, msg_type, set, NULL);
- }
-
--static void nft_setelem_data_deactivate(const struct net *net,
-- const struct nft_set *set,
-- struct nft_set_elem *elem);
--
- static int nft_mapelem_deactivate(const struct nft_ctx *ctx,
- struct nft_set *set,
- const struct nft_set_iter *iter,
-@@ -4853,6 +4854,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
-
- INIT_LIST_HEAD(&set->bindings);
- INIT_LIST_HEAD(&set->catchall_list);
-+ refcount_set(&set->refs, 1);
- set->table = table;
- write_pnet(&set->net, net);
- set->ops = ops;
-@@ -4920,6 +4922,14 @@ static void nft_set_catchall_destroy(const struct nft_ctx *ctx,
- }
- }
-
-+static void nft_set_put(struct nft_set *set)
-+{
-+ if (refcount_dec_and_test(&set->refs)) {
-+ kfree(set->name);
-+ kvfree(set);
-+ }
-+}
-+
- static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
- {
- int i;
-@@ -4932,8 +4942,7 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
-
- set->ops->destroy(ctx, set);
- nft_set_catchall_destroy(ctx, set);
-- kfree(set->name);
-- kvfree(set);
-+ nft_set_put(set);
- }
-
- static int nf_tables_delset(struct sk_buff *skb, const struct nfnl_info *info,
-@@ -6046,7 +6055,8 @@ struct nft_set_ext *nft_set_catchall_lookup(const struct net *net,
- list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
- ext = nft_set_elem_ext(set, catchall->elem);
- if (nft_set_elem_active(ext, genmask) &&
-- !nft_set_elem_expired(ext))
-+ !nft_set_elem_expired(ext) &&
-+ !nft_set_elem_is_dead(ext))
- return ext;
- }
-
-@@ -6698,9 +6708,9 @@ static void nft_setelem_data_activate(const struct net *net,
- nft_use_inc_restore(&(*nft_set_ext_obj(ext))->use);
- }
-
--static void nft_setelem_data_deactivate(const struct net *net,
-- const struct nft_set *set,
-- struct nft_set_elem *elem)
-+void nft_setelem_data_deactivate(const struct net *net,
-+ const struct nft_set *set,
-+ struct nft_set_elem *elem)
- {
- const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
-
-@@ -9088,6 +9098,207 @@ void nft_chain_del(struct nft_chain *chain)
- list_del_rcu(&chain->list);
- }
-
-+static void nft_trans_gc_setelem_remove(struct nft_ctx *ctx,
-+ struct nft_trans_gc *trans)
-+{
-+ void **priv = trans->priv;
-+ unsigned int i;
-+
-+ for (i = 0; i < trans->count; i++) {
-+ struct nft_set_elem elem = {
-+ .priv = priv[i],
-+ };
-+
-+ nft_setelem_data_deactivate(ctx->net, trans->set, &elem);
-+ nft_setelem_remove(ctx->net, trans->set, &elem);
-+ }
-+}
-+
-+void nft_trans_gc_destroy(struct nft_trans_gc *trans)
-+{
-+ nft_set_put(trans->set);
-+ put_net(trans->net);
-+ kfree(trans);
-+}
-+
-+static void nft_trans_gc_trans_free(struct rcu_head *rcu)
-+{
-+ struct nft_set_elem elem = {};
-+ struct nft_trans_gc *trans;
-+ struct nft_ctx ctx = {};
-+ unsigned int i;
-+
-+ trans = container_of(rcu, struct nft_trans_gc, rcu);
-+ ctx.net = read_pnet(&trans->set->net);
-+
-+ for (i = 0; i < trans->count; i++) {
-+ elem.priv = trans->priv[i];
-+ if (!nft_setelem_is_catchall(trans->set, &elem))
-+ atomic_dec(&trans->set->nelems);
-+
-+ nf_tables_set_elem_destroy(&ctx, trans->set, elem.priv);
-+ }
-+
-+ nft_trans_gc_destroy(trans);
-+}
-+
-+static bool nft_trans_gc_work_done(struct nft_trans_gc *trans)
-+{
-+ struct nftables_pernet *nft_net;
-+ struct nft_ctx ctx = {};
-+
-+ nft_net = nft_pernet(trans->net);
-+
-+ mutex_lock(&nft_net->commit_mutex);
-+
-+ /* Check for race with transaction, otherwise this batch refers to
-+ * stale objects that might not be there anymore. Skip transaction if
-+ * set has been destroyed from control plane transaction in case gc
-+ * worker loses race.
-+ */
-+ if (READ_ONCE(nft_net->gc_seq) != trans->seq || trans->set->dead) {
-+ mutex_unlock(&nft_net->commit_mutex);
-+ return false;
-+ }
-+
-+ ctx.net = trans->net;
-+ ctx.table = trans->set->table;
-+
-+ nft_trans_gc_setelem_remove(&ctx, trans);
-+ mutex_unlock(&nft_net->commit_mutex);
-+
-+ return true;
-+}
-+
-+static void nft_trans_gc_work(struct work_struct *work)
-+{
-+ struct nft_trans_gc *trans, *next;
-+ LIST_HEAD(trans_gc_list);
-+
-+ spin_lock(&nf_tables_destroy_list_lock);
-+ list_splice_init(&nf_tables_gc_list, &trans_gc_list);
-+ spin_unlock(&nf_tables_destroy_list_lock);
-+
-+ list_for_each_entry_safe(trans, next, &trans_gc_list, list) {
-+ list_del(&trans->list);
-+ if (!nft_trans_gc_work_done(trans)) {
-+ nft_trans_gc_destroy(trans);
-+ continue;
-+ }
-+ call_rcu(&trans->rcu, nft_trans_gc_trans_free);
-+ }
-+}
-+
-+struct nft_trans_gc *nft_trans_gc_alloc(struct nft_set *set,
-+ unsigned int gc_seq, gfp_t gfp)
-+{
-+ struct net *net = read_pnet(&set->net);
-+ struct nft_trans_gc *trans;
-+
-+ trans = kzalloc(sizeof(*trans), gfp);
-+ if (!trans)
-+ return NULL;
-+
-+ refcount_inc(&set->refs);
-+ trans->set = set;
-+ trans->net = get_net(net);
-+ trans->seq = gc_seq;
-+
-+ return trans;
-+}
-+
-+void nft_trans_gc_elem_add(struct nft_trans_gc *trans, void *priv)
-+{
-+ trans->priv[trans->count++] = priv;
-+}
-+
-+static void nft_trans_gc_queue_work(struct nft_trans_gc *trans)
-+{
-+ spin_lock(&nf_tables_gc_list_lock);
-+ list_add_tail(&trans->list, &nf_tables_gc_list);
-+ spin_unlock(&nf_tables_gc_list_lock);
-+
-+ schedule_work(&trans_gc_work);
-+}
-+
-+static int nft_trans_gc_space(struct nft_trans_gc *trans)
-+{
-+ return NFT_TRANS_GC_BATCHCOUNT - trans->count;
-+}
-+
-+struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc,
-+ unsigned int gc_seq, gfp_t gfp)
-+{
-+ if (nft_trans_gc_space(gc))
-+ return gc;
-+
-+ nft_trans_gc_queue_work(gc);
-+
-+ return nft_trans_gc_alloc(gc->set, gc_seq, gfp);
-+}
-+
-+void nft_trans_gc_queue_async_done(struct nft_trans_gc *trans)
-+{
-+ if (trans->count == 0) {
-+ nft_trans_gc_destroy(trans);
-+ return;
-+ }
-+
-+ nft_trans_gc_queue_work(trans);
-+}
-+
-+struct nft_trans_gc *nft_trans_gc_queue_sync(struct nft_trans_gc *gc, gfp_t gfp)
-+{
-+ if (WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net)))
-+ return NULL;
-+
-+ if (nft_trans_gc_space(gc))
-+ return gc;
-+
-+ call_rcu(&gc->rcu, nft_trans_gc_trans_free);
-+
-+ return nft_trans_gc_alloc(gc->set, 0, gfp);
-+}
-+
-+void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)
-+{
-+ WARN_ON_ONCE(!lockdep_commit_lock_is_held(trans->net));
-+
-+ if (trans->count == 0) {
-+ nft_trans_gc_destroy(trans);
-+ return;
-+ }
-+
-+ call_rcu(&trans->rcu, nft_trans_gc_trans_free);
-+}
-+
-+struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
-+ unsigned int gc_seq)
-+{
-+ struct nft_set_elem_catchall *catchall;
-+ const struct nft_set *set = gc->set;
-+ struct nft_set_ext *ext;
-+
-+ list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
-+ ext = nft_set_elem_ext(set, catchall->elem);
-+
-+ if (!nft_set_elem_expired(ext))
-+ continue;
-+ if (nft_set_elem_is_dead(ext))
-+ goto dead_elem;
-+
-+ nft_set_elem_dead(ext);
-+dead_elem:
-+ gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
-+ if (!gc)
-+ return NULL;
-+
-+ nft_trans_gc_elem_add(gc, catchall->elem);
-+ }
-+
-+ return gc;
-+}
-+
- static void nf_tables_module_autoload_cleanup(struct net *net)
- {
- struct nftables_pernet *nft_net = nft_pernet(net);
-@@ -9250,11 +9461,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
- {
- struct nftables_pernet *nft_net = nft_pernet(net);
- struct nft_trans *trans, *next;
-+ unsigned int base_seq, gc_seq;
- LIST_HEAD(set_update_list);
- struct nft_trans_elem *te;
- struct nft_chain *chain;
- struct nft_table *table;
-- unsigned int base_seq;
- LIST_HEAD(adl);
- int err;
-
-@@ -9331,6 +9542,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
-
- WRITE_ONCE(nft_net->base_seq, base_seq);
-
-+ /* Bump gc counter, it becomes odd, this is the busy mark. */
-+ gc_seq = READ_ONCE(nft_net->gc_seq);
-+ WRITE_ONCE(nft_net->gc_seq, ++gc_seq);
-+
- /* step 3. Start new generation, rules_gen_X now in use. */
- net->nft.gencursor = nft_gencursor_next(net);
-
-@@ -9419,6 +9634,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
- nft_trans_destroy(trans);
- break;
- case NFT_MSG_DELSET:
-+ nft_trans_set(trans)->dead = 1;
- list_del_rcu(&nft_trans_set(trans)->list);
- nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
- NFT_MSG_DELSET, GFP_KERNEL);
-@@ -9518,6 +9734,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
- nft_commit_notify(net, NETLINK_CB(skb).portid);
- nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
- nf_tables_commit_audit_log(&adl, nft_net->base_seq);
-+
-+ WRITE_ONCE(nft_net->gc_seq, ++gc_seq);
- nf_tables_commit_release(net);
-
- return 0;
-@@ -10549,6 +10767,7 @@ static int __net_init nf_tables_init_net(struct net *net)
- INIT_LIST_HEAD(&nft_net->notify_list);
- mutex_init(&nft_net->commit_mutex);
- nft_net->base_seq = 1;
-+ nft_net->gc_seq = 0;
-
- return 0;
- }
-@@ -10577,10 +10796,16 @@ static void __net_exit nf_tables_exit_net(struct net *net)
- WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
- }
-
-+static void nf_tables_exit_batch(struct list_head *net_exit_list)
-+{
-+ flush_work(&trans_gc_work);
-+}
-+
- static struct pernet_operations nf_tables_net_ops = {
- .init = nf_tables_init_net,
- .pre_exit = nf_tables_pre_exit_net,
- .exit = nf_tables_exit_net,
-+ .exit_batch = nf_tables_exit_batch,
- .id = &nf_tables_net_id,
- .size = sizeof(struct nftables_pernet),
- };
-@@ -10652,6 +10877,7 @@ static void __exit nf_tables_module_exit(void)
- nft_chain_filter_fini();
- nft_chain_route_fini();
- unregister_pernet_subsys(&nf_tables_net_ops);
-+ cancel_work_sync(&trans_gc_work);
- cancel_work_sync(&trans_destroy_work);
- rcu_barrier();
- rhltable_destroy(&nft_objname_ht);
---
-2.40.1
-
+++ /dev/null
-From f21c339a0ae22256625afacf18c14cfeffb6e63b Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Tue, 15 Aug 2023 15:39:01 +0200
-Subject: netfilter: nf_tables: GC transaction race with netns dismantle
-
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-
-[ Upstream commit 02c6c24402bf1c1e986899c14ba22a10b510916b ]
-
-Use maybe_get_net() since GC workqueue might race with netns exit path.
-
-Fixes: 5f68718b34a5 ("netfilter: nf_tables: GC transaction API to avoid race with control plane")
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Signed-off-by: Florian Westphal <fw@strlen.de>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- net/netfilter/nf_tables_api.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
-index c0f1eccf2283d..1624a5ca90fe8 100644
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -9150,9 +9150,14 @@ struct nft_trans_gc *nft_trans_gc_alloc(struct nft_set *set,
- if (!trans)
- return NULL;
-
-+ trans->net = maybe_get_net(net);
-+ if (!trans->net) {
-+ kfree(trans);
-+ return NULL;
-+ }
-+
- refcount_inc(&set->refs);
- trans->set = set;
-- trans->net = get_net(net);
- trans->seq = gc_seq;
-
- return trans;
---
-2.40.1
-
+++ /dev/null
-From 0fba82aaaed4af9a80b285f0ab53d935bb18f9f5 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Thu, 13 Apr 2023 17:13:20 +0200
-Subject: netfilter: nf_tables: make validation state per table
-
-From: Florian Westphal <fw@strlen.de>
-
-[ Upstream commit 00c320f9b75560628e840bef027a27c746706759 ]
-
-We only need to validate tables that saw changes in the current
-transaction.
-
-The existing code revalidates all tables, but this isn't needed as
-cross-table jumps are not allowed (chains have table scope).
-
-Signed-off-by: Florian Westphal <fw@strlen.de>
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Stable-dep-of: 5f68718b34a5 ("netfilter: nf_tables: GC transaction API to avoid race with control plane")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- include/net/netfilter/nf_tables.h | 3 ++-
- net/netfilter/nf_tables_api.c | 38 +++++++++++++++----------------
- 2 files changed, 20 insertions(+), 21 deletions(-)
-
-diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
-index c752b6f509791..120be22ef6aa2 100644
---- a/include/net/netfilter/nf_tables.h
-+++ b/include/net/netfilter/nf_tables.h
-@@ -1231,6 +1231,7 @@ static inline void nft_use_inc_restore(u32 *use)
- * @genmask: generation mask
- * @afinfo: address family info
- * @name: name of the table
-+ * @validate_state: internal, set when transaction adds jumps
- */
- struct nft_table {
- struct list_head list;
-@@ -1249,6 +1250,7 @@ struct nft_table {
- char *name;
- u16 udlen;
- u8 *udata;
-+ u8 validate_state;
- };
-
- static inline bool nft_table_has_owner(const struct nft_table *table)
-@@ -1734,7 +1736,6 @@ struct nftables_pernet {
- struct mutex commit_mutex;
- u64 table_handle;
- unsigned int base_seq;
-- u8 validate_state;
- };
-
- extern unsigned int nf_tables_net_id;
-diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
-index 3c5cac9bd9b70..a013d2a31f0b8 100644
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -102,11 +102,9 @@ static const u8 nft2audit_op[NFT_MSG_MAX] = { // enum nf_tables_msg_types
- [NFT_MSG_DELFLOWTABLE] = AUDIT_NFT_OP_FLOWTABLE_UNREGISTER,
- };
-
--static void nft_validate_state_update(struct net *net, u8 new_validate_state)
-+static void nft_validate_state_update(struct nft_table *table, u8 new_validate_state)
- {
-- struct nftables_pernet *nft_net = nft_pernet(net);
--
-- switch (nft_net->validate_state) {
-+ switch (table->validate_state) {
- case NFT_VALIDATE_SKIP:
- WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO);
- break;
-@@ -117,7 +115,7 @@ static void nft_validate_state_update(struct net *net, u8 new_validate_state)
- return;
- }
-
-- nft_net->validate_state = new_validate_state;
-+ table->validate_state = new_validate_state;
- }
- static void nf_tables_trans_destroy_work(struct work_struct *w);
- static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work);
-@@ -1365,6 +1363,7 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
- if (table == NULL)
- goto err_kzalloc;
-
-+ table->validate_state = NFT_VALIDATE_SKIP;
- table->name = nla_strdup(attr, GFP_KERNEL_ACCOUNT);
- if (table->name == NULL)
- goto err_strdup;
-@@ -3747,7 +3746,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
- }
-
- if (expr_info[i].ops->validate)
-- nft_validate_state_update(net, NFT_VALIDATE_NEED);
-+ nft_validate_state_update(table, NFT_VALIDATE_NEED);
-
- expr_info[i].ops = NULL;
- expr = nft_expr_next(expr);
-@@ -3801,7 +3800,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
- if (flow)
- nft_trans_flow_rule(trans) = flow;
-
-- if (nft_net->validate_state == NFT_VALIDATE_DO)
-+ if (table->validate_state == NFT_VALIDATE_DO)
- return nft_table_validate(net, table);
-
- return 0;
-@@ -6492,7 +6491,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
- if (desc.type == NFT_DATA_VERDICT &&
- (elem.data.val.verdict.code == NFT_GOTO ||
- elem.data.val.verdict.code == NFT_JUMP))
-- nft_validate_state_update(ctx->net,
-+ nft_validate_state_update(ctx->table,
- NFT_VALIDATE_NEED);
- }
-
-@@ -6616,7 +6615,6 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
- const struct nfnl_info *info,
- const struct nlattr * const nla[])
- {
-- struct nftables_pernet *nft_net = nft_pernet(info->net);
- struct netlink_ext_ack *extack = info->extack;
- u8 genmask = nft_genmask_next(info->net);
- u8 family = info->nfmsg->nfgen_family;
-@@ -6656,7 +6654,7 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
- }
- }
-
-- if (nft_net->validate_state == NFT_VALIDATE_DO)
-+ if (table->validate_state == NFT_VALIDATE_DO)
- return nft_table_validate(net, table);
-
- return 0;
-@@ -8757,19 +8755,20 @@ static int nf_tables_validate(struct net *net)
- struct nftables_pernet *nft_net = nft_pernet(net);
- struct nft_table *table;
-
-- switch (nft_net->validate_state) {
-- case NFT_VALIDATE_SKIP:
-- break;
-- case NFT_VALIDATE_NEED:
-- nft_validate_state_update(net, NFT_VALIDATE_DO);
-- fallthrough;
-- case NFT_VALIDATE_DO:
-- list_for_each_entry(table, &nft_net->tables, list) {
-+ list_for_each_entry(table, &nft_net->tables, list) {
-+ switch (table->validate_state) {
-+ case NFT_VALIDATE_SKIP:
-+ continue;
-+ case NFT_VALIDATE_NEED:
-+ nft_validate_state_update(table, NFT_VALIDATE_DO);
-+ fallthrough;
-+ case NFT_VALIDATE_DO:
- if (nft_table_validate(net, table) < 0)
- return -EAGAIN;
-+
-+ nft_validate_state_update(table, NFT_VALIDATE_SKIP);
- }
-
-- nft_validate_state_update(net, NFT_VALIDATE_SKIP);
- break;
- }
-
-@@ -10550,7 +10549,6 @@ static int __net_init nf_tables_init_net(struct net *net)
- INIT_LIST_HEAD(&nft_net->notify_list);
- mutex_init(&nft_net->commit_mutex);
- nft_net->base_seq = 1;
-- nft_net->validate_state = NFT_VALIDATE_SKIP;
-
- return 0;
- }
---
-2.40.1
-
+++ /dev/null
-From f98f3ca66166f3d4b05e14b38f7c42766b466950 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 9 Aug 2023 15:00:36 +0200
-Subject: netfilter: nf_tables: remove busy mark and gc batch API
-
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-
-[ Upstream commit a2dd0233cbc4d8a0abb5f64487487ffc9265beb5 ]
-
-Ditch it, it has been replace it by the GC transaction API and it has no
-clients anymore.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- include/net/netfilter/nf_tables.h | 98 +------------------------------
- net/netfilter/nf_tables_api.c | 48 +--------------
- 2 files changed, 4 insertions(+), 142 deletions(-)
-
-diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
-index 483b106466f3d..143c6db05be52 100644
---- a/include/net/netfilter/nf_tables.h
-+++ b/include/net/netfilter/nf_tables.h
-@@ -594,7 +594,6 @@ struct nft_set *nft_set_lookup_global(const struct net *net,
-
- struct nft_set_ext *nft_set_catchall_lookup(const struct net *net,
- const struct nft_set *set);
--void *nft_set_catchall_gc(const struct nft_set *set);
-
- static inline unsigned long nft_set_gc_interval(const struct nft_set *set)
- {
-@@ -811,62 +810,6 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
- void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
- const struct nft_set *set, void *elem);
-
--/**
-- * struct nft_set_gc_batch_head - nf_tables set garbage collection batch
-- *
-- * @rcu: rcu head
-- * @set: set the elements belong to
-- * @cnt: count of elements
-- */
--struct nft_set_gc_batch_head {
-- struct rcu_head rcu;
-- const struct nft_set *set;
-- unsigned int cnt;
--};
--
--#define NFT_SET_GC_BATCH_SIZE ((PAGE_SIZE - \
-- sizeof(struct nft_set_gc_batch_head)) / \
-- sizeof(void *))
--
--/**
-- * struct nft_set_gc_batch - nf_tables set garbage collection batch
-- *
-- * @head: GC batch head
-- * @elems: garbage collection elements
-- */
--struct nft_set_gc_batch {
-- struct nft_set_gc_batch_head head;
-- void *elems[NFT_SET_GC_BATCH_SIZE];
--};
--
--struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
-- gfp_t gfp);
--void nft_set_gc_batch_release(struct rcu_head *rcu);
--
--static inline void nft_set_gc_batch_complete(struct nft_set_gc_batch *gcb)
--{
-- if (gcb != NULL)
-- call_rcu(&gcb->head.rcu, nft_set_gc_batch_release);
--}
--
--static inline struct nft_set_gc_batch *
--nft_set_gc_batch_check(const struct nft_set *set, struct nft_set_gc_batch *gcb,
-- gfp_t gfp)
--{
-- if (gcb != NULL) {
-- if (gcb->head.cnt + 1 < ARRAY_SIZE(gcb->elems))
-- return gcb;
-- nft_set_gc_batch_complete(gcb);
-- }
-- return nft_set_gc_batch_alloc(set, gfp);
--}
--
--static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb,
-- void *elem)
--{
-- gcb->elems[gcb->head.cnt++] = elem;
--}
--
- struct nft_expr_ops;
- /**
- * struct nft_expr_type - nf_tables expression type
-@@ -1547,47 +1490,12 @@ static inline void nft_set_elem_change_active(const struct net *net,
-
- #endif /* IS_ENABLED(CONFIG_NF_TABLES) */
-
--/*
-- * We use a free bit in the genmask field to indicate the element
-- * is busy, meaning it is currently being processed either by
-- * the netlink API or GC.
-- *
-- * Even though the genmask is only a single byte wide, this works
-- * because the extension structure if fully constant once initialized,
-- * so there are no non-atomic write accesses unless it is already
-- * marked busy.
-- */
--#define NFT_SET_ELEM_BUSY_MASK (1 << 2)
--
--#if defined(__LITTLE_ENDIAN_BITFIELD)
--#define NFT_SET_ELEM_BUSY_BIT 2
--#elif defined(__BIG_ENDIAN_BITFIELD)
--#define NFT_SET_ELEM_BUSY_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2)
--#else
--#error
--#endif
--
--static inline int nft_set_elem_mark_busy(struct nft_set_ext *ext)
--{
-- unsigned long *word = (unsigned long *)ext;
--
-- BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0);
-- return test_and_set_bit(NFT_SET_ELEM_BUSY_BIT, word);
--}
--
--static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
--{
-- unsigned long *word = (unsigned long *)ext;
--
-- clear_bit(NFT_SET_ELEM_BUSY_BIT, word);
--}
--
--#define NFT_SET_ELEM_DEAD_MASK (1 << 3)
-+#define NFT_SET_ELEM_DEAD_MASK (1 << 2)
-
- #if defined(__LITTLE_ENDIAN_BITFIELD)
--#define NFT_SET_ELEM_DEAD_BIT 3
-+#define NFT_SET_ELEM_DEAD_BIT 2
- #elif defined(__BIG_ENDIAN_BITFIELD)
--#define NFT_SET_ELEM_DEAD_BIT (BITS_PER_LONG - BITS_PER_BYTE + 3)
-+#define NFT_SET_ELEM_DEAD_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2)
- #else
- #error
- #endif
-diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
-index 35d84e8b79524..2f8290e175ad7 100644
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -6064,29 +6064,6 @@ struct nft_set_ext *nft_set_catchall_lookup(const struct net *net,
- }
- EXPORT_SYMBOL_GPL(nft_set_catchall_lookup);
-
--void *nft_set_catchall_gc(const struct nft_set *set)
--{
-- struct nft_set_elem_catchall *catchall, *next;
-- struct nft_set_ext *ext;
-- void *elem = NULL;
--
-- list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
-- ext = nft_set_elem_ext(set, catchall->elem);
--
-- if (!nft_set_elem_expired(ext) ||
-- nft_set_elem_mark_busy(ext))
-- continue;
--
-- elem = catchall->elem;
-- list_del_rcu(&catchall->list);
-- kfree_rcu(catchall, rcu);
-- break;
-- }
--
-- return elem;
--}
--EXPORT_SYMBOL_GPL(nft_set_catchall_gc);
--
- static int nft_setelem_catchall_insert(const struct net *net,
- struct nft_set *set,
- const struct nft_set_elem *elem,
-@@ -6557,7 +6534,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
- goto err_elem_free;
- }
-
-- ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
-+ ext->genmask = nft_genmask_cur(ctx->net);
-
- err = nft_setelem_insert(ctx->net, set, &elem, &ext2, flags);
- if (err) {
-@@ -6943,29 +6920,6 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
- return err;
- }
-
--void nft_set_gc_batch_release(struct rcu_head *rcu)
--{
-- struct nft_set_gc_batch *gcb;
-- unsigned int i;
--
-- gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu);
-- for (i = 0; i < gcb->head.cnt; i++)
-- nft_set_elem_destroy(gcb->head.set, gcb->elems[i], true);
-- kfree(gcb);
--}
--
--struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
-- gfp_t gfp)
--{
-- struct nft_set_gc_batch *gcb;
--
-- gcb = kzalloc(sizeof(*gcb), gfp);
-- if (gcb == NULL)
-- return gcb;
-- gcb->head.set = set;
-- return gcb;
--}
--
- /*
- * Stateful objects
- */
---
-2.40.1
-
+++ /dev/null
-From e12238c7ff338cc87bbb51797345ecd3939f5353 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 9 Aug 2023 15:00:06 +0200
-Subject: netfilter: nft_set_hash: mark set element as dead when deleting from
- packet path
-
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-
-[ Upstream commit c92db3030492b8ad1d0faace7a93bbcf53850d0c ]
-
-Set on the NFT_SET_ELEM_DEAD_BIT flag on this element, instead of
-performing element removal which might race with an ongoing transaction.
-Enable gc when dynamic flag is set on since dynset deletion requires
-garbage collection after this patch.
-
-Fixes: d0a8d877da97 ("netfilter: nft_dynset: support for element deletion")
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- net/netfilter/nft_set_hash.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c
-index 960cbd56c0406..8dfa97105d8a3 100644
---- a/net/netfilter/nft_set_hash.c
-+++ b/net/netfilter/nft_set_hash.c
-@@ -249,7 +249,9 @@ static bool nft_rhash_delete(const struct nft_set *set,
- if (he == NULL)
- return false;
-
-- return rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params) == 0;
-+ nft_set_elem_dead(&he->ext);
-+
-+ return true;
- }
-
- static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set,
-@@ -414,7 +416,7 @@ static int nft_rhash_init(const struct nft_set *set,
- return err;
-
- INIT_DEFERRABLE_WORK(&priv->gc_work, nft_rhash_gc);
-- if (set->flags & NFT_SET_TIMEOUT)
-+ if (set->flags & (NFT_SET_TIMEOUT | NFT_SET_EVAL))
- nft_rhash_gc_init(set);
-
- return 0;
---
-2.40.1
-
interconnect-fix-locking-for-runpm-vs-reclaim.patch
printk-keep-non-panic-cpus-out-of-console-lock.patch
printk-consolidate-console-deferred-printing.patch
-netfilter-nf_tables-make-validation-state-per-table.patch
-netfilter-nf_tables-gc-transaction-api-to-avoid-race.patch
-netfilter-nf_tables-adapt-set-backend-to-use-gc-tran.patch
-netfilter-nft_set_hash-mark-set-element-as-dead-when.patch
-netfilter-nf_tables-remove-busy-mark-and-gc-batch-ap.patch
-netfilter-nf_tables-fix-kdoc-warnings-after-gc-rewor.patch
-netfilter-nf_tables-fix-gc-transaction-races-with-ne.patch
-netfilter-nf_tables-gc-transaction-race-with-netns-d.patch
dma-buf-add-unlocked-variant-of-attachment-mapping-f.patch
misc-fastrpc-prepare-to-dynamic-dma-buf-locking-spec.patch
misc-fastrpc-fix-incorrect-dma-mapping-unmap-request.patch