澄清 linux 的 nvme apst 問題
我遇到了一個與askubuntu社區中描述的幾乎相同的問題。
與發布此問題的使用者一樣,我的系統具有金士頓 NVME 磁碟,並且與該使用者一樣,我的問題通過在 grub 菜單中添加以下核心選項解決:
nvme_core.default_ps_max_latency_us=0
.使用者聲明的解決方案如下:
問題出在 SSD 功能上,自治電源狀態轉換 (APST) 導致了當機。為了緩解它,在他們發布修復程序之前,請
nvme_core.default_ps_max_latency_us=0
在GRUB_CMDLINE_LINUX_DEFAULT
選項中包含該行。儘管有幫助,但此評論留下了幾個問題,包括以下內容:
- 導致問題的具體缺陷是什麼以及在哪裡?
- 解決方法會更改什麼以防止出現缺陷?
- 由於這種解決方法,哪些功能或其他預期效果會失去?
- 尤其是,需要修復哪些核心、儲存介質韌體、系統韌體(即 UEFI/BIOS)或其他一些組件,以提供適當的解決方案?
任何評論都有助於嘗試解決所有或部分這種混淆。
Linux核心源程式碼中的程式碼註釋
drivers/nvme/host/core.c
似乎最好地解釋了它:static int nvme_configure_apst(struct nvme_ctrl *ctrl) { /* * APST (Autonomous Power State Transition) lets us program a * table of power state transitions that the controller will * perform automatically. We configure it with a simple * heuristic: we are willing to spend at most 2% of the time * transitioning between power states. Therefore, when running * in any given state, we will enter the next lower-power * non-operational state after waiting 50 * (enlat + exlat) * microseconds, as long as that state's exit latency is under * the requested maximum latency. * * We will not autonomously enter any non-operational state for * which the total latency exceeds ps_max_latency_us. Users * can set ps_max_latency_us to zero to turn off APST. */
因此,APST 是一項允許 NVMe 控制器(在 NVMe SSD 內)按照可配置規則在電源管理狀態之間自主切換的功能。NVMe 控制器指定進入和退出每個省電狀態需要多少微秒;核心使用此資訊來配置 NVMe 控制器內的狀態轉換規則。
- 導致問題的具體缺陷是什麼以及在哪裡?
看起來這種特殊的金士頓 NVMe SSD 要麼在其喚醒時間估計中過於樂觀,要麼在進入足夠深的省電狀態後根本無法喚醒(沒有完全重置控制器)。當被授予使用 APST 的權限時,它顯然進入了某種省電狀態,然後未能在指定時間內返回執行狀態,這使核心不高興。
- 解決方法會更改什麼以防止出現缺陷?
它告訴從 APST 電源管理狀態喚醒的最大允許時間正好是 0 微秒,這會導致 APST 功能被禁用。
- 由於這種解決方法,哪些功能或其他預期效果會失去?
如果無法使用 NVMe 控制器的自主電源管理功能,則只有在核心特別要求時才允許控制器進入省電狀態。這意味著省電很可能不會像使用 APST 那樣大。
- 特別是需要修復什麼,核心、儲存介質韌體、系統韌體(即UEFI/BIOS)或其他一些組件,以便使用者體驗到正確的解析度?
最佳解決方案是金士頓提供 NVMe 磁碟韌體更新,以使 APST 電源管理正常工作,或至少使驅動器不承諾它無法提供的東西,即不宣布轉換時間過於樂觀的 APST 模式,和/或根本不宣布任何會導致控制器在使用時失敗的 APST 模式。
如果事實證明可以通過例如程式 APST 來完全避免最深的節能狀態來避免該問題,則可以創建更具體的核心級解決方法。Linux 核心中的許多設備驅動程序都有“怪癖表”,為特定的硬體模型指定了解決方法。對於 NVMe,您可以
drivers/nvme/host/pci.c
在 Linux 核心原始碼中找到一個:static const struct pci_device_id nvme_id_table[] = { { PCI_VDEVICE(INTEL, 0x0953), /* Intel 750/P3500/P3600/P3700 */ .driver_data = NVME_QUIRK_STRIPE_SIZE | NVME_QUIRK_DEALLOCATE_ZEROES, }, { PCI_VDEVICE(INTEL, 0x0a53), /* Intel P3520 */ .driver_data = NVME_QUIRK_STRIPE_SIZE | NVME_QUIRK_DEALLOCATE_ZEROES, }, { PCI_VDEVICE(INTEL, 0x0a54), /* Intel P4500/P4600 */ .driver_data = NVME_QUIRK_STRIPE_SIZE | NVME_QUIRK_DEALLOCATE_ZEROES, }, { PCI_VDEVICE(INTEL, 0x0a55), /* Dell Express Flash P4600 */ .driver_data = NVME_QUIRK_STRIPE_SIZE | NVME_QUIRK_DEALLOCATE_ZEROES, }, { PCI_VDEVICE(INTEL, 0xf1a5), /* Intel 600P/P3100 */ .driver_data = NVME_QUIRK_NO_DEEPEST_PS | NVME_QUIRK_MEDIUM_PRIO_SQ | NVME_QUIRK_NO_TEMP_THRESH_CHANGE | NVME_QUIRK_DISABLE_WRITE_ZEROES, }, [...]
在這裡,各種
NVME_QUIRK_
設置會觸發驅動程序中的各種解決方法程式碼。請注意,已經存在一個名為的怪癖設置
NVME_QUIRK_NO_DEEPEST_PS
,它可以防止狀態轉換到最深的電源管理狀態。如果您的金士頓 NVMe 的 APST 問題與已經為 Intel 600P/P3100 和 ADATA SX8200PNP 實施的解決方法相同,那麼只需編寫一個像這樣的新 quirk 表條目(用<angle brackets>
適當的值替換其中的內容,你可以用lspci -nn
):{ PCI_DEVICE(<PCI vendor ID>, <PCI product ID of the SSD>), /* <specify make/model of SSD here> */ .driver_data = NVME_QUIRK_NO_DEEPEST_PS, },
並使用此修改重新編譯核心。
顯然,需要真正擁有這種精確 SSD 模型的人來測試它。如果您碰巧熟悉 C 程式基礎知識以及如何編譯自定義核心,那麼這可能是您在眾多 Linux 核心貢獻者中名列前茅的機會!如果您有興趣,您可能應該閱讀kernelnewbies.org以了解更多詳細資訊。
核心程式並不總是非常複雜:有很多簡單的部分只需要一個擁有合適硬體和一些基本程式知識的人。我已經送出了一些像這樣的小更新檔。
如果設置
NVME_QUIRK_NO_DEEPEST_PS
結果不能解決問題,則可能需要實施新的怪癖。這可能會更複雜,可能需要金士頓的一些實驗或理想的資訊來找出究竟需要做些什麼來避免這個問題,也許還需要與 Linux NVMe 驅動程序維護者討論實現它的最佳方法。