From: Kaitao Cheng <kaitao.cheng@linux.dev>
To: "David Laight" <david.laight.linux@gmail.com>,
"Jani Nikula" <jani.nikula@linux.intel.com>,
"Christian König" <christian.koenig@amd.com>,
"David Hildenbrand" <david@kernel.org>,
"Nathan Chancellor" <nathan@kernel.org>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Nicolas Schier" <nsc@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>,
"Paul E . McKenney" <paulmck@kernel.org>,
David Howells <dhowells@redhat.com>,
Simona Vetter <simona.vetter@ffwll.ch>,
Neeraj Upadhyay <neeraj.upadhyay@kernel.org>,
Luca Ceresoli <luca.ceresoli@bootlin.com>,
Randy Dunlap <rdunlap@infradead.org>,
Kaitao Cheng <chengkaitao@kylinos.cn>,
Andrew Morton <akpm@linux-foundation.org>,
Philipp Stanner <phasta@kernel.org>,
Alex Williamson <alex@shazbot.org>,
David Matlack <dmatlack@google.com>,
Shuah Khan <skhan@linuxfoundation.org>,
"Joy H . J . Lee" <rkr0k0r@gmail.com>,
Peter Zijlstra <peterz@infradead.org>,
Ian Rogers <irogers@google.com>,
Namhyung Kim <namhyung@kernel.org>,
Swapnil Sapkal <swapnil.sapkal@amd.com>,
linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v4 2/4] llist: Add mutable iterator variants
Date: Thu, 30 Jul 2026 17:50:34 +0800 [thread overview]
Message-ID: <20260730095041.35715-3-kaitao.cheng@linux.dev> (raw)
In-Reply-To: <20260730095041.35715-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The llist_for_each_safe() and llist_for_each_entry_safe() helpers allow
the current entry to be removed while iterating, but require callers to
declare and pass a temporary cursor. Most callers do not access that
cursor directly, so exposing it adds unnecessary boilerplate.
Add mutable variants that keep the temporary cursor in a uniquely named
local variable inside the macro while preserving removal-safe iteration
semantics.
Keep the existing *_safe() variants for callers that need to access or
reset the temporary cursor, and update their documentation to clarify
which interface to use.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
include/linux/llist.h | 67 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 63 insertions(+), 4 deletions(-)
diff --git a/include/linux/llist.h b/include/linux/llist.h
index 413249764f7b..559058a8d4a0 100644
--- a/include/linux/llist.h
+++ b/include/linux/llist.h
@@ -145,7 +145,7 @@ static inline bool llist_on_list(const struct llist_node *node)
/**
* llist_for_each_safe - iterate over some deleted entries of a lock-less list
- * safe against removal of list entry
+ * safe against entry removal with a caller-provided temporary cursor
* @pos: the &struct llist_node to use as a loop cursor
* @n: another &struct llist_node to use as temporary storage
* @node: the first entry of deleted list entries
@@ -158,10 +158,36 @@ static inline bool llist_on_list(const struct llist_node *node)
* traverse order is from the newest to the oldest added entry. If
* you want to traverse from the oldest to the newest, you must
* reverse the order by yourself before traversing.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * llist_for_each_mutable() instead.
*/
#define llist_for_each_safe(pos, n, node) \
for ((pos) = (node); (pos) && ((n) = (pos)->next, true); (pos) = (n))
+#define __llist_for_each_mutable(pos, tmp, node) \
+ for (typeof(pos) tmp = ((pos) = (node)) ? (pos)->next : NULL; \
+ (pos); \
+ (pos) = tmp, tmp = (pos) ? (pos)->next : NULL)
+
+/**
+ * llist_for_each_mutable - iterate over some deleted entries of a lock-less
+ * list safe against entry removal with an internal temporary cursor
+ * @pos: the &struct llist_node to use as a loop cursor
+ * @node: the first entry of deleted list entries
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being deleted from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry. If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each_mutable(pos, node) \
+ __llist_for_each_mutable(pos, __UNIQUE_ID(next), node)
+
/**
* llist_for_each_entry - iterate over some deleted entries of lock-less list of given type
* @pos: the type * to use as a loop cursor.
@@ -183,8 +209,9 @@ static inline bool llist_on_list(const struct llist_node *node)
(pos) = llist_entry((pos)->member.next, typeof(*(pos)), member))
/**
- * llist_for_each_entry_safe - iterate over some deleted entries of lock-less list of given type
- * safe against removal of list entry
+ * llist_for_each_entry_safe - iterate over some deleted entries of a lock-less
+ * list of given type safe against entry removal with a caller-provided
+ * temporary cursor
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @node: the first entry of deleted list entries.
@@ -198,13 +225,45 @@ static inline bool llist_on_list(const struct llist_node *node)
* traverse order is from the newest to the oldest added entry. If
* you want to traverse from the oldest to the newest, you must
* reverse the order by yourself before traversing.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * llist_for_each_entry_mutable() instead.
*/
#define llist_for_each_entry_safe(pos, n, node, member) \
for (pos = llist_entry((node), typeof(*pos), member); \
member_address_is_nonnull(pos, member) && \
- (n = llist_entry(pos->member.next, typeof(*n), member), true); \
+ (n = llist_entry(pos->member.next, typeof(*n), member), true); \
pos = n)
+#define __llist_for_each_entry_mutable(pos, tmp, node, member) \
+ for (typeof(pos) tmp = \
+ ((pos) = llist_entry((node), typeof(*(pos)), member), \
+ member_address_is_nonnull(pos, member) ? \
+ llist_entry((pos)->member.next, typeof(*(pos)), member) : NULL);\
+ member_address_is_nonnull(pos, member); \
+ (pos) = tmp, tmp = member_address_is_nonnull(pos, member) ? \
+ llist_entry((pos)->member.next, typeof(*(pos)), member) : NULL)
+
+/**
+ * llist_for_each_entry_mutable - iterate over some deleted entries of a
+ * lock-less list of given type safe against entry removal with an internal
+ * temporary cursor
+ * @pos: the type * to use as a loop cursor.
+ * @node: the first entry of deleted list entries.
+ * @member: the name of the llist_node with the struct.
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being removed from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry. If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each_entry_mutable(pos, node, member) \
+ __llist_for_each_entry_mutable(pos, __UNIQUE_ID(next), node, member)
+
/**
* llist_empty - tests whether a lock-less list is empty
* @head: the list to test
--
2.43.0
next prev parent reply other threads:[~2026-07-30 9:52 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 9:50 [PATCH v4 0/4] Prepare mutable list iterators to cache cursor state Kaitao Cheng
2026-07-30 9:50 ` [PATCH v4 1/4] list: Add mutable iterator variants Kaitao Cheng
2026-07-30 9:50 ` Kaitao Cheng [this message]
2026-07-30 9:50 ` [PATCH v4 3/4] tools/list: " Kaitao Cheng
2026-07-30 9:50 ` [PATCH v4 4/4] scripts/list: " Kaitao Cheng
2026-07-30 19:11 ` [PATCH v4 0/4] Prepare mutable list iterators to cache cursor state Andrew Morton
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=20260730095041.35715-3-kaitao.cheng@linux.dev \
--to=kaitao.cheng@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=alex@shazbot.org \
--cc=andriy.shevchenko@linux.intel.com \
--cc=brauner@kernel.org \
--cc=chengkaitao@kylinos.cn \
--cc=christian.koenig@amd.com \
--cc=david.laight.linux@gmail.com \
--cc=david@kernel.org \
--cc=dhowells@redhat.com \
--cc=dmatlack@google.com \
--cc=irogers@google.com \
--cc=jani.nikula@linux.intel.com \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luca.ceresoli@bootlin.com \
--cc=namhyung@kernel.org \
--cc=nathan@kernel.org \
--cc=neeraj.upadhyay@kernel.org \
--cc=nsc@kernel.org \
--cc=paulmck@kernel.org \
--cc=peterz@infradead.org \
--cc=phasta@kernel.org \
--cc=rdunlap@infradead.org \
--cc=rkr0k0r@gmail.com \
--cc=simona.vetter@ffwll.ch \
--cc=skhan@linuxfoundation.org \
--cc=swapnil.sapkal@amd.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox