本文介绍如何处理在部分高版本内核的ECS实例中热拔virtio设备时出现Oops异常的问题。
问题现象
在部分高版本内核的ECS实例中热拔virtio设备(例如磁盘、网卡)时,操作系统会发生如下Oops异常:
-
在配置了
kernel.panic_on_oops = 1的实例中,会发生内核Panic异常。 -
在配置了
kernel.panic_on_oops = 0的实例中,会出现hung错误。
kernel.panic_on_oops是一个内核参数,控制内核遇到Oops(内核错误)时的行为。
-
内核Panic异常:系统会停止运行当前任务,保存调试信息,然后重启或关机,有助于快速响应问题并减少潜在的损害。
-
内核hung错误:内核可能会尝试继续运行,但在生产环境中不推荐,因为可能会导致数据损坏或其他严重问题。
问题原因
Linux上游社区在内核社区中添加了对virtio设备Admin Virtqueue的支持:commit详情。
在该commit中:
-
对virtio_pci_device的定义中添加了一个is_avq的函数指针,用于判断是否存在Admin Virtqueue。
-
在modern virtio设备的初始化函数virtio_pci_modern_probe中赋值了is_avq函数指针。
@@ -588,6 +658,7 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) vp_dev->config_vector = vp_config_vector; vp_dev->setup_vq = setup_vq; vp_dev->del_vq = del_vq; + vp_dev->is_avq = vp_is_avq; vp_dev->isr = mdev->isr; vp_dev->vdev.id = mdev->id; -
在热拔virtio设备时,代码会检查当前队列是否为Admin Virtqueue。
@@ -236,6 +236,9 @@ void vp_del_vqs(struct virtio_device *vdev) int i; list_for_entry_safe(vq, n, &vdev->vqs, list) { + if (vp_dev->is_avq(vdev, vq->index)) + continue; + if (vp_dev->per_vq_vectors) { int v = vp_dev->vqs[vq->index]->msix_vector; } }
但是如果该virtio设备是legacy virtio设备,is_avq函数指针没有被赋值,导致legacy virtio设备中的virtio_pci_device结构体中的函数指针is_avq是一个空指针(NULL Pointer)。此时如果热拔virtio设备,调用if (vp_dev->is_avq(vdev, vq->index)) 时,会触发空指针异常,会导致程序崩溃或系统错误。
影响范围
-
Linux上游社区
上游社区已经针对该问题进行了修复:commit详情。在该commit中,对is_avq函数指针是否为空的情况进行了判断,修复了该问题。
-
操作系统
-
Ubuntu 24
-
其他内核版本在6.8左右的操作系统,且这些操作系统合入了Admin Virtqueue能力(virtio-pci: Introduce admin virtqueue),但未修复is_avq函数指针判断问题(virtio-pci: Check if is_avq is NULL)。
说明您可以通过
uname -r命令查询内核版本。
-
-
virtio设备
实例使用了legacy virtio设备,并在进行热拔操作。
解决方案
-
方案一:建议更换为modern virtio设备的实例规格族(8代及以上规格),该类实例规格族不受该问题影响。具体操作,请参见修改实例规格。关于实例规格族的更多信息,请参见实例规格族。
-
方案二:
-
升级最新内核软件包,并确认最新软件包是否已合入is_avq函数指针判断的补丁包:virtio-pci: Check if is_avq is NULL。
-
(条件必选)如果最新的内核中没有合入is_avq函数指针判断的补丁包,请自行合入。
-
附录:名词解释
关于文档中涉及的virtio设备、Admin Virtqueue、virtio_pci_device等名词解释说明如下:
|
名词 |
说明 |
|
virtio设备 |
virtio是一种标准化的虚拟化设备通信框架,允许虚拟机高效地与宿主机上的虚拟硬件设备交互。virtio设备是在虚拟化环境中模拟出来的硬件设备(如磁盘、网卡),分为传统legacy virtio和现代modern virtio两类,主要区别在于使用的配置接口不同。 |
|
Admin Virtqueue |
是一个特殊的virtio队列,用于执行设备的管理操作,比如获取设备状态、配置设备等。并非所有virtio设备都支持Admin Virtqueue。 |
|
virtio_pci_device |
是在内核中表示一个virtio PCI设备的数据结构,包含了指向各种功能函数的指针,其中之一就是新添加的is_avq函数指针,用于判断给定的队列是否为Admin Virtqueue。 |
|
is_avq |
该函数用于判断给定的virtio队列是否为Admin Virtqueue。 |
|
virtio_pci_modern_probe |
该函数负责virtio PCI设备的探测和初始化过程。在设备被系统发现后,此函数会被调用来完成设备的设置,包括配置空间的读取、设备特性的检测以及必要的资源分配等。 |
|
RIP寄存器 |
RIP(Instruction Pointer Register)寄存器是x86架构CPU中的一个寄存器,存储了当前执行指令的下一条指令的地址。当程序发生异常,如尝试执行一个空指针所指向的地址时,RIP寄存器会指向引发异常的那条指令的地址。 |