15分カーネルソースリーディング!(percpu編)

現在、Linux Kernel Developmentを読んでいるが、英語力のなさで256ページに書かれている
DEFINE_PER_CPU(type,name)とDECLARE_PER_CPU(type,name)の違いが良く分からなかった。
よって、コードを読んでみた。

これらの定義はinclude/linux/percpu-defs.hに存在する。

/*
 * Variant on the per-CPU variable declaration/definition theme used for
 * ordinary per-CPU variables.
 */
#define DECLARE_PER_CPU(type, name)         \
  DECLARE_PER_CPU_SECTION(type, name, "")

#define DEFINE_PER_CPU(type, name)          \
  DEFINE_PER_CPU_SECTION(type, name, "")

さらにDECLARE_PER_CPU_SECTIONとDEFINE_PER_CPU_SECTIONを追ってみると、同じくinclude/linux/percpu-defs.hで以下のように定義されている。

#if defined(ARCH_NEEDS_WEAK_PER_CPU) || defined(CONFIG_DEBUG_FORCE_WEAK_PER_CPU)
/*
 * __pcpu_scope_* dummy variable is used to enforce scope.  It
 * receives the static modifier when it's used in front of
 * DEFINE_PER_CPU() and will trigger build failure if
 * DECLARE_PER_CPU() is used for the same variable.
 *
 * __pcpu_unique_* dummy variable is used to enforce symbol uniqueness
 * such that hidden weak symbol collision, which will cause unrelated
 * variables to share the same address, can be detected during build.
 */
#define DECLARE_PER_CPU_SECTION(type, name, sec)      \
  extern __PCPU_DUMMY_ATTRS char __pcpu_scope_##name;   \
  extern __PCPU_ATTRS(sec) __typeof__(type) name

#define DEFINE_PER_CPU_SECTION(type, name, sec)       \
  __PCPU_DUMMY_ATTRS char __pcpu_scope_##name;      \
  extern __PCPU_DUMMY_ATTRS char __pcpu_unique_##name;    \
  __PCPU_DUMMY_ATTRS char __pcpu_unique_##name;     \
  __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES __weak     \
  __typeof__(type) name
#else
/*
 * Normal declaration and definition macros.
 */
#define DECLARE_PER_CPU_SECTION(type, name, sec)      \
  extern __PCPU_ATTRS(sec) __typeof__(type) name

#define DEFINE_PER_CPU_SECTION(type, name, sec)       \
  __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES      \
  __typeof__(type) name
#endif

ARCH_NEEDS_WEAK_PER_CPUは何だろうか。
コメントによると、”s390 and alpha modules require percpu variables...”と書かれている。
実際にARCH_NEEDS_WEAK_PER_CPUを定義しているのは
arch/alpha/include/asm/percpu.h
arch/s390/include/asm/percpu.h
だけである。

なので、今回は多くのアーキテクチャで通用し、かつ簡単そうな#else側を追ってみることにする。

#else側を見る限り、
DECLARE_PER_CPUは既に定義されているpercpu変数をexternしてくる。
DEFINE_PER_CPUはまさにpercpu変数の実体を宣言(なんか紛らわしい)する。
ということが言えそうである。なんだ、はっきりそう書いてくれればいいのに。

これで一件落着。
...けど、せっかくだからさらにこれらの定義に深入りして調べてみることにする。

まず__PCPU_ATTRSから。
同じくinclude/linux/percpu-defs.hで以下の様に定義。

#define __PCPU_ATTRS(sec)           \
  __percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) \
  PER_CPU_ATTRIBUTES

__percpuはinclude/linux/compiler.hで

#ifdef __CHECKER__
(略)
# define __percpu __attribute__((noderef, address_space(3)))
(略)
#else
(略)
# define __percpu
(略)

と定義されている。
__CHECKER__マクロは現状存在しないので、__percpuは空定義ということになる。

次にPER_CPU_BASE_SECTION。include/asm-generic/percpu.hで定義されており、CONFIG_SMPなカーネルでは.data.percpuとなっており、そうでない場合は単なる.dataとなっている。

#ifdef CONFIG_SMP
#define PER_CPU_BASE_SECTION ".data.percpu"
#else
#define PER_CPU_BASE_SECTION ".data"
#endif

※ia64の場合は、arch/ia64/include/asm/percpu.h内で常にPER_CPU_BASE_SECTION ".data.percpu"と定義されるようだ。

どうやら、変数が格納されるセクション名が定義されているようだ。

PER_CPU_ATTRIBUTESは
arch/ia64/include/asm/percpu.hの場合のみ

# define PER_CPU_ATTRIBUTES __attribute__((__model__ (__small__)))

となるが、それ以外は空定義。なので、ここでは空定義で押し通す。

最後に

__attribute__((section(name)) valiable 

とは何か?
これはgccでサポートしている変数属性で、nameと言う名前のセクションにvariableという変数を配置するためのものである。

つまり、大体のプロセッサにおけるCONFIG_SMPなカーネルではDEFINE_PER_CPU_SECTION(type,name)とは、
type型のnameという変数名の変数を.data.percpuセクションに配置する
ことを意味する。
そうでないシングルプロセッサオンリーなカーネルでは、
type型のnameという変数名の変数を.dataセクションに配置する
ことを意味する。
まずはここまで。
.data.percpuが実際どのようにカーネルが運用しているのか、そこを調べてみるつもりだ。
やっぱり、カーネルソース読むのは楽しいな★