nvme hardware queue 初始化流程

本文详细介绍了NVMe控制器初始化的过程,包括配置管理队列、请求多个I/O提交队列和完成队列,以及使用SPDK库进行控制器探测和队列创建的具体实现。通过代码示例展示了如何为每个命名空间或线程组合分配独立队列。

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

主要步骤

To setup and initialize I/O Submission Queues and I/O Completion Queues for use, host software follows these steps:

  1. Configures the Admin Submission and Completion Queues by initializing the Admin Queue Attributes (AQA), Admin Submission Queue Base Address (ASQ), and Admin Completion Queue Base Address (ACQ) registers appropriately;
  2. Submits a Set Features command with the Number of Queues attribute to request the desired number of I/O Submission Queues and I/O Completion Queues. The completion queue entry for this Set Features command indicates the number of I/O Submission Queues and I/O Completion Queues allocated by the controller;
  3. Determines the maximum number of entries supported per queue (CAP.MQES) and whether the queues are required to be physically contiguous (CAP.CQR);
  4. Creates the desired I/O Completion Queues within the limitations of the number allocated by the controller and the queue attributes supported (maximum entries and physically contiguous requirements) by using the Create I/O Completion Queue command; and
  5. Creates the desired I/O Submission Queues within the limitations of the number allocated by the controller and the queue attributes supported (maximum entries and physically contiguous requirements) by using the Create I/O Submission Queue command.

At the setup and initialized and may be used to complete I/O commands.

参考代码

spdk/examples/nvme/perf/perf.c


static void
attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
          struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
{
        struct trid_entry       *trid_entry = cb_ctx;
        struct spdk_pci_addr    pci_addr;
        struct spdk_pci_device  *pci_dev;
        struct spdk_pci_id      pci_id;

        g_controllers_found++;
        if (trid->trtype != SPDK_NVME_TRANSPORT_PCIE) {
                printf("Attached to NVMe over Fabrics controller at %s:%s: %s\n",
                       trid->traddr, trid->trsvcid,
                       trid->subnqn);
        } else {
                if (spdk_pci_addr_parse(&pci_addr, trid->traddr)) {
                        return;
                }

                pci_dev = spdk_nvme_ctrlr_get_pci_device(ctrlr);
                if (!pci_dev) {
                        return;
                }

                pci_id = spdk_pci_device_get_id(pci_dev);

                printf("Attached to NVMe Controller at %s [%04x:%04x]\n",
                       trid->traddr,
                       pci_id.vendor_id, pci_id.device_id);
        }

        register_ctrlr(ctrlr, trid_entry);
}

static int
register_controllers(void)
{
        struct trid_entry *trid_entry;

        printf("Initializing NVMe Controllers\n");

        TAILQ_FOREACH(trid_entry, &g_trid_list, tailq) {
                if (spdk_nvme_probe(&trid_entry->trid, trid_entry, probe_cb, attach_cb, NULL) != 0) {
                        fprintf(stderr, "spdk_nvme_probe() failed for transport address '%s'\n",
                                trid_entry->trid.traddr);
                        return -1;
                }
        }

        return 0;
}

/*
 * TODO: If a controller has multiple namespaces, they could all use the same queue.
 *  For now, give each namespace/thread combination its own queue.
 */
static int
nvme_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
        struct spdk_nvme_io_qpair_opts opts;
        struct ns_entry *entry = ns_ctx->entry;
    int i = 0;

        spdk_nvme_ctrlr_get_default_io_qpair_opts(entry->u.nvme.ctrlr, &opts, sizeof(opts));
        if (opts.io_queue_requests < entry->num_io_requests) {
                opts.io_queue_requests = entry->num_io_requests;
        }

    ns_ctx->u.nvme.qpair = calloc(g_num_pair, sizeof(struct spdk_nvme_qpair*));
    for (i = 0; i < g_num_pair; i++) {
        ns_ctx->u.nvme.qpair[i] = spdk_nvme_ctrlr_alloc_io_qpair(entry->u.nvme.ctrlr, &opts,
                       sizeof(opts));
        if (!ns_ctx->u.nvme.qpair[i]) {
            printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
            return -1;
        }
    }

        return 0;
}

参考代码

转载于:https://blog.51cto.com/xiamachao/2387265

### NVMe Admin Queue 中断处理及原因分析 #### 行政队列中断机制概述 行政队列(Admin Queue)是NVMe设备中的一个重要组成部分,用于执行管理和配置操作。当控制器完成这些命令后,会产生相应的中断通知宿主机。硬件队列数量由底层设备驱动决定,可以是一个或多个,最大支持数目通常与MSI-X中断的最大数目相同,可达2048个[^2]。 #### 中断向量IV0专属性保护 为了确保系统的稳定性和安全性,操作系统内核在配置输入/输出(I/O)完成队列时,必须防止将管理员专用的中断向量号IV0分配给任何其他类型的队列。这种共用可能导致不可预见的行为和错误发生[^3]。 #### 中断处理流程解析 当中断事件触发时,Linux内核中的NVMe驱动程序负责响应并处理来自存储控制器的通知消息。具体来说: - **识别源队列**:通过读取状态寄存器来确定哪个具体的提交队列产生了该次中断。 - **获取已完成条目**:访问对应的完成队列(CQ),从中提取出所有标记为已处理完毕的数据结构体(即CQE, Completion Queue Entry)。 - **更新指针位置**:每当消费掉一个CQE之后,都需要相应调整环形缓冲区内的头部指针head pointer),以便后续能够继续接收新的反馈信息。 - **调用回调函数**:对于每一个成功取出的CQE实例,都会关联有一个预先注册好的上层应用层逻辑处理器;此时则需立即激活它来进行进一步的动作派发工作。 ```c static irqreturn_t nvme_irq(int irq, void *data) { struct nvme_dev *dev = data; // 处理完成队列项... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值