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変数の概要は、雰囲気このような感じですが、そのマクロ展開はアラビア語みたいです。