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 tialwritel(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 headif (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_headnvme_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);}//下发ioret = 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 commitq->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;} elseblk_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.mzph.cn/diannao/7839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

京东生产环境十万并发秒杀系统三高架构

文章目录 三高——高并发、高可用、高可扩展用数据库乐观锁解决超卖阿里巴巴:为了提升数据库性能,对数据库的源码级别做了改造——在DB内部实现内存队列,一次性接收很多的请求,一次性更新。京东:redis,mq&a…

无线通信基础

这里写目录标题 通信概述什么是无线通信无线通信电磁波 通信概述 什么是无线通信 无线通信 : 是指利用电磁波信号可以在自由空间中传播的特性进行信息交换的一种通信方式 无线通信的关键技术包括调制技术、解调技术、信道编码技术、信号处理技术、天线技术等。这些技术的不断…

医药垃圾分类管理系统|基于SSM医药垃圾分类管理系统的系统设计与实现(源码+数据库+文档)

医药垃圾分类管理系统 目录 基于SSM医药垃圾分类管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统登录模块 2管理员模块实现 3用户模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博…

JavaScript中如何实现函数缓存,函数缓存有哪些应用场景

函数缓存就是将函数运算的结果进行缓存。 本质上是利用空间换时间。 常用于缓存数据计算结果和缓存对象。缓存只是一个临时的数据存储,它保存数据,为了方便将来对该数据的请求时,可以更快的得到处理。 缓存函数 实现一个缓存函数&#xf…

《十九》Qt Http协议及实战

前言 本篇文章来给大家讲解QT中的Http协议,Http协议主要用于网络中数据的请求和响应,那么这篇文章将给大家讲解一下这个协议。 一、HTTP概述 HTTP(超文本传输协议)是互联网上应用最为广泛的协议之一,它定义了客户端…

LM4562NA 直插DIP8双运放 音频hifi运算放大器

LM4562NA是一款高性能音频运算放大器,其应用领域主要集中在音频和声音处理方面,包括但不限于: 1. 专业录音设备:在录音棚、广播电台和电视台等专业环境中,用于信号放大和处理,确保高质量的声音录制和传输…

北邮22级信通院DSP:实验三(1):FFT变换、IFFT变换(附每步8点变换蝶形图)保姆级讲解+用C++程序实现复数域的FFT变换和IFFT变换

北邮22信通一枚~ 跟随课程进度更新北邮信通院DSP的笔记、代码和文章,欢迎关注~ 获取更多文章,请访问专栏: 北邮22级信通院DSP_青山入墨雨如画的博客-CSDN博客 目录 一、预备知识 1.1 FFT算法 1.2.1由DFT到FFT 1.2.2 基2时域抽选算法 …

并发编程之阻塞队列BlockingQueue实战及其原理分析

1. 阻塞队列介绍 1.1 队列 是限定在一端进行插入,另一端进行删除的特殊线性表。 先进先出(FIFO)线性表。 允许出队的一端称为队头,允许入队的一端称为队尾。

使用开放式用户通信连接两台西门子S71200plc

步骤1.在项目中创建两台PLC。 步骤2.分别设置两个PLC的参数。 plc1 plc2 步骤3.对两个plc进行组态 步骤4.在plc1和plc2中各自创建DB块,用于通信。 须在块的属性中取消优化块的访问选项。 plc1 plc2 步骤5.往plc1的main块中编写代码。 步骤6.往plc2的main块中编写…

Markdown 精简教程(胎教级教程)

文章目录 一、关于 Markdown1. 什么是 Markdown?2. 为什么要用 Markdown?3. 怎么用 Markdown?(编辑软件) 二、标题1. 常用标题写法2. 可选标题写法3. 自定义标题 ID4. 注意事项 三、段落四、换行五、字体选项1. 粗体2.…

15.计算机网络

1.物理层的互联设备 中继器 和 集线器 2.集线器可以看做特殊的多路中继器 集线器 不可以做到自动寻址的功能 3.数据链路层 网桥 和 交换机 4.交换机是多端口网桥 5.网络层 路由器 6.应用层 网关 7.广播域 网络层 可以形成多个广播域 冲突域 网络层数据链路层 可以形成多个冲突域…

网络安全--红队资源大合集

目录 相关资源列表 攻防测试手册 内网安全文档 学习靶场 工具包集合 内网安全文档 学习手册相关资源 产品设计文档 版本管理平台漏洞收集 相关工具拓展插件 Kali 环境下拓展插件 Nessus 相关工具拓展插件 Awvs 相关工具拓展插件 红队攻击的生命周期,…

第三节课,前端

一、参考链接; 总 知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具 分 2022-03-18 星球直播笔记-用户中心(下) 语雀 二、登录 2.1登录网址 2.2前端页面修改 2.1 页面修改 2.2 页脚的超链接 网址&am…

Linux初识

1.操作系统的那点事 (1)结论:操作系统是作软硬件管理的软件; (2)计算机是操作系统,设备驱动,硬件三个相互结合发挥作用的,操作系统是用来管理硬件的,常见的…

操作系统真象还原-bochs安装

今天读了《操作系统真象还原》这本书,写上比较幽默通俗。书中例子需要安装一个bochs系统,记录一下安装过程。参考了书中1.4,1.5两节,书中尽让有两处问题,也记录了下来。 1.3 操作系统的宿主环境 下载地址&#xff1a…

汽车混动结构概念

混动汽车中的PHEV、HEV和REEV分别代表了不同的技术概念和类型,它们各自有其特点和区别。以下是关于这三种混动汽车的概念和它们之间的主要区别: PHEV(插电式混合动力汽车,Plug-in Hybrid Electric Vehicle) 概念&…

简易录制视频做3D高斯

系统环境 ubuntu20 ,cuda11.8,anaconda配置好了3D高斯的环境。 具体参考3D高斯环境配置:https://blog.csdn.net/Son_of_the_Bronx/article/details/138527329?spm1001.2014.3001.5501 colmap安装:https://blog.csdn.net/Son_of…

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

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

linux 下 /usr/local的作用

在Linux系统中,/usr/local目录扮演着特定的角色,它是为用户自安装的软件提供一个标准位置。以下是/usr/local目录的主要用途和特点: 用户级程序目录:该目录用于存放用户自行编译安装的软件或者第三方应用程序,区别于操…

mysql5.7.44误删除数据后,使用binlog日志恢复

系统环境 #系统版本 # cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) #数据库版本 # mysql -V mysql Ver 14.14 Distrib 5.7.44, for Linux (x86_64) using EditLine wrapper恢复前提 本文仅适合mysql数据库已经开启binlog日志的情况。 查看binlog是否开启…