|
| 1 | +#ifndef _LINUX_RCULIST_BL_H |
| 2 | +#define _LINUX_RCULIST_BL_H |
| 3 | + |
| 4 | +/* |
| 5 | + * RCU-protected bl list version. See include/linux/list_bl.h. |
| 6 | + */ |
| 7 | +#include <linux/list_bl.h> |
| 8 | +#include <linux/rcupdate.h> |
| 9 | + |
| 10 | +static inline void hlist_bl_set_first_rcu(struct hlist_bl_head *h, |
| 11 | + struct hlist_bl_node *n) |
| 12 | +{ |
| 13 | + LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK); |
| 14 | + LIST_BL_BUG_ON(!((unsigned long)h->first & LIST_BL_LOCKMASK)); |
| 15 | + rcu_assign_pointer(h->first, |
| 16 | + (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK)); |
| 17 | +} |
| 18 | + |
| 19 | +static inline struct hlist_bl_node *hlist_bl_first_rcu(struct hlist_bl_head *h) |
| 20 | +{ |
| 21 | + return (struct hlist_bl_node *) |
| 22 | + ((unsigned long)rcu_dereference(h->first) & ~LIST_BL_LOCKMASK); |
| 23 | +} |
| 24 | + |
| 25 | +/** |
| 26 | + * hlist_bl_del_init_rcu - deletes entry from hash list with re-initialization |
| 27 | + * @n: the element to delete from the hash list. |
| 28 | + * |
| 29 | + * Note: hlist_bl_unhashed() on the node returns true after this. It is |
| 30 | + * useful for RCU based read lockfree traversal if the writer side |
| 31 | + * must know if the list entry is still hashed or already unhashed. |
| 32 | + * |
| 33 | + * In particular, it means that we can not poison the forward pointers |
| 34 | + * that may still be used for walking the hash list and we can only |
| 35 | + * zero the pprev pointer so list_unhashed() will return true after |
| 36 | + * this. |
| 37 | + * |
| 38 | + * The caller must take whatever precautions are necessary (such as |
| 39 | + * holding appropriate locks) to avoid racing with another |
| 40 | + * list-mutation primitive, such as hlist_bl_add_head_rcu() or |
| 41 | + * hlist_bl_del_rcu(), running on this same list. However, it is |
| 42 | + * perfectly legal to run concurrently with the _rcu list-traversal |
| 43 | + * primitives, such as hlist_bl_for_each_entry_rcu(). |
| 44 | + */ |
| 45 | +static inline void hlist_bl_del_init_rcu(struct hlist_bl_node *n) |
| 46 | +{ |
| 47 | + if (!hlist_bl_unhashed(n)) { |
| 48 | + __hlist_bl_del(n); |
| 49 | + n->pprev = NULL; |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +/** |
| 54 | + * hlist_bl_del_rcu - deletes entry from hash list without re-initialization |
| 55 | + * @n: the element to delete from the hash list. |
| 56 | + * |
| 57 | + * Note: hlist_bl_unhashed() on entry does not return true after this, |
| 58 | + * the entry is in an undefined state. It is useful for RCU based |
| 59 | + * lockfree traversal. |
| 60 | + * |
| 61 | + * In particular, it means that we can not poison the forward |
| 62 | + * pointers that may still be used for walking the hash list. |
| 63 | + * |
| 64 | + * The caller must take whatever precautions are necessary |
| 65 | + * (such as holding appropriate locks) to avoid racing |
| 66 | + * with another list-mutation primitive, such as hlist_bl_add_head_rcu() |
| 67 | + * or hlist_bl_del_rcu(), running on this same list. |
| 68 | + * However, it is perfectly legal to run concurrently with |
| 69 | + * the _rcu list-traversal primitives, such as |
| 70 | + * hlist_bl_for_each_entry(). |
| 71 | + */ |
| 72 | +static inline void hlist_bl_del_rcu(struct hlist_bl_node *n) |
| 73 | +{ |
| 74 | + __hlist_bl_del(n); |
| 75 | + n->pprev = LIST_POISON2; |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * hlist_bl_add_head_rcu |
| 80 | + * @n: the element to add to the hash list. |
| 81 | + * @h: the list to add to. |
| 82 | + * |
| 83 | + * Description: |
| 84 | + * Adds the specified element to the specified hlist_bl, |
| 85 | + * while permitting racing traversals. |
| 86 | + * |
| 87 | + * The caller must take whatever precautions are necessary |
| 88 | + * (such as holding appropriate locks) to avoid racing |
| 89 | + * with another list-mutation primitive, such as hlist_bl_add_head_rcu() |
| 90 | + * or hlist_bl_del_rcu(), running on this same list. |
| 91 | + * However, it is perfectly legal to run concurrently with |
| 92 | + * the _rcu list-traversal primitives, such as |
| 93 | + * hlist_bl_for_each_entry_rcu(), used to prevent memory-consistency |
| 94 | + * problems on Alpha CPUs. Regardless of the type of CPU, the |
| 95 | + * list-traversal primitive must be guarded by rcu_read_lock(). |
| 96 | + */ |
| 97 | +static inline void hlist_bl_add_head_rcu(struct hlist_bl_node *n, |
| 98 | + struct hlist_bl_head *h) |
| 99 | +{ |
| 100 | + struct hlist_bl_node *first; |
| 101 | + |
| 102 | + /* don't need hlist_bl_first_rcu because we're under lock */ |
| 103 | + first = hlist_bl_first(h); |
| 104 | + |
| 105 | + n->next = first; |
| 106 | + if (first) |
| 107 | + first->pprev = &n->next; |
| 108 | + n->pprev = &h->first; |
| 109 | + |
| 110 | + /* need _rcu because we can have concurrent lock free readers */ |
| 111 | + hlist_bl_set_first_rcu(h, n); |
| 112 | +} |
| 113 | +/** |
| 114 | + * hlist_bl_for_each_entry_rcu - iterate over rcu list of given type |
| 115 | + * @tpos: the type * to use as a loop cursor. |
| 116 | + * @pos: the &struct hlist_bl_node to use as a loop cursor. |
| 117 | + * @head: the head for your list. |
| 118 | + * @member: the name of the hlist_bl_node within the struct. |
| 119 | + * |
| 120 | + */ |
| 121 | +#define hlist_bl_for_each_entry_rcu(tpos, pos, head, member) \ |
| 122 | + for (pos = hlist_bl_first_rcu(head); \ |
| 123 | + pos && \ |
| 124 | + ({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1; }); \ |
| 125 | + pos = rcu_dereference_raw(pos->next)) |
| 126 | + |
| 127 | +#endif |
0 commit comments