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:11CPUには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"); }