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


# 概要

この資料を見て、パケット処理の割り込みとマルチコアによるスケールが気になったので自分でも調べてみたことをまとめる.
[はてなにおけるLinuxネットワークスタックパフォーマンス改善 / Linux network performance improvement at hatena](https://speakerdeck.com/yuukit/linux-network-performance-improvement-at-hatena)

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

- 受信パケットはハード割り込みとソフト割り込みを使って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](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/images/packet-reception.png)

(図は[Performance Tuning Guide - ⁠8.3. Overview of Packet Reception - Figure 8.1. Network receive path diagram](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-network-packet-reception.html)より引用)

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

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

NICに限った話ではなく、一般的な話として[パフォーマンスチューニングガイド - ⁠4.3. 割り込みおよび IRQ チューニング](https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-cpu-irq.html)によれば/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](http://image.slidesharecdn.com/devconf2014-kernelnetworkingwalkthrough-140304102610-phpapp01/95/devconf-2014-kernel-networking-walkthrough-7-638.jpg?cb=1393929597)

(図は[DevConf 2014 Kernel Networking Walkthrough](http://image.slidesharecdn.com/devconf2014-kernelnetworkingwalkthrough-140304102610-phpapp01/95/devconf-2014-kernel-networking-walkthrough-7-638.jpg?cb=1393929597)より引用)

受信キューの割り当て状況がどうなっているかは先ほどの/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)](https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/network-rss.html)より引用)

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

- IPアドレス
- TCP Port

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

また同じく[Scaling in the Linux Networking Stack](https://www.kernel.org/doc/Documentation/networking/scaling.txt)によればHTは割り込みのスケールに影響を与えないため、受信キューの数は物理的なコア数に抑えた方がいい.

NICがRSSに対応してサーバの設定を行えばマルチコアスケールすることはできそう。ただし、元のスライドでマルチコアスケールしなかったのはEC2の仮想環境でvirtioを使用していたのかもしれない。[Redhatのドキュメント](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Virtualization_Tuning_and_Optimization_Guide/sect-Virtualization_Tuning_Optimization_Guide-Networking-Techniques.html#sect-Virtualization_Tuning_Optimization_Guide-Networking-Multi-queue_virtio-net)を見ると仮想環境でも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](https://www.kernel.org/doc/Documentation/networking/scaling.txt)
[[Network]VIOPS06で「RPS・RFS等最新Linux Kernel事例」と題してお話してきました](http://d.hatena.ne.jp/syuu1228/touch/20110722/1311322653)
[イマドキなネットワークI/O](http://www.vyatta-users.jp/usermeeting/jvug02/imadoki-net-io.pdf)
[Linux tips Network Interrupts handling and monitor](http://yuzhang2.blogspot.jp/2011/12/network-interrupts-handling-and-monitor.html?m=1)
[SMP affinity and proper interrupt handling in Linux](http://www.alexonlinux.com/smp-affinity-and-proper-interrupt-handling-in-linux)
[Introduction to Linux Interrupts and CPU SMP Affinity](http://www.thegeekstuff.com/2014/01/linux-interrupts/)
[⁠4.3. Interrupts and IRQ Tuning](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-cpu-irq.html)
[⁠8.3. Overview of Packet Reception](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-network-packet-reception.html)
[Monitoring and Tuning the Linux Networking Stack: Receiving Data](http://blog.packagecloud.io/eng/2016/06/22/monitoring-tuning-linux-networking-stack-receiving-data/)
[DevConf 2014 Kernel Networking Walkthrough](http://www.slideshare.net/ThomasGraf5/devconf-2014-kernel-networking-walkthrough)
[SMP IRQ affinity](https://www.kernel.org/doc/Documentation/IRQ-affinity.txt)