This will be needed by the 'pipapo' set backend. If this is set, then it needs to perform copy-on-write of the active set data storage. Its not possible to use genmask test, the walker function is also used by (rcu locked) set listing which can run in parallel to set updates. If priv->clone is null, then fallback to the active data storeage is safe EXCEPT for the flush case, which must do the copy. Signed-off-by: Florian Westphal <fw@xxxxxxxxx> --- include/net/netfilter/nf_tables.h | 12 +++++++++++ net/netfilter/nf_tables_api.c | 1 + net/netfilter/nft_set_pipapo.c | 35 +++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e27c28b612e4..9912a2621344 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -307,9 +307,21 @@ static inline void *nft_elem_priv_cast(const struct nft_elem_priv *priv) return (void *)priv; } + +/** + * enum nft_iter_type - nftables set iterator type + * + * @NFT_ITER_FLUSH: destructive iteration, transaction mutex must be held + */ +enum nft_iter_type { + /* undef == 0 */ + NFT_ITER_FLUSH = 1, +}; + struct nft_set; struct nft_set_iter { u8 genmask; + enum nft_iter_type type:8; unsigned int count; unsigned int skip; int err; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index fd86f2720c9e..facd33e97dfe 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -7369,6 +7369,7 @@ static int nft_set_flush(struct nft_ctx *ctx, struct nft_set *set, u8 genmask) struct nft_set_iter iter = { .genmask = genmask, .fn = nft_setelem_flush, + .type = NFT_ITER_FLUSH, }; set->ops->walk(ctx, set, &iter); diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 57b1508d3502..eca81c5e5810 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -2161,21 +2161,34 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, struct net *net = read_pnet(&set->net); const struct nft_pipapo_match *m; - rcu_read_lock(); - if (iter->genmask == nft_genmask_cur(net)) { - m = rcu_dereference(priv->match); - } else { + switch (iter->type) { + case NFT_ITER_FLUSH: m = priv->clone; - if (!m) /* no pending updates */ - m = rcu_dereference(priv->match); - } + if (!m) { + iter->err = -ENOMEM; + return; + } - if (m) __nft_pipapo_walk(ctx, set, m, iter); - else - WARN_ON_ONCE(1); + break; + default: + rcu_read_lock(); + if (iter->genmask == nft_genmask_cur(net)) { + m = rcu_dereference(priv->match); + } else { + m = priv->clone; + if (!m) /* no pending updates */ + m = rcu_dereference(priv->match); + } - rcu_read_unlock(); + if (m) + __nft_pipapo_walk(ctx, set, m, iter); + else + WARN_ON_ONCE(1); + + rcu_read_unlock(); + break; + } } /** -- 2.43.2