飞牛 NAS 看电影死机排查实录:VFIO 直通 + md 阵列冲突根治指南

一台飞牛 NAS,用飞牛影视看了半小时电影后整机卡死——ping 不通但机器还在转。本文记录从现象到根治的完整排查过程,供遇到同类问题的朋友参考。

一、问题现象

  • 飞牛 NAS 正常运行,用飞牛影视看电影约 30 分钟后整机卡死

  • 网络不通(ping 不通),但机器电源、风扇仍在转(假死)

  • 强制长按电源 10-15 秒关机后重启才能恢复

  • 反复出现,每次都是看影视持续一段时间后触发

典型特征:不是开机即死,也不是随机死,而是"持续 IO 一段时间后才死"。这种模式高度指向 IO 死锁,而非硬件损坏。

二、排查思路

1. 先排除内存爆掉(OOM)

Plain Text

free -h

结果:内存 15G,可用 8.5G,Swap 未使用。排除 OOM

2. 查 RAID 状态

Plain Text

cat /proc/mdstat

发现关键异常:多个阵列标记为 broken,其中 md0/md1 显示 26/1 的诡异槽位数(RAID1 本应只有 2 个槽位),且盘符缺失。

3. 查所有盘的 SMART

Plain Text

lsblk -o NAME,SIZE,MODEL,SERIAL,ROTA
for d in /dev/sd? /dev/nvme?n1; do
  smartctl -a "$d" | grep -E "Reallocated|Pending|Uncorrectable|Power_On|Health|Critical"
done

结果:sda 和 sdb 两块盘从 lsblk 里彻底消失,但 mdstat 里还挂着它们的阵列。剩下三块盘(sdc 8TB 希捷 + 两块 NVMe)SMART 全部正常。

三、根因定位

关键线索

通过 virsh list --all 发现飞牛里跑着一个群晖 VM ossgyte7,且 /sys/bus/pci/drivers/vfio-pci/ 下有 0000:02:00.0,说明 SATA 控制器通过 VFIO 直通给了群晖 VM

用户确认:消失的 sda/sdb 正是直通给群晖的那两块盘。

死锁链条

启动时序冲突:

  1. 宿主机开机,initramfs 加载 md_mod,先于 VFIO 抢占 SATA 控制器

  2. mdadm 自动扫描,发现 sda/sdb 上有群晖的 RAID superblock,组装成 md0/md1/md3/md127

  3. VFIO 随后绑定控制器,宿主机失去对 sda/sdb 的访问

  4. 已组装的 md 阵列变成 broken,但内核仍持有 IO 请求

  5. 看影视持续读 IO → 碰到 broken 阵列 → md 重试超时 → D 状态进程堆积 → 整机挂起

为什么是"看半小时才死":平时空闲能撑着,一旦持续高 IO,到临界点就触发死锁。这是 IO 死锁的典型表现,跟"硬件坏盘立即掉线"完全不同。

四、根治方案

需要堵两层:mdadm 启动组装 + udev 热插拔增量组装。只堵一层不够。

第一层:mdadm 启动配置

1. 停掉所有 broken 阵列

Plain Text

mdadm --stop /dev/md0
mdadm --stop /dev/md1
mdadm --stop /dev/md3
mdadm --stop /dev/md127

2. 备份原配置

Plain Text

cp /etc/mdadm/mdadm.conf /etc/mdadm/mdadm.conf.bak

3. 只保留宿主机自己的阵列

Plain Text

mdadm --detail --brief /dev/md2 /dev/md4 /dev/md6 > /tmp/mdadm.host.conf
cat /tmp/mdadm.host.conf

4. 写入新配置,禁止自动组装

Plain Text

echo "AUTO -all" > /etc/mdadm/mdadm.conf
cat /tmp/mdadm.host.conf >> /etc/mdadm/mdadm.conf
echo "MAILADDR root" >> /etc/mdadm/mdadm.conf

AUTO -all 含义:默认不自动组装任何阵列,只组装下面显式列出的。

5. 更新 initramfs

Plain Text

update-initramfs -u

第二层:禁用 udev 增量组装

AUTO -all 管的是 mdadm 启动脚本,管不住 udev 热插拔。VM 关机后盘归还宿主机时,udev 会绕过 mdadm.conf 自动增量组装阵列。必须再堵这一层。

Plain Text

# 1. 停掉当前被组装回来的阵列
mdadm --stop /dev/md0

# 2. 屏蔽 mdadm 的 udev 规则
mkdir -p /etc/udev/rules.d
cp /dev/null /etc/udev/rules.d/85-mdadm.rules

# 3. 重载 udev
udevadm control --reload-rules

验证根治效果

Plain Text

# 确认阵列没了
cat /proc/mdstat | grep md0

# 模拟热插拔,看会不会被重新组装
for h in /sys/class/scsi_host/host*/scan; do echo "- - -" > "$h"; done
sleep 3
cat /proc/mdstat | grep md0

两次 grep 都无输出 = udev 不再自动组装,根治成功。

五、最终状态

Plain Text

md4 : active raid1 sdb1[0]    [U]   # 宿主机 8TB 数据盘
md2 : active raid1 nvme1n1p3  [U]   # 宿主机 1T NVMe
md6 : active raid1 nvme0n1p1  [U]   # 宿主机 512G NVMe

启动群晖 VM 后,VFIO 抢走 SATA 控制器,sda/sdc 从宿主机消失,broken 阵列连出现的土壤都没了。看影视 30 分钟以上不再死机,根因 100% 确认。

六、防御层总结

防御层

措施

防的是什么

mdadm 启动

AUTO -all + 显式 md2/md4/md6

开机时自动组装群晖盘的阵列

udev 热插拔

屏蔽 85-mdadm.rules

VM 关机后盘归还时的增量组装

双层防御,彻底根治。

七、附带建议:开启持久化日志

飞牛默认不保留重启后的日志,死机重启后查不到现场。排查时 journalctl -b -1 会报 no persistent journal was found。建议立即开启:

Plain Text

mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald

下次再死,重启后就能用 journalctl -b -1 -p err 抓到死机当时的错误日志。

八、经验总结

  1. "ping 不通但机器还转"几乎都是 IO 死锁,不是硬件坏。硬件坏通常是某块盘直接掉,不会有"用半小时才死"的特征。

  2. 飞牛直通盘给群晖 VM 是常见组合,但默认配置会抢盘。宿主机开机时 mdadm 会抢先扫描到直通盘上的群晖 RAID superblock 并组装,等 VFIO 抢回控制器后阵列就变 broken,埋下 IO 死锁雷。

  3. 只改 mdadm.conf 不够,还要堵 udev 热插拔。AUTO -all 管启动脚本,管不住 udev 增量组装。

  4. 持续 IO 才触发死锁是关键特征。空闲能撑很久,一看影视/转码就死,指向 IO 路径问题而非内存/CPU 问题。

  5. 持久化日志默认不开,排查前先开,否则重启后无现场可查。

九、适用范围

  • 飞牛 NAS(fnOS)直通 SATA 盘给群晖/其他 VM 的场景

  • 任何用 KVM/QEMU + VFIO 直通 + mdadm 的 Debian 系宿主机

  • 症状为"持续 IO 后整机卡死、ping 不通但机器还转"


排查日期:2026-09-12 环境:飞牛 fnOS,内核 6.18.18,群晖 VM via VFIO 直通 SATA 控制器