NVMe性能问题定位过程

0 阅读1分钟

一、问题概述

1.1 故障现象

在双路服务器上,同PCIe交换芯片下的两块NVMe硬盘分别绑定近端和远端NUMA节点进行并发随机读测试时,绑定远端NUMA节点的硬盘性能出现严重下降。其中Solidigm盘性能下降尤为显著,降幅达 79.8%(885K → 179K IOPS)。

1.2 测试环境

项目

配置

机型

操作系统

RedHat 9.2

CPU

Hygon C86-4G(2 Sockets × 64 Cores × 2 Threads = 256 vCPUs)

NUMA节点

8个(每节点32 vCPUs)

测试盘1

SOLIDIGM(PCIe 4.0)

测试盘2

Samsung(PCIe 4.0)

中断均衡

irqbalance服务已开启

1.3 测试方法

numactl --cpunodebind=<node> --membind=<node> \
  fio --ioengine=libaio --direct=1 --iodepth=32 --time_based \
      --bs=4k --runtime=60s --rw=randread --filename=/dev/nvmeXn1 \
      --numjobs=16 --thread --group_reporting \
      --norandommap --randrepeat=0

二、性能测试结果

2.1 跨NUMA vs 本地NUMA性能对比

硬盘型号

本地NUMA IOPS(K)

跨NUMA IOPS(K)

性能下降

Samsung MZQL23T8HCLS-00B7C

940

702

25.3%

SOLIDIGM SSDPF2KX038T1

885

179

79.8% ⚠️

2.2 关键观察

  • 三星盘跨NUMA性能下降约 25%(符合预期,跨NUMA内存访问本身有延迟开销)

  • Solidigm盘跨NUMA性能下降约 80%异常,远超正常跨NUMA开销)

  • 结论:Solidigm盘存在额外的跨NUMA性能瓶颈

三、内核函数级性能分析(ftrace)

3.1 测试方法

使用内核ftrace追踪NVMe驱动关键函数耗时:

echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo 'nvme_queue_rq' > /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_setup_cmd' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_pci_complete_batch' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_pci_complete_rq' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_irq' >> /sys/kernel/debug/tracing/set_ftrace_filter

3.2 提交路径耗时对比

函数

本地NUMA (μs)

跨NUMA (μs)

异常分析

nvme_setup_cmd

0.19 - 2.09 (~0.95)

0.24 - 1.57 (~0.8)

✅ 正常,无明显差异

nvme_queue_rq

4.23 - 36.49 (~8.5)

20.44 - 486.79 (~110)

⚠️ 异常:存在185μs/454μs/486μs长尾

3.3 完成路径耗时对比

函数

本地NUMA (μs)

跨NUMA (μs)

分析

nvme_pci_complete_batch

1.21 - 4.90 (~2.3)

1.10 - 4.36 (~2.2)

✅ 正常

nvme_irq

2.71 - 9.16 (~6.1)

4.62 - 12.03 (~6.9)

✅ 正常

3.4 ftrace分析结论

观察点

结论

PCIe完成路径(nvme_irq/nvme_pci_complete_batch

无明显差异,中断处理不是瓶颈

NVMe命令提交路径(nvme_queue_rq

存在严重异常长尾,问题出在提交阶段

命令设置阶段(nvme_setup_cmd

无差异,问题不在命令构造

锁定方向:问题出在 nvme_queue_rq() 内部,介于命令构造完成与门铃写入之间。

四、硬件级深入排查

4.1 门铃寄存器写入测试

硬盘

跨NUMA门铃写入延迟

相对速度

Solidigm

18.09 ns/write

更快

Samsung

57.69 ns/write

较慢

关键发现:Solidigm门铃写入更快,理论上IOPS应更高,但实际性能更差。排除门铃延迟为根因。

4.2 关键硬件参数对比(nvme id-ctrl)

参数

Samsung

Solidigm

差异分析

mdts

9

5

Samsung是Solidigm的 16倍

单次最大DMA传输

2 MB

128 KB

Samsung一次搬运16倍数据

sgls

0

0

均不支持SGL(使用PRP)

maxcmd

256

0

控制器命令限制

4.3 mdts对DMA能力的影响

mdts = Maximum Data Transfer Size
单次最大DMA = 2^(mdts) × 4096 字节

Samsung (mdts=9):  2^9 × 4096 = 512 × 4KB = 2,048 KB = 2 MB
Solidigm (mdts=5): 2^5 × 4096 = 32 × 4KB = 128 KB

在4K随机读场景下:

  • Samsung:单次DMA可聚合 512个 4K请求

  • Solidigm:单次DMA仅能聚合 32个 4K请求

  • Solidigm需要16倍的DMA事务数完成同样IOPS ⚠️

五、根因分析

5.1 PRP(Physical Region Page)机制

NVMe协议使用PRP列表描述数据在内存中的物理地址。每次DMA传输需要:

  1. dma_pool 分配PRP列表内存

  2. 填写PRP Entry(每个Entry指向一个4KB物理页)

  3. 传输完成后释放PRP列表

关键瓶颈dma_pool_alloc()dma_pool_free() 需要获取全局自旋锁

5.2 问题传导链

Solidigm mdts=5 (128KB)
         ↓
4K随机读需要32个PRP Entry/IO
         ↓
DMA事务数是Samsung的16倍
         ↓
dma_pool_alloc/free调用频率是Samsung的16倍
         ↓
dma_pool全局锁竞争加剧
         ↓
跨NUMA访问时锁竞争 + 远端内存访问延迟叠加
         ↓
导致nvme_queue_rq()出现数百微秒长尾
         ↓
IOPS从885K暴跌至179K (79.8%性能损失)

5.3 内核社区已知问题

该问题已被内核社区识别并正在解决:

Patch: [PATCH v5 0/3] nvme/pci: PRP list DMA pool partitioning
作者: Caleb Sander Mateos
核心方案: 将全局PRP DMA池拆分为per-NUMA-node独立池

text

优化前:所有CPU共享一个dma_pool(一把全局锁)
优化后:每个NUMA节点拥有独立的dma_pool(多把锁并行)

效果:跨NUMA场景下锁竞争减少约50%

六、结论与建议

6.1 根本原因

Solidigm盘 mdts=5 限制了单次DMA传输大小为128KB,导致在4K随机读测试中需要16倍于Samsung(mdts=9)的DMA事务数。大量PRP列表的频繁分配/释放操作,与dma_pool全局锁竞争相互叠加,在跨NUMA访问场景下形成严重性能瓶颈。

6.2 推荐解决方案

方案

措施

适用场景

短期规避

使用 numactl --cpunodebind=<盘所在node> --membind=<盘所在node> 强制绑定本地NUMA

生产环境立即生效

中期优化

升级内核至包含 nvme/pci: make PRP list DMA pools per-NUMA-node patch的版本

彻底解决锁竞争问题

长期选型

mdts >= 7(≥512KB)纳入NVMe盘选型评估指标

降低跨NUMA场景性能风险

监控预警

监控 perf lock contentiondma_pool_alloc 的锁竞争时间

提前发现类似问题

6.3 预期效果

  • 实施短期规避后,Solidigm盘跨NUMA性能预计可恢复至 本地NUMA的70-80%(与三星类似水平)

  • 实施per-NUMA-node PRP池优化后,跨NUMA锁竞争时间预计 减少50%以上

附录:关键源码路径

模块

文件路径

相关函数

NVMe驱动主逻辑

drivers/nvme/host/pci.c

nvme_queue_rq(), nvme_setup_cmd()

PRP内存分配

drivers/nvme/host/pci.c

nvme_alloc_prp_pool()

DMA池管理

mm/dmapool.c

dma_pool_alloc(), dma_pool_free()

跟踪点定义

include/trace/events/nvme.h

trace_nvme_*

报告日期: 2026-06-21
分析人: 内核性能分析团队