2

LWN在 RCU 上给出了以下示例:

订阅受 RCU 保护的 hlist 也类似于循环列表:

  1 rcu_read_lock();
  2 hlist_for_each_entry_rcu(p, q, head, list) {
  3   do_something_with(p->a, p->b, p->c);
  4 }
  5 rcu_read_unlock();

快速测验 3:当 list_for_each_entry_rcu() 只需要一个指针时,为什么我们需要将两个指针传递给 hlist_for_each_entry_rcu()?

回答: 因为在 hlist 中需要检查 NULL 而不是遇到头部。(尝试编写一个单指针 hlist_for_each_entry_rcu()。如果你想出了一个很好的解决方案,那将是一件非常好的事情!)


我认为它必须引用旧版本的 hlist_for_each_entry_rcu() 因为当前版本(3.13.0)在rculist.h标题中实际上提出了hlist_for_each_entry_rcu采用 3 个参数的定义,因此不需要额外的第 4 个指针并且似乎并不难发明:

#define hlist_for_each_entry_rcu(pos, head, member)         \
    for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\
            typeof(*(pos)), member);            \
        pos;                            \
        pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(\
            &(pos)->member)), typeof(*(pos)), member))

我是否遗漏了当前 rculist.h 中给出的某些内容或更高版本是一件非常好的事情

我们可以看到在__rcu_dereference_check创建附加指针的地方发生了一些微妙的事情:

#define rcu_dereference_raw(p) rcu_dereference_check(p, 1) /*@@@ needed? @@@*/


#define rcu_dereference_check(p, c) \
    __rcu_dereference_check((p), rcu_read_lock_held() || (c), __rcu)

#define __rcu_dereference_check(p, c, space) \
    ({ \
        typeof(*p) *_________p1 = (typeof(*p)*__force )ACCESS_ONCE(p); \
        rcu_lockdep_assert(c, "suspicious rcu_dereference_check()" \
                      " usage"); \
        rcu_dereference_sparse(p, space); \
        smp_read_barrier_depends(); \
        ((typeof(*p) __force __kernel *)(_________p1)); \
4

1 回答 1

2

我想知道同样的事情!深入研究源代码,似乎四参数版本被 3.8 之间的三参数版本取代(https://github.com/torvalds/linux/blob/v3.8/include/linux/rculist.h# L457)和 3.9(https://github.com/torvalds/linux/blob/v3.9/include/linux/rculist.h#L456

为了比较这两个宏,这里是旧的四个参数版本:

#define hlist_for_each_entry_rcu(tpos, pos, head, member)       \
    for (pos = rcu_dereference_raw(hlist_first_rcu(head));      \
        pos &&                           \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
        pos = rcu_dereference_raw(hlist_next_rcu(pos)))

这是新的三参数版本:

#define hlist_for_each_entry_rcu(pos, head, member)         \
    for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\
        typeof(*(pos)), member);            \
        pos;                            \
        pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(\
            &(pos)->member)), typeof(*(pos)), member))

所以看起来关键的区别是hlist_entry_safe宏,基本上是ptr? ptr->member : NULL.

这显然是不可能的,因为hlist_entry_safe它是一个宏,所以ptr可能是一个表达式,并且该表达式不应被多次评估。例如,显而易见的解决方案 - #define hlist_entry_safe(ptr, member) ((ptr)? (ptr)->member : NULL)- 将不起作用,因为(ptr)将被评估两次。

基于这个答案,我假设 3.9 中使用的语法 – … ({ typeof(ptr) ____ptr = (ptr); … })– 是一个仅限 GCC 的扩展,这可能解释了为什么在撰写本文时它是不可能的。

于 2016-11-03T04:55:43.943 回答