From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Jozsef Kadlecsik <kadlec@netfilter.org>,
Florian Westphal <fw@strlen.de>,
herbert@gondor.apana.org.au, linux-crypto@vger.kernel.org
Subject: [PATCH nf-next v4 01/13] rhashtable: add rhashtable_flush_and_free helper
Date: Fri, 4 Sep 2026 20:53:09 +0200 [thread overview]
Message-ID: <20260904185321.30313-2-fw@strlen.de> (raw)
In-Reply-To: <20260904185321.30313-1-fw@strlen.de>
Will be used by upcoming ipset rhashtable conversion.
"walk rht with unlink+free" triggers LLM reject pattern:
"possible softirq CPU stall".
"walk rht with unlink+free + cond_resched" triggers
"possibly skipped elements".
Add a helper to detach current hash backend storage from the
rhashtable, then iterate and flush all contained elements.
Cc: herbert@gondor.apana.org.au
Cc: linux-crypto@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260828152256.8759-1-fw%40strlen.de
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Florian Westphal <fw@strlen.de>
---
Herbert: If you prefer to take this via the crypto tree, please
let me know.
Otherwise, an explicit Ack would be appreciated, so this can
be handled via nf-next. Thanks.
net/ipv6/ila/ could be converted to use this helper too.
include/linux/rhashtable.h | 19 ++++++
lib/rhashtable.c | 127 +++++++++++++++++++++++++++++++++++++
2 files changed, 146 insertions(+)
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
index 57a2a29bef0e..213e1cb77d45 100644
--- a/include/linux/rhashtable.h
+++ b/include/linux/rhashtable.h
@@ -255,6 +255,10 @@ void rhashtable_free_and_destroy(struct rhashtable *ht,
void *arg);
void rhashtable_destroy(struct rhashtable *ht);
+void rhashtable_flush_and_free(struct rhashtable *ht,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg);
+
struct rhash_lock_head __rcu **rht_bucket_nested(
const struct bucket_table *tbl, unsigned int hash);
struct rhash_lock_head __rcu **__rht_bucket_nested(
@@ -1335,4 +1339,19 @@ static inline void rhltable_destroy(struct rhltable *hlt)
rhltable_free_and_destroy(hlt, NULL, NULL);
}
+/**
+ * rhltable_flush_and_free - unlink and free all elements in the hash list table
+ * @hlt: the hash list table to destroy
+ * @free_fn: callback to release resources of element
+ * @arg: pointer passed to free_fn
+ *
+ * See documentation for rhashtable_flush_and_free.
+ */
+static inline void rhltable_flush_and_free(struct rhltable *hlt,
+ void (*free_fn)(void *ptr,
+ void *arg),
+ void *arg)
+{
+ rhashtable_flush_and_free(&hlt->ht, free_fn, arg);
+}
#endif /* _LINUX_RHASHTABLE_H */
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 6362896e4f09..656c5021d8b2 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -1339,6 +1339,133 @@ void rhashtable_destroy(struct rhashtable *ht)
}
EXPORT_SYMBOL_GPL(rhashtable_destroy);
+struct rht_flush_arg {
+ struct rhashtable *ht;
+ void (*free_fn)(void *ptr, void *arg);
+ void *arg;
+};
+
+static void flush_cb(void *ptr, void *arg)
+{
+ struct rht_flush_arg *fa = arg;
+
+ atomic_dec(&fa->ht->nelems);
+ if (fa->free_fn)
+ fa->free_fn(ptr, fa->arg);
+}
+
+static void rhashtable_flush_one(struct rhashtable *ht, struct rhash_head *obj,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg)
+{
+ struct rht_flush_arg fa = {
+ .ht = ht,
+ .free_fn = free_fn,
+ .arg = arg,
+ };
+
+ rhashtable_free_one(ht, obj, flush_cb, &fa);
+}
+
+static void rhashtable_flush_chain(struct rhashtable *ht,
+ struct bucket_table *tbl,
+ unsigned int hash,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg)
+{
+ struct rhash_lock_head __rcu **bkt = rht_bucket_var(tbl, hash);
+ struct rhash_head *pos, *next;
+ unsigned long flags;
+
+ if (!bkt)
+ return;
+
+ flags = rht_lock(tbl, bkt);
+ pos = rht_ptr(bkt, tbl, hash);
+ rht_assign_unlock(tbl, bkt, NULL, flags);
+
+ /* Nothing can reach @pos through @tbl any more: the bucket has
+ * been emptied above, and @tbl itself is unreachable from ht->tbl
+ * (see rhashtable_flush_and_free()). Walk it the same way
+ * rhashtable_free_and_destroy() walks a table it exclusively
+ * owns.
+ */
+ while (!rht_is_a_nulls(pos)) {
+ next = rcu_dereference_raw(pos->next);
+ rhashtable_flush_one(ht, pos, free_fn, arg);
+ pos = next;
+ }
+}
+
+/**
+ * rhashtable_flush_and_free - detach and discard all current elements
+ * @ht: the hash table to flush
+ * @free_fn: callback to release resources of an element, may be %NULL
+ * @arg: pointer passed to free_fn
+ *
+ * Swaps the bucket table backing @ht for a new, empty table.
+ *
+ * The detached table is then walked and every element found is
+ * unlinked, and, if @free_fn is given, handed to it for release.
+ * Note that RCU protected readers may still be accessing the elements.
+ * Releasing of resources must occur in a compatible manner.
+ *
+ * Unlike rhashtable_destroy(), @ht is left fully initialized and may
+ * continue to be used for lookups, insertions, and removals.
+ *
+ * This function may sleep, it cannot be called from atomic context or
+ * RCU read-side critical sections.
+ */
+void rhashtable_flush_and_free(struct rhashtable *ht,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg)
+{
+ struct bucket_table *tbl, *old_tbl, *last_tbl, *new_tbl;
+ struct rhashtable_walker *walker;
+ unsigned int i;
+
+ new_tbl = bucket_table_alloc(ht, rounded_hashtable_size(&ht->p),
+ GFP_KERNEL);
+ if (!new_tbl)
+ new_tbl = bucket_table_alloc(ht, ht->p.min_size,
+ GFP_KERNEL | __GFP_NOFAIL);
+
+ mutex_lock(&ht->mutex);
+
+ /* Splice the new, empty table onto the tail of the live table ... */
+ old_tbl = rht_dereference(ht->tbl, ht);
+ do {
+ last_tbl = rhashtable_last_table(ht, old_tbl);
+ } while (rhashtable_rehash_attach(ht, last_tbl, new_tbl));
+
+ /* ...then publish it as ht->tbl. */
+ rcu_assign_pointer(ht->tbl, new_tbl);
+ mutex_unlock(&ht->mutex);
+
+ tbl = old_tbl;
+ do {
+ struct bucket_table *next_tbl = rcu_dereference_raw(tbl->future_tbl);
+
+ for (i = 0; i < tbl->size; i++) {
+ cond_resched();
+ rhashtable_flush_chain(ht, tbl, i, free_fn, arg);
+ }
+
+ spin_lock(&ht->lock);
+ list_for_each_entry(walker, &tbl->walkers, list)
+ walker->tbl = NULL;
+ /* See rhashtable_rehash_table(): done under ->lock so
+ * rhashtable_walk_stop() can use rcu_head_after_call_rcu()
+ * to decide whether to re-link the walker onto this table.
+ */
+ call_rcu(&tbl->rcu, bucket_table_free_rcu);
+ spin_unlock(&ht->lock);
+
+ tbl = next_tbl;
+ } while (tbl && tbl != new_tbl);
+}
+EXPORT_SYMBOL_GPL(rhashtable_flush_and_free);
+
struct rhash_lock_head __rcu **__rht_bucket_nested(
const struct bucket_table *tbl, unsigned int hash)
{
--
2.55.0
next prev parent reply other threads:[~2026-09-04 18:53 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 18:53 [PATCH nf-next v4 00/13] ipset: replace internal hash table with rhashtable Florian Westphal
2026-09-04 18:53 ` Florian Westphal [this message]
2026-09-04 19:29 ` [PATCH nf-next v4 01/13] rhashtable: add rhashtable_flush_and_free helper Florian Westphal
2026-09-08 5:12 ` Herbert Xu
2026-09-08 5:30 ` Florian Westphal
2026-09-08 9:04 ` Herbert Xu
2026-09-08 9:56 ` Florian Westphal
2026-09-08 12:39 ` Herbert Xu
2026-09-08 13:25 ` Florian Westphal
2026-09-09 3:49 ` Herbert Xu
2026-09-09 4:17 ` Herbert Xu
2026-09-09 14:45 ` Florian Westphal
2026-09-10 9:11 ` Herbert Xu
2026-09-10 10:41 ` Florian Westphal
2026-09-11 11:56 ` Herbert Xu
2026-09-11 12:54 ` Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 02/13] netfilter: ipset: add rhashtable boilerplate stubs Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 03/13] netfilter: ipset: add rhltable " Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 04/13] netfilter: ipset: replace internal hash table with rhashtable Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 05/13] netfilter: ipset: re-add forceadd support Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 06/13] netfilter: ipset: also report mem size for cidr storage to userspace Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 07/13] netfilter: ipset: remove obsolete data_next stubs Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 08/13] netfilter: ipset: remove last region lock usage Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 09/13] netfilter: ipset: remove multi-flag Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 10/13] netfilter: ipset: remove resize completely Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 11/13] netfilter: ipset: remove trivial kvfree wrapper Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 12/13] netfilter: ipset: use plain rcu_read_lock Florian Westphal
2026-09-04 18:53 ` [PATCH nf-next v4 13/13] netfilter: ipset: improve lockdep coverage Florian Westphal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260904185321.30313-2-fw@strlen.de \
--to=fw@strlen.de \
--cc=herbert@gondor.apana.org.au \
--cc=kadlec@netfilter.org \
--cc=linux-crypto@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.