From: "Paul E. McKenney" The attached patch improves the documentation of the _rcu list primitives. --- 25-akpm/include/linux/list.h | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 73 insertions(+) diff -puN include/linux/list.h~rcu_list-documentation include/linux/list.h --- 25/include/linux/list.h~rcu_list-documentation 2004-04-06 20:43:01.601847960 -0700 +++ 25-akpm/include/linux/list.h 2004-04-06 20:43:01.605847352 -0700 @@ -104,6 +104,14 @@ static __inline__ void __list_add_rcu(st * * 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) { @@ -117,6 +125,14 @@ static __inline__ void list_add_rcu(stru * * 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) { @@ -159,6 +175,19 @@ static inline void list_del(struct list_ * * 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_kernel() + * 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) { @@ -384,6 +413,10 @@ static inline void list_splice_init(stru * list_for_each_rcu - iterate over an rcu-protected list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. + * + * 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_rcu(pos, head) \ for (pos = (head)->next, prefetch(pos->next); pos != (head); \ @@ -399,6 +432,10 @@ static inline void list_splice_init(stru * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. + * + * 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_safe_rcu(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ @@ -409,6 +446,10 @@ static inline void list_splice_init(stru * @pos: the type * to use as a loop counter. * @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((head)->next, typeof(*pos), member), \ @@ -424,6 +465,10 @@ static inline void list_splice_init(stru * continuing after existing point. * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. + * + * 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) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ @@ -485,6 +530,14 @@ static __inline__ void hlist_del(struct * * 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) { @@ -512,6 +565,26 @@ static __inline__ void hlist_add_head(st n->pprev = &h->first; } + +/** + * hlist_add_head_rcu - adds the specified element to the specified hlist, + * while permitting racing traversals. + * @n: the element to add to the hash list. + * @h: the list to add to. + * + * 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(), but only if smp_read_barrier_depends() + * is 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(). + * + * OK, so why don't we have an hlist_for_each_entry_rcu()??? + */ static __inline__ void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; _