Chinaunix

标题: 哪位大牛懂smp_processor_id的原理? [打印本页]

作者: dingyujie    时间: 2012-08-26 16:23
标题: 哪位大牛懂smp_processor_id的原理?
我看的代码是2.6.32,smp_processor_id的定义过程是:
# define smp_processor_id() raw_smp_processor_id()
接下来:
#define raw_smp_processor_id() (percpu_read(cpu_number))
后面最终调用了:
percpu_from_op("mov", per_cpu__##var, "m" (per_cpu__##var))
这个宏展开后,实质上等价于:
#define percpu_from_op(op, var, constraint)       
({                                                       
        typeof(var) ret__;
        switch (sizeof(var)) {
        case 1:
            asm(“movb  %%fs:%P1, %0"
                : "=q" (ret__)
                : constraint);
           break;
        ...
})

后续的线索没了。。。。这里fs寄存器里面是什么东西?为什么这里就可以获取到cpu的id?请牛人指点!!
作者: amarant    时间: 2012-08-26 17:54
不同的arch不一样,mips是这样定义的
#define raw_smp_processor_id() (current_thread_info()->cpu)

X86的汇编我不是非常熟悉,看起来就是从某段:偏移量的一个内存地址读出来而已。或许,你应该找找cpu初始化的地方看看是怎么初始化这个内存吧
作者: zd零    时间: 2012-08-27 16:25
初始化 :
setup_per_cpu_areas()
{
    per_cpu(cpu_number, cpu) = cpu;
}      

段选择子 fs 初始化:
static inline void setup_percpu_segment(int cpu)
{
#ifdef CONFIG_X86_32
        struct desc_struct gdt;

        pack_descriptor(&gdt, per_cpu_offset(cpu), 0xFFFFF,
                        0x2 | DESCTYPE_S, 0x;
        gdt.s = 1;
        write_gdt_entry(get_cpu_gdt_table(cpu),
                        GDT_ENTRY_PERCPU, &gdt, DESCTYPE_S);
#endif
}
设置 GDT 描述符在 GDT 表中的偏移 GDT_ENTRY_PERCPU ,将相应 cpu 的每 cpu 变量写到 GDT 描述符地址字段
其实就是 fs 对应 per_cpu_offset[cpu],一切尽在不言中 GDTR,GDT ......


化简如下:
asm("movb %%fs:%P1, %0"
        :"=q"(ret__)
        :"m"(per_cpu__cpu_number));

每个 cpu 的 fs 的内容都不同,此时 fs:
GDT-> GDT_ENTRY_PERCPU-> 偏移
per_cpu_offset[cpu] + &per_cpu__cpu_number 便得到了相应 cpu 的每 per_cpu__cpu_number,关于 per_cpu_offset 请参考 setup_per_cpu_areas 函数

大致就是这样, hope useful for you ~

作者: dingyujie    时间: 2012-08-29 17:57
但是boot_cpu_init在这些函数之前调用的,在这个函数里面,已经在使用smp_processor_id了!这是怎么回事?回复 3# zd零


   
作者: zd零    时间: 2012-08-29 20:37
回复 4# dingyujie


此时 AP 还没有启动,BP 完成启动,所以就是 cpu 0

Details plz reference this  :
http://comments.gmane.org/gmane.linux.kernel.kernelnewbies/40924


作者: dingyujie    时间: 2012-08-30 10:56
谢谢zd零大牛的解答,懂了!回复 5# zd零


   
作者: dingyujie    时间: 2012-08-30 11:24
在x86/kernel/head_32.S中:

movl $(__KERNEL_PERCPU), %eax
movl %eax,%fs

而__KERNEL_PERCPU是per-CPU GDT的一项:

*  ------- start of kernel segments:
*
*  12 - kernel code segment                <==== new cacheline
*  13 - kernel data segment
*  14 - default user CS
*  15 - default user DS
*  16 - TSS
*  17 - LDT
*  18 - PNPBIOS support (16->32 gate)
*  19 - PNPBIOS support
*  20 - PNPBIOS support
*  21 - PNPBIOS support
*  22 - PNPBIOS support
*  23 - APM BIOS support
*  24 - APM BIOS support
*  25 - APM BIOS support
*
*  26 - ESPFIX small SS
*  27 - per-cpu                        [ offset to per-cpu data area ]
*  28 - stack_canary-20                [ for stack protector ]
*  29 - unused
*  30 - unused
*  31 - TSS for double fault handler

在BP运行时,这个27项,也就是per-cpu项中的值,是如何设置的呢?

因为在start_kernel的开始调用boot_cpu_init的时候,就会调用smp_processor_id了,这里面会使用%%fs:cpu_number来获取cpu id。。。
那说明这个fs在之前肯定需要设置妥当!

有人帮忙解答下这个问题吗?


   
作者: geforce_zf    时间: 2012-11-24 23:53
大概流程如下:
1.BP:kernel_init->smp_init->cpu_up->native_cpu_up->do_boot_cpu->
early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu);
将AP:cpu的gdt(之前由BP为每个AP初始化好了)写入early_gdt_descr中。

2.AP:trampoline_data->startup_32_smp->
lgdt early_gdt_descr
...
movl $(__KERNEL_PERCPU), %eax
movl %eax,%fs                       
将AP cpu对应的PER_CPU段的首地址读入到fs中。

3.AP:start_secondary->cpu_init->smp_processor_id读取设置在fs段中的cpu number值。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2