The Linux Kernel Mailing List
 help / color / mirror / Atom feed
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


  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