CPU変数


Rev.6を表示中。最新版はこちら

static変数は、他のプロセスとの競合を避ける必要があります。シングルCPUでは、preempt禁止(or 割り込み禁止)すればOKですが、マルチCPUではアトミックなフラグでのスピンロックが必要です。CPU変数は、同じ目的の変数をCPU毎に割り当てる事で、シングルCPUと同じ環境とし(他のCPUがこの変数を参照する事は無くなります。)、ロックを介さず参照できる仕組みです。

CPU変数のサンプルです。DEFINE_PER_CPUマクロでCPU変数を定義するだけ、static int hoge =1と宣言したものをCPU毎にstatic変数として割り当てられます。なお、CPU数はシステム依存で、実際割り当てるのは、ロード時で動的に割り当てられます。

percpu_add(hoge, 9)はカレントCPU変数のstatic hogeに9加算します。get_cpu()はカレントCPUを確認するために使用したもので、CPU変数参照での処理に必要ありません。
#include <linux/kmod.h>

static DEFINE_PER_CPU(int, hoge) = 1;

static int hoge_init (void)
{
       int cpu, current_cpu;
       int     cnt, total_cnt = 0;

       current_cpu = get_cpu();
       put_cpu();
       printk("current cpu:%d\n", current_cpu);

       percpu_add(hoge, 9);

       for_each_possible_cpu(cpu) {
               cnt = per_cpu(hoge, cpu);
               printk("cpu %d->%d\n", cpu, cnt);
               total_cnt += cnt;
       }
       printk("total:%d\n", total_cnt);
       return -1;
}
module_init(hoge_init);
LKMを3回insmodした結果です。1回目はCPUは0で、2/3回目はCPUは1で動作した事が分かります。で、その時のカレントCPU変数に9を加算しています。
[root@localhost lkm]# insmod percpu.ko
insmod: error inserting 'percpu.ko': -1 Operation not permitted
[root@localhost lkm]# insmod percpu.ko
insmod: error inserting 'percpu.ko': -1 Operation not permitted
[root@localhost lkm]# insmod percpu.ko
insmod: error inserting 'percpu.ko': -1 Operation not permitted
[root@localhost lkm]# dmesg
  :
[ 3461.516605]
[ 3584.600051] current cpu:0
[ 3584.600052] cpu 0->10
[ 3584.600052] cpu 1->1
[ 3584.600052] total:11
[ 3584.600052]
[ 3586.623181] current cpu:1
[ 3586.623186] cpu 0->1
[ 3586.623191] cpu 1->10
[ 3586.623196] total:11
[ 3586.623199]
[ 3588.207309] current cpu:1
[ 3588.207314] cpu 0->1
[ 3588.207319] cpu 1->10
[ 3588.207407] total:11
CPUにはpossible_cpu/online_cpu/present_cpuの区別があり、順に最大CPU数/動作CPU数/実装CPU数で(たぶん)、大小関係は、possible_cpu>=present_cpu>=online_cpuです。(まったくの推測です。)従って、全トータルの加算は、最大数のfor_each_possible_cpuとするのがいいのではと・・・。なお、私の環境はvmwareで、すべて2つのCPUとなっていました。
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
#define for_each_online_cpu(cpu)   for_each_cpu((cpu), cpu_online_mask)
#define for_each_present_cpu(cpu)  for_each_cpu((cpu), cpu_present_mask)

#define for_each_cpu(cpu, mask)                 \
       for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
get_cpu()はカレントCPUを取得し、同時にプリエンプと禁止するため、 put_cpu()でプリエンプ許可する必要があります。
#define get_cpu()               ({ preempt_disable(); smp_processor_id(); })
#define put_cpu()               preempt_enable()
DEFINE_PER_CPU(int, hoge) = 1;は、__section__(".data.percpu")内に、intサイズで初期値1で展開されます。
__attribute__((__section__(".data.percpu"))) __typeof__(int) per_cpu__hoge = 1;
load_module()は、find_pcpusec()で.data.percpuセクションを取得し、その領域をCPU数分percpu_modalloc()でメモリを取得します。その領域をmod->percpu = percpuとし、 percpu_modcopy()でpercpuセクションをCPU数分、mod->percpuに複写することでCPU変数をアロケートする事になりますす。
static struct module *load_module(void __user *umod,
                                 unsigned long len,
                                 const char __user *uargs)
{
       Elf_Ehdr *hdr;
       Elf_Shdr *sechdrs;
       char *secstrings, *args, *modmagic, *strtab = NULL;
       unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
               exportindex, modindex, obsparmindex, infoindex, gplindex,
               crcindex, gplcrcindex, versindex, pcpuindex;
       long arglen;
       struct module *mod;
       long err = 0;
       void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */

 :
       pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);

 :

       if (pcpuindex) {
               percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
                                        sechdrs[pcpuindex].sh_addralign);
               if (!percpu) {
                       err = -ENOMEM;
                       goto free_mod;
               }
               sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
               mod->percpu = percpu;
       }
 :
       percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
                      sechdrs[pcpuindex].sh_size);

       err = module_finalize(hdr, sechdrs, mod);
       if (err < 0)
               goto cleanup;

 :
}

static unsigned int find_pcpusec(Elf_Ehdr *hdr,
                                Elf_Shdr *sechdrs,
                                const char *secstrings)
{
       return find_sec(hdr, sechdrs, secstrings, ".data.percpu");
}

補足

具体的な実装はCPU依存で、その実装もCPU毎に異なっていて、正直よく分かりません。CPU変数のイメージとはと言う事です。とりあえず、DEFINE_PER_CPUマクロでシステムに依存することなく、容易にCPU変数を利用できます。


最終更新 2014/11/19 18:34:31 - north
(2012/03/23 16:19:37 作成)


検索

アクセス数
3590090
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。