【问题标题】:Does hlist_for_each_entry_rcu need additional pointer to be passed into it?hlist_for_each_entry_rcu 是否需要将额外的指针传递给它?
【发布时间】:2015-08-27 19:46:46
【问题描述】:

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:为什么我们需要将两个指针传递给 hlist_for_each_entry_rcu() 而只需要一个 list_for_each_entry_rcu()?

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


我认为它必须引用旧版本的 hlist_for_each_entry_rcu(),因为 rculist.h 标头中的当前版本 (3.13.0) 实际上呈现了 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)); \

【问题讨论】:

  • 这个问题应该在代码审查或类似的地方讨论吗? codereview.stackexchange.com
  • 拜托,在这里给它一个机会
  • 更改看起来只是指向指针技巧的指针,用于避免哨兵节点或单链表的特殊情况,但我不确定 RCU 的注意事项。

标签: c algorithm list linux-kernel rcu


【解决方案1】:

我想知道同样的事情!深入研究源代码,似乎四参数版本被 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) 将被评估两次。

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

【讨论】:

    猜你喜欢
    • 2012-07-15
    • 2015-12-21
    • 1970-01-01
    • 2012-08-22
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    相关资源
    最近更新 更多