现象
在使用 NVMe 磁盘的 Linux 系统中出现非预期的慢IO读写,导致系统或者应用程序对于 NVMe 磁盘的 IO 操作失败,并且可能导致 NVMe 磁盘上的文件系统从原来挂载为可读可写的状态切换为只读不可写的状态,使得后续的写操作均失败,导致系统异常、应用程序异常或者业务中断。
执行下列命令查看 NVMe 驱动中的 IO超时参数发现被设置为默认的 30 秒:
[root@localhost ~]# cat /sys/module/nvme_core/parameters/io_timeout 30或者在一些内核版本较老的Linux发行版本上,NVMe 驱动的内核模块只有nvme.ko而没有nvme_core.ko,此时可以通过下列命令查看nvme.io_timeout超时参数的值:
[root@localhost ~]# cat /sys/module/nvme/parameters/io_timeout 30原因
受多种原因影响,读写 NVMe 磁盘的 IO 操作可能会出现较大的延迟。NVMe 驱动中的io_timeout参数控制了最大能够容忍的 IO超时时间,在大部分 Linux 发行版本中默认配置为 30 秒。但如果 IO 读写操作的延迟过高以至于超过了该参数的配置值,NVMe 驱动会返回 IO 失败。一些情况下,系统或者应用程序可以对 IO 操作进行重试。但在某些情况下可能导致 NVMe 磁盘上的文件系统从原来挂载为可读可写的状态切换为只读不可写的状态,使得后续的写操作均失败,导致系统异常、应用程序异常或者业务中断。
为了减少 NVMe 磁盘的 IO 操作超时导致的异常情况,通常会将io_timeout参数设置为可能的最大值以提高对于 IO延迟的容忍度。在较新版本的内核中,这个最大值为 4294967295,较早版本中则为 255。此外不同的版本的内核中,NVMe 驱动的内核模块也有所不同,有的只有nvme.ko,有的还有nvme_core.ko,从而完整的参数名称也分别有nvme.io_timeout和nvme_core.io_timeout两种可能。
阿里云出品的支持 NVMe 磁盘的 Linux 系统镜像中均已进行这一配置。如果设置被修改或者没有这一项配置,就有可能出现上述问题。
问题解决
临时配置
1. 检查io_timeout参数所在的内核模块名称
首先尝试检查/sys/module/nvme_core/parameters/io_timeout路径是否存在,如果存在的话表明完整的参数名称为nvme_core.io_timeout。例如可以使用以下命令:
[root@localhost ~]# cat /sys/module/nvme_core/parameters/io_timeout 4294967295如果提示上述路径不存在,再尝试检查/sys/module/nvme/parameters/io_timeout路径是否存在,如果存在的话表明完整的参数名称为nvme.io_timeout。例如可以使用以下命令:
[root@localhost ~]# cat /sys/module/nvme/parameters/io_timeout 42949672952. 将io_timeout参数配置为所能接受的最大值
首先尝试检查io_timeout参数能否接受 4294967295 的最大值。例如使用以下命令尝试将最大值写入到上述步骤中检查得到的有效路径:
[root@localhost ~]# echo 4294967295 > /sys/module/nvme_core/parameters/io_timeout如果能够成功写入没有报错,表明其最大值为 4294967295。否则如果出现类似于Numerical result out of range的错误,使用 255 作为最大值重试上面的步骤。
手动修改 GRUB 中的内核启动参数进行长期配置
CentOS 系统
1. 查看内核是否已经包含 NVMe 驱动:
[root@localhost ~]# cat /boot/config-`uname -r` | grep -i nvme | grep -v "^#" CONFIG_NVME_CORE=m CONFIG_BLK_DEV_NVME=m CONFIG_NVME_MULTIPATH=y CONFIG_NVME_FABRICS=m CONFIG_NVME_RDMA=m CONFIG_NVME_FC=m CONFIG_NVME_TCP=m CONFIG_NVME_TARGET=m CONFIG_NVME_TARGET_LOOP=m CONFIG_NVME_TARGET_RDMA=m CONFIG_NVME_TARGET_FC=m CONFIG_NVME_TARGET_FCLOOP=m CONFIG_NVME_TARGET_TCP=m CONFIG_RTC_NVMEM=y CONFIG_NVMEM=y如果有 "CONFIG_BLK_DEV_NVME=y" 表示该系统已经包含了 NVMe 驱动
2. 添加 nvme timeout 参数至 grub:
参考前面一节检查io_timeout参数的完整参数名称和所能接受的最大值。例如完整参数名称为nvme_core.io_timeout,所能接受的最大值为 4294967295,那么添加nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295到 GRUB 配置中。
3. 重新生成grub配置
根据操作系统的启动方式不同,选择以下适用于您的操作系统的命令:
- Legacy启动方式
[root@localhost ~]# grub2-mkconfig -o /boot/grub2/grub.cfg- UEFI启动方式
[root@localhost ~]# grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg4. 重新启动系统使配置生效
5. 执行内核启动参数命令行确认相关配置已正确传递给内核
[root@localhost ~]# cat /proc/cmdline ... nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=42949672956. 查看 NVMe 驱动参数确认已正确配置 IO 超时参数
[root@localhost ~]# cat /sys/module/nvme_core/parameters/io_timeout 4294967295Debian 系统
Debian 系统中 NVMe 驱动已经默认编译到内核中,或者 initrd默认已经包含 nvme 模块:
~# lsinitramfs /boot/initrd.img-`uname -r` | grep -i nvme usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/host usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/host/nvme-core.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/host/nvme-fabrics.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/host/nvme-fc.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/host/nvme-rdma.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/host/nvme.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/target usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/target/nvmet-fc.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/target/nvmet-rdma.ko usr/lib/modules/4.19.0-18-amd64/kernel/drivers/nvme/target/nvmet.ko1. 添加 nvme timeout 参数至 grub:
参考前面一节检查io_timeout参数的完整参数名称和所能接受的最大值。例如完整参数名称为nvme_core.io_timeout,所能接受的最大值为 4294967295,那么添加nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295到 GRUB 配置中。
~# cat /etc/default/grub | grep -v "^#" GRUB_DEFAULT=0 GRUB_TIMEOUT=1 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet" GRUB_CMDLINE_LINUX=" vga=792 console=tty0 console=ttyS0,115200n8 net.ifnames=0 noibrs nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295"2. 重新生成grub配置:
方法1:以下两条命令,根据镜像启动方式,二选一
~# grub-mkconfig -o /boot/grub/grub.cfg (legacy启动方式使用该命令) ~# grub-mkconfig -o /boot/efi/EFI/debian/grub.cfg (uefi 启动方式使用该命令)方法2:采用debian提供的脚本更新 legacy/uefi grub文件
~# update-grub2 Generating grub configuration file ... Found linux image: /boot/vmlinuz-4.19.0-18-amd64 Found initrd image: /boot/initrd.img-4.19.0-18-amd64 done3. 重新启动系统使配置生效
4. 执行内核启动参数命令行确认相关配置已正确传递给内核
[root@localhost ~]# cat /proc/cmdline ... nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=42949672955. 查看 NVMe 驱动参数确认已正确配置 IO 超时参数
[root@localhost ~]# cat /sys/module/nvme_core/parameters/io_timeout 4294967295Ubuntu 系统
Ubuntu 系统中 NVMe 驱动已经默认编译到内核中,或者 initrd默认已经包含 nvme 模块:
~# lsinitramfs /boot/initrd.img-`uname -r` | grep -i nvme usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host/nvme-core.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host/nvme-fabrics.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host/nvme-fc.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host/nvme-rdma.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host/nvme-tcp.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/host/nvme.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/target usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/target/nvme-loop.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/target/nvmet-fc.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/target/nvmet-rdma.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/target/nvmet-tcp.ko usr/lib/modules/5.4.0-86-generic/kernel/drivers/nvme/target/nvmet.ko1. 添加 nvme timeout 参数至 grub:
参考前面一节检查io_timeout参数的完整参数名称和所能接受的最大值。例如完整参数名称为nvme_core.io_timeout,所能接受的最大值为 4294967295,那么添加nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295到 GRUB 配置中。
~# cat /etc/default/grub | grep -v "^#" GRUB_DEFAULT=0 GRUB_TIMEOUT=1 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet" GRUB_CMDLINE_LINUX=" vga=792 console=tty0 console=ttyS0,115200n8 net.ifnames=0 noibrs nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295"2. 重新生成grub配置:
方法1:以下两条命令,根据镜像启动方式,二选一
~# grub-mkconfig -o /boot/grub/grub.cfg (legacy 启动方式使用该命令) ~# grub-mkconfig -o /boot/efi/EFI/ubuntu/grub.cfg (uefi 启动方式使用该命令)方法2:采用ubuntu提供的脚本更新 legacy/uefi grub文件
~# update-grub2 Sourcing file `/etc/default/grub' Sourcing file `/etc/default/grub.d/init-select.cfg' Generating grub configuration file ... Found linux image: /boot/vmlinuz-5.4.0-86-generic Found initrd image: /boot/initrd.img-5.4.0-86-generic Found linux image: /boot/vmlinuz-5.4.0-26-generic Found initrd image: /boot/initrd.img-5.4.0-26-generic done3. 重新启动系统使配置生效
4. 执行内核启动参数命令行确认相关配置已正确传递给内核
[root@localhost ~]# cat /proc/cmdline ... nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=42949672955. 查看 NVMe 驱动参数确认已正确配置 IO 超时参数
[root@localhost ~]# cat /sys/module/nvme_core/parameters/io_timeout 4294967295OpenSUSE/SLES 系统
1. 查看kernel是否已经加载内核驱动:
~ # cat /boot/config-`uname -r` | grep -i nvme | grep -v "^#" CONFIG_NVME_CORE=m CONFIG_BLK_DEV_NVME=m CONFIG_NVME_MULTIPATH=y CONFIG_NVME_HWMON=y CONFIG_NVME_FABRICS=m CONFIG_NVME_RDMA=m CONFIG_NVME_FC=m CONFIG_NVME_TCP=m CONFIG_NVME_TARGET=m CONFIG_NVME_TARGET_PASSTHRU=y CONFIG_NVME_TARGET_LOOP=m CONFIG_NVME_TARGET_RDMA=m CONFIG_NVME_TARGET_FC=m CONFIG_NVME_TARGET_FCLOOP=m CONFIG_NVME_TARGET_TCP=m CONFIG_RTC_NVMEM=y CONFIG_NVMEM=y CONFIG_NVMEM_SYSFS=y如果有 "CONFIG_BLK_DEV_NVME=y" 表示该镜像该镜像已经包含了 NVMe 驱动
2. 添加 nvme timeout 参数至 grub:
参考前面一节检查io_timeout参数的完整参数名称和所能接受的最大值。例如完整参数名称为nvme_core.io_timeout,所能接受的最大值为 4294967295,那么添加nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295到 GRUB 配置中。
~ # cat /etc/default/grub | egrep -v "(^#|^$)" GRUB_DISTRIBUTOR= GRUB_DEFAULT=saved GRUB_HIDDEN_TIMEOUT=0 GRUB_HIDDEN_TIMEOUT_QUIET=true GRUB_TIMEOUT=1 GRUB_CMDLINE_LINUX_DEFAULT="splash=silent mitigations=auto quiet" GRUB_CMDLINE_LINUX=" net.ifnames=0 console=tty0 console=ttyS0,115200n8 nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=4294967295" GRUB_TERMINAL="console" GRUB_GFXMODE="auto" GRUB_BACKGROUND=/boot/grub2/themes/SLE/background.png GRUB_THEME=/boot/grub2/themes/SLE/theme.txt SUSE_BTRFS_SNAPSHOT_BOOTING="true" GRUB_DISABLE_OS_PROBER="true" GRUB_ENABLE_CRYPTODISK="n" GRUB_CMDLINE_XEN_DEFAULT="vga=gfx-1024x768x16"3. 重新生成grub配置:
提示:以下两条命令,根据镜像启动方式,二选一
~ # grub2-mkconfig -o /boot/grub2/grub.cfg (legacy启动方式使用该命令) ~ # grub2-mkconfig -o /boot/efi/EFI/sles/grub.cfg (uefi 启动方式使用该命令)4. 重新启动系统使配置生效
5. 执行内核启动参数命令行确认相关配置已正确传递给内核
[root@localhost ~]# cat /proc/cmdline ... nvme_core.multipath=n nvme_core.io_timeout=4294967295 nvme_core.admin_timeout=42949672956. 查看 NVMe 驱动参数确认已正确配置 IO 超时参数
[root@localhost ~]# cat /sys/module/nvme_core/parameters/io_timeout 4294967295