Skip to content
Published on

sched_ext サブスケジューラ — カーネル7.1に半分だけ届いたcgroup別スケジューラ

シェア
Authors

はじめに — 見出しとコミットログの間の溝

Linux 7.1が2026年6月14日にリリースされました。スケジューラ周りの見出しの一つはこれでした — sched_extにcgroupサブスケジューラのサポートが入った。文だけ見ればかなり大きな話です。cgroupごとに別のCPUスケジューラを付けられるという意味ですから。コンテナを運用している人なら耳を引かれるはずです。

ところが同じニュースをLWNのマージウィンドウまとめで読むと、トーンがはっきり違います。Jonathan Corbetは7.1マージウィンドウのまとめでこう書きました。原文をそのまま引きます。

The extensible scheduler class (sched_ext) subsystem now has the beginning of support for sub-schedulers, meaning that each control group can run with its own custom CPU scheduler. As of 7.1, this implementation is not complete (it lacks the enqueue path in particular), but much of the needed core structure is there.

肝心なのは2文目です — 7.1時点でこの実装は完成しておらず、特にenqueueパスが欠けています。入ったのは「サブスケジューラサポートの始まり」であって、サブスケジューラそのものではありません。

本稿はその溝についての話です。何をしようとしている機能で、なぜ必要で、7.1に実際に何が入り、何がまだなく、だから今あなたが何をすべきか(おおむね「何もしない」)を整理します。

復習 — sched_extとは何だったか

CFSからEEVDFまでの流れは別稿で整理したので、ここではsched_extだけ短く復習します。

sched_extはカーネル6.12にマージされた拡張スケジューラクラスです(元々6.11を狙っていて1サイクル遅れました)。スケジューリングポリシーをBPFプログラムとして書き、実行時にロード・差し替えできるようにします。struct sched_ext_opsにコールバックを埋めてロードすれば、その時点からそのBPFコードがどのタスクをどのCPUでいつ動かすかを決めます。

ここで最も重要な設計判断は安全機構です。カーネル文書の表現をそのまま引きます。

System integrity is maintained no matter what the BPF scheduler does. The default scheduling behavior is restored anytime an error is detected, a runnable task stalls, or on invoking the SysRq key sequence SysRq-S.

BPFスケジューラが何をしようとシステムの完全性は保たれ、エラーが検知されるか実行可能なタスクが止まれば、既定のスケジューリング動作に自動で戻ります。SysRq-Sで手動で外すこともできます。この監視機構があるからこそ、「カーネルスケジューラをユーザーコードに差し替える」という無謀に見える発想が実際に成り立ちます。

なぜサブスケジューラか — cpusetの限界

問題設定はTejun Heoが投稿したパッチシリーズのカバーレターによく書かれています。状況はこうです。大きなマシン1台に、性格の異なるワークロードが複数乗っています。それぞれが望むスケジューリングポリシーが違います。レイテンシに敏感なサービス、スループットが大事なバッチジョブ、ゲームのようにインタラクティブ性が重要なもの、それらが1つの箱に混ざっています。

これまでの答えはハードパーティショニングでした。カバーレターの文をそのまま引きます。

Traditional approaches rely on hard partitioning via cpuset, but this approach lacks the dynamism required by modern workloads.

cpusetでCPUを切り分ける方式には、現代のワークロードが求める動的な性質がありません。なぜ足りないのかもカバーレターが具体的に説明しています。

Users typically care less about specific CPU assignments and more about optimizations available on larger machines: opportunistic over-commit, improved latency for critical workloads while preserving bandwidth fairness, control mechanisms beyond simple CPU time (such as memory bandwidth isolation), and intelligent placement to optimize cache locality.

言い換えるとこうです。人々が本当に欲しいのは「このワークロードは3番から7番コア」ではありません。大きなマシンを共有して初めて得られるもの — 日和見的なオーバーコミット、帯域幅の公平性を保ちながら重要なワークロードのレイテンシを改善すること、単純なCPU時間を超えた制御(メモリ帯域幅の分離など)、キャッシュの局所性を活かす配置 — を欲しています。CPUを事前に切ってしまえば、このどれもできません。切られた区画の中で遊ぶだけだからです。

そしてなぜカーネルがこれを代わりにやってくれないのかという問いへの答えが、Phoronixが引用した第2版カバーレターの文です。

Applications often have domain-specific knowledge that generic schedulers cannot possess. Database systems understand query priorities and lock holder criticality.

アプリケーションは汎用スケジューラが持ちえないドメイン知識を持っています。DBはどのクエリが急ぎか、どのスレッドがロックを握っていて今動かさないと全体が詰まるかを知っています。カーネルは知りません。sched_extの元々の論旨は「では知っている側にポリシーを書かせよう」であり、サブスケジューラはその論旨をマシン全体ではなくcgroup単位に下ろす試みです。

これまでの方法 — scx_flatcgは階層を平坦化する

誤解を避けるために触れておくべきことがあります。sched_extがcgroupをまったく知らなかったわけではありません。サンプルスケジューラの中にscx_flatcgがあり、カーネル文書はこう説明しています。

implements hierarchical weight-based cgroup CPU control by compounding each cgroup's share at every level into a single flat scheduling layer

階層的な重み付けベースのcgroup CPU制御を実装しますが、各cgroupの取り分をレベルごとに掛け合わせて1枚の平坦なスケジューリング層にまとめてしまいます。名前のflatはその意味です。

これは重要な対比です。既存の方式は階層を表現しつつ、最後には押し潰してスケジューラ1つで全部を処理します。重みは守られますが、cgroupごとに別のポリシーを使うことは依然としてできません。結局スケジューラは1つだからです。サブスケジューラが変えようとしているのはまさにこの点です — 階層を押し潰さず、階層の各地点に本当に別のスケジューラを座らせよう。

設計 — cgroupにスケジューラを付ける

CorbetのSub-schedulers for sched_ext(2026年1月29日)が、v1時点での構造をよくまとめています。核となる規則は1文です。

Each task in the system will be governed by the scheduler attached to its control group (or to the nearest ancestor group).

システム内のすべてのタスクは、自分のコントロールグループに付いたスケジューラの支配を受けます。そのグループにスケジューラがなければ、最も近い祖先グループのものに従います。DNSの名前解決やファイルシステムの権限継承と同じ、見慣れたパターンです。

カバーレターの表現ではこうなります。

schedulers to attach anywhere in the cgroup hierarchy, with parent schedulers dynamically controlling CPU allocation to their children.

スケジューラはcgroup階層のどこにでも付けられ、親スケジューラが子へのCPU配分を動的に制御します。ここで役割分担が分かれます。Corbetの説明どおり、CPUコントローラは相変わらず「どれだけのCPU時間がこれらに与えられるか」を担当し、サブスケジューラはその時間の中で自分のグループのプロセスをどう動かすかを担当します。

深さには制限があります。カバーレターの文です。

The framework supports scheduling hierarchies up to SCX_SUB_MAX_DEPTH levels (currently set to 4).

SCX_SUB_MAX_DEPTHまで、現在の値は4です。無限のネストではなく4段階です。

図にするとこういう形です。

root cgroup  ── ルートBPFスケジューラ(子にCPUを配分)
   ├── /db          ── scxスケジューラA(クエリ優先度・ロック保持者を知るポリシー)
   ├── /web         ── scxスケジューラB(レイテンシ優先)
   │      └── /web/batch   ── スケジューラなし -> 最も近い祖先(B)に従う
   └── /batch       ── スケジューラなし -> ルートスケジューラに従う

深さ制限: SCX_SUB_MAX_DEPTH = 4

もう一つ印象的な仕組みがあります。Corbetが指摘したセキュリティモデルで、kfuncに暗黙の引数を付けてbpf_prog_aux構造体にアクセスできるようにしました。おかげでこうなります。

BPF programs themselves need never specify which scheduler they are operating on.

BPFプログラムは自分がどのスケジューラに属しているかを自分で申告する必要が一切ありません。申告させてしまえば嘘をつけるからです — 他人のスケジューラのキューに触るBPFプログラムを想像すれば、なぜこれが必要かすぐ分かります。カーネルがプログラム自体から所属を読み取ります。

7.1に実際に入ったもの — そして欠けているもの

ここが本稿の本論です。

7.1に入ったのはdispatchパスです。dispatchはCorbetの定義では「スケジューラに、次に動かすタスクを選んで特定CPUのディスパッチキューに入れるよう指示する」コールバックです。つまり「すでに動かす準備ができたタスクの中から何をいつ上げるか」を階層的に処理する骨格が入りました。

欠けているものの方が重要です。Corbetがv1をレビューして指摘した未実装項目はこうです。

  • select_cpu()コールバック — タスクが起きたときにどのCPUに送るか選ぶ経路
  • enqueue()コールバック — タスクをキューに入れる経路

そしてbypassモードは再設計が必要だと指摘されました。理由は具体的です。

allowing one sub-scheduler to toss processes into a global FIFO queue could lead to interference with other sub-schedulers

サブスケジューラ1つがプロセスをグローバルFIFOキューに放り込めるようにすると、他のサブスケジューラと干渉しうるということです。分離が目的の機能で分離が漏れる経路なので、見過ごせない問題です。ちなみにこの問題は痕跡を残しました — 現行のカーネル文書でサブスケジューラに触れている箇所は実質、SCX_EV_SUB_BYPASS_DISPATCHイベントカウンタの説明1行だけで、内容は「tasks dispatched from sub-scheduler bypass DSQs (only relevant with CONFIG_EXT_SUB_SCHED)」です。機能の文書としてではなく、診断カウンタとしてのみ存在します。

カバーレター自身もこの状態を隠していません。

While the enqueue path and other components require further development, this patchset establishes the core mechanisms for nested scheduler operation.

enqueueパスと他の構成要素はさらなる開発が必要だが、このパッチセットはネストしたスケジューラ動作の中核メカニズムを確立する。正直な文です。

kernelnewbiesの7.1変更点まとめも同じ線を守っています — sched_extがサブスケジューラサポートを追加し、この機能の目的は「将来のリリースで」cgroupごとに別のスケジューラを使えるようにすることだと書いています。原文は「The purpose of this feature is to eventually allow (in future releases) using different schedulers in different cgroups」です。eventuallyとin future releasesが肝心な言葉です。

まとめるとこうです。

サブスケジューラ — 何がどこまで来たか(2026-07-16時点)

dispatchパス                  [ 7.1にマージ済み ]
enqueueパス                   [ 未完成 — 後続パッチセットで開発中 ]
select_cpu()パス              [ v1レビュー時点で未実装 ]
bypassモード                  [ 再設計が必要(グローバルFIFO干渉問題) ]
cgroupごとに別のスケジューラ  [ まだ不可 — 「将来のリリース」 ]

タイムライン — なぜこんなに時間がかかるのか

時系列に並べると、この機能の速度が見えてきます。

2024-11     sched_ext自体がカーネル6.12にマージ(6.11目標だったが遅延)
2025-09-19  Tejun Heo、cgroupサブスケジューラサポートのRFCパッチセットを投稿
2026-01-29  LWN「Sub-schedulers for sched_ext」— v1レビュー
2026-02-25  v2投稿、sched_ext/for-7.1ブランチに乗る
2026-03-04  7.1に入ったシリーズのカバーレター
2026-04-27  LWN 7.1マージウィンドウまとめ — 「実装は完成していない」
2026-06-14  Linux 7.1リリース(dispatchパスを含む)
2026-06-18  LWN 7.2マージウィンドウ — 「サブスケジューラサポートの追加が続く」
2026-07-16  (今日)メインラインは7.2-rc3、安定版は7.1.3

RFCから部分マージまで9か月、そしてまだ使えません。ここでCorbetがv1レビュー時に残した予測をもう一度見る必要があります。

One should thus not expect to see sub-scheduler support in the kernel for some time yet.

しばらくの間、カーネルでサブスケジューラサポートを見られると期待してはいけない。1月に書かれたこの文は、5か月後の7.1に「サポートの始まり」が入った今も依然として有効です。骨格が入ったことと、機能が使えることは別の話だからです。カーネルで大きな機能が断片に分かれて複数サイクルにわたって入るのは正常で、むしろ健全なやり方です。ただ、その断片のうち最初の1つがマージされたというニュースを機能のリリースとして読むと足を踏み外します。

7.2で続く作業

7.2マージウィンドウでも作業は続きます。Corbetの7.2マージウィンドウ前半のまとめは短く1行です — 「The addition of support for sub-schedulers in sched_ext continues.」

Phoronixの7.2 sched_extまとめ(2026年6月22日)はもう少し具体的です。7.2に入ったのはトポロジーベースのCPU ID(CID)、cmaskインフラ、BPF arena統合、そして新しいarena・CIDインターフェースを実演するよう書き直されたscx_qmapの例です。すべてインフラです。

そしてプルリクエストにはこの文があります。

A follow-up patchset in development will complete enqueue-path support for hierarchical scheduling.

開発中の後続パッチセットが階層的スケジューリングのenqueueパスサポートを完成させる。未来形であることに注意してください。7.2にもenqueueパスはありません。7.2自体、今日時点でまだrc3で、リリースもされていません。

では今何をすべきか

正直に答えると — 大半は何もしない

今できないこと

  • cgroupごとに別のBPFスケジューラを付けること。これが見出しですが、まだできません。
  • 7.1にカーネルを上げてこの機能の恩恵を受けること。dispatchパスの骨格だけではワークロードに何の変化もありません。
  • この機能を前提にした容量計画やアーキテクチャの決定。enqueueパスがいつ完成するか、完成した形が今の設計と同じかは誰も確約していません。カバーレターには、upstreamのきちんとした解決策を待つ間の暫定的なBPF回避策も含まれていました。

今できること

  • sched_ext自体は6.12から使えます。マシン全体にスケジューラ1つを付ける方式で、監視機構によるフォールバックがあるので実験のコストは低いです。
  • cgroup重みベースのCPU制御が必要ならscx_flatcgがすでにあります。階層を平坦化する方式なのでポリシーは1つですが、多くの場合これで十分です。
  • マルチテナント分離が今すぐ急ぐなら、依然としてcpuset・cgroup CPUコントローラが答えです。カバーレターが指摘した限界(動的でない)を引き受ける代わりに、今日動きます。

いつまた見るべきか

enqueueパスがマージされたというニュースが出たときです。それまでこの機能はロードマップであって道具ではありません。そしてマージされた後も、sched_extの歴史が物語るとおり — 6.11目標が6.12へずれ、サブスケジューラのRFCが9か月かけて半分しか来ていない — 余裕を持って見積もるのが正しいでしょう。

おわりに

まとめるとこうです。sched_extサブスケジューラは良いアイデアです。問題設定が正確で(cpusetのハードパーティショニングは動的でない)、論旨が一貫しており(ポリシーはドメイン知識のある側が書くべき)、設計が合理的です(cgroup階層にスケジューラを付け、最も近い祖先を継承し、4段階に制限し、所属はカーネルが判別する)。

そして7.1にはそのうちdispatchパスが入りました。それだけです。enqueueパスはなく、select_cpu()もなく、bypassモードは再設計対象で、カーネル文書はこの機能をイベントカウンタ1行でしか触れていません。「各コントロールグループが自分専用のカスタムCPUスケジューラで動ける」という文は、今の時制ではなく意図の記述です。

これは批判ではありません。カーネルの仕事の進め方はもともとこうであり、カバーレターもLWNもkernelnewbiesも、みな自分の状態を正確に書き残しています — 「further development」「not complete」「eventually」「in future releases」。不正確だったのはソースではなく、これを機能のリリースとして読む読み方の方です。

カーネルのリリースノートを読むときに私が使う基準はこれです。マージされた使えるは別の出来事であり、機能が大きいほどその間隔は開きます。2つの出来事の距離を測る一番安い方法は、マージウィンドウまとめの中に「not complete」のような言葉を探すことです。たいてい、そこに書いてあります。

参考資料