CPU変数
Rev.5を表示中。最新版はこちら。
mount構造体でSMPのCPM変数の取り扱いです。mnt_countはマウントされている数で、mnt_writersはこのファイルシステムを書き込み処理している数です。struct mountのmnt_count/mnt_writersは下記のように展開され、SMPの時はmnt_count/mnt_writersの構造体のポインタとスピンロックの展開となっています。
struct mnt_pcp {
int mnt_count;
int mnt_writers;
};
struct mount {
:
#ifdef CONFIG_SMP
struct mnt_pcp __percpu *mnt_pcp;
atomic_t mnt_longterm; /* how many of the refs are longterm */
#else
int mnt_count;
int mnt_writers;
#endif
:
};
処理において頻繁に参照される変数は、このようにCPU毎に変数を持たせる事で、ロックを介さないで処理できるようにしています。mount構造体の取得時、SMPでない時は、mnt->mnt_count = 1/mnt->mnt_writers = 0としていますが、SMPの時はalloc_percpu()でthis_cpu_add()となっています。alloc_percpu()はstruct mnt_pcpをCPUの数分特定の領域からメモリーを確保します。それ故struct mountにおいてポインタとして宣言する必要があるわけです。this_cpu_add()は、上で確保したメモリの実行しているCPUのインデックスに対応するstruct mnt_pcp[]内のmnt_countに1を加算します。
このようにCPU毎に変数を割り当てると、他のCPUから参照される事はなく、この変数の更新においてスピンロックをかける必要がありません。
static struct mount *alloc_vfsmnt(const char *name)
{
struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
if (mnt) {
:
#ifdef CONFIG_SMP
mnt->mnt_pcp = alloc_percpu(struct mnt_pcp);
if (!mnt->mnt_pcp)
goto out_free_devname;
this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
#else
mnt->mnt_count = 1;
mnt->mnt_writers = 0;
#endif
:
}
mnt_count/mnt_writersを更新する際は、動作しているCPU変数に対して、また取得する際は、全てのCPU変数の合計としなければなりません。この場合他のCPU変数に対して参照するため、mnt_longtermを介してのスピンロックを掛ける必要があります。またロックは、CPU変数としない他のメンバーについても、更新時は必要となるわけです。SMPでない場合preempt_disable()とし、割り込みを禁止します。
static inline void mnt_add_count(struct mount *mnt, int n)
{
#ifdef CONFIG_SMP
this_cpu_add(mnt->mnt_pcp->mnt_count, n);
#else
preempt_disable();
mnt->mnt_count += n;
preempt_enable();
#endif
}
unsigned int mnt_get_count(struct mount *mnt)
{
#ifdef CONFIG_SMP
unsigned int count = 0;
int cpu;
for_each_possible_cpu(cpu) {
count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
}
return count;
#else
return mnt->mnt_count;
#endif
}
変数をCPU変数とするかどうかは、タスク毎の更新頻度に依存すると言えそうです。CPU変数とすると、ロックの必要性がなくなる訳ですが、その分メモリを必要とします。またそのトータル数を参照する処理が多い様なケースでは、ロックを伴って全CPU変数を走査する必要があるわけです。CPU変数の概要は、雰囲気このような感じですが、そのマクロ展開はアラビア語みたいです。





