[PATCH v4 2/4] llist: Add mutable iterator variants

Kaitao Cheng <[email protected]> Thu, 30 Jul 2026 17:50:34 +0800
Newsgroups org.kernel.vger.linux-kbuild,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Kaitao Cheng <[email protected]>

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 <[email protected]>
---
 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