]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rculist: add list_splice_rcu() for private lists
authorPablo Neira Ayuso <pablo@netfilter.org>
Wed, 15 Apr 2026 15:56:02 +0000 (17:56 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 21 Apr 2026 10:48:44 +0000 (12:48 +0200)
This patch adds a helper function, list_splice_rcu(), to safely splice
a private (non-RCU-protected) list into an RCU-protected list.

The function ensures that only the pointer visible to RCU readers
(prev->next) is updated using rcu_assign_pointer(), while the rest of
the list manipulations are performed with regular assignments, as the
source list is private and not visible to concurrent RCU readers.

This is useful for moving elements from a private list into a global
RCU-protected list, ensuring safe publication for RCU readers.
Subsystems with some sort of batching mechanism from userspace can
benefit from this new function.

The function __list_splice_rcu() has been added for clarity and to
follow the same pattern as in the existing list_splice*() interfaces,
where there is a check to ensure that the list to splice is not
empty. Note that __list_splice_rcu() has no documentation for this
reason.

Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/rculist.h

index 2abba7552605c539cfd0ee1c6a453a99c2e652d1..e3bc442256922bea4fb10b6ec370748a5b4712ff 100644 (file)
@@ -261,6 +261,35 @@ static inline void list_replace_rcu(struct list_head *old,
        old->prev = LIST_POISON2;
 }
 
+static inline void __list_splice_rcu(struct list_head *list,
+                                    struct list_head *prev,
+                                    struct list_head *next)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+
+       last->next = next;
+       first->prev = prev;
+       next->prev = last;
+       rcu_assign_pointer(list_next_rcu(prev), first);
+}
+
+/**
+ * list_splice_rcu - splice a non-RCU list into an RCU-protected list,
+ *                   designed for stacks.
+ * @list:      the non RCU-protected list to splice
+ * @head:      the place in the existing RCU-protected list to splice
+ *
+ * The list pointed to by @head can be RCU-read traversed concurrently with
+ * this function.
+ */
+static inline void list_splice_rcu(struct list_head *list,
+                                  struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice_rcu(list, head, head->next);
+}
+
 /**
  * __list_splice_init_rcu - join an RCU-protected list into an existing list.
  * @list:      the RCU-protected list to splice