找回密码
 立即注册
搜索
热搜: 活动 通知
查看: 53|回复: 0

关于linux内核的list.h中INIT_LIST_HEAD()中的WRITE_ONCE()的讨论

[复制链接]

3

主题

16

回帖

41

积分

至尊会员

积分
41

至尊会员

发表于 5 天前 | 显示全部楼层 |阅读模式
当前linux内核的主线的/include/linux/list.h的INIT_LIST_HEAD()的定义是这样的:
  1. /**
  2. * INIT_LIST_HEAD - Initialize a list_head structure
  3. * @list: list_head structure to be initialized.
  4. *
  5. * Initializes the list_head to point to itself.  If it is a list header,
  6. * the result is an empty list.
  7. */
  8. static inline void INIT_LIST_HEAD(struct list_head *list)
  9. {
  10.     WRITE_ONCE(list->next, list);
  11.     WRITE_ONCE(list->prev, list);
  12. }
复制代码


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;
}

我看了一下提交者的理由,自己想了一个例子来说明,如下:
  1. //CPU0和CPU1并发访问共享变量entry,且无锁
  2. //CPU0
  3. list_del_init(entry);
  4. do_something();          // 期间把 entry 指针写到一个全局可见的指针中
  5.                          // 或者释放了自旋锁,而其他 CPU 在等待
  6. // 其他 CPU 现在可以无锁地访问 entry

  7. //内联展开后
  8. __list_del_entry(entry);
  9. INIT_LIST_HEAD(entry);
  10. do_something();
  11. //内联继续展开
  12. __list_del_entry(entry);
  13. WRITE_ONCE(list->next, list);
  14. WRITE_ONCE(list->prev, list);
  15. do_something();

  16. //假设没有WRITE_ONCE,CPU0执行
  17. __list_del_entry(entry);
  18. list->next=list;
  19. list->prev=list;
  20. do_something();
  21. //假设do_something()的执行和entry无关,编译器可以这样重排
  22. __list_del_entry(entry);
  23. do_something();
  24. entry->next=entry;
  25. entry->prev=entry;

  26. // CPU1
  27. if (list_empty(entry)) {
  28.     //...
  29. }
  30. //展开后
  31. if (READ_ONCE(entry->next) == entry) {
  32.     //...
  33. }
  34. //假设没有READ_ONCE,CPU1执行
  35. if (entry->next == entry) { //无锁判空
  36.     //...
  37. }

  38. //最终编译器优化的结果
  39. //CPU0
  40. __list_del_entry(entry);
  41. do_something();
  42. entry->next=entry;
  43. entry->prev=entry;
  44. //CPU1
  45. if (entry->next == entry) { //无锁判空
  46.     //...
  47. }

  48. //并发执行
  49. CPU0执行__list_del_entry(entry); 和 do_something();
  50.             |
  51.             |
  52.             ↓
  53. CPU1执行if (entry->next == entry) { //无锁判空
  54.     //...
  55. }
  56.             |
  57.             |
  58.             ↓
  59. CPU1发现entry仍然是旧值,原因是CPU0尚未执行
  60. entry->next=entry;和entry->prev=entry;
  61.             |
  62.             |
  63.             ↓
  64. CPU1无锁判空失败!!!
复制代码


不知道我举的例子是否正确,向大家请教





您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|鼠侠网 ( 吉ICP备19001332号 )

GMT+8, 2026-7-13 20:44 , Processed in 0.241637 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表