chundev
日期:2026-05-12
環境:QEMU/KVM (含 Nutanix AHV),但概念也適用 Proxmox、libvirt、openstack 等所有 KVM-based 平台
emulated SATA 是讓 guest 以為自己有真實 SATA chipset 的演員,每次 I/O 都要 trap → emulate → translate; virtio-scsi 是 guest 跟 hypervisor 都不裝、直接寫 ring buffer 的 paravirtualized driver。 兩者在「平常」都能跑,但底層 storage 卡住時行為完全不同:SATA NCQ 30 秒 timeout 後 hard reset link(丟失 in-flight write),virtio-scsi 只是 queue 排隊等。
Guest 以為自己有真的 SATA 控制器
↓ 對 AHCI 暫存器 read/write
QEMU 攔截 + 翻譯成虛擬硬體行為
↓ 完整模擬 AHCI 規範
最後變成底層 storage 請求
→ 每個 I/O:trap → emulate → translate → 才到底層
Guest 「知道自己是 VM」
↓ 直接寫入 ring buffer
Hypervisor 直接讀 ring buffer 處理
↓ 沒有「假裝是真硬體」的翻譯
直接接到底層 storage
→ 每個 I/O:write ring → done
核心差別:emulated 假裝自己是真硬體(包含真硬體該有的故障保護機制),paravirtualized 雙方都承認是虛擬環境,省掉假裝的成本。
| SATA NCQ | virtio-scsi | |
|---|---|---|
| Queue 數 | 1 條 per controller | 多條(一個 vCPU 一條,可達 8-32+) |
| Queue 深度 | 最多 32 in-flight 指令 | 預設 128,可調更大 |
| 並發處理 | 單 queue 序列化 | 多 queue 真實並行 |
實測:guest 內 cat /sys/block/sda/queue/nr_requests
SATA 卡住的劇本:
1. 底層 storage 卡 20-30 秒
2. Guest 的 SATA NCQ queue 灌入指令,最多 32 個
3. 30 秒過去沒人回應 → kernel 判定 controller 死掉
4. kernel: "ata3: hard resetting link" ← 硬重置整條 SATA 連接
5. Reset 期間所有 in-flight 32 個寫入「全部退回」
6. ext4 看到大量 write fail → errors=remount-ro 觸發
virtio-scsi 卡住的劇本:
1. 底層 storage 卡 20-30 秒
2. Guest 的 virtio ring 灌入指令,可同時有 128+ 個
3. 「沒有 NCQ link 必須健康」的硬體假設
4. kernel 只是 wait(可調 timeout 給更寬鬆)
5. 底層恢復後,queue 自然排空
6. application 看到「I/O 慢了一下」而不是「fs 壞了」
為什麼 SATA 要 hard reset:實體 SATA driver 設計上假設「指令 30 秒沒回應 = 硬體真的壞了」。在實體環境這個假設合理(救命機制:避免 controller 卡死永遠不回)。在虛擬環境這個假設錯位:沒有真實硬體要救,hard reset 反而搞壞 fs。
| 操作 | SATA emulated | virtio-scsi |
|---|---|---|
| 一次 4 KB write | ~50-100k CPU cycles | ~10-20k CPU cycles |
| 主要成本 | 模擬 AHCI 暫存器 + IRQ injection + PCI 行為 | ring buffer 直接 enqueue |
| Per-op latency | 較高 | 較低 |
高 IOPS 下,SATA emulation 會額外吃 vCPU 給「翻譯」用。
virtio 是 KVM 上游的標準 paravirtualization framework(OASIS spec)。核心觀念:
virtio-net (網路)、virtio-blk (block)、virtio-scsi (SCSI)、virtio-gpu (GPU)、virtio-fs (filesystem)對應的 emulated 競爭者:
| 領域 | emulated | paravirtualized |
|---|---|---|
| 網路 | e1000 (Intel) | virtio-net |
| Block | SATA / IDE | virtio-blk / virtio-scsi |
| Display | VGA / Cirrus | virtio-gpu |
| FS pass | NFS | virtio-fs |
規律:所有 paravirtualized 版本都比 emulated 快、更穩定、CPU 更低。virtualization 平台給 emulated 是為了相容(OS 沒有 virtio driver 時的 fallback),不是為了效能。
歷史包袱多,但常見三大來源:
怎麼確認 guest 的 disk 是哪一種(Linux):
# 看 driver
ls -l /sys/block/sda/device/driver/
# → 指向 virtio_scsi = virtio-scsi
# → 指向 ata = SATA emulated
# 看 dmesg
dmesg | grep -iE 'scsi host|Virtio SCSI HBA|ata_piix'
# virtio-scsi: "scsi host2: Virtio SCSI HBA"
# SATA: "scsi host0: ata_piix"
如果 guest 本來就 boot in virtio-scsi 模式,當然零風險。 如果從 SATA 換 virtio-scsi,前提是:
| 檢查項 | 要怎麼確認 |
|---|---|
virtio_scsi 在 initramfs |
lsinitramfs /boot/initrd.img-$(uname -r) \| grep virtio_scsi |
virtio_scsi 可載入 |
modinfo virtio_scsi |
| fstab 用 UUID/LABEL | cat /etc/fstab(不能用 /dev/sda1 這種 device path) |
| grub 用 UUID/LVM ID | grep '^set root' /boot/grub/grub.cfg(要 lvmid 或 UUID) |
現代 distro (Ubuntu 18+、CentOS 7+、Debian 10+) 預設都符合。風險主要在 Windows 沒事先裝 virtio-win 就改 → BSOD。
預設 Linux SCSI device timeout 是 30 秒。對虛擬化 + 軟體 storage 環境偏短:
# 看當下值
cat /sys/block/sda/device/timeout
# 30
# 改大(暫時生效)
echo 180 | sudo tee /sys/block/sda/device/timeout
# 持久化(udev rule)
sudo tee /etc/udev/rules.d/99-nutanix-scsi-timeout.rules <<'EOF'
ACTION=="add|change", SUBSYSTEM=="scsi", ATTR{type}=="0", ATTR{vendor}=="NUTANIX", ATTR{timeout}="180"
EOF
sudo udevadm control --reload-rules
sudo udevadm trigger --action=change --subsystem-match=scsi
為什麼 180:給底層 storage 在分散式 service 失聯時有充足時間恢復(典型 ZK / consensus 重選大概需要 60-120 秒)。180 秒涵蓋極端情況,比 30 秒寬鬆 6 倍。
把 vendor 換成你 storage 平台對應字串(VMware 是 VMware,Nutanix 是 NUTANIX,Linode/AWS 視 driver 而異)。
/sys/block/*/device/timeout 官方說明接續閱讀: