Right now, without pending updates, priv->clone and priv->match will point to different memory locations, but they have identical content. Future patch will make priv->clone == NULL if there are no pending changes. We cannot just fallback to the live data in this case because there are different types of walks: - set dump: this can fallback to the live copy. - flush walk: all set elements should be deactivated. If no single element was removed before, then we must first make a copy of the live data. - deactivate/activate walks during abort or commit. This would always have a non-null clone. The existing test is unreliable, if genmask is not equal to current one, we can't infer that the transaction mutex is held, we could be in a (lockless) dump. Its only safe at this time because both commit and abort paths will re-clone the live copy, so ->clone is always non-null -- something that is about to change. Next patch will add explicit iter type to tell when flushing is requested (i.e., when live data must be copied first). Signed-off-by: Florian Westphal <fw@xxxxxxxxx> --- net/netfilter/nft_set_pipapo.c | 54 +++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index d2ac2d5560e4..57b1508d3502 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -2102,33 +2102,23 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set, } /** - * nft_pipapo_walk() - Walk over elements + * __nft_pipapo_walk() - Walk over elements in m * @ctx: nftables API context * @set: nftables API set representation + * @m: matching data pointing to key mapping array * @iter: Iterator * * As elements are referenced in the mapping array for the last field, directly * scan that array: there's no need to follow rule mappings from the first * field. */ -static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, - struct nft_set_iter *iter) +static void __nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, + const struct nft_pipapo_match *m, + struct nft_set_iter *iter) { - struct nft_pipapo *priv = nft_set_priv(set); - struct net *net = read_pnet(&set->net); - const struct nft_pipapo_match *m; const struct nft_pipapo_field *f; unsigned int i, r; - rcu_read_lock(); - if (iter->genmask == nft_genmask_cur(net)) - m = rcu_dereference(priv->match); - else - m = priv->clone; - - if (unlikely(!m)) - goto out; - for (i = 0, f = m->f; i < m->field_count - 1; i++, f++) ; @@ -2148,13 +2138,43 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, iter->err = iter->fn(ctx, set, iter, &e->priv); if (iter->err < 0) - goto out; + return; cont: iter->count++; } +} + +/** + * nft_pipapo_walk() - Walk over elements + * @ctx: nftables API context + * @set: nftables API set representation + * @iter: Iterator + * + * Test if destructive action is needed or not, clone active backend if needed + * and call the real function to work on the data. + */ +static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_iter *iter) +{ + struct nft_pipapo *priv = nft_set_priv(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 { + m = priv->clone; + if (!m) /* no pending updates */ + m = rcu_dereference(priv->match); + } + + if (m) + __nft_pipapo_walk(ctx, set, m, iter); + else + WARN_ON_ONCE(1); -out: rcu_read_unlock(); } -- 2.43.2