Skip to content

Commit 7f5f873

Browse files
committed
rculist: Use WRITE_ONCE() when deleting from reader-visible list
The various RCU list-deletion macros (list_del_rcu(), hlist_del_init_rcu(), hlist_del_rcu(), hlist_bl_del_init_rcu(), hlist_bl_del_rcu(), hlist_nulls_del_init_rcu(), and hlist_nulls_del_rcu()) do plain stores into the ->next pointer of the preceding list elemment. Unfortunately, the compiler is within its rights to (for example) use byte-at-a-time writes to update the pointer, which would fatally confuse concurrent readers. This patch therefore adds the needed WRITE_ONCE() macros. KernelThreadSanitizer (KTSAN) reported the __hlist_del() issue, which is a problem when __hlist_del() is invoked by hlist_del_rcu(). Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
1 parent e62e3f6 commit 7f5f873

File tree

3 files changed

+8
-5
lines changed

3 files changed

+8
-5
lines changed

include/linux/list.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head)
8787
static inline void __list_del(struct list_head * prev, struct list_head * next)
8888
{
8989
next->prev = prev;
90-
prev->next = next;
90+
WRITE_ONCE(prev->next, next);
9191
}
9292

9393
/**
@@ -615,7 +615,8 @@ static inline void __hlist_del(struct hlist_node *n)
615615
{
616616
struct hlist_node *next = n->next;
617617
struct hlist_node **pprev = n->pprev;
618-
*pprev = next;
618+
619+
WRITE_ONCE(*pprev, next);
619620
if (next)
620621
next->pprev = pprev;
621622
}

include/linux/list_bl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ static inline void __hlist_bl_del(struct hlist_bl_node *n)
9393
LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
9494

9595
/* pprev may be `first`, so be careful not to lose the lock bit */
96-
*pprev = (struct hlist_bl_node *)
96+
WRITE_ONCE(*pprev,
97+
(struct hlist_bl_node *)
9798
((unsigned long)next |
98-
((unsigned long)*pprev & LIST_BL_LOCKMASK));
99+
((unsigned long)*pprev & LIST_BL_LOCKMASK)));
99100
if (next)
100101
next->pprev = pprev;
101102
}

include/linux/list_nulls.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ static inline void __hlist_nulls_del(struct hlist_nulls_node *n)
7676
{
7777
struct hlist_nulls_node *next = n->next;
7878
struct hlist_nulls_node **pprev = n->pprev;
79-
*pprev = next;
79+
80+
WRITE_ONCE(*pprev, next);
8081
if (!is_a_nulls(next))
8182
next->pprev = pprev;
8283
}

0 commit comments

Comments
 (0)