[hardware development] 从NVMe硬盘节能导致的死机问题展开谈谈
Tofloor
poster avatar
忘怀
deepin
2025-04-25 10:41
Author

前言

这是一篇非专业科普性质的文章,目的是解决并理解此种常见问题的解决办法,

并由此出发了解到由此展开的一些信息和知识。

简单来说,如果遇到了开机一段时间后或者待机唤醒后死机的问题,

可以在启动时在GRUB启动菜单阶段按e在linux .... quiet splash那行添加

一个空格再加上nvme_core.default_ps_max_latency_us=0 后按ctrl+x组合键

启动系统,如果问题消失可以确认是 NVMe 硬盘 APST(Autonomous Power State Transition 自动电源状态转换) 功能引发的问题。

显然上面有很多对新手十分陌生的名词,下面逐一进行解答。

问题 1:什么是GRUB?

GRUB是一个启动器,主板启动后先启动到它,然后它再继续启动内核。

这里我们使用它给内核传递这个配置参数。

问题 2: 什么是内核?

内核介于硬件和系统之间,用于使能硬件的一些功能,连接系统到硬件。

问题 3: 这个命令像魔法咒语,怎么理解?

简单字面意思就是

设置NVMe核心模块的默认电源最大节能延迟时间为0。

效果是:关掉硬盘的APST功能

问题 4:那么什么是NVMe硬盘?

NVMe是NVM Express的缩写,是常见于近几年的固态硬盘用于连接到PCIe总线的一个标准。除了NVMe还有SCSI等等。

问题 5:你这不是把节能关了吗,我是笔记本电脑,我要续航

下面部分介绍 如何编译内核去尝试只关闭一部分的节能。

问题 6: 这么改了没效果

试试 pcie_aspm=0, pcie_port_pm=off等等参数,不在本文介绍。

问题 7: 我不想修改内核,想继续用参数

把nvme_core.default_ps_max_latency_us=0值修改成1000 - 20000之间的值,找到合适的值需要根据硬盘的APST表判断

内核补丁

简单来说,先 运行 lspci -nn | grep -i memory 命令

获得如下输出

03:00.0 Non-Volatile memory controller [xxxx]: xxxx [VID:PID]

拿到需要在内核里打补丁的硬盘的VID和PID,这样可以只针对这个盘生效。

修改内核代码的drivers/nvme/host/pci.c文件的

nvme_id_table数组添加两行类似其他的如下内容

{ PCI_DEVICE(0xVID, 0xPID),.driver_data = NVME_QUIRK_NO_DEEPEST_PS , },

后编译内核,然后安装编译的内核(记得去掉之前添加的关闭APST的启动参数,

这个可以通过查看当前内核启动参数cat /proc/cmdline 确认)

问题 1: 怎么编译内核?

如果有这个问题,需要看另外一篇专门介绍如何在这个Linux发行版编译内核的文章。

问题 2: 什么是PID,VID?

简单来说,VID是厂商ID(Vendor ID),用来标识厂商,PID是产品ID

(Product ID),结合厂商ID标识厂商的一个产品,还有子系统的VID和

子系统的PID,此处不多赘述。

问题 3: 和之前命令禁用的方式有什么区别?

{ PCI_DEVICE(0xVID, 0xPID),.driver_data = NVME_QUIRK_NO_DEEPEST_PS , }, 只停止转换到这个VID:PID硬盘的最深的电源状态,不会禁用其他盘的,也不会禁用其他等级的电源状态。

问题 4: 这么改后没效果 怎么办?

  1. 可能内核没生效
  2. 可能不止要禁用这个状态,更浅的状态也要禁用

问题 5:我的电脑在Windows下面待机没有Linux功耗高

可能可以尝试给你的盘如同此法添加一个NVME_QUIRK_SIMPLE_SUSPEND怪癖(假如dmesg | grep nvme有输出 using simple suspend的话可以试着切换一下)

APST信息读取

简单来说 运行下面命令(下面命令中/dev/nvme0为你想查询的那个NVMe盘的设备路径)

  1. nvme get-feature /dev/nvme0 -f 0x0c -H

    可以获得这么一个输出结构,其中 Enabled 和 Current value:0x000001 就表示支持 APST自动电源状态转换。

    get-feature:0xc (Autonomous Power State Transition),

    Current value:0x000001

    Autonomous Power State Transition Enable (APSTE): Enabled

  2. smartctl -c /dev/nvme0

    Supported Power States

    St Op Max Active Idle RL RT WL WT Ent_Lat Ex_Lat

    0 + ?W - - 0 0 0 0 0 0

    1 + ?W - - 1 1 1 1 0 0

    2 + ?W - - 2 2 2 2 0 0

    3 - ?W - - 3 3 3 3 xx xx

    4 - ?W - - 4 4 4 4 xx xx

怎么解读上面这个表,意思是支持的电源状态从0-4 Op的意思是+就是进入之后还能进行io的,-就是不能,Max就是此状态下**声称**的最大功耗,Ent_Lat和Ex_Lat对应进入和退出的延迟

APST设置

APST(自动电源状态转换)是两个组成部分,由一个电源状态转换表和自动执行该转换的控制器组成。

简单来说可以通过 nvme 命令 去设置最浅的电源状态来节能,

默认是硬件能在比如0-4自动转换,设置后如果硬件接受就在比如1-4自动转换

问题 1:看了代码 apst_primary_timeout_ms和apst_secondary_timeout_ms和apst_primary_latency_tol_us和apst_secondary_latency_tol_us 参数是什么意思 ?

可以读下这个补丁 在

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebd8a93aa4f50e9e013e6aa7fe601b4ce7565c28

下面是翻译的注释

根据模块参数的不同,将采用以下两种支持技术之一:

若参数提供了明确的超时时间和容忍值,系统将利用这些参数构建一个最多包含2个非工作状态的转换表。

默认参数值参考了微软和英特尔NVMe驱动采用的数值。但由于本方案未实现外接电源与电池供电切换时

动态重建APST表的功能,这些超时设置和容忍值实际综合了微软在交流供电和电池供电场景下的设定。

若未提供参数,系统将采用简易启发式算法配置转换表:设定设备在电源状态转换过程中最多消耗2%的时间。

因此,在任一给定状态下运行时,只要目标状态的退出延迟低于设定的最大延迟阈值,

系统将在等待50*(进入延迟+退出延迟)微秒后进入下一级低功耗非工作状态。

参考资料

内核代码

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/nvme/host/core.c

Reply Favorite View the author
All Replies
MeGusta
deepin
2025-04-25 12:52
#1

我之前发过关于matebook16s睡眠恢复掉盘死机的故障,也是这个问题,通过添加内核参数解决的。

https://bbs.deepin.org.cn/phone/zh/post/277081

Reply View the author
jjcui8595
deepin
Resources Team Moderator
2025-04-25 14:56
#2

like已收藏

Reply View the author
g***0@lushosa.com
deepin
2025-04-25 17:30
#3
The user is banned, and the content is hidden.
Amber
deepin
2025-04-25 18:44
#4

还好我是sata硬盘joy

Reply View the author
wcs4221
deepin beta test group
2025-04-26 07:39
#5

好文。支持一下。

Reply View the author
fslong
deepin beta test group
2025-04-27 19:43
#6

pcie_aspm=0后功耗会显著提升,并且发热严重,有没有更折中的方案。

Reply View the author
MeGusta
deepin
2025-04-28 14:07
#7
fslong

pcie_aspm=0后功耗会显著提升,并且发热严重,有没有更折中的方案。

你用 sudo smartctl -c /dev/nvme0n1 | grep -A7 "Supported Power States"命令查询硬盘的功耗状态,需要安装 smartmontools

nvme_core.default_ps_max_latency_us=xxxx 内核参数来定义硬盘的倒数第二位的功耗状态,这样,既能避免掉盘,又能减少硬盘的发热。

我查询出来致钛PC005的倒数第二位的功耗参数(Ex_Lat)是 2000nvme_core.default_ps_max_latency_us=2000 ,我就用的这个内核参数。

# 以下是致钛PC005硬盘支持的电源状态
# sudo smartctl -c /dev/nvme0n1 | grep -A7 "Supported Power States"
#
# Supported Power States
# St Op     Max   Active     Idle   RL RT WL WT  Ent_Lat  Ex_Lat
#  0 +     9.00W       -        -    0  0  0  0        0       0
#  1 +     4.60W       -        -    1  1  1  1        0       0
#  2 +     3.80W       -        -    2  2  2  2        0       0
#  3 -   0.0450W       -        -    3  3  3  3     2000    2000
#  4 -   0.0040W       -        -    4  4  4  4    15000   15000
Reply View the author
136******45
deepin
2025-04-28 14:18
#8

这个不是应该根本的算内核的bug嘛,属于硬件和内核兼容不够好嘛, 得给他们提issue

Reply View the author
忘怀
deepin
2025-04-28 23:56
#9
MeGusta

你用 sudo smartctl -c /dev/nvme0n1 | grep -A7 "Supported Power States"命令查询硬盘的功耗状态,需要安装 smartmontools

nvme_core.default_ps_max_latency_us=xxxx 内核参数来定义硬盘的倒数第二位的功耗状态,这样,既能避免掉盘,又能减少硬盘的发热。

我查询出来致钛PC005的倒数第二位的功耗参数(Ex_Lat)是 2000nvme_core.default_ps_max_latency_us=2000 ,我就用的这个内核参数。

# 以下是致钛PC005硬盘支持的电源状态
# sudo smartctl -c /dev/nvme0n1 | grep -A7 "Supported Power States"
#
# Supported Power States
# St Op     Max   Active     Idle   RL RT WL WT  Ent_Lat  Ex_Lat
#  0 +     9.00W       -        -    0  0  0  0        0       0
#  1 +     4.60W       -        -    1  1  1  1        0       0
#  2 +     3.80W       -        -    2  2  2  2        0       0
#  3 -   0.0450W       -        -    3  3  3  3     2000    2000
#  4 -   0.0040W       -        -    4  4  4  4    15000   15000

是的

Reply View the author
fslong
deepin beta test group
2025-04-29 13:22
#10

我的是这样:

Supported Power States
St Op     Max   Active     Idle   RL RT WL WT  Ent_Lat  Ex_Lat
 0 +     4.20W    3.70W       -    0  0  0  0        0       0
 1 +     2.70W    2.30W       -    0  0  0  0        0       0
 2 +     1.90W    1.80W       -    0  0  0  0        0       0
 3 -   0.0250W       -        -    3  3  3  3     3900   11000
 4 -   0.0050W       -        -    4  4  4  4     5000   44000

Reply View the author
fslong
deepin beta test group
2025-04-29 13:51
#11
MeGusta

你用 sudo smartctl -c /dev/nvme0n1 | grep -A7 "Supported Power States"命令查询硬盘的功耗状态,需要安装 smartmontools

nvme_core.default_ps_max_latency_us=xxxx 内核参数来定义硬盘的倒数第二位的功耗状态,这样,既能避免掉盘,又能减少硬盘的发热。

我查询出来致钛PC005的倒数第二位的功耗参数(Ex_Lat)是 2000nvme_core.default_ps_max_latency_us=2000 ,我就用的这个内核参数。

# 以下是致钛PC005硬盘支持的电源状态
# sudo smartctl -c /dev/nvme0n1 | grep -A7 "Supported Power States"
#
# Supported Power States
# St Op     Max   Active     Idle   RL RT WL WT  Ent_Lat  Ex_Lat
#  0 +     9.00W       -        -    0  0  0  0        0       0
#  1 +     4.60W       -        -    1  1  1  1        0       0
#  2 +     3.80W       -        -    2  2  2  2        0       0
#  3 -   0.0450W       -        -    3  3  3  3     2000    2000
#  4 -   0.0040W       -        -    4  4  4  4    15000   15000

我注意到,新的内核源码当中本身就定义的是这个参数:

static unsigned long default_ps_max_latency_us = 100000;
int nvme_default_ps_max_latency_us = 2000;
module_param_named(default_ps_max_latency_us, nvme_default_ps_max_latency_us, int, 0644);
module_param(default_ps_max_latency_us, ulong, 0644);
MODULE_PARM_DESC(default_ps_max_latency_us,
		 "max power saving latency for new devices; use PM QOS to change per device");

但我的硬盘好像不是2000这个参数。

Reply View the author
MeGusta
deepin
2025-04-30 08:04
#12
fslong

我注意到,新的内核源码当中本身就定义的是这个参数:

static unsigned long default_ps_max_latency_us = 100000;
int nvme_default_ps_max_latency_us = 2000;
module_param_named(default_ps_max_latency_us, nvme_default_ps_max_latency_us, int, 0644);
module_param(default_ps_max_latency_us, ulong, 0644);
MODULE_PARM_DESC(default_ps_max_latency_us,
		 "max power saving latency for new devices; use PM QOS to change per device");

但我的硬盘好像不是2000这个参数。

1.各个品牌的硬盘参数会有区别,我认为你的这块硬盘应该使用内核参数:

nvme_core.default_ps_max_latency_us=11000

2.我的matebook16s笔记本使用的12代cpu,之前用的6.6.x内核,用参数值 2000

就很正常,由于 >6.12内核的 kvm模块问题导致的 virtualbox启动错误,就一直没有采用新内核。后来笔记本不在我手上了,就没再深入研究了。

3.我现在用的主机是机械革命的Ultra5 125H迷你主机,6.6.x内核,里面有两张nvme硬盘,不添加加任何额外的内核参数,都可以正常睡眠唤醒。所以我觉得这个问题是特定硬件环境下的特定问题。

Reply View the author
fslong
deepin beta test group
2025-04-30 14:17
#13
MeGusta

1.各个品牌的硬盘参数会有区别,我认为你的这块硬盘应该使用内核参数:

nvme_core.default_ps_max_latency_us=11000

2.我的matebook16s笔记本使用的12代cpu,之前用的6.6.x内核,用参数值 2000

就很正常,由于 >6.12内核的 kvm模块问题导致的 virtualbox启动错误,就一直没有采用新内核。后来笔记本不在我手上了,就没再深入研究了。

3.我现在用的主机是机械革命的Ultra5 125H迷你主机,6.6.x内核,里面有两张nvme硬盘,不添加加任何额外的内核参数,都可以正常睡眠唤醒。所以我觉得这个问题是特定硬件环境下的特定问题。

我现在是睡死问题没有,但偶尔会莫名其妙卡死,考虑直接改编译内核的参数试试看。

Reply View the author
fslong
deepin beta test group
2025-04-30 16:06
#14
MeGusta

1.各个品牌的硬盘参数会有区别,我认为你的这块硬盘应该使用内核参数:

nvme_core.default_ps_max_latency_us=11000

2.我的matebook16s笔记本使用的12代cpu,之前用的6.6.x内核,用参数值 2000

就很正常,由于 >6.12内核的 kvm模块问题导致的 virtualbox启动错误,就一直没有采用新内核。后来笔记本不在我手上了,就没再深入研究了。

3.我现在用的主机是机械革命的Ultra5 125H迷你主机,6.6.x内核,里面有两张nvme硬盘,不添加加任何额外的内核参数,都可以正常睡眠唤醒。所以我觉得这个问题是特定硬件环境下的特定问题。

我看内核源码,这个参数默认是100000这么大,我打算给改成2000测试一下。

Reply View the author