NVME Doorbell 寄存器 数据请求时doorbell 处理

3.NVMe寄存器配置
3.1 寄存器定义
NVMe寄存器主要分为两部分,一部分定义了Controller整体属性,一部分用来存放每组队列的头尾DB寄存器。

CAP——控制器能力,定义了内存页大小的最大最小值、支持的I/O指令集、DB寄存器步长、等待时间界限、仲裁机制、队列是否物理上连续、队列大小;
VS——版本号,定义了控制器实现NVMe协议的版本号;
INTMS——中断掩码,每个bit对应一个中断向量,使用MSI-X中断时,此寄存器无效;
INTMC——中断有效,每个bit对应一个中断向量,使用MSI-X中断时,此寄存器无效;
CC——控制器配置,定义了I/O SQ和CQ队列元素大小、关机状态提醒、仲裁机制、内存页大小、支持的I/O指令集、使能;
CSTS——控制器状态,包括关机状态、控制器致命错误、就绪状态;
AQA——Admin 队列属性,包括SQ大小和CQ大小;
ASQ——Admin SQ基地址;
ACQ——Admin CQ基地址;
1000h之后的寄存器定义了队列的头、尾DB寄存器。
3.2寄存器理解
CAP寄存器标识的是Controller具有多少能力,而CC寄存器则是指当前Controller选择了哪些能力,可以理解为CC是CAP的一个子集;如果重启(reset)的话,可以更换CC配置;
CC.EN置一,表示Controller已经可以开始处理NVM命令,从1到0表示Controller重启;
CC.EN与CSTS.RDY关系密切,CSTS.RDY总是在CC.EN之后由Controller改变,其他不符合执行顺序的操作都将产生未定义的行为;
Admin队列有host直接创建,AQA、ASQ、ACQ三个寄存器标识了Admin队列,而其他I/O队列则有Admin命令创建(eg,创建I/O CQ命令);
Admin队列的头、尾DB寄存器标识为0,其他I/O队列标识由host按照一定规则分配;只有16bit的有效位,是因为队列深度最大64K。
实际的物理设备CAP.DSTRD值为0,dev->db_stride为1,之后分析中默认db_stride为1
                        
原文链接:https://blog.csdn.net/qq_39021670/article/details/114896973

由dev->dbs使用方式可知,每一个DB寄存器对,前4个字节为SQ Tail DB,后四个字节为CQ Head DB

/*
 * Write sq tail if we are asked to, or if the next command would wrap.
 */
static inline void nvme_write_sq_db(struct nvme_queue *nvmeq, bool write_sq)
{
        if (!write_sq) {
                u16 next_tail = nvmeq->sq_tail + 1;

                if (next_tail == nvmeq->q_depth)
                        next_tail = 0;
                if (next_tail != nvmeq->last_sq_tail)
                        return;
        }

        if (nvme_dbbuf_update_and_check_event(nvmeq->sq_tail,
                        nvmeq->dbbuf_sq_db, nvmeq->dbbuf_sq_ei))
       //前4字节写入sq tial
                writel(nvmeq->sq_tail, nvmeq->q_db);
        nvmeq->last_sq_tail = nvmeq->sq_tail;
}
static inline void nvme_ring_cq_doorbell(struct nvme_queue *nvmeq)
{
        u16 head = nvmeq->cq_head;
         //后4字节写入 cq head

        if (nvme_dbbuf_update_and_check_event(head, nvmeq->dbbuf_cq_db,
                                              nvmeq->dbbuf_cq_ei))
                writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
}

static irqreturn_t nvme_irq(int irq, void *data)
{
        struct nvme_queue *nvmeq = data;
        irqreturn_t ret = IRQ_NONE;
        u16 start, end;

        /*
         * The rmb/wmb pair ensures we see all updates from a previous run of
         * the irq handler, even if that was on another CPU.
         */
        rmb();
        if (nvmeq->cq_head != nvmeq->last_cq_head)
                ret = IRQ_HANDLED;
        //找到当前CQ队列的尾部,并更新cq_head
        nvme_process_cq(nvmeq, &start, &end, -1);
        nvmeq->last_cq_head = nvmeq->cq_head;
        wmb();

        if (start != end) {
           // 依次处理CQ队列中的请求
                nvme_complete_cqes(nvmeq, start, end);
                return IRQ_HANDLED;
        }

        return ret;
}

依次取出ssd 中已经返回的数据,然后写入cq 的head 到Doorbell 寄存器

static inline int nvme_process_cq(struct nvme_queue *nvmeq, u16 *start,
                                  u16 *end, unsigned int tag)
{
        int found = 0;

        *start = nvmeq->cq_head;
        while (nvme_cqe_pending(nvmeq)) {
                if (tag == -1U || nvmeq->cqes[nvmeq->cq_head].command_id == tag)
                        found++;
                nvme_update_cq_head(nvmeq);
        }
        *end = nvmeq->cq_head;

        if (*start != *end)
                nvme_ring_cq_doorbell(nvmeq);
        return found;
}

static inline void nvme_ring_cq_doorbell(struct nvme_queue *nvmeq)
{
        u16 head = nvmeq->cq_head;

        if (nvme_dbbuf_update_and_check_event(head, nvmeq->dbbuf_cq_db,
                                              nvmeq->dbbuf_cq_ei))
                writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
}

依次处理cq 中的数据返回给block 层

static inline void nvme_end_request(struct request *req, __le16 status,
                union nvme_result result)
{
        struct nvme_request *rq = nvme_req(req);

        rq->status = le16_to_cpu(status) >> 1;
        rq->result = result;
        /* inject error when permitted by fault injection framework */
        nvme_should_fail(req);
       //block 请求返回
        blk_mq_complete_request(req);
}

static inline void nvme_handle_cqe(struct nvme_queue *nvmeq, u16 idx)
{
        volatile struct nvme_completion *cqe = &nvmeq->cqes[idx];
        struct request *req;

        /*
         * AEN requests are special as they don't time out and can
         * survive any kind of queue freeze and often don't respond to
         * aborts.  We don't even bother to allocate a struct request
         * for them but rather special case them here.
         */
        if (unlikely(nvmeq->qid == 0 &&
                        cqe->command_id >= NVME_AQ_BLK_MQ_DEPTH)) {
                nvme_complete_async_event(&nvmeq->dev->ctrl,
                                cqe->status, &cqe->result);
                return;
        }
        //将通过tag 将reqeust 转换出来
        req = blk_mq_tag_to_rq(nvme_queue_tagset(nvmeq), cqe->command_id);
        if (unlikely(!req)) {
                dev_warn(nvmeq->dev->ctrl.device,
                        "invalid id %d completed on queue %d\n",
                        cqe->command_id, le16_to_cpu(cqe->sq_id));
                return;
        }

        trace_nvme_sq(req, cqe->sq_head, nvmeq->sq_tail);
        nvme_end_request(req, cqe->status, cqe->result);
}


static void nvme_complete_cqes(struct nvme_queue *nvmeq, u16 start, u16 end)
{
        while (start != end) {
                nvme_handle_cqe(nvmeq, start);
                if (++start == nvmeq->q_depth)
                        start = 0;
        }
}

static const struct blk_mq_ops nvme_mq_admin_ops = {
        .queue_rq       = nvme_queue_rq,
        .complete       = nvme_pci_complete_rq,
        .init_hctx      = nvme_admin_init_hctx,
        .init_request   = nvme_init_request,
        .timeout        = nvme_timeout,
};

static const struct blk_mq_ops nvme_mq_ops = {
        .queue_rq       = nvme_queue_rq,
        .complete       = nvme_pci_complete_rq,
        .commit_rqs     = nvme_commit_rqs,
        .init_hctx      = nvme_init_hctx,
        .init_request   = nvme_init_request,
        .map_queues     = nvme_pci_map_queues,
        .timeout        = nvme_timeout,
        .poll           = nvme_poll,
};
 

admin  queue

nvme_queue_rq

io queue 

nvme_queue_rq

nvme_commit_rqs

static blk_status_t __blk_mq_issue_directly(struct blk_mq_hw_ctx *hctx,
                                            struct request *rq,
                                            blk_qc_t *cookie, bool last)
{
        struct request_queue *q = rq->q;
        struct blk_mq_queue_data bd = {
                .rq = rq,
                .last = last,
        };
        blk_qc_t new_cookie;
        blk_status_t ret;

        new_cookie = request_to_qc_t(hctx, rq);

        /*
         * For OK queue, we are done. For error, caller may kill it.
         * Any other error (busy), just add it to our list as we
         * previously would have done.
         */
        ret = q->mq_ops->queue_rq(hctx, &bd);
        switch (ret) {
        case BLK_STS_OK:
                blk_mq_update_dispatch_busy(hctx, false);
                *cookie = new_cookie;
                break;
        case BLK_STS_RESOURCE:
        case BLK_STS_DEV_RESOURCE:
                blk_mq_update_dispatch_busy(hctx, true);
                __blk_mq_requeue_request(rq);
                break;
        default:
                blk_mq_update_dispatch_busy(hctx, false);
                *cookie = BLK_QC_T_NONE;
                break;
        }

        return ret;
}


 */
bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list,
                             bool got_budget)
{
        struct blk_mq_hw_ctx *hctx;
        struct request *rq, *nxt;
        bool no_tag = false;
        int errors, queued;
        blk_status_t ret = BLK_STS_OK;
        bool no_budget_avail = false;

        if (list_empty(list))
                return false;

        WARN_ON(!list_is_singular(list) && got_budget);

        /*
         * Now process all the entries, sending them to the driver.
         */
        errors = queued = 0;
        do {
                struct blk_mq_queue_data bd;

                rq = list_first_entry(list, struct request, queuelist);

                hctx = rq->mq_hctx;
                if (!got_budget && !blk_mq_get_dispatch_budget(hctx)) {
                        blk_mq_put_driver_tag(rq);
                        no_budget_avail = true;
                        break;
                }

                if (!blk_mq_get_driver_tag(rq)) {
                        /*
                         * The initial allocation attempt failed, so we need to
                         * rerun the hardware queue when a tag is freed. The
                         * waitqueue takes care of that. If the queue is run
                         * before we add this entry back on the dispatch list,
                         * we'll re-run it below.
                         */
                        if (!blk_mq_mark_tag_wait(hctx, rq)) {
                                blk_mq_put_dispatch_budget(hctx);
                                /*
                                 * For non-shared tags, the RESTART check
                                 * will suffice.
                                 */
                                if (hctx->flags & BLK_MQ_F_TAG_SHARED)
                                        no_tag = true;
                                break;
                        }
                }

                list_del_init(&rq->queuelist);

                bd.rq = rq;

                /*
                 * Flag last if we have no more requests, or if we have more
                 * but can't assign a driver tag to it.
                 */
                if (list_empty(list))
                        bd.last = true;
                else {
                        nxt = list_first_entry(list, struct request, queuelist);
                        bd.last = !blk_mq_get_driver_tag(nxt);
                }
                //下发io
                ret = q->mq_ops->queue_rq(hctx, &bd);
                if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) {
                        blk_mq_handle_dev_resource(rq, list);
                        break;
                }

                if (unlikely(ret != BLK_STS_OK)) {
                        errors++;
                        blk_mq_end_request(rq, BLK_STS_IOERR);
                        continue;
                }

                queued++;
        } while (!list_empty(list));

        hctx->dispatched[queued_to_index(queued)]++;

        /*
         * Any items that need requeuing? Stuff them into hctx->dispatch,
         * that is where we will continue on next queue run.
         */
        if (!list_empty(list)) {
                bool needs_restart;

                /*
                 * If we didn't flush the entire list, we could have told
                 * the driver there was more coming, but that turned out to
                 * be a lie.
                 */
                if (q->mq_ops->commit_rqs)
                       //nvme io commit

                        q->mq_ops->commit_rqs(hctx);

                spin_lock(&hctx->lock);
                list_splice_tail_init(list, &hctx->dispatch);
                spin_unlock(&hctx->lock);

                /*
                 * Order adding requests to hctx->dispatch and checking
                 * SCHED_RESTART flag. The pair of this smp_mb() is the one
                 * in blk_mq_sched_restart(). Avoid restart code path to
                 * miss the new added requests to hctx->dispatch, meantime
                 * SCHED_RESTART is observed here.
                 */
                smp_mb();

                /*
                 * If SCHED_RESTART was set by the caller of this function and
                 * it is no longer set that means that it was cleared by another
                 * thread and hence that a queue rerun is needed.
                 *
                 * If 'no_tag' is set, that means that we failed getting
                 * a driver tag with an I/O scheduler attached. If our dispatch
                 * waitqueue is no longer active, ensure that we run the queue
                 * AFTER adding our entries back to the list.
                 *
                 * If no I/O scheduler has been configured it is possible that
                 * the hardware queue got stopped and restarted before requests
                 * were pushed back onto the dispatch list. Rerun the queue to
                 * avoid starvation. Notes:
                 * - blk_mq_run_hw_queue() checks whether or not a queue has
                 *   been stopped before rerunning a queue.
                 * - Some but not all block drivers stop a queue before
                 *   returning BLK_STS_RESOURCE. Two exceptions are scsi-mq
                 *   and dm-rq.
                 *
                 * If driver returns BLK_STS_RESOURCE and SCHED_RESTART
                 * bit is set, run queue after a delay to avoid IO stalls
                 * that could otherwise occur if the queue is idle.  We'll do
                 * similar if we couldn't get budget and SCHED_RESTART is set.
                 */
                needs_restart = blk_mq_sched_needs_restart(hctx);
                if (!needs_restart ||
                    (no_tag && list_empty_careful(&hctx->dispatch_wait.entry)))
                        blk_mq_run_hw_queue(hctx, true);
                else if (needs_restart && (ret == BLK_STS_RESOURCE ||
                                           no_budget_avail))
                        blk_mq_delay_run_hw_queue(hctx, BLK_MQ_RESOURCE_DELAY);

                blk_mq_update_dispatch_busy(hctx, true);
                return false;
        } else
                blk_mq_update_dispatch_busy(hctx, false);

        /*
         * If the host/device is unable to accept more work, inform the
         * caller of that.
         */
        if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE)
                return false;

        return (queued + errors) != 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/600254.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NumPy及Matplotlib基本用法

NumPy及Matplotlib基本用法 导语NumPy导入与生成算术运算N维数组广播元素访问 Matplotlib简单图案绘制多函数绘制图像显示参考文献 导语 深度学习中经常需要对图像和矩阵进行操作,好在python提供了Numpy和Matplotlib库,前者类似一个已经定义的数组类&am…

【负载均衡在线OJ项目日记】编译与日志功能开发

目录 日志功能开发 常见的日志等级 日志功能代码 编译功能开发 创建子进程和程序替换 重定向 编译功能代码 日志功能开发 日志在软件开发和运维中起着至关重要的作用,目前我们不谈运维只谈软件开发;日志最大的作用就是用于故障排查和调试&#x…

国货美妆进入新纪元之际,毛戈平打好“高端牌”了吗?

当前,国内美妆市场的格局已发生较大变化。 一边是国际品牌的“退场”,据统计,2023年退出中国市场的海外美妆品牌有20多个;一边是国内美妆品牌正在迎来自己的时代。 根据魔镜洞察数据,2024年一季度,国货彩…

论文辅助笔记:Tempo之modules/lora.py

1 LoRALayer 基类 2 Linear 2.1 __init__ 2.2 reset_parameter & train 2.3 forward 3 MergeLinear 3.1__init__ enable_lora指定了哪些输出特征使用lora 3.2 reset_parameters & zero_pad & merge_AB 3.3 train & forward

纯血鸿蒙APP实战开发——底部面板嵌套列表滑动案例

介绍 本示例主要介绍了利用panel实现底部面板内嵌套列表,分阶段滑动效果场景。 效果图预览 使用说明 点击底部“展开”,弹出panel面板。在panel半展开时,手指向上滑动panel高度充满页面,手指向下滑动panel隐藏。在panel完全展开…

从零开始的软件测试学习之旅(七)接口测试三要素及案例

接口测试三要素及案例 接口测试介绍接口预定义接口测试的主要作用测试接口流程如下接口测试三要素接口测试分类RESTful架构风格RESTful架构三要素要素一要素二要素三 RESTful架构风格实现案例复习复盘 接口测试介绍 接口介绍 不同主体之间进行通信的通道,它应具有一套规范/标准…

Vulnhub项目:NAPPING: 1.0.1

1、靶机介绍 靶机地址:Napping: 1.0.1 ~ VulnHub 2、渗透过程 老规矩,先探测,靶机ip:192.168.56.152 本机ip:192.168.56.146 来看一看靶机开放哪些端口,nmap一下 nmap -sS -sV -A -T5 192.168.56.152 开…

UE5自动生成地形二:自动生成插件

UE5自动生成地形二:自动生成插件 Polycam使用步骤 本篇主要讲解UE5的一些自动生成地形的插件 Polycam 此插件是通过现实的多角度照片自动建模生成地形数据,也是免费的。这里感谢B站up主古道兮峰的分享 Polycam网站 插件下载地址 插件网盘下载 提取码&a…

6.移除元素

文章目录 题目简介题目解答解法一:双指针代码:复杂度分析: 解法二:双指针优化代码:复杂度分析: 题目链接 大家好,我是晓星航。今天为大家带来的是 相关的讲解!😀 题目简…

Portforge:一款功能强大的轻量级端口混淆工具

关于Portforge Portforge是一款功能强大的轻量级端口混淆工具,该工具使用Crystal语言开发,可以帮助广大研究人员防止网络映射,这样一来,他人就无法查看到你设备正在运行(或没有运行)的服务和程序了。简而言…

数据库(MySQL)—— 初识和创建用户

数据库(MySQL)—— 初识 什么是数据库数据库的种类创建用户mysql -h 主机名或IP地址 -u 用户名 -p 登录mysqlSELECT USER(); 查看当前用户切换用户GRANT ALL PRIVILEGES ON 赋予用户权限 REVOKE 撤销权限示例注意事项 MySQL的图形化界面工具查看所有用户…

ldap对接jenkins

ldap结构 配置 - jenkins进入到 系统管理–>全局安全配置 - 安全域 选择ldap - 配置ldap服务器地址,和配置ldap顶层唯一标识名 配置用户搜索路径 - 配置管理员DN和密码 测试认证是否OK

【xxl-job | 第三篇】SpringBoot整合xxl-job

文章目录 3.SpringBoot整合xxl-job3.1定时任务服务配置3.1.1导入maven依赖3.1.2yml配置3.1.3XxlJobConfig配置类3.1.4定时任务类 3.2xxl-job配置3.2.1新增执行器3.2.2新增任务3.2.3执行任务3.2.4查看日志3.2.5查看任务后台日志 3.3小结 3.SpringBoot整合xxl-job 3.1定时任务服…

B端UX/UI设计面试作品集分层源文件figmasketch模板

当您考虑找工作时,是否曾质疑过项目复盘作品集的重要性?实际上,一份精心准备的项目复盘作品集对于求职者来说具有无可估量的价值,特别是对于设计师这一职业领域。 以下所述或许对您而言已非陌生。您的作品集应当成为您专业技能与…

嵌入式linux学习第三天汇编语言点灯

嵌入式linux学习第三天汇编语言点灯 今天学习如何在linux板子上点灯。 I.MX6U GPIO 详解 我们发现I.MX6U GPIO是分为两类的,:SNVS 域的和通用的。在讨论i.MX6U或类似的复杂微处理器时,了解其GPIO(通用输入输出)引脚…

vivado Zynq UltraScale+ MPSoC 比特流设置

Zynq UltraScale MPSoC 比特流设置 下表所示 Zynq UltraScale MPSoC 器件的器件配置设置可搭配 set_property <Setting> <Value> [current_design] Vivado 工具 Tcl 命令一起使用。

科研学习|可视化——ggplot2版本的网络可视化

ggplot2是R语言中一个非常流行的数据可视化包&#xff0c;它也可以用于网络可视化。以下是三个基于ggplot2并专门用于网络可视化的R包&#xff1a; ggnet2: 这个包的使用方法与传统的plot函数相似&#xff0c;易于使用。更多信息可在其官方页面查看&#xff1a;ggnet2 geomnet…

python+flask+ldap3搭建简易版IDaaS系统(前端站点)

Python工具开源专栏 Py0006 pythonflaskldap3搭建简易版IDaaS系统&#xff08;前端站点&#xff09; Python工具开源专栏前言目录结构前端网站的部分演示首页查询数据数据同步数据关联查询系统日志 完整代码已在GitHub上开源 前言 pythonflaskldap3搭建简易版IDaaS系统的前端站…

VALSE 2024 Tutorial内容总结--开放词汇视觉感知

视觉与学习青年学者研讨会&#xff08;VALSE&#xff09;旨在为从事计算机视觉、图像处理、模式识别与机器学习研究的中国青年学者提供一个广泛而深入的学术交流平台。该平台旨在促进国内青年学者的思想交流和学术合作&#xff0c;以期在相关领域做出显著的学术贡献&#xff0c…

【Linux进程信号(一)】信号概念/产生/保存/处理

目录 入门 前&后台进程 前台进程&#xff1a; 后台进程 常用命令 ./XXX & fg命令 & bg命令 Ctrl c / Ctrl \ 信号的概念 信号的产生 1.键盘产生 2.系统调用指令 3.异常 4.软件条件 信号的保存 信号的处理 1.信号屏蔽字 2.未决信号表 3.信号处理…
最新文章