澄清 linux 的 nvme apst 问题

epl*_*epl 3 linux boot kernel hardware nvme

我遇到了一个与askubuntu社区中描述的问题几乎相同的问题。

与发布此问题的用户一样,我的系统具有金士顿 NVME 磁盘,并且与该用户一样,通过在 grub 菜单中添加以下内核选项解决了我的问题:nvme_core.default_ps_max_latency_us=0.

用户声明的解决方案开始如下:

问题出在 SSD 功能上,自主电源状态转换 (APST) 导致了冻结。为了缓解它,直到他们发布修复程序,请nvme_core.default_ps_max_latency_us=0GRUB_CMDLINE_LINUX_DEFAULT选项中包含该行。

虽然有用,但此评论留下了几个问题,包括以下内容:

  1. 导致问题的具体缺陷是什么以及在哪里?
  2. 为了防止出现缺陷,变通方法有哪些变化?
  3. 由于这种变通方法,哪些功能或其他预期效果会丢失?
  4. 尤其是,内核、存储介质固件、系统固件(即 UEFI/BIOS)或某些其他组件需要修复什么才能提供适当的解决方案?

任何评论都有助于解决所有或部分这种混淆。

tel*_*coM 8

范围内的代码注释drivers/nvme/host/core.c在Linux内核源似乎是最好的解释:

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.
     */
Run Code Online (Sandbox Code Playgroud)

因此,APST 是一项功能,它允许 NVMe 控制器(在 NVMe SSD 内)按照可配置的规则在电源管理状态之间自主切换。NVMe 控制器指定进入和退出每个省电状态需要多少微秒;内核使用此信息来配置 NVMe 控制器内的状态转换规则。

  1. 导致问题的具体缺陷是什么以及在哪里?

看起来这个特殊的金士顿 NVMe SSD 要么在其唤醒时间估计方面过于乐观,要么在进入足够深的省电状态后根本无法唤醒(没有完全重置控制器)。当被授予使用 APST 的权限时,它显然会进入某种节能状态,然后无法在指定的时间内返回到操作状态,这使内核不高兴。

  1. 为了防止出现缺陷,变通方法有哪些变化?

它告诉从 APST 电源管理状态唤醒的最大允许时间正好是 0 微秒,这会导致 APST 功能被禁用。

  1. 由于这种变通方法,哪些功能或其他预期效果会丢失?

如果无法使用 NVMe 控制器的自主电源管理功能,则只有在内核特别请求时,控制器才会被允许进入省电状态。这意味着节电很可能不会像使用 APST 时那样大。

  1. 尤其是需要修复哪些内容,内核、存储介质固件、系统固件(即 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, },
[...]
Run Code Online (Sandbox Code Playgroud)

这里的各种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, },
Run Code Online (Sandbox Code Playgroud)

并使用此修改重新编译内核。

显然,需要真正拥有这种确切 SSD 模型的人来测试这一点。如果您碰巧熟悉 C 编程基础知识以及如何编译自定义内核,这可能是您在 Linux 内核贡献者一长串名单中名列前茅的机会!如果您有兴趣,您可能应该阅读kernelnewbies.org以获取更多详细信息。

内核编程并不总是非常复杂:有很多简单的部分,只需要一个拥有合适硬件类型和一些基本编程知识的人。我已经提交了一些像这样的小补丁。

如果设置NVME_QUIRK_NO_DEEPEST_PS结果不能解决问题,则可能需要实施新的怪癖。这可能会更复杂,并且可能需要一些实验或最好来自金士顿的信息,以找出确切需要做些什么来避免这个问题,并可能与 Linux NVMe 驱动程序维护者讨论实现它的最佳方法。