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

generic_processor_info()によるcpuの処理部分を読む

linux kernel

前回の続きとしてgeneric_processor_info()を読む。

まずはgeneric_processor_info()が呼ばれる流れを。これはacpi_register_lapic()から呼ばれていて、acpi_register_lapic()は以下の関数などから呼ばれる。

  • acpi_parse_x2apic()
  • acpi_parse_lapic()
  • acpi_parse_x2apic_nmi()
  • acpi_parse_lapic_nmi()

フロートしてはだいたいこんなもの。
acpi_parse_madt_lapic_entries() <- acpi_parse_x2apic()などはacpi_table_parse_madt()の引数として関数ポインタで渡す
 -> acpi_table_parse_madt()
  -> acpi_table_parse_entries() handler(entry, table_end)という形でacpi_parse_x2apic()などの呼び出し
   -> acpi_parse_x2apic()
    -> acpi_register_lapic()
     -> generic_processor_info()

では、generic_processor_info()を見ていく。

2110 void generic_processor_info(int apicid, int version)
2111 {
2112         int cpu, max = nr_cpu_ids;
2113         bool boot_cpu_detected = physid_isset(boot_cpu_physical_apicid,
2114                                 phys_cpu_present_map);
2115 
2116         /*
2117          * If boot cpu has not been detected yet, then only allow upto
2118          * nr_cpu_ids - 1 processors and keep one slot free for boot cpu
2119          */
2120         if (!boot_cpu_detected && num_processors >= nr_cpu_ids - 1 &&
2121             apicid != boot_cpu_physical_apicid) {
2122                 int thiscpu = max + disabled_cpus - 1;
2123 
2124                 pr_warning(
2125                         "ACPI: NR_CPUS/possible_cpus limit of %i almost"
2126                         " reached. Keeping one slot for boot cpu."
2127                         "  Processor %d/0x%x ignored.\n", max, thiscpu, apicid);
2128 
2129                 disabled_cpus++;
2130                 return;
2131         }
2132 

ここまではBSP(Boot Strap Processorはコンピューターの起動時に動いたcpu。カーネルが別途起動させるのはApplication Processorで略称はAP)が検出済みかをチェックして、もし未検出ならAPのほうはdisableにしておくということか。
num_processors、nr_cpu_idsはグローバル変数

2133         if (num_processors >= nr_cpu_ids) {
2134                 int thiscpu = max + disabled_cpus;
2135 
2136                 pr_warning(
2137                         "ACPI: NR_CPUS/possible_cpus limit of %i reached."
2138                         "  Processor %d/0x%x ignored.\n", max, thiscpu, apicid);
2139 
2140                 disabled_cpus++;
2141                 return;
2142         }
2143 

ここも整合性のチェックでcpuの検出数があってないようならそのcpuはdisableになる。

2144         num_processors++;
2145         if (apicid == boot_cpu_physical_apicid) {
2146                 /*
2147                  * x86_bios_cpu_apicid is required to have processors listed
2148                  * in same order as logical cpu numbers. Hence the first
2149                  * entry is BSP, and so on.
2150                  * boot_cpu_init() already hold bit 0 in cpu_present_mask
2151                  * for BSP.
2152                  */
2153                 cpu = 0;
2154         } else
2155                 cpu = cpumask_next_zero(-1, cpu_present_mask);
2156 

今見ているcpuがBSPの場合の処理だけど、ここはBIOSのバグ対策なのね。

2157         /*
2158          * Validate version
2159          */
2160         if (version == 0x0) {
2161                 pr_warning("BIOS bug: APIC version is 0 for CPU %d/0x%x, fixing up to 0x10\n",
2162                            cpu, apicid);
2163                 version = 0x10;
2164         }
2165         apic_version[apicid] = version;
2166 

バージョンについてはさらにもう一個チェックがある。

2167         if (version != apic_version[boot_cpu_physical_apicid]) {
2168                 pr_warning("BIOS bug: APIC version mismatch, boot CPU: %x, CPU %d: version %x\n",
2169                         apic_version[boot_cpu_physical_apicid], cpu, version);
2170         }
2171 

次は今処理してきたcpuをカーネルのほうで覚えておく(という言い方は微妙だけど)。

2172         physid_set(apicid, phys_cpu_present_map);
2173         if (apicid > max_physical_apicid)
2174                 max_physical_apicid = apicid;
2175 
2176 #if defined(CONFIG_SMP) || defined(CONFIG_X86_64)
2177         early_per_cpu(x86_cpu_to_apicid, cpu) = apicid;
2178         early_per_cpu(x86_bios_cpu_apicid, cpu) = apicid;
2179 #endif
2180 #ifdef CONFIG_X86_32
2181         early_per_cpu(x86_cpu_to_logical_apicid, cpu) =
2182                 apic->x86_32_early_logical_apicid(cpu);
2183 #endif

ここの処理で登録したcpuはnum_possible_cpus()num_present_cpus() で見れるようになるはず。

2184         set_cpu_possible(cpu, true);
2185         set_cpu_present(cpu, true);
2186 }

generic_processor_info()はループの中で呼ばれてくるので、1cpu毎に処理が行われて最終的にすべての(CPUコア、HT)がcpuとして登録されるということかな。
ブート時のcpu数の認識に関してはdmesgの以下の部分が出た時点で確定できているので今度はここをちょっと見てみる。

[    0.000000] smpboot: Allowing 8 CPUs, 0 hotplug CPUs

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

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