NFSでksoftirqdが詰まる → softnet_statで裏どりし、netdev_budget調整で90s→40sに短縮した話
この記事で言いたいこと(TL;DR)
- NFS 読み込み時に ksoftirqd が占有してユーザ処理が遅延。
-
/proc/net/softnet_statの 3列目(time_squeeze=NAPIポーリングの失敗数) がガンガン増えており、「SoftIRQの処理時間が足りていない」状況を確認。 -
net.core.netdev_budget/net.core.netdev_budget_usecsを 2倍程度に拡大したところ、約90秒 → 約40秒まで短縮。
背景・構成
- ホストA上: VMA(アプリ実行)
- ホストB上: VMB(ここで NetApp Storage を NFS マウント)
- アプリが読み込むデータは NetApp Storage上にあり、VMA → VMB 経由で読み込む構成

症状
VMA上のアプリ実行時にksoftirqd/1がコアを長時間占有し、pt_main_thread(ユーザ処理)が目に見えて遅くなる。
前提知識
-
SoftIRQ: ソフト割り込み処理の略。割り込みとは外部デバイスからイベントを通知する為の仕組み。例えば、キーボードのキーを押下すると、キーボード デバイスからキーが押下されたことをCPUへ通知されることを割込みと言う。
今回のソフト割り込みは、割込み処理のうち、緊急度がそれほど高くなく、遅れて実行される処理。*1 -
NAPI: New APIの略。NICがネットワークからパケットを受け取って、DMAでRx(受信)リング(いわゆるリングバッファ)にデータを書き込みNICに対応するドライバがNAPIを呼び出す。NAPIを呼び出す過程で割り込みハンドラがSoftIRQを発生させる👈今回のポイント。
この説明だと省略しすぎているので、参考文献の記事を読むともっと詳細に書かれていてわかりやすいです。 -
NAPIポーリング: ドライバからNAPIがRxリングにデータを取りに行って、SKB化(次のレイヤのソケットでデータを扱えるようにする処理)を行うこと。
仮説
- MTU や VM 間よりも、VM の NIC ↔ カーネル間の処理がボトルネック。
- 割り込みハンドラがNAPIをスケジュールするときにNET_RX SoftIRQ を起こす(raise)。
SoftIRQの回数が増える原因は、ポーリング1回で取り切れずに再スケジュールが繰り返されるから。
見たもの(softnet_stat)
観測コマンド(1sごとにNAPIポーリングとかの結果がわかる。):
while :; do
date '+%F %T'
awk '{for (i=1; i<=NF; i++) printf strtonum("0x" $i) (i==NF?"\n":" ")}' /proc/net/softnet_stat | column -t
sleep 1
done
主要列の意味(左から)
1列目: processed(受信処理数累計)
2列目: dropped(バックログ溢れ等のドロップ)
3列目: time_squeeze(NAPI ポーリングのリソース不足による打ち切り回数)
10列目: CPU 番号
観測結果(抜粋)
time_squeeze が特定 CPU で秒ごとに大きく増加。dropped は 0 だが、リソース不足で再スケジュールが続いていることが読み取れた。
→ SoftIRQ の処理時間/パケット数の割り当てを増やす方向でチューニング。
実験パターン
事前操作:ページキャッシュのリセット
echo 3 | sudo tee /proc/sys/vm/drop_caches
これはカーネルキャッシュのリセット。/proc/sys/vm/drop_cachesに3を書き込むとカーネルキャッシュがドロップする。ここにもNFSのデータはキャッシュされる。最初にキャッシュをクリアすることで、今回のパターンに影響が出ないようにした。
パターンと所見
| パターン | VMA のページキャッシュ | VMB 側キャッシュ | 所見 |
|---|---|---|---|
| A | 消す | 消す | 1 回の NAPI ポーリングサイクルでインターフェイスからすべてのパケットを取得できなかった softirqd プロセスの回数 が顕著に増 → IRQ 由来の詰まり発生 |
| B | 維持 | 消す | 詰まりなし |
| C | 消す | 維持 | 詰まり発生 |
結論(中間)
CPU 側のページキャッシュを消すと SoftIRQ(受信処理)に負荷が集中し、リソース不足→再スケジュールが多発した。B は安定。
対処:SoftIRQ のリソースを増やす
NAPI 1 サイクルの処理量を増やすため、**netdev_budget(パケット数)とnetdev_budget_usecs(時間)**を引き上げる。
設定ファイル(恒久化)
#sudo vi /etc/sysctl.d/10-netdev_budget.conf
net.core.netdev_budget=600
net.core.netdev_budget_usecs=8000
反映
sudo sysctl -p /etc/sysctl.d/10-netdev_budget.conf
反映確認
cat /proc/sys/net/core/netdev_budget
cat /proc/sys/net/core/netdev_budget_usecs
# 期待: 600 / 8000
変更後は /proc/net/softnet_stat の 3列目増加が収まるか、を確認する。
効果
-
softnet_statの time_squeeze 増加がなくなった。 - 同一ワークロードで 約 90 秒 → 約 40 秒に短縮(NFS 読み出しの所要時間)。
再現・確認用スニペット集
softnet_stat の秒差分(ざっくり)
以下が1秒ごとにsoftnet_statファイルを出力するコマンド。以下のように3列目の値が増加する傾向にあったらこれが原因。
3番目の列:1回の NAPI ポーリングサイクルでインターフェイスからすべてのパケットを取得できなかったsoftirqd プロセスの回数。
while :; do
date '+%F %T'
awk '{for (i=1; i<=NF; i++) printf strtonum("0x" $i) (i==NF?"\n":" ")}' /proc/net/softnet_stat | column -t
sleep 1
done
2025-08-13 06:56:55
221951548 0 0 0 0 0 0 0 0 0 0 0 0
192058677 0 **20380** 0 0 0 0 0 0 0 0 0 1
455324886 0 0 0 0 0 0 0 0 0 0 0 2
2025-08-13 06:56:56
221951548 0 0 0 0 0 0 0 0 0 0 0 0
192058677 0 **20580** 0 0 0 0 0 0 0 0 0 1
455324886 0 0 0 0 0 0 0 0 0 0 0 2
さいごに(学び)
- ksoftirqd が暴れている=悪ではなく、NAPI のリソース不足で何回も再スケジューリングが走って、結果として占有が長く見えているだけというケースがある。
付録:後処理コマンド群
sudo rm -f /etc/sysctl.d/10-netdev_budget.conf
sudo sysctl -p || true
# あるいは既定値に戻す(以下がUbuntu 22.04の規定値だった。)
sudo sysctl -w net.core.netdev_budget=300
sudo sysctl -w net.core.netdev_budget_usecs=2000
参考記事
ネットワーク通信がアプリケーションによって利用されるまで
2.5. CPU 上で SoftIRQ を実行できる時間の増加
*1【読解入門】Linuxカーネル (スケジューラと割込みハンドラの関係)
新Linuxカーネル解読室 - パケット受信処理 ~Ethernetドライバ 概要編~
Discussion