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

Linuxカーネルで「ACPI: LAPIC ~」などを出している辺りを読む

linux kernel

前回の続きとしてacpi_parse_madt_lapic_entries()内で acpi_table_parse_madt()の2番目の引数で渡している関数を見ていこう。
見るのはうちの環境に関係ありそうなところでこの辺を。

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

4つあるけどx2apicとlapicの違いくらいで終わってくれれば良いんだけど。あとACPIの仕様書は今日も必要か。

最初に4関数共通なところでいくと、これらはacpi_table_parse_madt()に関数ポインタとして渡してhandlerという名前になり、実際に使われるのはacpi_table_parse_entries()の以下の部分。

while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) <
240                table_end) {
241                 if (entry->type == entry_id
242                     && (!max_entries || count++ < max_entries))
243                         if (handler(entry, table_end))
244                                 goto err;

ではまずはacpi_parse_x2apic()から。
この関数はProcessor Local x2APIC Structureのデータを見ていく。

213 acpi_parse_x2apic(struct acpi_subtable_header *header, const unsigned long end)
214 {
215         struct acpi_madt_local_x2apic *processor = NULL;
216         int apic_id;
217         u8 enabled;
218 
219         processor = (struct acpi_madt_local_x2apic *)header;
220 
221         if (BAD_MADT_ENTRY(processor, end))
222                 return -EINVAL;
223 

最初にするのが引数で渡ってきたデータのアドレス範囲のチェック。

テーブル内容の表示。

224         acpi_table_print_madt_entry(header);
225 

次にIDとフラグを見ている部分。

226         apic_id = processor->local_apic_id;
227         enabled = processor->lapic_flags & ACPI_MADT_ENABLED;

Processor Local x2APIC Structureの構造は「5.2.12.12 Processor Local x2APIC Structure」にあって以下の通り。

Filed Byte Length Byte Offset Description
Type 1 0 typeは9
Length 1 1 このテーブルの長さで16バイト
Reserved 2 2 予約領域で0埋め
X2APIC ID 4 4 プロセッサローカルなAPIC ID
Flags 4 8 Local APIC Flagsと同じ
ACPI Processor UID 4 12  

Flagsは「Local APIC Flags」と同じというのでここで見てしまう。「Table 5-47 Local APIC Flags」を見ると至って単純で、32bit中、先頭の1bitをフラグとして使っているだけ。

LocalAPIC Flags Bit Length Bit Offset Description
Enabled 1 0 0: disabled 1:enabled
Reserved 31 1 0埋め

「rocessor->local_apic_id」で見ているのはテーブルの「X2APIC ID」で、processor->lapic_flagsは「Flags」の部分。

最後の処理はacpi_register_lapic()の呼び出し。

228 #ifdef CONFIG_X86_X2APIC
229         /*
230          * We need to register disabled CPU as well to permit
231          * counting disabled CPUs. This allows us to size
232          * cpus_possible_map more accurately, to permit
233          * to not preallocating memory for all NR_CPUS
234          * when we use CPU hotplug.
235          */
236         if (!apic->apic_id_valid(apic_id) && enabled)
237                 printk(KERN_WARNING PREFIX "x2apic entry ignored\n");
238         else
239                 acpi_register_lapic(apic_id, enabled);
240 #else
241         printk(KERN_WARNING PREFIX "x2apic entry ignored\n");
242 #endif
243 
244         return 0;
245 }

acpi_register_lapic()はacpi_register_lapic()も使っていて、この関数の中ではgeneric_processor_info()というまた重要そうな関数を読んでいたりするので後で読む。

と言う訳でacpi_parse_lapic()を。これは短い!

248 acpi_parse_lapic(struct acpi_subtable_header * header, const unsigned long end)
249 {
250         struct acpi_madt_local_apic *processor = NULL;
251 
252         processor = (struct acpi_madt_local_apic *)header;
253 
254         if (BAD_MADT_ENTRY(processor, end))
255                 return -EINVAL;
256 
257         acpi_table_print_madt_entry(header);
258 
259         /*
260          * We need to register disabled CPU as well to permit
261          * counting disabled CPUs. This allows us to size
262          * cpus_possible_map more accurately, to permit
263          * to not preallocating memory for all NR_CPUS
264          * when we use CPU hotplug.
265          */
266         acpi_register_lapic(processor->id,      /* APIC ID */
267                             processor->lapic_flags & ACPI_MADT_ENABLED);
268 
269         return 0;
270 }

テーブルの表示とacpi_register_lapic()を呼ぶだけというシンプルさ。
ここで見ているデータは

Filed Byte Length Byte Offset Description
Type 1 0 typeは0
Length 1 1 このテーブルの長さで8バイト
ACPI Processor ID 1 2 プロセッサ ID
APIC ID 1 3 プロセッサローカルなAPICID
Flags 4 4 Local APIC Flagsと同じ

processor->idはACPI Processor IDを見ている。

acpi_table_print_madt_entry()で表示しているのはdmesgしたときの↓の部分。acpi_parse_lapic()ではループ処理は無いけど呼び出し元のacpi_table_parse_madt()ではwhileループで処理しているので複数回呼び出されてる。

[    0.000000] ACPI: LAPIC (acpi_id[0x01] lapic_id[0x00] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x02] lapic_id[0x02] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x03] lapic_id[0x04] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x04] lapic_id[0x06] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x05] lapic_id[0x01] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x06] lapic_id[0x03] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x07] lapic_id[0x05] enabled)
[    0.000000] ACPI: LAPIC (acpi_id[0x08] lapic_id[0x07] enabled)

データは8回表示されているので8スレッド分ということですな。


次はacpi_parse_x2apic_nmi()をば。処理は短い。

306 static int __init
307 acpi_parse_x2apic_nmi(struct acpi_subtable_header *header,
308                       const unsigned long end)
309 {
310         struct acpi_madt_local_x2apic_nmi *x2apic_nmi = NULL;
311 
312         x2apic_nmi = (struct acpi_madt_local_x2apic_nmi *)header;
313 
314         if (BAD_MADT_ENTRY(x2apic_nmi, end))
315                 return -EINVAL;
316 
317         acpi_table_print_madt_entry(header);
318 
319         if (x2apic_nmi->lint != 1)
320                 printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n");
321 
322         return 0;
323 }

見たままで特筆すべきことはなさそうですね。じゃあ、acpi_parse_lapic_nmi()はどうかな?

325 static int __init
326 acpi_parse_lapic_nmi(struct acpi_subtable_header * header, const unsigned long end)
327 {
328         struct acpi_madt_local_apic_nmi *lapic_nmi = NULL;
329 
330         lapic_nmi = (struct acpi_madt_local_apic_nmi *)header;
331 
332         if (BAD_MADT_ENTRY(lapic_nmi, end))
333                 return -EINVAL;
334 
335         acpi_table_print_madt_entry(header);
336 
337         if (lapic_nmi->lint != 1)
338                 printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n");
339 
340         return 0;
341 }

これも特に処理といった処理はない。

うちの環境だとacpi_table_print_madt_entry()では↓が出ている。

[    0.000000] ACPI: LAPIC_NMI (acpi_id[0xff] high edge lint[0x1])

さて、さっき後で読むとしたacpi_register_lapic()なんだけど、関数は以下の通り。

192 static void acpi_register_lapic(int id, u8 enabled)
193 {
194         unsigned int ver = 0;
195 
196         if (id >= MAX_LOCAL_APIC) {
197                 printk(KERN_INFO PREFIX "skipped apicid that is too big\n");
198                 return;
199         }
200 
201         if (!enabled) {
202                 ++disabled_cpus;
203                 return;
204         }
205 
206         if (boot_cpu_physical_apicid != -1U)
207                 ver = apic_version[boot_cpu_physical_apicid];
208 
209         generic_processor_info(id, ver);
210 }
211 

generic_processor_info()が名前からして重要そうな感じ。
そんな訳で次はgeneric_processor_info()を読むφ( ̄ー ̄ )メモメモ

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)