カーネルのパケット受信経路:NAPI と割り込み緩和
高負荷時にCPUが割り込みで埋め尽くされ受信が破綻する理由と、NAPIがポーリングへ切り替えてそれを防ぐ原理が分かる。RPS/RFSによる複数コア分散とリングバッファ設計まで内部から押さえられる。
- 1.NICは受信パケットをDMAでリングバッファに置き割り込みを上げるが、毎パケット割り込みだと高負荷時に割り込みストームでCPUが飽和する。NAPIは割り込みを止めてポーリングへ切り替え、1回のソフトIRQで複数パケットをまとめて刈り取ることで割り込み回数を抑える。
- 2.受信処理はハードIRQ(最小限・割り込み禁止)とソフトIRQ(NET_RX、ksoftirqd)に分離され、ハードIRQはNAPIをスケジュールするだけで即座に返り、重いプロトコル処理はソフトIRQ側で予算(budget)の上限まで行う。
- 3.単一コアがソフトIRQで律速にならないよう、RPSはパケットのハッシュで処理を別コアへソフトウェア分散し、RFSはアプリが動くコアへ受信処理を寄せてキャッシュ局所性を高める。ハード分散はRSSがNIC側で行う。
毎パケット割り込みはなぜ破綻するのか
NICがパケットを受け取ると、まずそのデータをホストメモリの 受信リングバッファ へ DMA で書き込み、続いてCPUへ 割り込み(ハードウェア割り込み, ハードIRQ) を上げて「パケットが届いた」と知らせます。素朴な設計では1パケットごとに1割り込みです。低負荷ならこれで何の問題もありません。
ところが回線が10Gbps級になり、毎秒数百万パケットが届く状況を考えてください。割り込みは現在実行中の処理を強制的に中断させる重い操作で、コンテキスト保存・割り込みハンドラ起動・復帰のコストが毎回かかります。割り込み頻度が高すぎると、CPUは割り込み処理だけで時間を使い果たし、肝心のパケット本体を処理する余力がなくなります。最悪の場合、割り込み処理に追われてアプリもカーネルのプロトコル処理も前進できなくなる receive livelock(受信ライブロック) に陥ります。これが 割り込みストーム の害です。
割り込みの本質は「いま走っているコードを止めて割り込みハンドラへ飛ぶ」ことです。1回あたりのオーバーヘッドは小さくても、毎秒100万回起きれば積み上がってCPUを食い潰します。スループットが上がるほど割り込み頻度が上がり、ある点を超えると逆に処理量が落ちる——この非線形な崩れ方が livelock の怖さです。
NAPI:割り込みを止めてポーリングへ切り替える
この問題を解くのが NAPI(New API) です。発想は単純で、負荷が高いときは割り込みをやめてポーリングに切り替える というものです。
動作はこうです。最初のパケットでNICが割り込みを上げると、ドライバのハードIRQハンドラは(1)このデバイスの 受信割り込みを一旦マスク(無効化) し、(2)NAPIのポーリングを スケジュール するだけで即座に返ります。以降カーネルは割り込みを待たず、ドライバの poll() 関数を呼んでリングバッファに溜まったパケットを まとめて刈り取り(poll) ます。リングが空になったらポーリングを終了し、再び割り込みを有効化して低負荷モードへ戻ります。
低負荷: 割り込み駆動(パケットごとに割り込み、レイテンシ最小)
│ パケット殺到で割り込み発生
▼
高負荷: 割り込みマスク → poll でまとめ刈り取り(割り込み回数を抑制)
│ リングが空 = 負荷が引いた
▼
低負荷へ復帰: 割り込み再有効化
要点は 負荷適応 であること。低負荷ではパケットごとに割り込みが上がり遅延が小さく、高負荷では割り込みを止めて1回のポーリングで何百パケットもまとめて処理するため、割り込み回数がパケット数から切り離されます。「割り込みとポーリングのいいとこ取り」を負荷に応じ自動で行う仕組みです。
NICには rx-usecs / rx-frames のように「N個たまる/T時間経つまで割り込みを遅らせる」ハードウェアの 割り込み合体 設定もあります。これは割り込み“発火そのもの”を間引く仕組みで、NAPI(割り込みを止めてソフトでポーリングする仕組み)と補完関係にあります。合体を効かせすぎると低負荷時のレイテンシが悪化するため、両者を合わせてスループットと遅延のバランスを取ります。
ハードIRQとソフトIRQ:処理の二段分割
NAPIを理解する鍵は、受信処理が 2つのフェーズに分割 されている点です。割り込みハンドラ(ハードIRQ)の中では割り込みが禁止されており、ここに重い処理を書くと他の割り込みを長く塞いでしまいます。そこでLinuxは「すぐ済む最小限」と「重いが急がない本体」を分けます。
- ハードIRQ(top half): 割り込み禁止コンテキストで走る。やることは最小限——割り込みをマスクしてNAPIポーリングをスケジュールし、即返る。
- ソフトIRQ(bottom half,
NET_RX_SOFTIRQ): 割り込みを許可した状態で走る遅延処理。ここでpoll()を回し、リングからパケットを取り出してプロトコルスタック(IP・TCP/UDP)へ流し込む重い処理を行う。
ソフトIRQは割り込み復帰時などに実行されますが、ソフトIRQ処理が長引いてCPUを占有し続けないよう、1回の処理には 予算(budget) という上限が設けられています。poll() が1度に刈り取れるパケット数の上限(weight)と、ソフトIRQ全体で処理できる総量(netdev_budget と時間)です。予算を使い切ってもまだパケットが残っていれば、ソフトIRQはそこで中断し、ksoftirqd というカーネルスレッドへ処理を委ねます。
ハードIRQ: NAPIをスケジュール(割り込み禁止・最小限)
│
▼
ソフトIRQ NET_RX: poll() で budget 件まで刈り取り → スタックへ
│ budget を超えてもまだ残っている
▼
ksoftirqd: 残りを通常スレッドとして処理(他タスクと公平にスケジュール)
ksoftirqd/N がCPUを多く使っているのは、その都度のソフトIRQ予算内で受信を捌き切れず、繰り返し残りをスレッドへ追い出しているサインです。つまり該当コアがパケット処理で律速になっている。原因はトラフィック過多のこともあれば、後述するように受信処理が単一コアに偏っていることもあります。まず「どのコアのソフトIRQが詰まっているか」を見極めるのが定石です。
リングバッファ:NICとカーネルの受け渡し場所
受信リングバッファ(RX ring)は、NICとカーネルがパケットを受け渡す 固定長の環状ディスクリプタ配列 です。各エントリは、DMAで書き込む先のメモリ(受信バッファ)を指すディスクリプタです。
仕組みは生産者・消費者モデルです。NICが空きディスクリプタの指すバッファへパケットをDMAで書き込んで進める側(生産者)、カーネルが poll() でパケットを取り出し、新しい空きバッファを補充して進める側(消費者)です。両者は別々のポインタ(head/tail)で環を周回します。
ここで重要なのが オーバーフロー です。トラフィックが届くペースにカーネルの刈り取りが追いつかないと、リングに空きディスクリプタがなくなり、以降に届いたパケットはNICが ドロップ します。これが ethtool -S で見える rx_dropped / rx_no_buffer の正体です。
| 症状 | 起きていること | 効きやすい対処 |
|---|---|---|
| rx_dropped 増加 | リングが満杯で刈り取りが追いつかない | リングサイズ拡大・RPS/RFSでコア分散 |
| ksoftirqd 高負荷 | 単一コアのソフトIRQ予算オーバー | 受信処理を複数コアへ分散 |
| 低負荷なのに高遅延 | 割り込み合体の効かせ過ぎ | rx-usecs を下げる |
リングを大きくすればバースト耐性は上がりますが、滞留時間(遅延)が伸び、根本的に処理が追いつかないなら溢れは止まりません。リング拡大は「瞬間的なバーストの吸収」には効いても、「定常的な処理能力不足」の解にはならない、と切り分けるのが肝心です。ユーザー空間への往復コピーを削る話は ソケットバッファとゼロコピー送受信 で、カーネルそのものを外す究極形は RDMA と RoCE:カーネルバイパス転送の原理 で扱っています。
RPS と RFS:受信処理を複数コアへ分散する
ここまでで「割り込みは抑えた」「重い処理はソフトIRQへ回した」状態ですが、もう一つの壁があります。ソフトIRQは割り込みを受けたコアで走る ため、1つのキューしか持たないNICでは受信処理が 単一コアに集中 し、そのコアが100%になって他コアが遊ぶ、という偏りが起きます。
これを分散する仕組みが3層あります。まずハードウェアの RSS(Receive Side Scaling) で、NICが複数の受信キューを持ち、パケットの5タプル(送信元/宛先IP・ポート・プロトコル)の ハッシュ で振り分け、キューごとに別コアへ割り込みを上げます。NICが複数キュー対応ならこれが一次分散です。フローのハッシュで経路や処理先を分ける一般原理は ECMP とフローハッシュによる負荷分散 と同じ考え方です。
RSSが使えない(単一キューNIC)場合や、さらにソフトで散らしたい場合に使うのが RPS(Receive Packet Steering) です。RPSはソフトIRQの中でパケットのハッシュを計算し、処理を 別コアのバックログキューへ転送 して、そのコアでプロトコル処理を行わせます。RSSのソフトウェア版で、5タプルのハッシュにより 同一フローは同じコア へ送られ、TCPのパケット順序が保たれます。
| 機構 | 層 | 振り分け基準 | 狙い |
|---|---|---|---|
| RSS | NICハードウェア | 5タプルのハッシュ→受信キュー | 割り込み段階で複数コアへ |
| RPS | カーネル(ソフトIRQ内) | 5タプルのハッシュ→別コア | 単一キューNICでもコア分散 |
| RFS | カーネル(フロー追跡) | アプリが動いているコア | 受信処理とアプリを同コアに寄せる |
RPSの弱点は、ハッシュで決めた処理コアと、recvmsg() でデータを読むアプリが動いているコアが 一致するとは限らない 点です。受信処理をコアAで終えても、アプリがコアBにいれば、データがコア間を渡りCPUキャッシュのミスが増えます。これを直すのが RFS(Receive Flow Steering) です。RFSはフローごとに「直近どのコアのアプリが recvmsg() したか」を記録し、そのフローの受信処理を アプリと同じコア へ寄せます。受信処理・プロトコル処理・アプリ読み出しが同一コアに揃うことで、キャッシュ局所性が最大化され遅延が下がります。フローと処理先の対応はTCP/UDPの識別子に基づくため TCP と UDP の違い の5タプルが土台になります。
「なぜ毎パケット割り込みが破綻するか」→ 高pps時に割り込み処理でCPUが飽和しreceive livelockに陥る。「NAPIの本質」→ 高負荷時に割り込みをマスクしポーリングへ切替、1回のソフトIRQで複数パケットをまとめ刈り取り、負荷適応で割り込みとポーリングを使い分ける。「ハードIRQとソフトIRQの分担」→ ハードIRQはNAPIスケジュールのみで即返り、重い処理はソフトIRQ(NET_RX)が予算上限まで、超過分はksoftirqd。「RSS/RPS/RFSの違い」→ RSSはNICハードのキュー分散、RPSはソフトでコア分散、RFSはアプリのコアへ寄せてキャッシュ局所性を上げる。
まとめ
NICは受信パケットをDMAでリングバッファに置き割り込みを上げますが、毎パケット割り込みは高pps時に割り込みストームを起こしCPUを飽和させ、receive livelockを招きます。NAPIは高負荷時に割り込みをマスクしてポーリングへ切り替え、1回のソフトIRQで複数パケットをまとめて刈り取ることで割り込み回数をパケット数から切り離します。処理はハードIRQ(最小限・NAPIスケジュールのみ)とソフトIRQ(NET_RXで予算上限まで処理、超過分はksoftirqd)に二段分割され、リングの溢れは刈り取りの遅れを意味します。そして単一コアへの集中はRSS(NICハード)・RPS(ソフトのコア分散)・RFS(アプリのコアへ寄せる)で散らす——割り込みを抑え、処理を遅延実行へ回し、複数コアへ広げる、という三段構えで高負荷の受信を支えるのがLinuxのパケット受信経路の全体像です。
ネットワーク Article
カーネルのパケット受信経路:NAPI と割り込み緩和を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
NAPI
比較で見る軸
難易度: advanced / カテゴリ: ネットワーク / タグ数: 5
導入後に効く点
受信処理はハードIRQ(最小限・割り込み禁止)とソフトIRQ(NET_RX、ksoftirqd)に分離され、ハードIRQはNAPIをスケジュールするだけで即座に返り、重いプロトコル処理はソフトIRQ側で予算(budget)の上限まで行う。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ネットワーク
- タグ数
- 5
判断チェックリスト
- 自社の用途が「NAPI / 割り込み」に近いか確認する。
- 強みである「NICは受信パケットをDMAでリングバッファに置き割り込みを上げるが、毎パケット割り込みだと高負荷時に割り込みストームでCPUが飽和する。NAPIは割り込みを止めてポーリングへ切り替え、1回のソフトIRQで複数パケットをまとめて刈り取ることで割り込み回数を抑える。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。