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() には 1 つしか必要ないのに、hlist_for_each_entry_rcu() に 2 つのポインターを渡す必要があるのはなぜですか?

回答: hlist では、先頭に遭遇するのではなく、NULL をチェックする必要があるためです。(単一ポインタ hlist_for_each_entry_rcu() をコーディングしてみてください。いい解決策を思いついたら、それはとても良いことです!)


hlist_for_each_entry_rcu() の古いバージョンを参照する必要があると思います。rculist.hヘッダーの現在のバージョン (3.13.0) は、実際には 3 つの引数を取る定義を示しているhlist_for_each_entry_rcuため、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

私も同じことを疑問に思いました!ソースを掘り下げると、4 つの引数のバージョンが 3.8 の間のどこかで 3 つの引数のバージョンに置き換えられたようです ( 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 )

2 つのマクロを比較するために、古い 4 つの引数のバージョンを次に示します。

#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)))

そして、これが新しい 3 つの引数のバージョンです。

#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あり、式である可能性があり、その式は複数回評価されるべきではないため、これは明らかに不可能です。例: は2 回評価されるため、明白な解決策 – #define hlist_entry_safe(ptr, member) ((ptr)? (ptr)->member : NULL)– は機能しません。(ptr)

この回答に基づいて、3.9 – … ({ typeof(ptr) ____ptr = (ptr); … })– で使用されている構文は GCC のみの拡張であると想定しています。

于 2016-11-03T04:55:43.943 に答える