|
|
当前linux内核的主线的/include/linux/list.h的INIT_LIST_HEAD()的定义是这样的:
- /**
- * INIT_LIST_HEAD - Initialize a list_head structure
- * @list: list_head structure to be initialized.
- *
- * Initializes the list_head to point to itself. If it is a list header,
- * the result is an empty list.
- */
- static inline void INIT_LIST_HEAD(struct list_head *list)
- {
- WRITE_ONCE(list->next, list);
- WRITE_ONCE(list->prev, list);
- }
复制代码
WRITE_ONCE(list->next, list)等同于list->next=list;
WRITE_ONCE(list->prev, list)等同于list->prev=list;
据我了解,加了WRITE_ONCE()是为了防止编译器自作主张进行重排
第一个WRITE_ONCE的提交链接: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2f073848c3cc8aff2655ab7c46d8c0de90cf4e50
list: Use WRITE_ONCE() when initializing list_head structures
Code that does lockless emptiness testing of non-RCU lists is relying
on INIT_LIST_HEAD() to write the list head's ->next pointer atomically,
particularly when INIT_LIST_HEAD() is invoked from list_del_init().
This commit therefore adds WRITE_ONCE() to this function's pointer stores
that could affect the head's ->next pointer.
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat
-rw-r--r-- include/linux/list.h 2
1 files changed, 1 insertions, 1 deletions
diff --git a/include/linux/list.h b/include/linux/list.h
index 06c2d887a9188..5356f4d661a72 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -24,7 +24,7 @@
static inline void INIT_LIST_HEAD(struct list_head *list)
{
- list->next = list;
+ WRITE_ONCE(list->next, list);
list->prev = list;
}
我看了一下提交者的理由,自己想了一个例子来说明,如下:
- //CPU0和CPU1并发访问共享变量entry,且无锁
- //CPU0
- list_del_init(entry);
- do_something(); // 期间把 entry 指针写到一个全局可见的指针中
- // 或者释放了自旋锁,而其他 CPU 在等待
- // 其他 CPU 现在可以无锁地访问 entry
- //内联展开后
- __list_del_entry(entry);
- INIT_LIST_HEAD(entry);
- do_something();
- //内联继续展开
- __list_del_entry(entry);
- WRITE_ONCE(list->next, list);
- WRITE_ONCE(list->prev, list);
- do_something();
- //假设没有WRITE_ONCE,CPU0执行
- __list_del_entry(entry);
- list->next=list;
- list->prev=list;
- do_something();
- //假设do_something()的执行和entry无关,编译器可以这样重排
- __list_del_entry(entry);
- do_something();
- entry->next=entry;
- entry->prev=entry;
- // CPU1
- if (list_empty(entry)) {
- //...
- }
- //展开后
- if (READ_ONCE(entry->next) == entry) {
- //...
- }
- //假设没有READ_ONCE,CPU1执行
- if (entry->next == entry) { //无锁判空
- //...
- }
- //最终编译器优化的结果
- //CPU0
- __list_del_entry(entry);
- do_something();
- entry->next=entry;
- entry->prev=entry;
- //CPU1
- if (entry->next == entry) { //无锁判空
- //...
- }
- //并发执行
- CPU0执行__list_del_entry(entry); 和 do_something();
- |
- |
- ↓
- CPU1执行if (entry->next == entry) { //无锁判空
- //...
- }
- |
- |
- ↓
- CPU1发现entry仍然是旧值,原因是CPU0尚未执行
- entry->next=entry;和entry->prev=entry;
- |
- |
- ↓
- CPU1无锁判空失败!!!
复制代码
不知道我举的例子是否正确,向大家请教
|
|