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 1/4] list: Add mutable iterator variants
Date: Thu, 30 Jul 2026 17:50:33 +0800 [thread overview]
Message-ID: <20260730095041.35715-2-kaitao.cheng@linux.dev> (raw)
In-Reply-To: <20260730095041.35715-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The list_for_each*_safe() helpers allow the current entry to be removed
while iterating, but require callers to declare and pass a temporary
cursor. Most callers never use that cursor directly, so exposing it adds
boilerplate and leaks an implementation detail into each call site.
Add *_mutable() variants for list and hlist iterators. These variants
keep the temporary cursor in a uniquely named local variable inside the
macro while preserving the 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/list.h | 180 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 175 insertions(+), 5 deletions(-)
diff --git a/include/linux/list.h b/include/linux/list.h
index 09d979976b3b..373b08b929d3 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -765,26 +765,62 @@ static inline void list_splice_tail_init(struct list_head *list,
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
+ * with a caller-provided temporary cursor
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * list_for_each_mutable() instead.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; \
!list_is_head(pos, (head)); \
pos = n, n = pos->next)
+#define __list_for_each_mutable(pos, tmp, head) \
+ for (typeof(pos) tmp = (pos = (head)->next)->next; \
+ !list_is_head(pos, (head)); \
+ pos = tmp, tmp = pos->next)
+
+/**
+ * list_for_each_mutable - iterate over a list safe against entry removal
+ * with an internal temporary cursor
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_mutable(pos, head) \
+ __list_for_each_mutable(pos, __UNIQUE_ID(next), head)
+
/**
- * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal
+ * of list entry with a caller-provided temporary cursor
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * list_for_each_prev_mutable() instead.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
!list_is_head(pos, (head)); \
pos = n, n = pos->prev)
+#define __list_for_each_prev_mutable(pos, tmp, head) \
+ for (typeof(pos) tmp = (pos = (head)->prev)->prev; \
+ !list_is_head(pos, (head)); \
+ pos = tmp, tmp = pos->prev)
+
+/**
+ * list_for_each_prev_mutable - iterate over a list backwards safe against entry
+ * removal with an internal temporary cursor
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_mutable(pos, head) \
+ __list_for_each_prev_mutable(pos, __UNIQUE_ID(prev), head)
+
/**
* list_count_nodes - count nodes in the list
* @head: the head for your list.
@@ -896,11 +932,15 @@ static inline size_t list_count_nodes(struct list_head *head)
pos = list_prev_entry(pos, member))
/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * list_for_each_entry_safe - iterate over list of given type safe against
+ * removal of list entry with a caller-provided temporary cursor
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * list_for_each_entry_mutable() instead.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
@@ -908,8 +948,25 @@ static inline size_t list_count_nodes(struct list_head *head)
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
+#define __list_for_each_entry_mutable(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_next_entry(pos = \
+ list_first_entry(head, typeof(*pos), member), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_next_entry(tmp, member))
+
+/**
+ * list_for_each_entry_mutable - iterate over a list safe against entry removal
+ * with an internal temporary cursor
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_mutable(pos, head, member) \
+ __list_for_each_entry_mutable(pos, __UNIQUE_ID(next), head, member)
+
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
+ * with a caller-provided temporary cursor
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
@@ -917,6 +974,9 @@ static inline size_t list_count_nodes(struct list_head *head)
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * list_for_each_entry_mutable_continue() instead.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
@@ -924,8 +984,28 @@ static inline size_t list_count_nodes(struct list_head *head)
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
+#define __list_for_each_entry_mutable_continue(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_next_entry(pos = \
+ list_next_entry(pos, member), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_next_entry(tmp, member))
+
/**
- * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ * list_for_each_entry_mutable_continue - continue list iteration safe against
+ * removal with an internal temporary cursor
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_mutable_continue(pos, head, member) \
+ __list_for_each_entry_mutable_continue(pos, __UNIQUE_ID(next), head, member)
+
+/**
+ * list_for_each_entry_safe_from - iterate over list from current point safe
+ * against removal with a caller-provided temporary cursor
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
@@ -933,14 +1013,36 @@ static inline size_t list_count_nodes(struct list_head *head)
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * list_for_each_entry_mutable_from() instead.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
+#define __list_for_each_entry_mutable_from(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_next_entry(pos, member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_next_entry(tmp, member))
+
+/**
+ * list_for_each_entry_mutable_from - iterate over list from current point safe
+ * against removal with an internal temporary cursor
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_mutable_from(pos, head, member) \
+ __list_for_each_entry_mutable_from(pos, __UNIQUE_ID(next), head, member)
+
/**
- * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against
+ * removal with a caller-provided temporary cursor
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
@@ -948,6 +1050,9 @@ static inline size_t list_count_nodes(struct list_head *head)
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * list_for_each_entry_mutable_reverse() instead.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member), \
@@ -955,6 +1060,25 @@ static inline size_t list_count_nodes(struct list_head *head)
!list_entry_is_head(pos, head, member); \
pos = n, n = list_prev_entry(n, member))
+#define __list_for_each_entry_mutable_reverse(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_prev_entry(pos = \
+ list_last_entry(head, typeof(*pos), member), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_prev_entry(tmp, member))
+
+/**
+ * list_for_each_entry_mutable_reverse - iterate backwards over list safe
+ * against removal with an internal temporary cursor
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_mutable_reverse(pos, head, member) \
+ __list_for_each_entry_mutable_reverse(pos, __UNIQUE_ID(prev), head, member)
+
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
@@ -1185,10 +1309,34 @@ static inline void hlist_splice_init(struct hlist_head *from,
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
+/**
+ * hlist_for_each_safe - iterate over a hlist safe against entry removal
+ * with a caller-provided temporary cursor
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @n: another &struct hlist_node to use as temporary storage.
+ * @head: the head for your hlist.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * hlist_for_each_mutable() instead.
+ */
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
+#define __hlist_for_each_mutable(pos, tmp, head) \
+ for (typeof(pos) tmp = (pos = (head)->first) ? pos->next : NULL; \
+ pos; \
+ pos = tmp, tmp = pos ? pos->next : NULL)
+
+/**
+ * hlist_for_each_mutable - iterate over a hlist safe against entry removal
+ * with an internal temporary cursor
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @head: the head for your hlist.
+ */
+#define hlist_for_each_mutable(pos, head) \
+ __hlist_for_each_mutable(pos, __UNIQUE_ID(next), head)
+
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
@@ -1225,17 +1373,39 @@ static inline void hlist_splice_init(struct hlist_head *from,
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
- * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * hlist_for_each_entry_safe - iterate over list of given type safe against
+ * removal of list entry with a caller-provided temporary cursor
* @pos: the type * to use as a loop cursor.
* @n: a &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
+ *
+ * If the caller does not need to access the temporary cursor, use
+ * hlist_for_each_entry_mutable() instead.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
+#define __hlist_for_each_entry_mutable(pos, tmp, head, member) \
+ for (struct hlist_node *tmp = (pos = \
+ hlist_entry_safe((head)->first, typeof(*pos), member)) ? \
+ pos->member.next : NULL; \
+ pos; \
+ pos = hlist_entry_safe((tmp), typeof(*pos), member), \
+ tmp = pos ? pos->member.next : NULL)
+
+/**
+ * hlist_for_each_entry_mutable - iterate over hlist safe against entry removal
+ * with an internal temporary cursor
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your hlist.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_mutable(pos, head, member) \
+ __hlist_for_each_entry_mutable(pos, __UNIQUE_ID(next), head, member)
+
/**
* hlist_count_nodes - count nodes in the hlist
* @head: the head for your hlist.
--
2.43.0
next prev parent reply other threads:[~2026-07-30 9:52 UTC|newest]
Thread overview: 5+ 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 ` Kaitao Cheng [this message]
2026-07-30 9:50 ` [PATCH v4 2/4] llist: Add mutable iterator variants Kaitao Cheng
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
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-2-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