mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-21 11:44:01 +08:00
88ab1932ea
This is a straightforward patch, using hlist_nulls infrastructure. RCUification already done on UDP two weeks ago. Using hlist_nulls permits us to avoid some memory barriers, both at lookup time and delete time. Patch is large because it adds new macros to include/net/sock.h. These macros will be used by TCP & DCCP in next patch. Signed-off-by: Eric Dumazet <dada1@cosmosbay.com> Signed-off-by: David S. Miller <davem@davemloft.net>
388 lines
12 KiB
C
388 lines
12 KiB
C
#ifndef _LINUX_RCULIST_H
|
|
#define _LINUX_RCULIST_H
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
/*
|
|
* RCU-protected list version
|
|
*/
|
|
#include <linux/list.h>
|
|
#include <linux/rcupdate.h>
|
|
|
|
/*
|
|
* Insert a new entry between two known consecutive entries.
|
|
*
|
|
* This is only for internal list manipulation where we know
|
|
* the prev/next entries already!
|
|
*/
|
|
static inline void __list_add_rcu(struct list_head *new,
|
|
struct list_head *prev, struct list_head *next)
|
|
{
|
|
new->next = next;
|
|
new->prev = prev;
|
|
rcu_assign_pointer(prev->next, new);
|
|
next->prev = new;
|
|
}
|
|
|
|
/**
|
|
* list_add_rcu - add a new entry to rcu-protected list
|
|
* @new: new entry to be added
|
|
* @head: list head to add it after
|
|
*
|
|
* Insert a new entry after the specified head.
|
|
* This is good for implementing stacks.
|
|
*
|
|
* The caller must take whatever precautions are necessary
|
|
* (such as holding appropriate locks) to avoid racing
|
|
* with another list-mutation primitive, such as list_add_rcu()
|
|
* or list_del_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().
|
|
*/
|
|
static inline void list_add_rcu(struct list_head *new, struct list_head *head)
|
|
{
|
|
__list_add_rcu(new, head, head->next);
|
|
}
|
|
|
|
/**
|
|
* list_add_tail_rcu - add a new entry to rcu-protected list
|
|
* @new: new entry to be added
|
|
* @head: list head to add it before
|
|
*
|
|
* Insert a new entry before the specified head.
|
|
* This is useful for implementing queues.
|
|
*
|
|
* The caller must take whatever precautions are necessary
|
|
* (such as holding appropriate locks) to avoid racing
|
|
* with another list-mutation primitive, such as list_add_tail_rcu()
|
|
* or list_del_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().
|
|
*/
|
|
static inline void list_add_tail_rcu(struct list_head *new,
|
|
struct list_head *head)
|
|
{
|
|
__list_add_rcu(new, head->prev, head);
|
|
}
|
|
|
|
/**
|
|
* list_del_rcu - deletes entry from list without re-initialization
|
|
* @entry: the element to delete from the list.
|
|
*
|
|
* Note: list_empty() on entry does not return true after this,
|
|
* the entry is in an undefined state. It is useful for RCU based
|
|
* lockfree traversal.
|
|
*
|
|
* In particular, it means that we can not poison the forward
|
|
* 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_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 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_del_rcu(struct list_head *entry)
|
|
{
|
|
__list_del(entry->prev, entry->next);
|
|
entry->prev = LIST_POISON2;
|
|
}
|
|
|
|
/**
|
|
* hlist_del_init_rcu - deletes entry from hash list with re-initialization
|
|
* @n: the element to delete from the hash list.
|
|
*
|
|
* Note: list_unhashed() on the node return true after this. It is
|
|
* useful for RCU based read lockfree traversal if the writer side
|
|
* must know if the list entry is still hashed or already unhashed.
|
|
*
|
|
* In particular, it means that we can not poison the forward pointers
|
|
* that may still be used for walking the hash list and we can only
|
|
* zero the pprev pointer so list_unhashed() will return true after
|
|
* this.
|
|
*
|
|
* The caller must take whatever precautions are necessary (such as
|
|
* holding appropriate locks) to avoid racing with another
|
|
* list-mutation primitive, such as hlist_add_head_rcu() or
|
|
* hlist_del_rcu(), running on this same list. However, it is
|
|
* perfectly legal to run concurrently with the _rcu list-traversal
|
|
* primitives, such as hlist_for_each_entry_rcu().
|
|
*/
|
|
static inline void hlist_del_init_rcu(struct hlist_node *n)
|
|
{
|
|
if (!hlist_unhashed(n)) {
|
|
__hlist_del(n);
|
|
n->pprev = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* list_replace_rcu - replace old entry by new one
|
|
* @old : the element to be replaced
|
|
* @new : the new element to insert
|
|
*
|
|
* The @old entry will be replaced with the @new entry atomically.
|
|
* Note: @old should not be empty.
|
|
*/
|
|
static inline void list_replace_rcu(struct list_head *old,
|
|
struct list_head *new)
|
|
{
|
|
new->next = old->next;
|
|
new->prev = old->prev;
|
|
rcu_assign_pointer(new->prev->next, new);
|
|
new->next->prev = new;
|
|
old->prev = LIST_POISON2;
|
|
}
|
|
|
|
/**
|
|
* list_splice_init_rcu - splice an RCU-protected list into an existing list.
|
|
* @list: the RCU-protected list to splice
|
|
* @head: the place in the list to splice the first list into
|
|
* @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
|
|
*
|
|
* @head can be RCU-read traversed concurrently with this function.
|
|
*
|
|
* Note that this function blocks.
|
|
*
|
|
* Important note: the caller must take whatever action is necessary to
|
|
* prevent any other updates to @head. In principle, it is possible
|
|
* to modify the list as soon as sync() begins execution.
|
|
* If this sort of thing becomes necessary, an alternative version
|
|
* based on call_rcu() could be created. But only if -really-
|
|
* needed -- there is no shortage of RCU API members.
|
|
*/
|
|
static inline void list_splice_init_rcu(struct list_head *list,
|
|
struct list_head *head,
|
|
void (*sync)(void))
|
|
{
|
|
struct list_head *first = list->next;
|
|
struct list_head *last = list->prev;
|
|
struct list_head *at = head->next;
|
|
|
|
if (list_empty(head))
|
|
return;
|
|
|
|
/* "first" and "last" tracking list, so initialize it. */
|
|
|
|
INIT_LIST_HEAD(list);
|
|
|
|
/*
|
|
* At this point, the list body still points to the source list.
|
|
* Wait for any readers to finish using the list before splicing
|
|
* the list body into the new list. Any new readers will see
|
|
* an empty list.
|
|
*/
|
|
|
|
sync();
|
|
|
|
/*
|
|
* Readers are finished with the source list, so perform splice.
|
|
* The order is important if the new list is global and accessible
|
|
* to concurrent RCU readers. Note that RCU readers are not
|
|
* permitted to traverse the prev pointers without excluding
|
|
* this function.
|
|
*/
|
|
|
|
last->next = at;
|
|
rcu_assign_pointer(head->next, first);
|
|
first->prev = head;
|
|
at->prev = last;
|
|
}
|
|
|
|
#define __list_for_each_rcu(pos, head) \
|
|
for (pos = rcu_dereference((head)->next); \
|
|
pos != (head); \
|
|
pos = rcu_dereference(pos->next))
|
|
|
|
/**
|
|
* list_for_each_entry_rcu - iterate over rcu list of given type
|
|
* @pos: the type * to use as a loop cursor.
|
|
* @head: the head for your list.
|
|
* @member: the name of the list_struct within the struct.
|
|
*
|
|
* This list-traversal primitive may safely run concurrently with
|
|
* the _rcu list-mutation primitives such as list_add_rcu()
|
|
* as long as the traversal is guarded by rcu_read_lock().
|
|
*/
|
|
#define list_for_each_entry_rcu(pos, head, member) \
|
|
for (pos = list_entry(rcu_dereference((head)->next), typeof(*pos), member); \
|
|
prefetch(pos->member.next), &pos->member != (head); \
|
|
pos = list_entry(rcu_dereference(pos->member.next), typeof(*pos), member))
|
|
|
|
|
|
/**
|
|
* list_for_each_continue_rcu
|
|
* @pos: the &struct list_head to use as a loop cursor.
|
|
* @head: the head for your list.
|
|
*
|
|
* Iterate over an rcu-protected list, continuing after current point.
|
|
*
|
|
* This list-traversal primitive may safely run concurrently with
|
|
* the _rcu list-mutation primitives such as list_add_rcu()
|
|
* as long as the traversal is guarded by rcu_read_lock().
|
|
*/
|
|
#define list_for_each_continue_rcu(pos, head) \
|
|
for ((pos) = rcu_dereference((pos)->next); \
|
|
prefetch((pos)->next), (pos) != (head); \
|
|
(pos) = rcu_dereference((pos)->next))
|
|
|
|
/**
|
|
* hlist_del_rcu - deletes entry from hash list without re-initialization
|
|
* @n: the element to delete from the hash list.
|
|
*
|
|
* Note: list_unhashed() on entry does not return true after this,
|
|
* the entry is in an undefined state. It is useful for RCU based
|
|
* lockfree traversal.
|
|
*
|
|
* In particular, it means that we can not poison the forward
|
|
* pointers that may still be used for walking the hash list.
|
|
*
|
|
* The caller must take whatever precautions are necessary
|
|
* (such as holding appropriate locks) to avoid racing
|
|
* with another list-mutation primitive, such as hlist_add_head_rcu()
|
|
* or hlist_del_rcu(), running on this same list.
|
|
* However, it is perfectly legal to run concurrently with
|
|
* the _rcu list-traversal primitives, such as
|
|
* hlist_for_each_entry().
|
|
*/
|
|
static inline void hlist_del_rcu(struct hlist_node *n)
|
|
{
|
|
__hlist_del(n);
|
|
n->pprev = LIST_POISON2;
|
|
}
|
|
|
|
/**
|
|
* hlist_replace_rcu - replace old entry by new one
|
|
* @old : the element to be replaced
|
|
* @new : the new element to insert
|
|
*
|
|
* The @old entry will be replaced with the @new entry atomically.
|
|
*/
|
|
static inline void hlist_replace_rcu(struct hlist_node *old,
|
|
struct hlist_node *new)
|
|
{
|
|
struct hlist_node *next = old->next;
|
|
|
|
new->next = next;
|
|
new->pprev = old->pprev;
|
|
rcu_assign_pointer(*new->pprev, new);
|
|
if (next)
|
|
new->next->pprev = &new->next;
|
|
old->pprev = LIST_POISON2;
|
|
}
|
|
|
|
/**
|
|
* hlist_add_head_rcu
|
|
* @n: the element to add to the hash list.
|
|
* @h: the list to add to.
|
|
*
|
|
* Description:
|
|
* Adds the specified element to the specified hlist,
|
|
* while permitting racing traversals.
|
|
*
|
|
* The caller must take whatever precautions are necessary
|
|
* (such as holding appropriate locks) to avoid racing
|
|
* with another list-mutation primitive, such as hlist_add_head_rcu()
|
|
* or hlist_del_rcu(), running on this same list.
|
|
* However, it is perfectly legal to run concurrently with
|
|
* the _rcu list-traversal primitives, such as
|
|
* hlist_for_each_entry_rcu(), used to prevent memory-consistency
|
|
* problems on Alpha CPUs. Regardless of the type of CPU, the
|
|
* list-traversal primitive must be guarded by rcu_read_lock().
|
|
*/
|
|
static inline void hlist_add_head_rcu(struct hlist_node *n,
|
|
struct hlist_head *h)
|
|
{
|
|
struct hlist_node *first = h->first;
|
|
|
|
n->next = first;
|
|
n->pprev = &h->first;
|
|
rcu_assign_pointer(h->first, n);
|
|
if (first)
|
|
first->pprev = &n->next;
|
|
}
|
|
|
|
/**
|
|
* hlist_add_before_rcu
|
|
* @n: the new element to add to the hash list.
|
|
* @next: the existing element to add the new element before.
|
|
*
|
|
* Description:
|
|
* Adds the specified element to the specified hlist
|
|
* before the specified node while permitting racing traversals.
|
|
*
|
|
* The caller must take whatever precautions are necessary
|
|
* (such as holding appropriate locks) to avoid racing
|
|
* with another list-mutation primitive, such as hlist_add_head_rcu()
|
|
* or hlist_del_rcu(), running on this same list.
|
|
* However, it is perfectly legal to run concurrently with
|
|
* the _rcu list-traversal primitives, such as
|
|
* hlist_for_each_entry_rcu(), used to prevent memory-consistency
|
|
* problems on Alpha CPUs.
|
|
*/
|
|
static inline void hlist_add_before_rcu(struct hlist_node *n,
|
|
struct hlist_node *next)
|
|
{
|
|
n->pprev = next->pprev;
|
|
n->next = next;
|
|
rcu_assign_pointer(*(n->pprev), n);
|
|
next->pprev = &n->next;
|
|
}
|
|
|
|
/**
|
|
* hlist_add_after_rcu
|
|
* @prev: the existing element to add the new element after.
|
|
* @n: the new element to add to the hash list.
|
|
*
|
|
* Description:
|
|
* Adds the specified element to the specified hlist
|
|
* after the specified node while permitting racing traversals.
|
|
*
|
|
* The caller must take whatever precautions are necessary
|
|
* (such as holding appropriate locks) to avoid racing
|
|
* with another list-mutation primitive, such as hlist_add_head_rcu()
|
|
* or hlist_del_rcu(), running on this same list.
|
|
* However, it is perfectly legal to run concurrently with
|
|
* the _rcu list-traversal primitives, such as
|
|
* hlist_for_each_entry_rcu(), used to prevent memory-consistency
|
|
* problems on Alpha CPUs.
|
|
*/
|
|
static inline void hlist_add_after_rcu(struct hlist_node *prev,
|
|
struct hlist_node *n)
|
|
{
|
|
n->next = prev->next;
|
|
n->pprev = &prev->next;
|
|
rcu_assign_pointer(prev->next, n);
|
|
if (n->next)
|
|
n->next->pprev = &n->next;
|
|
}
|
|
|
|
/**
|
|
* hlist_for_each_entry_rcu - iterate over rcu list of given type
|
|
* @tpos: the type * to use as a loop cursor.
|
|
* @pos: the &struct hlist_node to use as a loop cursor.
|
|
* @head: the head for your list.
|
|
* @member: the name of the hlist_node within the struct.
|
|
*
|
|
* This list-traversal primitive may safely run concurrently with
|
|
* the _rcu list-mutation primitives such as hlist_add_head_rcu()
|
|
* as long as the traversal is guarded by rcu_read_lock().
|
|
*/
|
|
#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
|
|
for (pos = rcu_dereference((head)->first); \
|
|
pos && ({ prefetch(pos->next); 1; }) && \
|
|
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
|
|
pos = rcu_dereference(pos->next))
|
|
|
|
#endif /* __KERNEL__ */
|
|
#endif
|