Skip to content

Commit 67d676b

Browse files
committed
rculist: add list_bidir_{del,prev}_rcu()
Currently there is no primitive for retrieving the previous list member. To do this we need a new deletion primitive that doesn't poison the prev pointer and a corresponding retrieval helper. Note that it is not valid to ues both list_del_rcu() and list_bidir_del_rcu() on the same list. Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Paul E. McKenney <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Suggested-by: Paul E. McKenney <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 5dcbd85 commit 67d676b

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

include/linux/rculist.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
3030
* way, we must not access it directly
3131
*/
3232
#define list_next_rcu(list) (*((struct list_head __rcu **)(&(list)->next)))
33+
/*
34+
* Return the ->prev pointer of a list_head in an rcu safe way. Don't
35+
* access it directly.
36+
*
37+
* Any list traversed with list_bidir_prev_rcu() must never use
38+
* list_del_rcu(). Doing so will poison the ->prev pointer that
39+
* list_bidir_prev_rcu() relies on, which will result in segfaults.
40+
* To prevent these segfaults, use list_bidir_del_rcu() instead
41+
* of list_del_rcu().
42+
*/
43+
#define list_bidir_prev_rcu(list) (*((struct list_head __rcu **)(&(list)->prev)))
3344

3445
/**
3546
* list_tail_rcu - returns the prev pointer of the head of the list
@@ -158,6 +169,39 @@ static inline void list_del_rcu(struct list_head *entry)
158169
entry->prev = LIST_POISON2;
159170
}
160171

172+
/**
173+
* list_bidir_del_rcu - deletes entry from list without re-initialization
174+
* @entry: the element to delete from the list.
175+
*
176+
* In contrast to list_del_rcu() doesn't poison the prev pointer thus
177+
* allowing backwards traversal via list_bidir_prev_rcu().
178+
*
179+
* Note: list_empty() on entry does not return true after this because
180+
* the entry is in a special undefined state that permits RCU-based
181+
* lockfree reverse traversal. In particular this means that we can not
182+
* poison the forward and backwards pointers that may still be used for
183+
* walking the list.
184+
*
185+
* The caller must take whatever precautions are necessary (such as
186+
* holding appropriate locks) to avoid racing with another list-mutation
187+
* primitive, such as list_bidir_del_rcu() or list_add_rcu(), running on
188+
* this same list. However, it is perfectly legal to run concurrently
189+
* with the _rcu list-traversal primitives, such as
190+
* list_for_each_entry_rcu().
191+
*
192+
* Note that list_del_rcu() and list_bidir_del_rcu() must not be used on
193+
* the same list.
194+
*
195+
* Note that the caller is not permitted to immediately free
196+
* the newly deleted entry. Instead, either synchronize_rcu()
197+
* or call_rcu() must be used to defer freeing until an RCU
198+
* grace period has elapsed.
199+
*/
200+
static inline void list_bidir_del_rcu(struct list_head *entry)
201+
{
202+
__list_del_entry(entry);
203+
}
204+
161205
/**
162206
* hlist_del_init_rcu - deletes entry from hash list with re-initialization
163207
* @n: the element to delete from the hash list.

0 commit comments

Comments
 (0)