概要
WSLでは通常USB2系8ポート、USB3系が8ポートの上限があります。8ポート上限にあたることが多かったのでカーネルリビルドで上限突破してみました。
カーネルビルドの準備
まずはカーネルをビルドできる状態を作ります。
(Windows)WSLの更新確認
>wsl --update
更新プログラムを確認しています。
Linux 用 Windows サブシステムの最新バージョンは既にインストールされています。
まずはWindows上でWSLが最新かをたしかめます。
(WSL)とりあえず最新化
sudo apt update
sudo apt upgrade
念のため最新化しておきます。
(WSL)カーネルバージョン確認
$ uname -r
6.18.33.2-microsoft-standard-WSL2
WSLなのでMicrosoftのカーネルで動いています。
(WSL)アーキテクチャ確認
$ uname -m
x86_64
ARMの特殊Windows以外であれば通常はx86_64のはず。
(WSL)オプション確認
$ zgrep 'CONFIG_USBIP_VHCI' /proc/config.gz
CONFIG_USBIP_VHCI_HCD=m
CONFIG_USBIP_VHCI_HC_PORTS=8
CONFIG_USBIP_VHCI_NR_HCS=1
上記で8ポートのUSBが1組あるので、8ポート上限であることがわかります。
(WSL)カーネルビルド環境構築
sudo apt install git build-essential flex bison dwarves libssl-dev libelf-dev libncurses-dev cpio qemu-utils rsync
必要なパッケージをインストールします。
カーネルビルド
(WSL)ディレクトリ移動
cd /tmp/
とりあえず適当なところに移動してビルドします。
(WSL)ソース取得
KVER="$(uname -r | sed -E 's/-microsoft-standard-WSL2.*//')"
echo "$KVER"
git clone --depth 1 --branch "linux-msft-wsl-${KVER}" https://github.com/microsoft/WSL2-Linux-Kernel.git
自分のカーネルと同じバージョンを指定する必要があります。
(WSL)ソースディレクトリに移動
cd WSL2-Linux-Kernel
(WSL)ビルドオプション変更
./scripts/config --file Microsoft/config-wsl \
--set-val CONFIG_USBIP_VHCI_NR_HCS 4
(WSL)コンフィグ確認
make KCONFIG_CONFIG=Microsoft/config-wsl olddefconfig
エラーがでなければ大丈夫のはずです。
(WSL)ビルド
make -j"$(nproc)" KCONFIG_CONFIG=Microsoft/config-wsl
これでカーネルのビルドが始まります。
(WSL)モジュールのビルド
make KCONFIG_CONFIG=Microsoft/config-wsl \
INSTALL_MOD_PATH="$PWD/modules" modules_install
make KCONFIG_CONFIG=Microsoft/config-wsl \
headers_install INSTALL_HDR_PATH="$PWD/headers"
make -C tools/perf \
NO_JEVENTS=1 \
NO_JVMTI=1 \
NO_LIBTRACEEVENT=1 \
install DESTDIR="$PWD/perf" prefix=/
上記でVHDXモジュールに必要なものをビルドします。
(WSL)VHDXモジュール作成
./Microsoft/scripts/gen_artifacts_vhdx.sh \
"$PWD/modules" \
"$PWD/headers" \
"$PWD/perf" \
"$(make -s kernelrelease)" \
modules.vhdx
(WSL)Windows側へコピー
mkdir -p /mnt/c/Users/<Windowsユーザー名>/wsl-kernel
cp arch/x86/boot/bzImage \
/mnt/c/Users/<Windowsユーザー名>/wsl-kernel/
cp modules.vhdx \
/mnt/c/Users/<Windowsユーザー名>/wsl-kernel/
適当な場所にコピーします。
(Windows)%USERPROFILE%.wslconfig を設定
notepad $env:USERPROFILE\.wslconfig
上記で編集をします。
[wsl2]
kernel=C:\\Users\\<Windowsユーザー名>\\wsl-kernel\\bzImage
kernelModules=C:\\Users\\<Windowsユーザー名>\\wsl-kernel\\modules.vhdx
さっき作成したファイルを追加します。これで次回からこのカーネルとモジュールで起動するはずです。ただし最新バージョンではこの手順だとたぶん失敗します。したにあるスクリプトの中身のほうがたぶん安全です。
(Windows)WSLの停止
wsl --shutdown
いま動いているWSLを停止します。これで次回から新しいカーネルで起動するはずです。
(WSL)動作確認
$ ls /sys/devices/platform/ | grep vhci
vhci_hcd.0
上記の行が増えていれば成功です。増えていなかったりまったく起動しなかった場合にはいろいろ失敗していますので、AIに確認してもらってください。
自動化
ここまでの手順を自動化したスクリプトを作りました。カーネルを自分でビルドすると古くなりますので適度に最新に入れ替える必要があります。ちなみにちょうど最新のカーネルと使っていたカーネルで作業内容が変わっています。。。
#!/usr/bin/env bash
set -Eeuo pipefail
# ============================================================
# ここだけ設定
# ============================================================
NR_HCS=4
# ============================================================
BUILD_DIR="$HOME/wsl-usbip-kernel-build"
REPO_URL="https://github.com/microsoft/WSL2-Linux-Kernel.git"
API_URL="https://api.github.com/repos/microsoft/WSL2-Linux-Kernel/releases/latest"
echo
echo "========================================"
echo " WSL USB/IP kernel build"
echo " NR_HCS=$NR_HCS"
echo "========================================"
echo
# ------------------------------------------------------------
# WSL確認
# ------------------------------------------------------------
if ! grep -qi microsoft /proc/sys/kernel/osrelease; then
echo "ERROR: WSL2内で実行してください。" >&2
exit 1
fi
# ------------------------------------------------------------
# Architecture
# ------------------------------------------------------------
case "$(uname -m)" in
x86_64)
CONFIG_FILE="Microsoft/config-wsl"
KERNEL_IMAGE="arch/x86/boot/bzImage"
;;
aarch64|arm64)
CONFIG_FILE="Microsoft/config-wsl-arm64"
KERNEL_IMAGE="arch/arm64/boot/Image"
;;
*)
echo "ERROR: unsupported architecture: $(uname -m)" >&2
exit 1
;;
esac
# ------------------------------------------------------------
# 必要パッケージ
# ------------------------------------------------------------
echo "==> Installing build dependencies"
sudo apt-get update
sudo apt-get install -y \
git \
curl \
jq \
build-essential \
flex \
bison \
dwarves \
libssl-dev \
libelf-dev \
libncurses-dev \
cpio \
rsync
# ------------------------------------------------------------
# Windows USERPROFILE
# ------------------------------------------------------------
WIN_PROFILE_WIN="$(
cmd.exe /C 'echo %USERPROFILE%' 2>/dev/null |
tr -d '\r' |
tail -n 1
)"
if [[ ! "$WIN_PROFILE_WIN" =~ ^[A-Za-z]:\\ ]]; then
echo "ERROR: Windows USERPROFILEを取得できません。" >&2
exit 1
fi
WIN_PROFILE="$(
wslpath -u "$WIN_PROFILE_WIN"
)"
DEST_DIR="$WIN_PROFILE/wsl-kernel"
WSLCONFIG="$WIN_PROFILE/.wslconfig"
# ------------------------------------------------------------
# kernelModules が残っていないか確認
#
# modules.vhdx は今回一切使わない
# ------------------------------------------------------------
if [[ -f "$WSLCONFIG" ]] &&
grep -qi '^[[:space:]]*kernelModules[[:space:]]*=' "$WSLCONFIG"
then
echo
echo "ERROR:"
echo " .wslconfig に kernelModules= が残っています。"
echo
echo " $WSLCONFIG"
echo
echo "kernelModules= の行を削除して、"
echo
echo " wsl --shutdown"
echo
echo "した後、Ubuntuを起動し直してこのスクリプトを再実行してください。"
echo
exit 1
fi
# ------------------------------------------------------------
# 最新release取得
# ------------------------------------------------------------
echo
echo "==> Finding latest WSL kernel release"
TAG="$(
curl -fsSL "$API_URL" |
jq -r '.tag_name'
)"
if [[ -z "$TAG" || "$TAG" == "null" ]]; then
echo "ERROR: latest releaseを取得できません。" >&2
exit 1
fi
echo " $TAG"
# ------------------------------------------------------------
# Source取得
# ------------------------------------------------------------
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
echo
echo "==> Cloning $TAG"
git clone \
--depth 1 \
--branch "$TAG" \
"$REPO_URL" \
src
cd src
# ------------------------------------------------------------
# Config
#
# NR_HCSだけ変更
# USBIP_CORE / VHCI_HCD は標準の =m を維持
# ------------------------------------------------------------
CONFIG_FILE="$(
readlink -f "$CONFIG_FILE"
)"
echo
echo "==> Setting CONFIG_USBIP_VHCI_NR_HCS=$NR_HCS"
./scripts/config \
--file "$CONFIG_FILE" \
--set-val USBIP_VHCI_NR_HCS "$NR_HCS"
make \
KCONFIG_CONFIG="$CONFIG_FILE" \
olddefconfig
# ------------------------------------------------------------
# 設定確認
# ------------------------------------------------------------
echo
echo "==> Checking config"
grep -E \
'^CONFIG_USBIP_(CORE|VHCI_HCD|VHCI_HC_PORTS|VHCI_NR_HCS)=' \
"$CONFIG_FILE"
CORE="$(
sed -n 's/^CONFIG_USBIP_CORE=//p' "$CONFIG_FILE"
)"
VHCI="$(
sed -n 's/^CONFIG_USBIP_VHCI_HCD=//p' "$CONFIG_FILE"
)"
HCS="$(
sed -n 's/^CONFIG_USBIP_VHCI_NR_HCS=//p' "$CONFIG_FILE"
)"
if [[ "$CORE" != "m" ]]; then
echo "ERROR: CONFIG_USBIP_CORE が m ではありません。" >&2
exit 1
fi
if [[ "$VHCI" != "m" ]]; then
echo "ERROR: CONFIG_USBIP_VHCI_HCD が m ではありません。" >&2
exit 1
fi
if [[ "$HCS" != "$NR_HCS" ]]; then
echo "ERROR: CONFIG_USBIP_VHCI_NR_HCS の設定に失敗しました。" >&2
exit 1
fi
# ------------------------------------------------------------
# Kernel build
# ------------------------------------------------------------
echo
echo "==> Building kernel"
make \
-j"$(nproc)" \
KCONFIG_CONFIG="$CONFIG_FILE"
# ------------------------------------------------------------
# Kernel release
# ------------------------------------------------------------
KREL="$(
make \
-s \
KCONFIG_CONFIG="$CONFIG_FILE" \
kernelrelease
)"
echo
echo "==> Kernel release"
echo " $KREL"
if [[ ! -f "$KERNEL_IMAGE" ]]; then
echo "ERROR: kernel image がありません。" >&2
exit 1
fi
# ------------------------------------------------------------
# modules_install
#
# 一度 staging directory に作る
# ------------------------------------------------------------
echo
echo "==> Building kernel modules"
rm -rf "$PWD/modules"
make \
KCONFIG_CONFIG="$CONFIG_FILE" \
INSTALL_MOD_PATH="$PWD/modules" \
modules_install
MODULE_SOURCE="$PWD/modules/lib/modules/$KREL"
if [[ ! -d "$MODULE_SOURCE" ]]; then
echo "ERROR: modules_install に失敗しました。" >&2
exit 1
fi
# ------------------------------------------------------------
# vhci-hcd.ko確認
# ------------------------------------------------------------
VHCI_KO="$(
find "$MODULE_SOURCE" \
-type f \
-name 'vhci-hcd.ko*' \
-print \
-quit
)"
USBIP_KO="$(
find "$MODULE_SOURCE" \
-type f \
-name 'usbip-core.ko*' \
-print \
-quit
)"
if [[ -z "$VHCI_KO" ]]; then
echo "ERROR: vhci-hcd.ko が生成されていません。" >&2
exit 1
fi
if [[ -z "$USBIP_KO" ]]; then
echo "ERROR: usbip-core.ko が生成されていません。" >&2
exit 1
fi
echo
echo " $VHCI_KO"
echo " $USBIP_KO"
# ------------------------------------------------------------
# /lib/modules へ直接install
#
# modules.vhdx は使わない
# ------------------------------------------------------------
MODULE_DEST="/lib/modules/$KREL"
echo
echo "==> Installing modules"
echo " $MODULE_DEST"
# kernelModules VHDがmountされていたら止める
if mountpoint -q "$MODULE_DEST" 2>/dev/null; then
echo
echo "ERROR:"
echo " $MODULE_DEST"
echo " がmount pointになっています。"
echo
echo "古い kernelModules VHD がまだ有効です。"
echo ".wslconfig の kernelModules= を削除して"
echo "wsl --shutdown 後に再実行してください。"
echo
exit 1
fi
sudo rm -rf "$MODULE_DEST"
sudo cp -a \
"$MODULE_SOURCE" \
/lib/modules/
sudo rm -f \
"$MODULE_DEST/build" \
"$MODULE_DEST/source"
sudo depmod -a "$KREL"
# ------------------------------------------------------------
# 正しい位置に入ったことを確認
# ------------------------------------------------------------
echo
echo "==> Checking installed modules"
find "$MODULE_DEST" \
-type f \
\( \
-name 'vhci-hcd.ko*' \
-o \
-name 'usbip-core.ko*' \
\) \
-print
if [[ ! -f "$MODULE_DEST/kernel/drivers/usb/usbip/vhci-hcd.ko" ]]; then
echo
echo "ERROR:"
echo "vhci-hcd.ko が正しい場所にありません。"
echo
echo "expected:"
echo " $MODULE_DEST/kernel/drivers/usb/usbip/vhci-hcd.ko"
exit 1
fi
# ------------------------------------------------------------
# Windows側へkernelコピー
# ------------------------------------------------------------
echo
echo "==> Installing kernel into Windows"
mkdir -p "$DEST_DIR"
if [[ -f "$DEST_DIR/kernel" ]]; then
cp -a \
"$DEST_DIR/kernel" \
"$DEST_DIR/kernel.bak.$(date +%Y%m%d-%H%M%S)"
fi
cp -f \
"$KERNEL_IMAGE" \
"$DEST_DIR/kernel"
# ------------------------------------------------------------
# .wslconfig
#
# kernel= のみ使用
# kernelModules= は使用しない
# ------------------------------------------------------------
KERNEL_WIN="${WIN_PROFILE_WIN}\\wsl-kernel\\kernel"
KERNEL_CFG="${KERNEL_WIN//\\/\\\\}"
if [[ -f "$WSLCONFIG" ]]; then
cp -a \
"$WSLCONFIG" \
"$WSLCONFIG.bak.$(date +%Y%m%d-%H%M%S)"
fi
python3 - \
"$WSLCONFIG" \
"$KERNEL_CFG" <<'PY'
import re
import sys
from pathlib import Path
path = Path(sys.argv[1])
kernel = sys.argv[2]
if path.exists():
lines = path.read_text(
encoding="utf-8-sig"
).splitlines()
else:
lines = []
section_re = re.compile(
r"^\s*\[([^]]+)\]\s*$"
)
start = None
for i, line in enumerate(lines):
m = section_re.match(line)
if (
m
and
m.group(1).strip().lower() == "wsl2"
):
start = i
break
if start is None:
if lines and lines[-1].strip():
lines.append("")
lines.extend(
[
"[wsl2]",
f"kernel={kernel}",
]
)
else:
end = len(lines)
for i in range(start + 1, len(lines)):
if section_re.match(lines[i]):
end = i
break
block = lines[start + 1:end]
new_block = []
kernel_done = False
for line in block:
if re.match(
r"^\s*kernelModules\s*=",
line,
re.I
):
continue
if re.match(
r"^\s*kernel\s*=",
line,
re.I
):
if not kernel_done:
new_block.append(
f"kernel={kernel}"
)
kernel_done = True
continue
new_block.append(line)
if not kernel_done:
new_block.append(
f"kernel={kernel}"
)
lines = (
lines[:start + 1]
+ new_block
+ lines[end:]
)
path.write_text(
"\n".join(lines) + "\n",
encoding="utf-8"
)
PY
# ------------------------------------------------------------
# 最終表示
# ------------------------------------------------------------
echo
echo "========================================"
echo " Build/install completed"
echo "========================================"
echo
echo "Kernel:"
echo " $KREL"
echo
echo "Config:"
echo " CONFIG_USBIP_CORE=m"
echo " CONFIG_USBIP_VHCI_HCD=m"
echo " CONFIG_USBIP_VHCI_NR_HCS=$NR_HCS"
echo
echo "Kernel image:"
echo " $KERNEL_WIN"
echo
echo "Modules:"
echo " $MODULE_DEST"
echo
echo ".wslconfig:"
cat "$WSLCONFIG"
echo
echo "========================================"
echo " インストール完了"
echo "========================================"
echo
echo "このシェルを終了したあと、"
echo "Windows の PowerShell またはコマンドプロンプトから:"
echo
echo " wsl --shutdown"
echo
echo "を実行してください。"
echo
echo "その後、新しいターミナルでUbuntuを起動してください。"
echo
上記を実行することで最新カーネルのソースを取得して更新するところまでを自動化しています。ただ最近のバージョンでいろいろ動きが変わっているところがあるみたいなので、変更してもおかしくなったら.wslconfigのカーネル指定を削除してみてください。
まとめ
普通に使っているとWSLでUSBを同時8台以上を使うことはないと思います。しかしpytestの実機検証用サーバーには検証機材が大量にぶら下がっていて同時利用出来ない状態になってしまいました。
現状のところ無印ESP32が2台、ESP32-S3が3台、ESP32-P4が4台、WCH-LinkEが6台接続されていて、用途の応じてattachし直しているので不便になっています。そしてどれがどれだかわからなくなるので困ったものです。。。
$ ll /dev/serial/by-path/|grep v2
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:1:1.0 -> ../../ttyACM0
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:2:1.0 -> ../../ttyACM1
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:3:1.0 -> ../../ttyACM2
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:4:1.0 -> ../../ttyACM3
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:5:1.0-port0 -> ../../ttyUSB0
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:6:1.0-port0 -> ../../ttyUSB1
lrwxrwxrwx 1 root root 13 Sep 14 21:08 platform-vhci_hcd.0-usbv2-0:7:1.0-port0 -> ../../ttyUSB2
lrwxrwxrwx 1 root root 13 Sep 14 21:09 platform-vhci_hcd.0-usbv2-0:8:1.1 -> ../../ttyACM4
lrwxrwxrwx 1 root root 13 Sep 14 21:09 platform-vhci_hcd.1-usbv2-0:1:1.1 -> ../../ttyACM5
lrwxrwxrwx 1 root root 13 Sep 14 21:09 platform-vhci_hcd.1-usbv2-0:2:1.1 -> ../../ttyACM6
lrwxrwxrwx 1 root root 13 Sep 14 21:09 platform-vhci_hcd.1-usbv2-0:3:1.1 -> ../../ttyACM7
lrwxrwxrwx 1 root root 13 Sep 14 21:21 platform-vhci_hcd.1-usbv2-0:4:1.1 -> ../../ttyACM8
lrwxrwxrwx 1 root root 13 Sep 14 21:21 platform-vhci_hcd.1-usbv2-0:5:1.1 -> ../../ttyACM9
こんな感じでUSBの認識が8台を超えてできるようになりました。


コメント