Edited at

パケット処理のマルチコアスケールについて

More than 3 years have passed since last update.


概要

この資料を見て、パケット処理の割り込みとマルチコアによるスケールが気になったので自分でも調べてみたことをまとめる.

はてなにおけるLinuxネットワークスタックパフォーマンス改善 / Linux network performance improvement at hatena

まず上記資料を3行でまとめると


  • 受信パケットはハード割り込みとソフト割り込みを使ってCPUに処理される

  • 受信パケットの割り込みが特定のCPUにしかいかずマルチコアスケールしない問題に遭遇

  • RPS (Receive Packet Steering)を使用してマルチコアで受信パケットを処理しロードバランサーのネットワーク処理の性能を向上させた

資料を見ていて以下の点が気になったので、ここら辺をスッキリさせるためにいくつかの資料を読み、まとめておく


  • 割り込みが特定のCPUにしかいかなかった理由


  • RPS(Receive Packet Sterring), RFS(Receive Flow Sterring), RSS(Receive Side Scaling)といったマルチコアスケールさせるための技術について



Linux 受信パケットの処理の流れ

まずLinuxにおける受信パケットの流れを Redhatのドキュメントでおさらいしておく.


  1. NICに到着したパケットはDMAを用いてメモリ上に転送される

  2. NICはCPUにハードウェア割り込みを行いパケットが来た事を伝え、ソフトウェア割り込みをスケジュールさせる

  3. ソフトウェア割り込みの処理によりパケットのデータはソケットバッファーにコピーされる

  4. アプリケーションがソケットからデータを受信する

Figure 8.1. Network receive path diagram

(図はPerformance Tuning Guide - ⁠8.3. Overview of Packet Reception - Figure 8.1. Network receive path diagramより引用)

では、複数のCPUがあるシステム上で、NICはどのCPUに割り込みを行うのか?


割り込み先のCPUはどう決まるのか

NICに限った話ではなく、一般的な話としてパフォーマンスチューニングガイド - ⁠4.3. 割り込みおよび IRQ チューニングによれば/procの下記ファイルによってIRQ番号毎にどのプロセッサに対して割り込みが行われるかを調整(及び現在の設定の確認が)できる.

#設定値の確認(0ビット目がCPU0, 1ビット目がCPU1という順に対応している)

$sudo cat /proc/irq/IRQ_NUMBER/smp_affinity

#設定値の変更
$sudo echo <ビットマスク> > /proc/irq/IRQ_NUMBER/smp_affinity

また、どのIRQ番号がどのCPUに今までどれくらい割り込みを起こしているかは以下で確認できる。

$cat /proc/interrupts

では、NICのパケット受信時の割り込み先CPUはどう決まるのか?何の機能も使わなければ恐らく特定のコアにしか割り込みがいかないのだと思う。しかし、RSS(Receive-Side Scaling)が使えればマルチコアに割り込みを分配できるようだ。


RSS(Receive-Side Scaling)


  • RSSをサポートしているNICはパケットの受信キューを複数持つ事が可能

  • 各受信キューを異なるCPUに割り当てることが可能

  • 割り込みは受信キュー毎に行うえるため、受信キューを異なるCPUに割り当てれば割り込みが特定のコアに偏らなくなるハズ

(図はDevConf 2014 Kernel Networking Walkthroughより引用)

受信キューの割り当て状況がどうなっているかは先ほどの/proc/interruptsを見る事で分かる。

# egrep 'CPU|p1p1' /proc/interrupts

CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
89: 40187 0 0 0 0 0 IR-PCI-MSI-edge p1p1-0
90: 0 790 0 0 0 0 IR-PCI-MSI-edge p1p1-1
91: 0 0 959 0 0 0 IR-PCI-MSI-edge p1p1-2
92: 0 0 0 3310 0 0 IR-PCI-MSI-edge p1p1-3
93: 0 0 0 0 622 0 IR-PCI-MSI-edge p1p1-4
94: 0 0 0 0 0 2475 IR-PCI-MSI-edge p1p1-5

(コマンドの実行結果はパフォーマンスチューニングガイド - ⁠8.6. Receive-Side Scaling (RSS)より引用)

パケットを受信した際に、どの受信キューに入れられるかはパケットの以下の値のハッシュ値によって決まる。


  • IPアドレス

  • TCP Port

IRQは上記にも記載した通り、どのCPUに割り込みをかけるか/proc以下の値を調整する事で変更が可能。Scaling in the Linux Networking Stackによれば、irqbalanceデーモンによってIRQをどのCPUに割り振るかを動的に最適化する事もできる。

また同じくScaling in the Linux Networking StackによればHTは割り込みのスケールに影響を与えないため、受信キューの数は物理的なコア数に抑えた方がいい.

NICがRSSに対応してサーバの設定を行えばマルチコアスケールすることはできそう。ただし、元のスライドでマルチコアスケールしなかったのはEC2の仮想環境でvirtioを使用していたのかもしれない。Redhatのドキュメントを見ると仮想環境でもRSS(仮想のネットワークドライバ上でRSSっぽいのを実現しているだけ?)でマルチコアスケールさせることは出来そうだけど、ホストOS側も管理出来るような状況じゃないと無理かな?

multiqueue virtioはここら辺のドキュメントが詳しそう.仮想環境のネットワーク回りも面白そうなのでまた別途まとめる.

http://www.linux-kvm.org/page/Multiqueue#Multiqueue_virtio-net


RPS(Receive Packet Sterring)

RPSについては元のドキュメントに書いてあったので、ここではあまり詳しくは触れない.

ただ、仮想環境ではRPSは逆にパフォーマンスを悪化させるようなことを書いてあるドキュメントも見つけたので、ここら辺の設定をいじる時は要検証.

https://www.nic.ad.jp/ja/materials/iw/2011/proceedings/s09/s09-02.pdf


RFS


  • cpu cacheを意識したRPS

  • RPSを使ってパケットの受信処理を分散させても、分散させた先でアプリケーションがパケットの処理をするとは限らないため、そのようなケースではキャッシュミスが発生してパケット処理に余計な遅延が発生してしまう

  • RPSでは受信パケットを処理するアプリケーションのスレッドがどのcpuで処理されたかに基づいてパケットをキューイングする受信キューを変更する


参考資料

Scaling in the Linux Networking Stack

[Network]VIOPS06で「RPS・RFS等最新Linux Kernel事例」と題してお話してきました

イマドキなネットワークI/O

Linux tips Network Interrupts handling and monitor

SMP affinity and proper interrupt handling in Linux

Introduction to Linux Interrupts and CPU SMP Affinity

⁠4.3. Interrupts and IRQ Tuning

⁠8.3. Overview of Packet Reception

Monitoring and Tuning the Linux Networking Stack: Receiving Data

DevConf 2014 Kernel Networking Walkthrough

SMP IRQ affinity