Disable the HVF in-kernel irqchip for Windows 11 on macOS and document why

With the HVF in-kernel GICv3, QEMU's usb-storage races on the status packet
and Windows Setup stalls 10 s after every burst of reads when installing from
the ISO (utmapp/UTM#7970). Use the emulated interrupt controller until
upstream QEMU gets the fix from the UTM fork. Record the investigation in
windows11-arm64-install-speed.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
guofangandClaude Sonnet 5.5 committed 2026-10-23 23:17:55 +08:00
1 parent 6ff9570ca0
commit b073d87a56
2 files changed
+79 -5

No files matched your search

+74 -5
View File
@@ -5,10 +5,14 @@
## 结论
- 慢的原因是**安装介质走了模拟的 USB 光驱**(`usb-storage` 挂 ISO):读安装文件平均只有约 4 MB/s。
- 把安装介质改成**FAT32 分区里的 NVMe 磁盘镜像**(`install.wim` 拆成 `.swm`)后,读速达到 54 到 92 MB/s
(前 7 个采样平均约 74 MB/s),安装在约 80 秒内到 40%,复制阶段约 2 分钟结束。
- 方案已接进 `run-windows11.py`:`--install-from-img`,镜像由 `vm-images/build-windows-install-disk.sh` 生成。
- **根因(第五节)**:HVF 的内核态 GICv3 与 QEMU 的 `usb-storage` 有竞态,Windows Setup 每读一批数据就卡约 10 秒。
在 `[machine]` 里加 `kernel-irqchip = "off"`(已写入 `qemu/windows11-darwin-arm64.ini`)后,
**同样走 USB 光驱的 `--install-from-iso` 也变快了**,和 UTM 的速度一致。
- 下面第一到五节是更早的分析:当时把慢归因于"模拟 USB 光驱吞吐低"(平均约 4 MB/s),
这个归因不准确,真实原因是上面的 10 秒停顿;那时测到的低吞吐就是停顿拉低的平均值。
- 把安装介质改成**FAT32 分区里的 NVMe 磁盘镜像**(`install.wim` 拆成 `.swm`,`--install-from-img`)仍然有效,
因为它绕开了 usb-storage,读速达到 54 到 92 MB/s,镜像由 `vm-images/build-windows-install-disk.sh` 生成。
但现在不是必须的。
## 环境
@@ -158,7 +162,72 @@ vm-images/build-windows-install-disk.sh [--force] ISO [OUTPUT.img]
- **显卡**:arm64 配置用 `ramfb`。`virtio-gpu-pci` 会让画面冻结在 "Start boot option"(QEMU 固件的 virtio-gpu 输出在 Windows 引导管理器处冻结)。
- **`-daemonize` 与 Cocoa**:Cocoa 窗口不能和 `-daemonize` 一起用,要看界面只能前台运行。
## 五、后续(需要决定)
## 五、为什么 UTM 用同样的 USB 光驱却很快
现象:同一台机器、同一个 ISO,UTM 里装 Windows 11 Arm 很快,本仓库的 `--install-from-iso` 很慢。
UTM 和本仓库都是 QEMU + HVF,ISO 也都是 `usb-storage`(UTM 加 `removable=true`,挂在 `nec-usb-xhci` 上),所以设备类型不是原因。
### 排查过程
1. 在 UTM 里建一台 QEMU 后端的 Windows ARM 虚拟机,用同一个 ISO 启动,再用
`ps -ww -o args= -p $(pgrep -f QEMULauncher)` 取出完整命令行。UTM 的 QEMU 是 `Frameworks/qemu-aarch64-softmmu.framework`
里的 dylib,由 `QEMULauncher` 加载(沙盒继承,不能脱离 UTM 单独运行)。
2. 与本仓库的配置逐项对照,差异有:`-machine virt,gic-version=3`、`-cpu host`、
`-accel hvf,ipa-granule-size=0x1000`、6 核、系统盘 `discard=unmap,detect-zeroes=unmap`、
`file.locking=off`、`nec-usb-xhci`、`-nodefaults`、`-rtc base=localtime`、UTM 自己的 UEFI 固件等。
把前面几项全部对齐后速度**没有改善**。
3. 查 UTM 仓库的提交,找到 [95a7f4a2](https://github.com/utmapp/UTM/commit/95a7f4a2)
(PR [#7971](https://github.com/utmapp/UTM/pull/7971),Issue [#7970](https://github.com/utmapp/UTM/issues/7970))。
### 根因
UTM 5.0.7 启用了 Hypervisor.framework 的内核态 GICv3。这暴露了 QEMU `usb-storage` 的一个竞态:
命令完成时,客户机已经排队的状态包(CSW)被重入处理,请求仍标记为在途,被"挂起"后永远得不到应答。
Windows 对 USB 存储传输的超时是 10 秒,于是每一批读取之后都会停约 10 秒,随后重置设备并重试。
这与第一节测到的现象一致:客户机发出的请求很少、主机很闲、读吞吐只有几 MB/s。
UTM 记录的"复制文件"阶段耗时(Windows 11 24H2 ARM64,USB CD):
| 构建 | 耗时 |
|---|---|
| 5.0.6 | 165 秒 |
| 5.0.7(有竞态) | 约 1230 秒 |
| 5.0.7 + `-machine kernel-irqchip=off` | 160 秒 |
| 5.0.7 + 修复补丁 | 约 135 秒 |
修复补丁在 UTM 的 QEMU fork(`utmapp/qemu@ae92cdc41f`),在完成数据包之前先释放 SCSI 请求。
UTM 5.0.8 带有这个修复;Homebrew 的 QEMU 11.1.2 没有,所以上游版本会遇到这个问题。
### 处理
在 `qemu/windows11-darwin-arm64.ini` 的 `[machine]` 加 `kernel-irqchip = "off"`,改用模拟的中断控制器。
这一项改完后速度明显提升,已确认和 UTM 一致。
```ini
[machine]
type = "virt"
kernel-irqchip = "off"
```
只验证单项(不改配置文件)时,可以用 `./run-windows11.py --install-from-iso -- -machine virt,kernel-irqchip=off`。
### 对齐 UTM 时试过、后来回退的改动
对齐 UTM 时一度同时改了下面这些,但只有 `kernel-irqchip=off` 确认有效,其余没有单独验证过,已全部回退:
- `-machine` 的 `gic-version=3`、`-cpu host`、`-nodefaults`、`-rtc base=localtime`。
- 安装介质挂在独立的 `nec-usb-xhci` 上,`usb-storage` 加 `removable=true,bus=usb-bus.0`,ISO 的 `-drive` 加 `file.locking=off`。
- `cpus` 从 8 改为 6,系统盘加 `discard=unmap`、`detect-zeroes=unmap`。
没有对齐的:`-accel hvf,ipa-granule-size=0x1000`(UTM 的补丁参数)、UTM 的 `edk2-aarch64-secure-code.fd`、TPM、
`virtio-rng-pci`、Intel HDA、`virtio-ramfb-gl`、`vmnet-shared`。
### 代价与后续
- 模拟中断控制器的运行时性能略低于内核态 GIC。若只在安装阶段需要,日常运行可以改回去(去掉 `kernel-irqchip`)。
- 等上游 QEMU 合入这个 `usb-storage` 修复后,可以去掉这一项。
## 六、后续(需要决定)
1. (已完成)`run-windows11.py --install-from-img` 和 `build-windows-install-disk.sh`。
2. 核对 `Autounattend.xml` 的映像序号。