LinuxでIRQの割り込みを複数CPUに分散する方法

2019年4月4日

マルチプロセッサ

周辺ハードウェアはCPUに処理して欲しい出来事があるとCPUに割り込みをかけて処理してもらいます。

例えばネットワークインターフェースカード(NIC)にパケットが到着した時などです。

パケットが到着するごとにCPUに割り込みが入ると効率が悪いので、ここでは割愛しますが現在のLinuxには割り込みを緩やかにする仕組みがあります。

デフォルトではこの割り込み処理は先頭のCPUが受け持つ事になるので、トラフィックが多いサーバ等では先頭のCPUだけ負荷が高くなることがあります。

IRQごとに担当CPUを割り当てることによって、複数NICを搭載した環境では複数のCPUに負荷を分散することができます。

割り込み数の確認方法

procファイルシステムのinterruptsを見ると現在の割り込み数の統計を確認することができます。

$ cat /proc/interrupts 
           CPU0       CPU1       
  0:         25          0   IO-APIC   2-edge      timer
  8:          1          0   IO-APIC   8-edge      rtc0
  9:          0          0   IO-APIC   9-fasteoi   acpi
 14:    1640643          0   IO-APIC  14-edge      pata_sch
 15:          0          0   IO-APIC  15-edge      pata_sch
 18:          0          0   IO-APIC  18-fasteoi   uhci_hcd:usb4
 19:          0          0   IO-APIC  19-fasteoi   uhci_hcd:usb3
 21:         18          0   IO-APIC  21-fasteoi   ehci_hcd:usb1
 23:          0          0   IO-APIC  23-fasteoi   uhci_hcd:usb2
 24:   12368433          0   PCI-MSI 1048576-edge      eth0
 25:   36473102          0   PCI-MSI 1572864-edge      eth1
NMI:          0          0   Non-maskable interrupts
LOC:  226472199  217458810   Local timer interrupts
SPU:          0          0   Spurious interrupts
PMI:          0          0   Performance monitoring interrupts
IWI:          0          0   IRQ work interrupts
RTR:          0          0   APIC ICR read retries
RES:     421745     436286   Rescheduling interrupts
CAL:     651056   16002572   Function call interrupts
TLB:      89231      73300   TLB shootdowns
ERR:          0
MIS:          0
PIN:          0          0   Posted-interrupt notification event
NPI:          0          0   Nested posted-interrupt event
PIW:          0          0   Posted-interrupt wakeup event

上は現在の私の自宅サーバのinterruptsを表示したものになります。

IRQ24と25がeth0と1ということが分かると思います。

私の環境でCPUは0と1の2つあるように見えますが、残念ながら32bitのATOMプロセッサでHyper Threadingを有効にしてあるのでそう見えているだけです。

Hyper Threadingに分散することはできませんが、2コア以上の環境であればそれぞれのIRQの割り込み処理を別々のCPUに割り当てて負荷を分散することが可能です。

割り込みを処理するCPUの割り当て

現在のCPU割り当ての設定はprocファイルシステムのsmp_affinityを読み出す事で確認できます。

$ cat /proc/irq/IRQ番号/smp_affinity

ex. IRQ24の設定値の確認
$ cat /proc/irq/24/smp_affinity
1

smp_affinityはビットごとのフラグになっており、下位ビットから順番に1ビット目が1番目のCPU、2ビット目が2番目のCPU、・・・と対応しています。

3番目のCPUは2進数で100の4、4番目のCPUは2進数で1000の8となることに注意して下さい。

そして以下のようにIRQごとに処理するCPUを割り当てることができます。

# echo 設定値 >/proc/irq/IRQ番号/smp_affinity

ex. IRQ25を5番目のCPUに割り当てる場合
# echo 16 >/proc/irq/25/smp_affinity

まとめ

物理的に複数のNICとCPUを搭載したマシンで、システムの負荷が1つのCPUに偏っている場合は状況をみて適切に設定してあげるとシステム全体のパフォーマンスが改善する事があります。

このIRQによる物理的な負荷分散以外にもLinuxのネットワークについてはRSS(Receive Side Scaling)やRPS(Receive Packet Steering)、RFS(Receive Flow Steering)といった負荷分散の仕組みがあります。

これらについてはまた別の機会に解説していこうかと思いますので、今後ともこのアカスブログを宜しくお願い致します。