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");
}






