NVMe管理命令为何不用SGL?-1

本文详细解释了NVMePRP和SGL在Host与Controller数据交互中的作用,包括它们如何帮助定位内存地址,Linux内核中如何通过iod->use_sgl和sgl_threshold决定使用哪种模型,以及在不同场景下的应用和控制器兼容性检查。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上周末在公众号后台收到粉丝留言,主要是关于SGL的交流:“SGL为啥不能用于nvme admin cmd”?

图片


回答这个问题前,首先,我们先回顾下NVME PRP和SGL的基本原理以及应用场景。

在Host与Controller之间有数据交互时,Controller会多次访问Host内存。比如执行NVMe Read/Write:

  • 当Host下发NVMe Write命令时,Host会先放数据放在Host内存中,然后通知Controller过来取数据。Controller接到信息后,会通过PCIe Memory Read TLP读取相应的数据,接着Host返回的PCIe Completion报文中会携带数据给Controller,最后再写入NAND中。

  • 当Host下发NVMe Read命令时,Controller先从NAND中读出相应数据,然后通过PCIe Memory Write TLP将数据写入Host内存中。

图片


 

上述Read/Write均有访问Host内存进行数据交互,那么,问题来了:

  • NVMe Write时,Controller怎么知道数据在Host内存的具体位置?

  • NVMe Read时,Controller怎么知道要把数据写到Host内存中哪个位置?

不怕,NVMe给Host配备了两大"法宝":PRP和SGL。这两个模型均可以帮助Host告知Controller数据在Host内存中的具体地址。

图片

图片

PRP和SGL是描述Host内存物理空间的两种方式,本质的不同是:PRP必须是物理页对齐的,而SGL则可以表述任意的物理空间。如下图。

图片

在Linux内核中,Block层下发的IO请求以BIO表示。我们需要通过DMA发送这些数据,Command使用dma_alloc_coherent分配DMA地址,但是BIO是存放在普通的内核线程空间的(线程的虚拟空间不能直接作为DMA地址)。

图片

Linux函数nvme_map_data能够将虚拟空间地址(BIO数据存放地址)转换成DMA可用地址,并且多个IO请求的DMA地址可以通过scatterlist来表示。有了DMA地址就可以把BIO封装成NVMe Command发送出去。

所以,linux驱动中nvme_map_data中针对NVME Command的IO传输格式就有了很重要的设定。比如,函数nvme_map_data中有iod->use_sgl的结果可以觉得IO传输过程中是使用SGL还是PRP。

图片

而iod->use_sgl的返回结果依赖函数nvme_pci_use_sgls的判断,主要有两种情况:

  • 当SGL不支持的时,iod->use_sgl返回false,对应的IO数据传输就采用PRP了。

  • 当平均请求大小avg_seg_size值小于SGL阈值sgl_threshold时,也返回false,需要采用PRP。也就是说,使用SGL情况,必须要要求avg_seg_size大于等于sgl_threshold。SGL比较适合大块数据的传输。

图片

sgl_threshold的定义是32KB,avg_seg_size和sgl_threshold的计算对比关系如下示例:

  • blk_rq_nr_phys_segments = 2,blk_rq_payload_bytes = 8k,那么,avg_seg_size = blk_rq_payload_bytes/blk_rq_nr_phys_segments=4K,这种情况avg_seg_size<sgl_threshold,那就需要采用PRP了。

  • blk_rq_nr_phys_segments = 2,blk_rq_payload_bytes = 64k,那么,avg_seg_size = blk_rq_payload_bytes/blk_rq_nr_phys_segments=32K,这种情况avg_seg_size=sgl_threshold,那就可以采用SGL了。

  • blk_rq_nr_phys_segments = 16,blk_rq_payload_bytes = 64k,那么,avg_seg_size = blk_rq_payload_bytes/blk_rq_nr_phys_segments=4K,这种情况avg_seg_size<sgl_threshold,那就需要采用PRP了。

图片

sgl_threshold这个参数在linux内核中也可以通过修改/etc/default/grub文件,添加修改nvme.sgl_threshold参数即可

图片

图片

SQ队列中的PSDT参数中,可以定义每个IO数据传输采用PRP还是SGL。特别是NVME over Fabrics场景中,设定SGL时,需要关注metadata的MPTR设定,采用连续的物理空间,还是采用QWORD对齐的方式。

图片

图片

NVME Controller的Identify页面信息中,Byte536的前2个bit可以查询对SGL的支持情况。比如nvme-cli获取的2个nvme id-ctrl信息:

  • NVMe SSD A:  sgls : 0,bit0=0,说明不支持SGL

  • NVMe SSD B:  sgls : 0x70001,bit0=1,说明支持SGL,且对Data Block数据的对齐策略没有要求。

图片

其中SGL Descriptor Threshold(SDT)代表SGL Descriptor最大的数量。如果SDT设置0,则代表没有设定最大建议值。

图片

<think>好的,我现在需要回答用户关于NVMe驱动是否向主机发送TLP的问题。首先,我得回忆一下NVMePCIe的基本知识。NVMe是建立在PCIe协议之上的,而TLP是PCIe事务层的数据包,用于在设备主机之间传输数据。用户想知道NVMe驱动是否会主动发送TLP给主机。 首先,驱动通常运行在主机端,负责管理硬件设备。所以NVMe驱动本身可能不会直接生成TLP,而是通过操作硬件来触发设备发送TLP。比如,当驱动发出一个I/O请求时,NVMe控制器可能会生成相应的TLP来完成数据传输。 接下来,我需要确认TLP的产生机制。根据引用[1],设备或主机有唤醒事件时会发送TLP,比如设备有中断或数据传输。这说明TLP是由设备(如NVMe SSD)主动发送的,而不是驱动直接发送的。驱动的作用是初始化请求,然后由设备控制器处理这些请求并通过PCIe链路发送TLP。 然后,关于DMAMSI-X中断。NVMe设备使用DMA将数据从主机内存传输到设备,这可能涉及设备生成的TLP来读写主机内存。而中断也是设备通过TLP通知主机完成操作。驱动在这里配置DMA区域中断处理程序,但实际的TLP发送是由设备硬件完成的。 另外,用户提到的引用[2]涉及KVM虚拟化中的NVMe配置,这可能影响TLP的路径,但机制应该类似,虚拟化环境下设备模拟或直通时TLP的处理方式不同,但驱动的作用不变。 总结一下,NVMe驱动不直接发送TLP,而是通过命令提交到队列,触发设备控制器生成TLP。需要确保回答结构清晰,分点说明驱动、设备角色,以及相关机制如DMA中断。同时生成相关问题,帮助用户深入理解。</think>### NVMe驱动发送TLP的机制解析 NVMe驱动本身**不直接生成或发送PCIe事务层数据包(TLP)**,而是通过控制NVMe设备控制器间接触发TLP的生成与传输。以下是具体机制的分步说明: --- #### 1. **驱动与硬件的分工** - **NVMe驱动**:运行在主机端(CPU),负责管理I/O队列、提交命令、处理中断内存映射。例如,当用户发起读写请求时,驱动会将命令写入设备的**提交队列(Submission Queue)**[^1]。 - **NVMe设备控制器**:位于SSD硬件中,主动轮询提交队列,解析命令后生成对应的PCIe TLP,完成数据传输或状态通知。 --- #### 2. **TLP生成的触发场景** NVMe设备在以下场景中会向主机发送TLP: - **数据传输**: 当设备需要读取主机内存中的数据(DMA读)或写入数据到主机内存(DMA写)时,设备控制器生成`Memory Read/Write TLP`[^1]。例如: $$ \text{设备发送 Memory Write TLP} \rightarrow \text{主机内存写入用户数据} $$ - **中断通知**: I/O操作完成后,设备通过`MSI/MSI-X TLP`向主机发送中断,驱动随后处理完成队列(Completion Queue)中的条目[^1]。 - **状态反馈**: 设备通过TLP返回命令状态(如SMART日志、错误日志)。 --- #### 3. **驱动对TLP的间接控制** 驱动通过以下行为间接影响TLP生成: - **队列配置**: 初始化Admin队列I/O队列时,驱动通过MMIO(内存映射I/O)写入设备寄存器,触发设备获取队列地址并生成对应TLP。 - **DMA内存分配**: 驱动为数据传输分配物理连续内存(PRPSGL),设备通过DMA TLP直接访问这些区域[^2]。 - **中断设置**: 驱动配置MSI-X中断向量表,设备根据此表发送中断TLP。 --- #### 4. **关键代码逻辑示例 (Linux NVMe驱动)** ```c // 提交I/O命令到队列 struct nvme_command cmd = { .rw = { .opcode = nvme_cmd_write, .nsid = namespace_id, .prp1 = cpu_to_le64(dma_addr), // DMA地址触发设备生成Memory Write TLP .slba = cpu_to_le64(sector), }, }; nvme_queue_rq(q, &cmd); // 将命令写入提交队列 // 中断处理函数 irqreturn_t nvme_irq(int irq, void *data) { // 读取完成队列,处理设备发送的中断TLP while (completion_entry = get_completion_entry()) { handle_io_completion(completion_entry); } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

古猫先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值