読者です 読者をやめる 読者になる 読者になる

LinuxカーネルのSMPまわりの処理を調べてみる

linux kernel

うちではデスクトップとして使っているPCにIntel® Core™ i7-3770S Processorを使っていて、コア数が4、8スレッドということで/proc/cpuinfoでは8個のcpuがLinuxによって認識されています。では、これがどのような手順で認識されていったのか?というのをちょっと調べて見たくなった訳です(/ω\)

まずは本題に入る前に、x86でのSMPに関する仕様はIntelが出している仕様書がIntelのWebサイトからダウンロードできます。
あとは、ずっと前に自分がはてなダイアリーのほうに書いた記事ですがこんなのとか見ておくと予備知識になるかも。これらはMinix 2.0でSMP対応した人が居てそのコードを読みながら調べたやつです。

Linuxのソースを見ていて、名前からsmp_init()が初期処理なんだろうと思ったら実は違っていて、この関数の実行時点ではCPUがいくつあるかとかは既に判明しているっぽいのです。なので
dmesgでログを見て、順番に追いかけていこうと思います。

最初に見つかったのがこれで「 found SMP MP-table ~」とありますね。これが見つかったということはCPUが複数有るのは確定できます。

[    0.000000] e820: update [mem 0x7b800000-0xffffffff] usable ==> reserved
[    0.000000] e820: last_pfn = 0x7b000 max_arch_pfn = 0x400000000
[    0.000000] found SMP MP-table at [mem 0x000fd7d0-0x000fd7df] mapped at [ffff8800000fd7d0]
[    0.000000] Scanning 1 areas for low memory corruption
[    0.000000] Base memory trampoline at [ffff880000097000] 97000 size 24576

次に見つかるのがこれ。SMPの設定にMADTを使いますよとい内容のログが。

[    0.000000] ACPI: IRQ9 used by override.
[    0.000000] Using ACPI (MADT) for SMP configuration information

そしてしばらく進むとこんなログが。これはログの先頭に「smpboot:」が有るしどこで出したログかは分かりやすそう。
で、最後の行で8cpuがアクティブになったと言ってます。

[    0.103172] smpboot: CPU0: Intel(R) Core(TM) i7-3770S CPU @ 3.10GHz (fam: 06, model: 3a, stepping: 09)
[    0.103177] TSC deadline timer enabled
[    0.103184] Performance Events: PEBS fmt1+, 16-deep LBR, IvyBridge events, full-width counters, Intel PMU driver.
[    0.103190] ... version:                3
[    0.103191] ... bit width:              48
[    0.103192] ... generic registers:      4
[    0.103192] ... value mask:             0000ffffffffffff
[    0.103193] ... max period:             0000ffffffffffff
[    0.103194] ... fixed-purpose events:   3
[    0.103195] ... event mask:             000000070000000f
[    0.130009] x86: Booting SMP configuration:
[    0.143576] NMI watchdog: enabled on all CPUs, permanently consumes one hw-PMU counter.
[    0.130011] .... node  #0, CPUs:      #1 #2 #3 #4 #5 #6 #7
[    0.264952] x86: Booted up 1 node, 8 CPUs
[    0.264955] smpboot: Total of 8 processors activated (49668.45 BogoMIPS)

では、「found SMP MP-table at ~」はどこでだしているのか見ていこう。この文字列で検索をかけるとarch/x86/kernel/mpparse.cがヒットします。

564 static int __init smp_scan_config(unsigned long base, unsigned long length)
565 {
566         unsigned int *bp = phys_to_virt(base);
567         struct mpf_intel *mpf;
568         unsigned long mem;
569 
570         apic_printk(APIC_VERBOSE, "Scan for SMP in [mem %#010lx-%#010lx]\n",
571                     base, base + length - 1);
572         BUILD_BUG_ON(sizeof(*mpf) != 16);
573 
574         while (length > 0) {
575                 mpf = (struct mpf_intel *)bp;
576                 if ((*bp == SMP_MAGIC_IDENT) &&
577                     (mpf->length == 1) &&
578                     !mpf_checksum((unsigned char *)bp, 16) &&
579                     ((mpf->specification == 1)
580                      || (mpf->specification == 4))) {
581 #ifdef CONFIG_X86_LOCAL_APIC
582                         smp_found_config = 1;
583 #endif
584                         mpf_found = mpf;
585 
586                         printk(KERN_INFO "found SMP MP-table at [mem %#010llx-%#010llx] mapped at [%p]\n",
587                                (unsigned long long) virt_to_phys(mpf),
588                                (unsigned long long) virt_to_phys(mpf) +
589                                sizeof(*mpf) - 1, mpf);
590 
591                         mem = virt_to_phys(mpf);
592                         memblock_reserve(mem, sizeof(*mpf));
593                         if (mpf->physptr)
594                                 smp_reserve_memory(mpf);
595 
596                         return 1;
597                 }
598                 bp += 4;
599                 length -= 16;
600         }
601         return 0;
602 }

ここでやっている内容はまさしく「MP Floating Pointer Structureを調べる」で調べた内容です。ここで探しているのは「MP Floating Pointer Structure」というものです。これは以下の場所にあります。

  • Extended BIOS Data Area(EBDA)の最初の1KB以内。または、
  • EDBAが定義されていない場合は、ベースメモリ領域の最後の1KB(ベースメモリ領域が640KBなら639-640KBの範囲)。または、
  • BIOSのROM内。アドレス0F0000h~0FFFFFの範囲。

smp_scan_config()を呼んでいるdefault_find_smp_config()を見るとこのようになっています。

604 void __init default_find_smp_config(void)
605 {
606         unsigned int address;
607 
608         /*
609          * FIXME: Linux assumes you have 640K of base ram..
610          * this continues the error...
611          *
612          * 1) Scan the bottom 1K for a signature
613          * 2) Scan the top 1K of base RAM
614          * 3) Scan the 64K of bios
615          */
616         if (smp_scan_config(0x0, 0x400) ||
617             smp_scan_config(639 * 0x400, 0x400) ||
618             smp_scan_config(0xF0000, 0x10000))
619                 return;
620         /*

さて、smp_scan_config()ではこのテーブルを見つけるまでが仕事ですね。この関数までのコールフローは以下のような感じです。
start_kernel()
 -> setup_arch()
  ->  find_smp_config()
   -> default_find_smp_config()
    -> smp_scan_config()

(´-`).。oO(次は「Using ACPI (MADT) for SMP configuration information」を見よう

Linuxカーネル Hacks ―パフォーマンス改善、開発効率向上、省電力化のためのテクニック

Linuxカーネル Hacks ―パフォーマンス改善、開発効率向上、省電力化のためのテクニック