MSI/MSI-Xとx2APIC

MSI (Message Signaled Interrupts) はPCI Technology Specificationが策定した,PCI/PCIeで利用される割り込み方法の一つです.最初から存在したpin-basedな割り込み(INTx割り込み)と比べ,MSIを利用する利点として以下が挙げられます.

  • pin-base割り込み(INTx割り込み)と違って割り込みが共有されない
  • バイスは複数の割り込みを持てる

MSIではデータをメモリに書き込むことで割り込みを発生させますが,このとき割り込み発生時には必ずメモリの書き込みが完了していることが保証されます. MSI-XはMSIの拡張です.MSIMSI-Xの主な違いは以下の通りです.

  • MSIは32までの割り込み,MSI-Xは2048までの割り込みをサポート
  • MSIでは割り込み数は2のべき乗でなければならないが,MSI-Xにはそのような制約は ない
  • MSIでは割り込みベクタは連続でなければならないが,MSI-Xにはそのような制約はない

文献

MSI

MSIの設定はPCIのConfiguration Spaceの中でおこないます. PCIのConfiguration Spaceは以下の図のようになっていますが,ここで,図のCapability Pointerから数珠繋ぎのようにそのPCIバイスのCapabilityを表す構造が存在しています.

f:id:mm_i:20170328031807p:plain (Intel Arria 10 User Guideより)

MSIのCapabilityは以下のようになります.

f:id:mm_i:20170328032437p:plain (同上)

ここで,重要なのがMessage Addressと,Message Dataの部分です.PCIの仕様ではMessage AddressとMessage Dataの大まかな構造は規定しているものの,具体的な内容に関してはデバイス依存になっています.x86の場合,この構造はIntel SDMの10.11に書いてあります.

f:id:mm_i:20170328031858p:plain f:id:mm_i:20170328031901p:plain (Intel SDMより)

MSI Message Address Registerの19-12bit目のDestination IDと,3bit目のRH (Redirection Hint)と2bit目のDM(Destination Moede)で割り込み先を決定します.具体的には,

となります.Local APIC IDやLogical APIC IDに関しては前回の記事を参照してください.

Message Data Registerは,0-7bit目で割り込みベクタ番号を指定します.

Lowest Priority mode

Address Register の delivery mode で 001 を指定すると,lowest priority mode になります (SDM 10.6.2.4). lowest priority modeを指定し,destination field で複数のコアを指定した場合,そのコアの中で最も優先度が低いものが割り込みを受信します. 優先度はAPR (Application Priority Register) の値によって決まります.APR自体はread-onlyで,実際には read/writeできる TPR (Task Priority Register) と,現在処理中及び処理待ち中の割り込みの値からAPRが決まります.割り込みを処理していなければ APR = TPR です.SDMの10.6.2.4には APRの値が複数のコアで同じだった場合のどうなるかが書いてないような気がしますが,よく読むと SDMの10.10 に,もし低優先度のものが複数存在した場合,arbitration priority (Arb ID) を利用すると書いてあります.

このArb IDはバスアクセスの権限を得るために各LAPICが内部で持つ4bitの値で,

  • バスへアクセスしたい場合,バス要求を出し,Arb IDを1増やす
  • バスへのアクセス権は最も大きいArb IDのものが得る
  • バスへアクセスしたらArb IDを0にする

ということおおまかにをするようです. Linuxでは各LAPICのTPRの値はブート時に0に初期化し,それ以降変更することがないとのことです. したがって,lowest priority modeを選択した場合,Arb IDに基づいて送信先のコアが選ばれることになり,結果的に複数のCPUを選択した場合はラウンドロビン的にCPUが選択されます.

ただし,lowest priority modeに関してはうまく動かないといった情報もあったりします(e.g. http://forum.osdev.org/viewtopic.php?t=25372) *2

MSI-X

MSI-XのCapabilityは以下のようになっています.

f:id:mm_i:20170328032003p:plain (Intel Arria 10 User Guideより)

MSI-XではMSIと違い,Message Address RegisterやData RegsiterはBARで指定されるMMIO領域に存在します.Table BIRとPBA BIRが対応するBARの番号を,MSI-X Table Offset とMSI-X PBA Offset がBARからのオフセットを意味しています.またMessage Controlレジスタの26-16bit目がMSI-X Tableのエントリ数を表します.このビットが11bitであるため,MSI-Xでは最大2048までの割り込みが持てることになります.

MSI-X TableとMSI-X PBA は以下のようになっています.

f:id:mm_i:20170328032025p:plain f:id:mm_i:20170328032036p:plain (同上)

MSI-X Table内に,Message Data RegsiterやMessage Address Registerが存在します. また,Vector Controlは割り込みのマスクに利用します.PBAはpending messageがあるかどうかを示すビットのようです*3

MSI/MSI-X と x2APIC

前回の記事で説明した通り,x2APICはアドレス幅が8bitから32bitへ拡張されています.MSI/MSI-XではDestination Fieldが8bitなので,このままではx2APICが使用できないことになります.一体これをどうしてるんだろうと数日悩んでいましたが,どうやらIntel VT-d のinterrupt remappingを使用するようです.

VT-dのinterrupt remappingを利用する場合,まずInterrupt Remapping Table Entryというものを作成します.

f:id:mm_i:20170328032051p:plain (Intel VT-d Specより)

IRTEの63-32の32bitのフィールドでDesitinationを指定します.また24-16bit目がベクタ番号です.

IRTE自体はメモリ上に作成し,Interrupt Remapping Table Address Register (IRTA)にその先頭のアドレスを指定します.(IRTA自体はmemory-mappedされる)

f:id:mm_i:20170328032112p:plain (同上)

このとき,MSIのAddress Register とData Registerは以下のようになります.

f:id:mm_i:20170328032140p:plain (同上)

Address Registerの19-5bit目でIRTEのインデックスを指定します. Data registerは0になります.Interrupt Remappingが有効の場合,MSIでの割り込みはまずAddress RegisterのDestinationの値を使ってIRTEのエントリを取り出し,それに応じて実際の割り込みが発行されることになります. Address Registerの使ってない部分を利用してDestination Fieldを32bitにすればいいんじゃないかと思ったりもしましたが,VT-dの統一したInterrupt Remappingの仕組みを使うようにしてるんでしょうか.

*1:ここもしかすると理解が間違っているかもしれない..

*2:というか自分の環境でうまく動いてない.. 何か設定を見逃してるのだろうか

*3:これが何なのかよく分かってない..