WSLでUSBメモリとかを使いたい

いつ頃かは知りませんが,USBIPを利用してWSL2上のLinuxにUSBデバイスを共有できるようになってたみたいですね.

Windows11のWSL標準Linuxカーネルには,既にUSBIPのモジュールが組み込まれています.なので単に接続するだけであれば,USBIPホストとクライアントの設定だけで利用できます.
ただ,USB Mass Storage Driverが標準カーネルに組み込まれていないので,ストレージデバイスとして使いたい場合は独自にビルドしたカーネルを使用する必要があります.

Windows10の場合はUSBIPのモジュールもデフォルトで組み込まれていないようなので,これも独自にビルドする必要があるようですね.

備忘録がてら手順を残しておきます.

使用環境

  • Windows 11 Pro 22H2 (x64)
  • WSL (Version 1.0.0, MS Store版)
    • Distribution: Arch Linux (ArchWSL 22.10.16.0, WSL2)
    • Kernel: 5.15.74.2-microsoft-standard-WSL2

要件

基本はMicrosoftのドキュメントの要件を満たしていればOKです.

Linuxカーネルのビルドをするので,ある程度リソースに余裕のあるWSLか普通のLinux環境があると望ましいです.ここでは既存のWSL上でそのままビルドします.

Linuxカーネルのビルド・変更

依存パッケージの導入

カーネルビルドに必要なパッケージを調べて,先に導入しておきます.
Linuxの公式ドキュメントに最低限必要なプログラムのリストがありますが,<ディストリビューション名> kernel build requirementsとかでググれば,パッケージマネージャのコマンドごと出てくると思います.

Ubuntuの場合は以下のパッケージをインストールしておきます.

sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev cpio

ArchLinuxは公式カーネルのPKGBUILDに従うと以下が最低限必要と思われます.

pacman -S base-devel xmlto kmod inetutils bc libelf git cpio perl tar xz asp

リポジトリのクローン

WSL2カーネルのリポジトリをクローンします.

https://github.com/microsoft/WSL2-Linux-Kernel

git clone https://github.com/microsoft/WSL2-Linux-Kernel.git --depth=1

--depth=1でshallow cloneをしています.フルでクローンするとトータルで3~4GBくらいになるので,ストレージに余裕のない方は注意した方が良いです.

Build Configuration

いくつかやり方はありますが,ここではデフォルトのconfigをコピーしてきて,menuconfigで変更します.

  1. クローンしたディレクトリのルートで,以下を実行します.

    cp ./Microsoft/config-wsl ./.config
    make menuconfig
  2. このようなTUIが出てきます.十字キーと<Enter>で操作します.
    menuconfig
    Device DriversUSB Support と進み,USB Mass Storage support を探します.



    これを選択した状態で,Yを押します.すると項目左側のカッコに*が表示され,ドライバが有効になります.
    こんな感じ
    基本的にはこれでOKですが,別途特別なドライバが必要な場合はここで一緒に有効にしておきます.心当たりがなければ,このままで問題無いです.

  3. 変更したConfigを保存します.左右キーで下側のコマンドのカーソル(画像参照)を動かし,Saveを選んでEnterを押します.

    保存するファイル名を聞かれますが,特に変更の必要もないのでそのままEnterでOKです.

Tips

General SetupLocal version から,Local version名の変更が可能です.uname -aとかで確認したときに出てくるやつですね.
そのままでも問題無いですが,オリジナルと区別できるように適当に変えておくと良いでしょう.
デフォルトは-microsoft-standard-WSL2です.

  1. 同様にExitを選択して終了します.

ビルド

以下のコマンドでビルドします.

make -j $(nproc)

何回もカーネルのビルドを経験してると最早気になりませんが,貧弱なCPUだと結構時間がかかります.
筆者のデスクトップで4分程度,ラップトップでは十数分かかりました.
-jオプションなしだと単一ジョブで走るので,更に遅くなります.

参考までにビルドした環境も載せときます.WSLで利用できるリソースに制限をかけているので,フルパワーの時より劣る結果になっています.

  • ビルド環境
    • デスクトップ
      • CPU: Intel Core i7-12700K 12C/20T 8コア制限
      • RAM: DDR5-5200 64GB 16GB制限
    • ラップトップ
      • CPU: Intel Core i7-8750H 6C/12T 6コア制限
      • RAM: DDR4-2666 16GB 8GB制限

特に設定をいじっていなければ,CPUは搭載の全コア,メモリは全体の80%上限だったと思います.

完成したカーネルは,vmlinuxという名前でディレクトリのルートにいます.

$ ls -lh vmlinux
-rwxr-xr-x 1 nanami nanami 808M Nov 28 01:39 vmlinux

ちなみに,ビルド後は成果物で +4GB 程度ストレージを圧迫します.
そもそもLinuxをフルでクローンすると,リポジトリ自体で3GB程度あります.
Shallow cloneであればもう少し少ないはずです.

$ du -sh
7.1G    .

カスタムカーネルに変更

.wslconfigで作成したカーネルを指定し,WSLで使われるカーネルを差し替えます.
この操作はWSLの全ディストリビューションに対して有効です.

  1. vmlinuxをWindows側のファイルシステムへコピー
    置き場所はWindows側のファイルシステム上であればどこでも良いです.
    わかりやすい場所が良いので,Cドライブ直下などにディレクトリを作るのがベターでしょう.
    併せて適当にリネームもしています.

    mkdir -p /mnt/c/wsl
    cp ./vmlinux /mnt/c/wsl/515-microsoft-nanamiiiii-WSL
  2. (作成していない場合) %USERPROFILE%直下に.wslconfigファイルを作成する
    %USERPROFILE%はユーザーディレクトリ,つまりC:\Users\username直下のことです.

  3. .wslconfigにカーネルの設定を書き込む
    .wslconfigini形式の設定ファイルで,全WSL環境で共通の設定になります.
    ここでkernelに先ほどコピーしたvmlinuxへのパスを指定します.
    \はエスケープをしないと正しく解釈されません

    [wsl2]
    kernel=C:\\wsl\\515-microsoft-nanamiiiii-WSL

WSLを再起動

以下はWindows側のシェルから実行してください.

wsl --shutdown
wsl -d <distribution>

これで正常にWSLが立ち上がれば完了です.uname -aでVersion Stringを確認すると,変化していることが分かります.

usbipd-winのインストール

https://github.com/dorssel/usbipd-win

上のリポジトリのReleaseより,最新のmsiをダウンロード・インストールします.

wingetでもインストールできます.

winget install --interactive --exact dorssel.usbipd-win

PATHを反映するため,ここで再起動 or 再ログインしておくと良いでしょう.

LinuxにUSBIPクライアントをインストール

ここもディストリビューションによって異なるので,自身の環境にあったものを導入してください.
Ubuntuの場合はMicrosoft公式Docsに記載があります.

ArchLinuxの場合は以下でOKです.

yay -S usbip usbutils

デバイスをUSBIPの共有対象にする

> usbipdは基本的にAdministratorで実行する必要があります

Tips

Windowsでも,LinuxのsudoのようにAdministratorへ昇格できる方法があります.
以下の記事の方法が一番簡単でしょう.

https://zenn.dev/ryuu/articles/wanttousesudo-with-win

これを導入すると,sudo <command>を打つことでUACダイアログが現れ,管理者権限でコマンドを実行できます.

まず,対象のUSBデバイスをUSBIPによる共有の対象にする必要があります.

  1. ホストのWindowsで接続可能なUSBデバイスをリストアップ

    usbipd list

    スクリーンショット2024-03-02182844.png
    ここではUSB Mass Storage DeviceUSB 大容量記憶装置としか表示されないので,接続したいデバイスが特定できない場合があります.そのときは,USBデバイスのプロパティでVendor IDとProduct IDを特定します.

  2. 共有対象にする
    リスト時に既にSharedであればこの手順は不要です.

    usbipd bind -b <BUSID>

    スクリーンショット2024-03-02190117.png

デバイスをWSLにアタッチ

usbipd attach --wsl [DISTRIBUTION] -b <BUSID>

--wslオプションでWSLのディストリビューション名を指定しますが,関係なく全てのディストリビューションでマウントされるようです.
なので引数を省略し,オプションのみでもOKです.

WSLにアタッチしている間,そのデバイスにはWindows側からアクセスできないので注意してください.

スクリーンショット2024-03-02192716.png

Warning

WSL環境によっては以下のようなエラーが発生することがあります.

スクリーンショット2024-03-02190345.png

Windows側のC:\Program Files\usbpid-win\WSLがWSLで正常にマウント出来ないことが原因です.
WSLにて以下のコマンドを正常に実行できれば問題無く使用可能となります.

sudo mount -t drvfs -o "ro,umask=222" "C:\Program Files\usbipd-win\WSL" "/var/run/usbipd-win"

Warning

Windows側で常にデバイスが使用中扱いとなり,アタッチ出来ないことがあります.

スクリーンショット2024-03-02194313.png

この場合,バインドの際にforceバインドをすることでWindowsから完全に切り離され,アタッチ可能になります.

usbipd bind -b \<BUSID\> -f

スクリーンショット2024-03-02194516.png

forceバインド後はデバイスを再接続してください.

WSL側で確認

lsusbや,ストレージデバイスならlsblkで確認します.

スクリーンショット2024-03-02200724.png

デタッチ

WSLからデタッチして,Windows側に戻します.同じく管理者権限のPowershellで以下を実行します.

usbipd detach -b <BUSID>

スクリーンショット2024-03-02213552.png

おわりに

大したことをやっているわけではないですが,慣れない人からすれば面倒な点がいくらかあるかと思ったので,少し丁寧に書きました.

自分は組み込みデバイスのFWフラッシュやシリアルコンソール,JTAGデバッガなどをよく使うので,この機能は重宝しています.

間違いや指摘などあれば気軽に教えていただけると助かります.