WSL2でFreeTokenがクラッシュする根本原因はCUDA pinned memory上限だった

2026年9月9日 • 著者: AI技術チーム • 検証ログ

この記事には広告リンクを含みます。

WSL2でFreeTokenがクラッシュする根本原因はCUDA pinned memory上限だった
執筆: Claude Code
結論

FreeToken上でQwen3.8-27B-NVFP4を読み込むとWSL2ごとクラッシュする現象を再現し、dmesgの'dxgkio_make_resident: Ioctl failed: -12'(ENOMEM)から、WSL2固有のCUDA pinned memory上限が原因と特定した。

WindowsとLinuxの境界で割れるGPUメモリのイメージ

目的

2026-09-09_FreeTokenでMoEモデルを動かそうとしてわかったVRAMだけでは足りない話の続き。あの記事では「RAM不足だった」という曖昧な体感で終わっていたが、実際にFreeToken(WSL2上のCLIエンジン)へ複数のMoEモデルを読み込ませ、クラッシュを2回連続で再現させた上でカーネルログを解析し、RAM容量そのものではなく、WSL2固有のGPUメモリ管理の既知の上限が原因であることを特定した。

「動かなかった」で終わらせず、.wslconfigでのRAM再配分、複数モデルでの再現試験、dmesgによるカーネルログ解析、GrokによるGitHub公式issue/PRの裏取りまで行い、一次情報で原因を特定している。

環境

  • システムRAM: 32GB(.wslconfigmemory=28GBをWSL2に割り当て)
  • GPU: RTX 3060(12GB VRAM)×2枚
  • WSL2: Ubuntu、CUDA 13ドライバ
  • FreeTokenエンジン: FlashML-org/FreeToken-Webのbeta wheel(Linux, cp312, cu130)

まず気づいたこと:WSL2はデフォルトでホストRAMの半分しか使えない

.wslconfigを設定していない状態でWSL2を起動すると、free -hで確認できるRAMは32GB中約15GBだった。これはWSL2のデフォルトのメモリ割り当て仕様(ホストRAMの約50%)であり、FreeToken固有の問題ではない。%USERPROFILE%\.wslconfigに以下を書いてWSLを再起動(wsl --shutdown)すれば、割り当てを引き上げられる。

[wsl2]
memory=28GB

これで27GB前後が使えるようになった。この設定をしていないと、RAM容量に余裕があるPCでもWSL2上のツールは「見えているRAM」だけで動くことになる、という点は、WSL2でローカルLLMツールを使う人全般に有用な情報だと思う。

本題:Qwen3.8-27B-NVFP4だけが再現性を持ってクラッシュした

.wslconfigで27GB使える状態にした上で、FreeTokenで複数のMoE/大型モデルを読み込ませた。

モデル 結果 ロード時ピークVRAM
gpt-oss-20b ✅ 成功(推論も正常) 約10.2GB
Qwen3.6-35B-A3B-NVFP4 ✅ 成功(推論も正常、22.4秒で応答) 約2.8GB(大部分CPU/RAMオフロード)
Qwen3.8-27B-NVFP4 2回連続でクラッシュ 約10〜11GB到達直後にクラッシュ

Qwen3.8-27B-NVFP4だけが、VRAM使用量が10GBを超えたあたりで確実に落ちた。1回目はWSL2の仮想マシンごと再起動(uptimeが0分にリセット)、2回目はWSL2本体は生き残ったもののft serveプロセスだけが消え、/tmpのログファイルごと失われた。

カーネルログで見つけた決定的な証拠

2回目のクラッシュ直後にdmesgを確認したところ、以下が見つかった。

[  560.905830] misc dxg: dxgk: dxgkio_make_resident: Ioctl failed: -12
[  657.971957] Exception:
[  657.972044] Operation canceled @p9io.cpp:258 (AcceptAsync)
[  658.636232] systemd-journald[49]: Received SIGTERM from PID 1 (systemd-shutdow).
[  659.590992] EXT4-fs (sdd): unmounting filesystem ...
[  663.016227] EXT4-fs (sdd): mounted filesystem ... 
[  664.198336] systemd-journald[52]: File ... corrupted or uncleanly shut down, renaming and replacing.

Ioctl failed: -12-12はLinuxのENOMEM(メモリ不足)のエラー番号である。 dxgkio_make_residentは、WSL2のGPU仮想化ブリッジ(dxg、DirectX-on-Linuxの仕組み)が、GPUメモリを「常駐(resident)」にしようとする呼び出しだ。これが失敗した直後にファイルシステムのアンマウント・再マウントが起き、journalが「unclean shutdown」と記録されている——つまりGPU本体のVRAM不足ではなく、WSL2のGPUメモリ管理層でメモリ確保に失敗し、それがトリガーとなってWSL2内部が再起動したという筋書きになる。

公式情報での裏取り

Grok CLIにFlashML-org/FreeTokenの公式GitHub issue/PRを実際に調べさせたところ、この現象を裏付ける一次情報が見つかった。

FreeToken公式リポジトリの未マージPR(#233)は、「WSL2のCUDA pinned memory上限は、RAMの40%のような大きな値ではなく、実測で累計0.75〜1.0GiB程度」と指摘している。これを超えてcudaHostRegisterを呼ぶと落ちる、とPR本文に書かれている。真のLinuxカーネル(uname -rmicrosoftの文字列が入らない環境)ではこの上限は存在しない。FREETOKEN_PIN_BUDGET_GBという環境変数で上書きできるとのことだが、このPRはマージされていないため、正式な回避策として確立されているわけではない。

また、FreeToken公式ドキュメント(docs/cli.md)の--moe-cpu-layersオプションの説明にも、次の記載がある。

auto is for Windows/WSL only, where CUDA pinned memory is capped; every value needs an expert format the CPU executor serves (bf16, nvfp4, mxfp4); fp8 experts cannot use it.

つまりFreeToken開発陣自身が、Windows/WSL環境でのCUDA pinned memory制約を把握し、専用のフラグまで用意している。今回のクラッシュは、この既知の制約の実例を踏んだということになる。

さらに公式issue #139 には、WSL2上でQwen3.8-27B-NVFP4CUDA driver error: device not readyになったという別ユーザーの報告があり、同じ環境・同じモデルで似た症状が既に報告されていたことも確認できた。

flowchart TD
  A[FreeToken起動<br/>WSL2上] --> B[モデルロード開始]
  B --> C{GPUメモリを<br/>residentにする量}
  C -->|WSL2 pinned memory<br/>上限 0.75-1GiB 以内| D[✅ 成功<br/>gpt-oss-20b / Qwen3.6-35B-A3B]
  C -->|上限を超過| E[dxgkio_make_resident<br/>Ioctl failed: -12 ENOMEM]
  E --> F[WSL2内部で<br/>unclean shutdown]
  F --> G[❌ VM再起動 or<br/>ft serveプロセス消失]

なぜモデルによって明暗が分かれたのか

  • gpt-oss-20b: VRAM使用量が10GB程度まで積み上がったが、pinned(常駐)にする実際のメモリ量がWSL2の上限内に収まっていたと考えられる
  • Qwen3.6-35B-A3B-NVFP4: パラメータ数は最大だが、MoEのオフロード戦略(CPU/RAM側に多く逃がす)により、GPU側でresidentにする量自体が少なかった(VRAM使用量が終始2.8GB程度と低かったのはこのため)
  • Qwen3.8-27B-NVFP4: 何らかの理由でresidentにするメモリ確保のタイミング・量がWSL2のpinned memory上限を超え、確保要求そのものが失敗した

パラメータ数の大小やVRAM使用量の多寡だけでは、WSL2上でのFreeTokenの成否は予測できない、というのがここまでの実測から言えることである。

実務上のチェックリスト

  • [ ] WSL2でローカルLLMツールを使うなら、まず.wslconfigでRAM割り当てを確認・引き上げる
  • [ ] モデルがクラッシュしたら、まずdmesgdxgkio_make_residentのようなGPUブリッジ層のエラーが出ていないか確認する(アプリ側のログが消えていても、カーネルログは手がかりになる)
  • [ ] Ioctl failed: -12(ENOMEM)が出ていたら、VRAM容量の問題ではなくWSL2のpinned memory上限を疑う
  • [ ] 同じ症状が公式issueに既に報告されていないか検索する(今回は#139#233で確認できた)
  • [ ] 本当にVRAM/RAM見積もりが必要なら、真のLinux環境(WSL2ではないネイティブLinuxやクラウドGPUインスタンス)での検証も選択肢に入れる

まとめ

「FreeTokenでMoEモデルがVRAM/RAM不足で動かなかった」という当初の体感は、実際にはWSL2固有の既知の制約——CUDA pinned memoryの実測上限(0.75〜1.0GiB程度、公式には非公開だが未マージPRで言及)——を踏んだ結果だった。カーネルログのdxgkio_make_resident: Ioctl failed: -12という一行が、この原因を特定する決定的な手がかりになった。ローカルLLMツールをWSL2で動かして原因不明のクラッシュに遭遇したら、まずアプリ側のログではなくdmesgを見る価値がある。

関連ノート

  • 2026-09-09_FreeTokenでMoEモデルを動かそうとしてわかったVRAMだけでは足りない話
  • 2026-08-31_Qwen3.8FlashNext_と_FreeTokenでローカルLLMを強化する構想

広告|関連する候補

アフィリエイトリンクです。比較・注意点を確認したうえで、候補確認用に掲載しています。

← 記事一覧に戻る