X540のMSI-X設定
実際のデバイスでMSI-Xの設定の確認をしてみます. 今手元にX540があるので,それで実際に確認してみます.
文献
X540の仕様は以下から入手できます.
7.3章にX540におけるMSI-Xについて書いてあります.また,9.3章にPCIeのConfiguration Spaceの構造が書いてあります.
Configuration Space及びMSI-X Tableの確認
lspci -vvv
とすると,デバイスのConfiguration Spaceの情報を表示してくれます(Capabilityを表示するには,管理者権限が必要です).
% sudo lspci -vvv -s 04:00.0 04:00.0 Ethernet controller: Intel Corporation Ethernet Controller 10-Gigabit X540-AT2 (rev 01) Subsystem: Intel Corporation Ethernet Converged Network Adapter X540-T2 Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+ Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin B routed to IRQ 17 Region 0: Memory at f0200000 (64-bit, prefetchable) [size=2M] Region 4: Memory at f0404000 (64-bit, prefetchable) [size=16K] Expansion ROM at f0480000 [disabled] [size=512K] Capabilities: [40] Power Management version 3 Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold-) Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=1 PME- Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+ Address: 0000000000000000 Data: 0000 Masking: 00000000 Pending: 00000000 Capabilities: [70] MSI-X: Enable+ Count=64 Masked- Vector table: BAR=4 offset=00000000 PBA: BAR=4 offset=00002000 ... (省略) ...
この結果より,X540はMSI,MSI-Xともに対応しており,またMSI-Xが有効(Enable+
)になっていることが分かります.Count=64なので,デバイスには64個の割り込みが登録されています.またMSI-X TableはBAR 4のoffset 0から,PBAはBAR 4 のoffset 02000hからあることが分かります.BAR 4のアドレスは,Region 4: Memory at f0404000
のアドレスになります.
lspci -xxx
を利用すると,configuration spaceのダンプが見れるので,こちらも一応確認してみます.
% sudo lspci -xxx -s 04:00.0 04:00.0 Ethernet controller: Intel Corporation Ethernet Controller 10-Gigabit X540-AT2 (rev 01) 00: 86 80 28 15 06 04 10 00 01 00 00 02 10 00 80 00 10: 0c 00 20 f0 00 00 00 00 00 00 00 00 00 00 00 00 20: 0c 40 40 f0 00 00 00 00 00 00 00 00 86 80 01 00 30: 00 00 88 f7 40 00 00 00 00 00 00 00 0a 02 00 00 40: 01 50 23 48 00 20 00 00 00 00 00 00 00 00 00 00 50: 05 70 80 01 00 00 00 00 00 00 00 00 00 00 00 00 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70: 11 a0 3f 80 04 00 00 00 04 20 00 00 00 00 00 00 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a0: 10 00 02 00 c2 8c 00 10 2f 28 09 00 82 fc 42 10 b0: 00 00 82 10 00 00 00 00 00 00 00 00 00 00 00 00 c0: 00 00 00 00 1f 00 00 00 00 00 00 00 00 00 00 00 d0: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
まず,Configuration Spaceの0x34がCapability Pointerで,その部分は40です(ちなみに,Configuration Spaceはリトルエンディアンです).そこで,40h番地を見てみるとCapability IDが01h, Next Cap Ptrが50hです.Capability ID 01hはMSIでもMSI-Xでもないので,次に50h番地を見てみると,Capability IDが05h, Next Cap Ptrが70hです.Capability IDが05hなので,ここがMSIのCapabilityに相当します.また,次の70h番地を見てみると,Capability IDが11hで,ここがMSI-XのCapabilityです.この領域をよくみると,MSI-X Table BIRとMSI-X PBA BIRが4で,またTable Offsetが0,PBA Offsetが2000hであることが確認できます.
PCIデバイスのBAR領域にアクセスするには,/sys/bus/pci/devices/<bus>:<device>:<num>:.<function num>/resource<BAR番号>
のファイルをmmapすればアクセスすることができます.そのためのソフトウェアとして,pcimemがあります.pcimemを利用してX540のBAR4の領域にアクセスしてみると以下のようになりました.
% for j in `seq 0 1 64`; do; for i in `seq $((($j+1)*16-4)) -4 $(($j*16))`; sudo ./pcimem /sys/bus/pci/devices/0000:04:00.0/resource4 $i w | tail -n1 | cut -d':' -f 2 | tr '\n' ' '; echo ; done 0x0 0x41A2 0x0 0xFEE8000C 0x0 0x41B2 0x0 0xFEE0400C 0x0 0x41C2 0x0 0xFEE4000C 0x0 0x41D2 0x0 0xFEE8000C 0x0 0x41E2 0x0 0xFEE4000C 0x0 0x4123 0x0 0xFEE1000C 0x0 0x4143 0x0 0xFEE4000C 0x0 0x4153 0x0 0xFEE1000C 0x0 0x4163 0x0 0xFEEFF00C 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0xDEADBEAF 0xDEADBEAF 0xDEADBEAF 0xDEADBEAF
これは各行につきMSI-X Tableのエントリ一つで,左からControl Vector, Message Data Register, Message Upper Address, Message Addressです.まず,今回Configuration Spaceで設定されたエントリ数は64でしたが,実際には9つのエントリしか有効になっていないことが分かります(Control Vector=1の場合,その割り込みはマスクされる).また,65番目以降のエントリにアクセスすると0xDEADBEAFが返ってきます(これは仕様です).ixgbeのドライバでは基本的にコア数分だけキューを用意して,割り込みはキュー間で分散させるよう設定するのだと思います(このあたり,また後で詳しく調べたい).
Message Address Registerを見てみると,下位4bitがC0であることから,RH=1, DM=1であることが分かります.Data Registerの値は下位8bitの割り込みベクタ番号のみが違い,割り込みベクタはそれぞれA2h, B2h, C2h, D2h, E2h, 23h, 43h, 53h, 63hとなっています.Local APICでは割り込みベクタの上位4bitで割り込みの優先度が決まりますが,それぞれの割り込みで同じ優先度にならないように割り込みベクタを割り振っているようです.
割り込みベクタ番号と,linuxカーネル内で利用されるirq番号の対応はvector_irq
という名のstruct irq_desc*
の配列に入っています.以下のようなカーネルモジュールでベクタ番号とirq番号の確認ができます(vector_irq
はEXPORT_SYMBOL()
されていないので,kallsyms_lookup_name
を使う必要があります).
int hello_init(){ vector_irq_t* vector_irq = (vector_irq_t*)kallsyms_lookup_name("vector_irq"); int vector_num[] = {0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0x23, 0x43, 0x53, 0x63}; int i = 0; for (i = 0; i < 9; i++) { struct irq_desc* desc = __this_cpu_read((*vector_irq)[vector_num[i]]); if (IS_ERR_OR_NULL(desc)){ continue; } struct irq_data* data = irq_desc_get_irq_data(desc); pr_info("%x : %d\n", vector_num[i], data->irq); } return 0; }
実行結果:
% sudo insmod hello5.ko && sudo rmmod hello5 && dmesg | tail -n9 [91213.998593] a2 : 33 [91213.999377] b2 : 34 [91214.000155] c2 : 35 [91214.000933] d2 : 36 [91214.001710] e2 : 37 [91214.002555] 23 : 38 [91214.003359] 43 : 39 [91214.004136] 53 : 40 [91234.077288] 63 : 41
これより,各割り込みはこの環境ではirq番号33-41に対応していることが分かります.
割り込み状況の確認
/proc/interrupts
で割り込み状況を確認してみます.
% cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 ... 33: 52 1158 0 0 0 0 0 44844 PCI-MSI 2097152-edge enp4s0f0-TxRx-0 34: 1 45 45965 0 0 0 0 0 PCI-MSI 2097153-edge enp4s0f0-TxRx-1 35: 1 0 45 0 0 0 46211 0 PCI-MSI 2097154-edge enp4s0f0-TxRx-2 36: 1 0 0 45 0 0 0 45965 PCI-MSI 2097155-edge enp4s0f0-TxRx-3 37: 0 1150 0 1 45 0 44872 0 PCI-MSI 2097156-edge enp4s0f0-TxRx-4 38: 0 1145 0 0 44816 51 0 0 PCI-MSI 2097157-edge enp4s0f0-TxRx-5 39: 1 0 0 0 0 0 46066 0 PCI-MSI 2097158-edge enp4s0f0-TxRx-6 40: 0 0 0 0 45969 0 0 45 PCI-MSI 2097159-edge enp4s0f0-TxRx-7 41: 1 0 0 0 0 0 0 0 PCI-MSI 2097160-edge enp4s0f0 ...
各irqのsmp_affinityを確認すると,以下のようになっています.
% for i in `seq 33 1 41`; cat /proc/irq/$i/smp_affinity 80 04 40 80 40 10 40 10 08
この値をよく見てみると,ベクタ番号41を除き,smp_affinityの値はMSI Address Registerの12-19bit目と一致していることが分かります.また,それぞれのビットが立っている位置とCPU番号を対応付けて /proc/interrupts を見てみると,そのビットが立っているCPUに割り込みが多く入っていることが分かります.ということは,今現在MSI-Xのlogical apic IDはflat modeで運用されていると予想されます.flat modeかどうかはLocal APICのDestination Fromat Register (DFR)の値によります.また,Logical APIC IDはLogical Destination Register (LDR)に格納されています.LDRは0fee000d0h, FDRは0fee000e0h 番地にmemory-mappedされています.
以下のようなカーネルモジュールを書いて確認してみます(CPUはi7 3770Kです).
int hello_init(){ void *addr = ioremap(0xfee000D0, 32); pr_info("LDR,DFR : %08x, %08x \n", readl(addr), readl(addr+16)); iounmap(addr); return 0; }
実行結果:
% for i in `seq 0 1 7`; sudo taskset -c $i insmod hello.ko && dmesg | tail -n1 && sudo rmmod hello [81324.591351] LDR,DFR : 01000000, ffffffff [81324.651546] LDR,DFR : 02000000, ffffffff [81324.702683] LDR,DFR : 04000000, ffffffff [81324.758692] LDR,DFR : 08000000, ffffffff [81324.802557] LDR,DFR : 10000000, ffffffff [81324.855086] LDR,DFR : 20000000, ffffffff [81324.905689] LDR,DFR : 40000000, ffffffff [81324.945385] LDR,DFR : 80000000, ffffffff
DFRの31-28bit目が全て1なので,これはfalt modelです.また,各CPUコアごとに異なるビットがLogical APIC IDとして振られていることが分かります.この結果は/proc/interrupts の結果と矛盾しません(なんでデフォルトでMSI-Xの各エントリが各コアに対応していないのかはよく分かりませんが..).
smp_affintyを変化させた場合
smp_affinityを変更して各キューに一つのCPUが対応するようにしてみます.
% for i in `seq 0 1 7`; echo "obase=16; $((1<<i))" | bc | sudo tee /proc/irq/$((33+i))/smp_affinity
ちゃんと変更されちるかsmp_affinityを確認してみます.
% for i in `seq 0 1 8`; cat /proc/irq/$((33+i))/smp_affinity 01 02 04 08 10 20 40 80
で,これで割り込みがコアごとに分散されるかと思ったのですが,しばらく/proc/interrupts
観測を続けても特に変化なし..
おかしいと思ってMSI-X Tableを確認してみると,何も変わっていませんでした.
% for j in `seq 0 1 8`; do; for i in `seq $((($j+1)*16-4)) -4 $(($j*16))`; sudo ./pcimem /sys/bus/pci/devices/0000:04:00.1/resource4 $i w | tail -n1 | cut -d':' -f 2 | tr '\n' ' '; echo ; done 0x0 0x4183 0x0 0xFEE1000C 0x0 0x4193 0x0 0xFEE4000C 0x0 0x41A3 0x0 0xFEE0400C 0x0 0x41B3 0x0 0xFEE0800C 0x0 0x41C3 0x0 0xFEE1000C 0x0 0x41D3 0x0 0xFEE4000C 0x0 0x41E3 0x0 0xFEE0400C 0x0 0x4124 0x0 0xFEE8000C 0x0 0x4144 0x0 0xFEEFF00C
何が原因なのか分かりませんが,仕方ないので直接MSI-X TableのAddress RegisterのDestination Fieldを書き換えてみます.
% for i in `seq 0 1 7`; sudo ./pcimem /sys/bus/pci/devices/0000:04:00.1/resource4 $((i*16)) w "0xFEE"`echo "$((1<<i))"| awk '{printf "%02x",$1}'`"00C"
以下のように書き換わりました.
% for j in `seq 0 1 8`; do; for i in `seq $((($j+1)*16-4)) -4 $(($j*16))`; sudo ./pcimem /sys/bus/pci/devices/0000:04:00.1/resource4 $i w | tail -n1 | cut -d':' -f 2 | tr '\n' ' '; echo ; done 0x0 0x4183 0x0 0xFEE0100C 0x0 0x4193 0x0 0xFEE0200C 0x0 0x41A3 0x0 0xFEE0400C 0x0 0x41B3 0x0 0xFEE0800C 0x0 0x41C3 0x0 0xFEE1000C 0x0 0x41D3 0x0 0xFEE2000C 0x0 0x41E3 0x0 0xFEE4000C 0x0 0x4124 0x0 0xFEE8000C 0x0 0x4144 0x0 0xFEEFF00C
これで,割り込みがコアごとに分散されるはずです. しばらくpingを動かしてみると,以下のようになりました.
33: 1130 1158 0 0 0 0 0 45552 PCI-MSI 2097152-edge enp4s0f0-TxRx-0 34: 1 1102 46673 0 0 0 0 0 PCI-MSI 2097153-edge enp4s0f0-TxRx-1 35: 1 0 1109 0 0 0 46923 0 PCI-MSI 2097154-edge enp4s0f0-TxRx-2 36: 1 0 0 1102 0 0 0 46673 PCI-MSI 2097155-edge enp4s0f0-TxRx-3 37: 0 1150 0 1 1805 0 45580 0 PCI-MSI 2097156-edge enp4s0f0-TxRx-4 38: 0 1145 0 0 45524 1108 0 0 PCI-MSI 2097157-edge enp4s0f0-TxRx-5 39: 1 0 0 0 0 0 47832 0 PCI-MSI 2097158-edge enp4s0f0-TxRx-6 40: 0 0 0 0 46677 0 0 1102 PCI-MSI 2097159-edge enp4s0f0-TxRx-7 41: 1 0 0 0 0 0 0 0 PCI-MSI 2097160-edge enp4s0f0
数分後:
33: 1343 1158 0 0 0 0 0 45552 PCI-MSI 2097152-edge enp4s0f0-TxRx-0 34: 1 1314 46673 0 0 0 0 0 PCI-MSI 2097153-edge enp4s0f0-TxRx-1 35: 1 0 1323 0 0 0 46923 0 PCI-MSI 2097154-edge enp4s0f0-TxRx-2 36: 1 0 0 1314 0 0 0 46673 PCI-MSI 2097155-edge enp4s0f0-TxRx-3 37: 0 1150 0 1 2017 0 45580 0 PCI-MSI 2097156-edge enp4s0f0-TxRx-4 38: 0 1145 0 0 45524 1320 0 0 PCI-MSI 2097157-edge enp4s0f0-TxRx-5 39: 1 0 0 0 0 0 48044 0 PCI-MSI 2097158-edge enp4s0f0-TxRx-6 40: 0 0 0 0 46677 0 0 1314 PCI-MSI 2097159-edge enp4s0f0-TxRx-7 41: 1 0 0 0 0 0 0 0 PCI-MSI 2097160-edge enp4s0f0
この二つの差分をとると,以下のようになります.
33: 213 0 0 0 0 0 0 0 34: 0 212 0 0 0 0 0 0 35: 0 0 214 0 0 0 0 0 36: 0 0 0 212 0 0 0 0 37: 0 0 0 0 212 0 0 0 38: 0 0 0 0 0 212 0 0 39: 0 0 0 0 0 0 212 0 40: 0 0 0 0 0 0 0 212
目標通り,割り込みが分散されていることが確認できます.
smp_affinityを変更してもAddress Registerが書き変わらないのはixgbeドライバの 問題でしょうか.要調査ですね..