本文介紹如何處理在部分高版本核心的ECS執行個體中熱拔virtio裝置時出現Oops異常的問題。
問題現象
在部分高版本核心的ECS執行個體中熱拔virtio裝置(例如磁碟、網卡)時,作業系統會發生如下Oops異常:
在配置了
kernel.panic_on_oops = 1
的執行個體中,會發生核心Panic異常。在配置了
kernel.panic_on_oops = 0
的執行個體中,會出現hung錯誤。
在Linux核心中,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函數指標的賦值。
在熱拔virtio裝置時,代碼會檢查當前隊列是否為Admin Virtqueue。
但是上遊社區忽略了一個問題:如果該virtio裝置不是一個modern virtio裝置,而是一個legacy virtio裝置時,並沒有對is_avq函數指標進行賦值,導致legacy virtio裝置中的virtio_pci_device結構體中的函數指標is_avq是一個null 指標(NULL Pointer)。此時如果熱拔virtio裝置,調用到if (vp_dev->is_avq(vdev, vq->index))
代碼時,CPU的RIP寄存器執行了一個null 指標地址,從而觸發了null 指標異常,也就是嘗試執行一個無效的記憶體位址,這是作業系統不允許的,會導致程式崩潰或系統錯誤。
影響範圍
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裝置的執行個體規格類型系列。具體操作,請參見修改執行個體規格。
建議使用以下執行個體規格類型系列,以下規格類型系列是modern virtio裝置,不受該問題影響。
ecs.c8i、ecs.g8i、ecs.r8i、ecs.c8ae、ecs.g8ae、ecs.r8ae、ecs.c8a、ecs.g8a、ecs.r8a。更多資訊,請參見執行個體規格類型系列。
方案二
升級最新核心軟體包,並確認最新軟體包是否已合入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中的一個寄存器,儲存了當前執行指令的下一條指令的地址。當程式發生異常,如嘗試執行一個null 指標所指向的地址時,RIP寄存器會指向引發異常的那條指令的地址。 |