mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-04-13 09:59:31 +00:00
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/20241213-work-mount-rbtree-lockless-v3-4-6e3cdaf9b280@kernel.org Reviewed-by: Paul E. McKenney <paulmck@kernel.org> Reviewed-by: Jeff Layton <jlayton@kernel.org> Suggested-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
5dcbd85d35
commit
67d676bb13
1 changed files with 44 additions and 0 deletions
|
@ -30,6 +30,17 @@ static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
|
|||
* way, we must not access it directly
|
||||
*/
|
||||
#define list_next_rcu(list) (*((struct list_head __rcu **)(&(list)->next)))
|
||||
/*
|
||||
* Return the ->prev pointer of a list_head in an rcu safe way. Don't
|
||||
* access it directly.
|
||||
*
|
||||
* Any list traversed with list_bidir_prev_rcu() must never use
|
||||
* list_del_rcu(). Doing so will poison the ->prev pointer that
|
||||
* list_bidir_prev_rcu() relies on, which will result in segfaults.
|
||||
* To prevent these segfaults, use list_bidir_del_rcu() instead
|
||||
* of list_del_rcu().
|
||||
*/
|
||||
#define list_bidir_prev_rcu(list) (*((struct list_head __rcu **)(&(list)->prev)))
|
||||
|
||||
/**
|
||||
* 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)
|
|||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_bidir_del_rcu - deletes entry from list without re-initialization
|
||||
* @entry: the element to delete from the list.
|
||||
*
|
||||
* In contrast to list_del_rcu() doesn't poison the prev pointer thus
|
||||
* allowing backwards traversal via list_bidir_prev_rcu().
|
||||
*
|
||||
* Note: list_empty() on entry does not return true after this because
|
||||
* the entry is in a special undefined state that permits RCU-based
|
||||
* lockfree reverse traversal. In particular this means that we can not
|
||||
* poison the forward and backwards pointers that may still be used for
|
||||
* walking the list.
|
||||
*
|
||||
* The caller must take whatever precautions are necessary (such as
|
||||
* holding appropriate locks) to avoid racing with another list-mutation
|
||||
* primitive, such as list_bidir_del_rcu() or list_add_rcu(), running on
|
||||
* this same list. However, it is perfectly legal to run concurrently
|
||||
* with the _rcu list-traversal primitives, such as
|
||||
* list_for_each_entry_rcu().
|
||||
*
|
||||
* Note that list_del_rcu() and list_bidir_del_rcu() must not be used on
|
||||
* the same list.
|
||||
*
|
||||
* Note that the caller is not permitted to immediately free
|
||||
* the newly deleted entry. Instead, either synchronize_rcu()
|
||||
* or call_rcu() must be used to defer freeing until an RCU
|
||||
* grace period has elapsed.
|
||||
*/
|
||||
static inline void list_bidir_del_rcu(struct list_head *entry)
|
||||
{
|
||||
__list_del_entry(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* hlist_del_init_rcu - deletes entry from hash list with re-initialization
|
||||
* @n: the element to delete from the hash list.
|
||||
|
|
Loading…
Add table
Reference in a new issue