[PATCH v4 2/4] llist: Add mutable iterator variants
Kaitao Cheng <[email protected]> Thu, 30 Jul 2026 17:50:34 +0800
| Newsgroups | gmane.linux.kernel,gmane.linux.kbuild.devel |
|---|---|
| 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