From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from out-188.mta0.migadu.com (out-188.mta0.migadu.com [91.218.175.188]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DAD6A3559F5; Mon, 22 Jun 2026 04:07:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.188 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782101229; cv=none; b=Rw6eYPs4OyjfOZdSufuv89vil+3JSsifaE9Nm5RwiF45sE+GGZB7I36wfYnMoBjUP1dJxdNgL3gi10Rus59Fkfdc+DSBH06s57ObC7PMiRPl+10DPcX0MkUEcL0PY/UPI5YvaV10uRLXcaOFb+N7yvQsxqozbXeiu9UMZu6b3/0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782101229; c=relaxed/simple; bh=m2LLV+BA5spGIUzLab9tWQ1i0a9h7daxqjgqeVb30rU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=e8FswySvW4fe2WigvJDq2yhOKA+D/xf4CLlcvWwkHwq27QzV0MxpLyiobP0eLRRzl3JKS/st5Fj10yF8G+itSastOV8AjTUVePaoWtauHeYpCXBhHI1tX9rXve9dzVsDk2O3VAXJFFIPa5iRbnoaTDyskOwbrbzRV7txlkjh1no= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=RKNYTOPI; arc=none smtp.client-ip=91.218.175.188 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="RKNYTOPI" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1782101224; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vFaoYZTJfRDH9hDuVHVBgZkR1flChQcp5jR5STw0K2M=; b=RKNYTOPII31SLlFemAcWqWE/TeS/HQJl7crveXc7GdzZz5I+TjbXRbbkZaQSvN3TmVtVYT nSSDvsZk7jNyt6B/E4YmD4cYyLjd5RE2oOf/Hiy1FZ2NcIRHyitOaBZNUaYo6dvHseOLCi TzRgynUCXY7wguiC8GKfv0LXaYiHBGc= From: Kaitao Cheng To: Andrew Morton , David Hildenbrand , Jens Axboe , Tejun Heo , Alexander Viro , Christian Brauner , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Johannes Weiner , Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Thomas Gleixner , Juri Lelli , Vincent Guittot , Paul Moore , Andy Shevchenko , "Paul E. McKenney" , Shakeel Butt , =?UTF-8?q?Christian=20K=C3=B6nig?= Cc: David Howells , Simona Vetter , Randy Dunlap , Luca Ceresoli , Philipp Stanner , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, cgroups@vger.kernel.org, linux-ntfs-dev@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, io-uring@vger.kernel.org, audit@vger.kernel.org, bpf@vger.kernel.org, netdev@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-perf-users@vger.kernel.org, linux-trace-kernel@vger.kernel.org, kexec@lists.infradead.org, live-patching@vger.kernel.org, linux-modules@vger.kernel.org, linux-crypto@vger.kernel.org, linux-pm@vger.kernel.org, rcu@vger.kernel.org, sched-ext@lists.linux.dev, linux-mm@kvack.org, virtualization@lists.linux.dev, damon@lists.linux.dev, llvm@lists.linux.dev, Kaitao Cheng Subject: [PATCH v3 2/7] llist: Add mutable iterator variants Date: Mon, 22 Jun 2026 12:05:32 +0800 Message-ID: <20260622040533.29824-3-kaitao.cheng@linux.dev> In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev> References: <20260622040533.29824-1-kaitao.cheng@linux.dev> Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT From: Kaitao Cheng llist_for_each_safe() and llist_for_each_entry_safe() require callers to provide a temporary cursor even when the cursor is only needed by the iterator itself. This makes call sites noisier than necessary for the common case where the loop body may remove the current entry but does not otherwise inspect the saved next pointer. Add llist_for_each_mutable() and llist_for_each_entry_mutable() variants that support both forms. Callers may omit the temporary cursor and let the helper create an internal unique cursor, or keep passing an explicit cursor when the loop needs to inspect or reset it. Keep the existing safe helpers as compatibility wrappers so current users continue to build unchanged while new code can use the shorter mutable form. Signed-off-by: Kaitao Cheng --- include/linux/llist.h | 81 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/include/linux/llist.h b/include/linux/llist.h index 8846b7709669..1c6f12411d5e 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -49,6 +49,7 @@ */ #include +#include #include #include #include @@ -143,12 +144,33 @@ static inline bool llist_on_list(const struct llist_node *node) #define llist_for_each(pos, node) \ for ((pos) = (node); pos; (pos) = (pos)->next) +/* + * llist_for_each_safe is an old interface, 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_internal(pos, tmp, node) \ + for (typeof(pos) tmp = ((pos) = (node)) ? (pos)->next : NULL; \ + (pos); \ + (pos) = tmp, tmp = (pos) ? (pos)->next : NULL) + +#define __llist_for_each_mutable1(pos, node) \ + __llist_for_each_mutable_internal(pos, __UNIQUE_ID(next), node) + +#define __llist_for_each_mutable2(pos, next, node) \ + llist_for_each_safe(pos, next, node) + /** - * llist_for_each_safe - iterate over some deleted entries of a lock-less list - * safe against removal of list entry + * llist_for_each_mutable - iterate over some deleted entries of a lock-less list + * safe against removal of list entry * @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 + * @...: either (node) or (next, node) + * + * next: another &struct llist_node to use as optional temporary storage. + * The temporary cursor is internal unless explicitly supplied by + * the caller. + * 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 @@ -159,8 +181,9 @@ static inline bool llist_on_list(const struct llist_node *node) * you want to traverse from the oldest to the newest, you must * reverse the order by yourself before traversing. */ -#define llist_for_each_safe(pos, n, node) \ - for ((pos) = (node); (pos) && ((n) = (pos)->next, true); (pos) = (n)) +#define llist_for_each_mutable(pos, ...) \ + CONCATENATE(__llist_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \ + (pos, __VA_ARGS__) /** * llist_for_each_entry - iterate over some deleted entries of lock-less list of given type @@ -182,13 +205,41 @@ static inline bool llist_on_list(const struct llist_node *node) member_address_is_nonnull(pos, member); \ (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) +/* + * llist_for_each_entry_safe is an old interface, 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); \ + pos = n) + +#define __llist_for_each_entry_mutable_internal(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) + +#define __llist_for_each_entry_mutable2(pos, node, member) \ + __llist_for_each_entry_mutable_internal(pos, __UNIQUE_ID(next), node, member) + +#define __llist_for_each_entry_mutable3(pos, next, node, member) \ + llist_for_each_entry_safe(pos, next, node, 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_mutable - iterate over some deleted entries of + * lock-less list of given type safe against + * removal of list entry * @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. - * @member: the name of the llist_node with the struct. + * @...: either (node, member) or (next, node, member) + * + * next: another type * to use as optional temporary storage. The + * temporary cursor is internal unless explicitly supplied by the + * caller. + * 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 @@ -199,11 +250,9 @@ static inline bool llist_on_list(const struct llist_node *node) * you want to traverse from the oldest to the newest, you must * reverse the order by yourself before traversing. */ -#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); \ - pos = n) +#define llist_for_each_entry_mutable(pos, ...) \ + CONCATENATE(__llist_for_each_entry_mutable, \ + COUNT_ARGS(__VA_ARGS__))(pos, __VA_ARGS__) /** * llist_empty - tests whether a lock-less list is empty -- 2.43.0