hardware queue pair是什么
hardware queue pair是我们理解nvme/spdk的牛鼻子,只有深入理解才可能把nvme用好。
从nvme控制器寄存器的角度看
顾名思义,就是一些硬件寄存器组成的队列。
空队列
满队列
- 问题:
能否并发入队、出队?不行
submission hardware queue entry
每个entry如下表所示:
- 入队:
host software 提交命令到tail entry, 通过操作submission hardware queue tail doorbell register;
- 出队:
hardware 依次取head entry 去处理
completion hardware hardware queue entry
每个entry的主要fileds 如下:
-
入队
hardware 处理完上面取到的nvme command之后,把对应的完成的信息放在completion queue head entry; - 出队
host software 被中断或主动轮询到上面有新的entry 添加之后,去completion queue tail 去取最近提交的命令的处理结果。
这个主要是操作completion queue doorbell 寄存器去实现:
queue pair
nvme控制器内部有多个submission hardware queue,也有多个completion hardware queue。 提交、执行 NVME command的时候,需要使用上面两种hardware queue中的entry,成对使用。
从nvme控制器架构的角度看
hardware queue pair 是抽取"nvme command"的水车。设计到NVME 指令的提交/执行和完成。
概览
下面是一张经典的图:
NVME command提交
NVME已完成命令的处理
spdk对hardware queue pair的封装
IO请求提交函数
- spdk_nvme_ns_cmd_read
-
生产request
_nvme_ns_cmd_rw(ns, qpair, &payload, ....) -
提交请求
nvme_qpair_submmit_request()2.1 nvme_transport_qpair_submit_request(qpair, request)
2.1.1 nvme_pcie_qpair_submit_request (qpair, request)
2.1.1.1 nvme_pcie_qpair_build_contig_request(qpair, req, tr);
2.1.1.2 nvme_pcie_qpair_submit_tracker(qpair, tr)
nvme_pcie_qpair_submit_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr)
{
struct nvme_request *req;
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
struct nvme_pcie_ctrlr *pctrlr = nvme_pcie_ctrlr(qpair->ctrlr);
req = tr->req;
assert(req != NULL);
req->timed_out = false;
if (spdk_unlikely(pctrlr->ctrlr.timeout_enabled)) {
req->submit_tick = spdk_get_ticks();
} else {
req->submit_tick = 0;
}
pqpair->tr[tr->cid].active = true;
/* Copy the command from the tracker to the submission queue. */
nvme_pcie_copy_command(&pqpair->cmd[pqpair->sq_tail], &req->cmd);
if (spdk_unlikely(++pqpair->sq_tail == pqpair->num_entries)) {
pqpair->sq_tail = 0;
}
if (spdk_unlikely(pqpair->sq_tail == pqpair->sq_head)) {
SPDK_ERRLOG("sq_tail is passing sq_head!\n");
}
spdk_wmb();
if (spdk_likely(nvme_pcie_qpair_update_mmio_required(qpair,
pqpair->sq_tail,
pqpair->sq_shadow_tdbl,
pqpair->sq_eventidx))) {
g_thread_mmio_ctrlr = pctrlr;
spdk_mmio_write_4(pqpair->sq_tdbl, pqpair->sq_tail); // <---
g_thread_mmio_ctrlr = NULL;
}
}
通过上面code path可以看到,spdk nvme 需要操作硬件寄存器,并且一路无锁和原子操作。需要上层保护。
IO完成查询函数
spdk_nvme_qpair_process_completions
同上类似操作对应的cq_hdbel,无锁和原子操作,需要上层保护,防止并发修改doorbell 寄存器。
如何用好多个hardware queue pair
需要管理好实际应用中hardware queue pair和线程、处理器核的对应关系,避免多个线程同时访问某个hardware queue。