深入理解nvme hardware queue pair

hardware queue pair是什么

hardware queue pair是我们理解nvme/spdk的牛鼻子,只有深入理解才可能把nvme用好。

从nvme控制器寄存器的角度看

顾名思义,就是一些硬件寄存器组成的队列。

空队列

深入理解nvme hardware queue pair

满队列

深入理解nvme hardware queue pair

  • 问题:
    能否并发入队、出队?不行

submission hardware queue entry

每个entry如下表所示:

深入理解nvme hardware queue pair

  • 入队:

host software 提交命令到tail entry, 通过操作submission hardware queue tail doorbell register;
深入理解nvme hardware queue pair

  • 出队:
    hardware 依次取head entry 去处理

completion hardware hardware queue entry

每个entry的主要fileds 如下:

深入理解nvme hardware queue pair

  • 入队
    hardware 处理完上面取到的nvme command之后,把对应的完成的信息放在completion queue head entry;

  • 出队
    host software 被中断或主动轮询到上面有新的entry 添加之后,去completion queue tail 去取最近提交的命令的处理结果。

这个主要是操作completion queue doorbell 寄存器去实现:
深入理解nvme hardware queue pair

queue pair

nvme控制器内部有多个submission hardware queue,也有多个completion hardware queue。 提交、执行 NVME command的时候,需要使用上面两种hardware queue中的entry,成对使用。

从nvme控制器架构的角度看

hardware queue pair 是抽取"nvme command"的水车。设计到NVME 指令的提交/执行和完成。

概览

下面是一张经典的图:
深入理解nvme hardware queue pair

NVME command提交

深入理解nvme hardware queue pair

NVME已完成命令的处理

深入理解nvme hardware queue pair

spdk对hardware queue pair的封装

IO请求提交函数

  • spdk_nvme_ns_cmd_read
  1. 生产request
    _nvme_ns_cmd_rw(ns, qpair, &payload, ....)

  2. 提交请求
    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。

猜你喜欢

转载自blog.51cto.com/xiamachao/2380622