Linuxカーネル7.0のPREEMPT_NONE廃止でPostgreSQLスループット半減、カーネル開発者はリバート拒否
目次
AWSエンジニアの Salvatore Dipietro が4月3日、Linuxカーネル 7.0-rc でPostgreSQLのスループットが約51%まで低下するリグレッションをLKML(Linux Kernel Mailing List)に報告した。原因はカーネル 7.0で PREEMPT_NONE と PREEMPT_VOLUNTARY が主要アーキテクチャから削除され、PREEMPT_LAZY と PREEMPT_FULL のみになったこと。スケジューラメンテナの Peter Zijlstra(Intel)はリバートを拒否し、PostgreSQL側にカーネル 7.0の新機能「RSEQ timeslice extension」を使えと返答した。
記事タイトルにある「Linuxカーネル」は、UbuntuやFedoraといったLinuxディストリビューション(OS)の中核部分にあたるソフトウェアで、プロセス管理やメモリ管理などハードウェアに近い処理を受け持つ。普段はディストリビューション側がカーネルのバージョン選定やパッチ適用を行うため、サーバー管理者が直接カーネルバージョンを意識する場面は少ない。今回問題になっている「カーネル 7.0」の安定版リリースは4月中旬予定で、以下のディストリビューションに搭載される。
| ディストリビューション | カーネル 7.0の搭載時期 | 影響度 |
|---|---|---|
| Ubuntu 26.04 LTS | 4月23日リリース予定 | 直撃。エンタープライズ採用が多く影響大 |
| Fedora 44 | リリース後すぐに追従 | 直撃 |
| Arch Linux | リリース後すぐに追従 | 直撃 |
| CentOS / RHEL | 独自バックポートのため当面搭載しない | すぐには影響しない |
| Amazon Linux | 独自バックポートのため当面搭載しない | すぐには影響しない |
つまり、Ubuntu LTSへのアップグレードやFedora/Archの通常アップデートでカーネル 7.0に更新されたPostgreSQLサーバーが、直撃を受ける可能性がある。
Linuxカーネルには「ユーザースペースを壊すな」という鉄則がある。Linus Torvaldsが繰り返し明言してきたこのルールに照らすと、今回の対応はかなり異例だ。
プリエンプションモデルとは何か
Linuxカーネルのプリエンプション(preemption)は、実行中のプロセスをカーネルが強制的に中断して別のプロセスにCPUを渡す仕組み。どのタイミングで中断を許すかによって、複数のモデルが存在する。
| モデル | 動作 | 用途 |
|---|---|---|
| PREEMPT_NONE | カーネルコードの実行中はプリエンプトしない | サーバー向け。スループット最優先 |
| PREEMPT_VOLUNTARY | 明示的なプリエンプションポイントでのみ中断 | デスクトップ向けの妥協点 |
| PREEMPT_LAZY | タイムスライス境界でのみプリエンプト(即座には中断しない) | Linux 6.13で導入された新モデル |
| PREEMPT_FULL | いつでもプリエンプト可能 | リアルタイム性重視 |
PREEMPT_NONE はサーバーワークロードで広く使われてきた。カーネルがプロセスを途中で止めないため、ユーザースペースのクリティカルセクション(排他制御を行う区間)が中断されるリスクが最小限に抑えられる。
Linux 7.0では Peter Zijlstra の長期的なプリエンプションモデル簡素化の一環として、x86、ARM64、LoongArch、PowerPC、RISC-V、s390といった主要アーキテクチャで PREEMPT_NONE と PREEMPT_VOLUNTARY が選択肢から削除された。PREEMPT_LAZY(Linux 6.13で導入)が PREEMPT_NONE の後継という位置づけだが、名前の通り「遅延」するだけで、プリエンプション自体は発生する。
なぜPostgreSQLだけが深刻な影響を受けるのか
PostgreSQLはコネクションごとにプロセスをforkするアーキテクチャを採用している。プロセス間で共有バッファプール(shared_buffers)にアクセスする際、ユーザースペースのスピンロック(s_lock)で排他制御を行う。
PREEMPT_NONEの場合(従来):
graph TD
A[スピンロック取得] --> B[カーネルはプリエンプトしない]
B --> C[クリティカルセクション即完了]
C --> D[スピンロック解放]
D --> E[他プロセスの待ち時間: 最小]
PREEMPT_LAZYの場合(カーネル 7.0):
graph TD
A[スピンロック取得] --> B[カーネルがプリエンプト]
B --> C[ロック保持プロセスが中断]
C --> D[残り95プロセスが<br/>スピンウェイト]
D --> E[CPU時間の55%が<br/>ロック待ちに消費]
PREEMPT_NONE 環境では、スピンロックを保持しているプロセスがカーネルによって中断されることはなかった。クリティカルセクションは高速に完了し、他のプロセスがロック待ちで空回りする時間は最小限だった。
PREEMPT_LAZY 環境では、スピンロック保持中でもカーネルがプリエンプトする可能性がある。96コアのサーバーでは、ロックを持ったプロセスが中断されると残り95プロセスがビジーウェイト状態に陥り、カスケード的にコンテンション(競合)が発生する。プロファイリングの結果、CPU時間の55%がPostgreSQLのユーザースペーススピンロック(s_lock)で消費されていた。
ページフォルトが火に油を注ぐ
問題をさらに悪化させるのがマイナーページフォルト。新規コネクションがforkして共有メモリに初めてアクセスするとき、マイナーページフォルトが発生する。デフォルトの4KBページを使っている環境では、大量のページフォルトがスピンロック保持中に発生し、ロック保持時間が想定を大幅に超える。
Huge Pages(1GBまたは2MBページ)を使えばページフォルトの回数が激減するため、リグレッションの影響は大幅に軽減される。ただし、コンテナ環境ではHuge Pagesの有効化に特権が必要な場合が多く、万能な回避策ではない。
ベンチマーク結果
Dipietroが報告したベンチマーク環境と結果は以下の通り。
| 項目 | 詳細 |
|---|---|
| インスタンス | AWS EC2 m8g.24xlarge |
| CPU | Graviton4(ARM64)96 vCPU |
| PostgreSQL | バージョン17 |
| ベンチマーク | pgbench simple-update |
| クライアント数 | 1024 |
| スレッド数 | 96 |
| 実行時間 | 1200秒 |
| カーネル設定 | TPS(トランザクション/秒) | 比率 |
|---|---|---|
| Linux 7.0(PREEMPT_LAZY) | 約50,751 | 0.51x |
| PREEMPT_NONE復元パッチ適用 | 約98,565 | 1.0x(ベースライン) |
スループットが約49%低下している。Dipietroはリバートパッチを提出したが、Zijlstraはこれを却下した。
開発者間の対立
今回の騒動がきな臭いのは、カーネル開発者の対応だ。
Zijlstraの立場は明確で、「PostgreSQLがRSEQ timeslice extensionを使えばいい」というもの。RSEQ(Restartable Sequences)はもともとper-CPUデータアクセス用のカーネル機構だが、Linux 7.0で「timeslice extension」機能が追加された。クリティカルセクション突入前にRSEQ経由でスケジューラにプリエンプション猶予を要求できる仕組みだ。
graph TD
A[アプリケーション] --> B["rseq::slice_ctrl::request = 1<br/>(猶予を要求)"]
B --> C[クリティカルセクション実行]
C --> D{カーネルがプリエンプト<br/>しようとする}
D --> E["猶予付与<br/>(デフォルト5μs延長)"]
E --> F[クリティカルセクション完了]
F --> G["rseq::slice_ctrl::request = 0"]
これに対してPostgreSQLコミッターの Andres Freund がLKMLで反論した。
- 7.0で壊れた問題の回避策が7.0の新機能って、それはおかしくないか
- PostgreSQLは幅広いカーネルバージョンとOSをサポートしている。RSEQ対応はLinux 7.0以降でしか動かない
- スピンロック実装にカーネル固有の低レベルAPIを組み込むのは重大な設計変更であり、既存のメジャーバージョン(PostgreSQL 14〜17)へのバックポートは非現実的
Freundの「requiring the use of a new low level facility that was introduced in the 7.0 kernel, to address a regression that exists only in 7.0+, seems not great(7.0以降でしか存在しないリグレッションの対策に、7.0で入った新しい低レベル機構の使用を要求するのは筋が悪い)」という指摘は、この問題の構造を端的に表している。
4月5日時点で、Linus Torvalds本人がこのスレッドにコメントした形跡はない。
過去にも同じことが起きている
LinuxカーネルのスケジューラがPostgreSQLを壊すのは今回が初めてではない。
| 時期 | カーネル | 内容 | 結果 |
|---|---|---|---|
| 2008年 | Linux 2.6.23+ | CFS(Completely Fair Scheduler)導入 | PostgreSQLにパフォーマンスリグレッション |
| 2012年 | Linux 3.6-rc | Mike Galbraithのスケジューリング最適化 | pgbenchが20%低下。最終リリース前にリバート |
| 2026年 | Linux 7.0-rc | PREEMPT_NONE廃止 | スループット49%低下。リバート拒否 |
2012年のLinux 3.6のケースでは、同様にpgbenchのパフォーマンスが低下した問題が最終リリース前にリバートされた。LWN.netの記事に詳細がある。当時はLinusのユーザースペース保護ポリシーに従ってリバートされたが、今回はメンテナがリバートを拒否している点が大きく異なる。
PostgreSQLのアーキテクチャ(プロセスモデル、共有メモリ、ユーザースペーススピンロック)がカーネルのスケジューラ挙動に特に敏感だという構造的な問題は変わっていない。
影響範囲
直近のリリーススケジュール
冒頭でも触れた通り、カーネル 7.0の安定版リリースは4月中旬(推定4月13日頃)。問題はその直後に控えるUbuntu 26.04 LTSのリリース(4月23日)で、エンタープライズのPostgreSQLデプロイメントがUbuntu LTSを採用しているケースは多い。LTSにリグレッションが乗ったまま出荷されると、ディストリビューション側でカーネルパッチを当てるかPostgreSQL側でRSEQ対応するまで、サーバー管理者が自力で回避策を適用する必要が出てくる。
他のデータベースへの影響
MySQL/InnoDBはfutexベースのmutexを使用しており、ユーザースペーススピンロックへの依存度はPostgreSQLより低い。ただし、ユーザースペースのスピンロックを使うアプリケーションは他にも存在するはずで、体系的なテストは行われていない。高コア数(96 vCPU以上)、ARM64アーキテクチャ、Huge Pages未使用の環境では、PostgreSQL以外でも影響が出る可能性がある。
当面の回避策
現時点で確認されている回避策はHuge Pagesの有効化。
# Huge Pagesの有効化(例: 8GB分を確保)
echo 4096 > /proc/sys/vm/nr_hugepages
# postgresql.conf
huge_pages = on
1GBページを使うことでページフォルト回数が激減し、スピンロック保持中のページフォルト問題がほぼ解消される。ただし前述の通り、コンテナ環境やマネージドサービスではHuge Pagesを設定できないケースがある。
PostgreSQLの開発版trunkでは、問題の原因となっているBufFreeListLock(バッファフリーリストを保護するスピンロック)のリファクタリングが既に完了している。ただし、これは将来のメジャーバージョンの話であり、現行のPostgreSQL 14〜17には適用されない。