Kernel/VM Advent Calendar 25日目 最近のPCアーキテクチャにおける割り込みルーティングの仕組み
※追記:MSIはPCI 3.0からじゃなくてPCI 2.3からだとの指摘を受けて書き換え。
※追記:hisakさんから詳しくコメントが入っているので、併せて読んで下さい。
とてつもなく遅れたKernel/VM Advent Calendarの25日目の記事です。
Linuxにおける/proc/irq/
結構こんがらがっているので、予想外に時間を食ってしまった…まだ調べ尽くせていないが、分からない事はTODOとして一旦現時点での理解を書いておこうと思う。
前提条件
PCIeに於ける割り込みの種類
割り込みルーティング
レガシー割り込み
デバイスからピン経由で割り込みを通知→IOAPICでRedirection Table Entryを参照、通知先LAPICを決定→CPU内のLAPICへ割り込みを通知
※TODO:図、これで本当に正しいかどうか確認
MSI割り込みの詳細
※TODO:Interrupt Remappingについて追記
Capability Structure
BIOSがPCI Expressを初期化する手順が見えてきた: なひたふJTAG日記を見るとイメージが分かると思うが、Configuration SpaceからLinked List状に複数のcapabilityが繋がる構造になっていて、CAPIDが0xd0なのがMSIのフィールドで、ここにはMSICTL, MSIAR, MSIDRの3つのレジスタがある。
MSI Address Register(MSIAR)
- 31:20 = 0xfee
- 19:12 = Destination ID
- 11:4 = IA32では未使用
- 3 = Address Redirection Hint(RH)
- 0: Directed
- 1: Redirectable
- 2 = Address Destination Mode(DM)
- 0: Physical Mode
- 1: Logical Mode
- 1:0 = 予約
Destination ModeがLogicalかつRedirection HintがRedirectableな場合はDestination IDでビットが立っているCPUの中でTask Priority Register(TPR)が最も低いCPUのLAPICへ割り込みが送られる。
それ以外のRH, DMの組み合わせではDestination IDで指定されているビットの中で特定のCPUのLAPICへ割り込みが送られる(※bitが複数立ってる場合どう選ぶのだろう…)
Physical ModeでDestination IDが0xffの場合はブロードキャスト割り込みを行う。
※TODO:Extended Mode(x2APIC)について追記
MSI Data Register(MSIDR)
- 31:16 = 0x0000
- 15 = Trigger mode
- 0: Edge
- 1: Level
- 14 = Delivery status
- 0: Deassert
- 1: Assert
- 13:12 = 0x00
- 11:8 = Delivery mode
- 0000: Fixed
- 0001: Lowest priority
- 0010: SMI/PMI/MCA
- 0011: Reserved
- 0100: NMI
- 0101: INIT
- 0110: Reserved
- 0111: ExtINT
- 1000-1111: Reserved
- 7:0 = Interrupt Vector
Delivery modeがFixedの場合はDestinationに指定された全てのCPUへ割り込みを行う。
Lowest Priorityの場合はTask Priority Registerの値が最も低いCPUへ割り込みを行う。
Interrupt Vectorに割り込み先LAPICのVector番号を指定。
※TODO:MSIDRのDelivery ModeとMSIARのRH、DMの組み合わせによってどのような事が起きるのか良く分からないのをはっきりさせたい
Linuxカーネルで実際にレジスタの値を設定している所を見てみる
msi_compose_msgでレジスタに書き込みたい値を用意しているので、これを見てみる。
msg->address_loがMSIARレジスタで、apic->irq_dest_modeが0ならphysical mode、1ならlogical modeを設定、apic->irq_delivery_modeがdest_LowestPrioならRedirectable(MSI_ADDR_REDIRECTION_LOWPRI)を、そうでなければDirected(MSI_ADDR_REDIRECTION_CPU)を設定、変数destをDestination IDとして設定している。
msg->dataがMSIDRレジスタで、apic->irq_delivery_modeがdest_LowestPrioならLowest priorityを、そうでなければFixedを設定、cfg->vectorの値をInterrupt Vectorとして設定している。
apic->irq_dest_modeとapic->irq_delivery_modeの値はIO APICのドライバ毎に違うのだが、x86_64の標準ドライバのapic_flat_64.cではirq_dest_modeは1, irq_delivery_modeはdest_LowestPrioに設定されている。
これらの値は割り込み初期化時に設定され、/proc/irq/
smp_affinityの書き換え時には、Destination IDとInterrupt Vectorだけが変更される。
全ての環境でLogical modeかつLowest priorityが使えるとは限らないので、場合によってはPhysical Modeで初期化されていてsmp_affinityの値を0xffにしてもCPU0にしか割り込まないという挙動を行う事も有り得る。
実際、論理CPUが12個あるCore i7上でLinux 3.2.0+を走らせている環境ではExtended Physical Mode(※TODO:Extended Modeの解説)で初期化されていて、割り込み分散が行われていなかった(※TODO:Extended Logical Modeで動いていない理由は何故か考えてみる)。
参考資料
- Intel® 64 and IA-32 Architectures Software Developer Manuals | Intel® Software
- Intel® Support
- PCI Local Bus Specification Revision 3.0
- PCI Express 2.0 Base Specification Revision 0.9