linux 3.13版本nvme驱动阅读记录二

这里主要记录nvme_dev_start函数的阅读记录

内核3.13版本。

static int nvme_dev_start(struct nvme_dev *dev)
{int result;result = nvme_dev_map(dev); //pcie bar空间映射if (result)return result;result = nvme_configure_admin_queue(dev); //管理队列配置if (result)goto unmap;spin_lock(&dev_list_lock);list_add(&dev->node, &dev_list); //dev_list没太明白是定义在那个文件的?spin_unlock(&dev_list_lock);result = nvme_setup_io_queues(dev); //io队列配置if (result && result != -EBUSY)goto disable;return result;
disable:spin_lock(&dev_list_lock);list_del_init(&dev->node);spin_unlock(&dev_list_lock);
unmap:nvme_dev_unmap(dev);return result;
}

该函数主要做了4减事情。
1:nvme_dev_map,主要就是pci bar 那一套编程套路
2:管理队列的配置
3:dev_list?
4:io队列的配置

nvme_dev_map

static int nvme_dev_map(struct nvme_dev *dev)
{int bars, result = -ENOMEM;struct pci_dev *pdev = dev->pci_dev;if (pci_enable_device_mem(pdev))return result;dev->entry[0].vector = pdev->irq;pci_set_master(pdev);bars = pci_select_bars(pdev, IORESOURCE_MEM);if (pci_request_selected_regions(pdev, bars, "nvme"))goto disable_pci;if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)) && dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))goto disable;pci_set_drvdata(pdev, dev);dev->bar = ioremap(pci_resource_start(pdev, 0), 8192);//bar0if (!dev->bar)goto disable;dev->db_stride = NVME_CAP_STRIDE(readq(&dev->bar->cap)); //doorbell stride 32-35dev->dbs = ((void __iomem *)dev->bar) + 4096; //sq tailreturn 0;
disable:pci_release_regions(pdev);
disable_pci:pci_disable_device(pdev);return result;
}

这个函数没啥介绍的,对应的unmap如下:

static void nvme_dev_unmap(struct nvme_dev *dev)
{if(dev->pci_dev->msi_enabled)pci_disable_msi(dev->pci_dev);else if (dev->pci_dev->msix_enabled)pci_disable_msix(dev->pci_dev);if(dev->bar) {iounmap(dev->bar);dev->bar = NULL;}pci_release_regions(dev->pci_dev);if (pci_is_enabled(dev->pci_dev))pci_disable_device(dev->pci_dev);
}

nvme_configure_admin_queue

static int nvme_configure_admin_queue(struct nvme_dev *dev)
{int result;u32 aqa;u64 cap = readq(&dev->bar->cap);struct nvme_queue *nvmeq;result = nvme_disable_ctrl(dev, cap); //根据文档说明,配置队列时先disable控制器if (result < 0)return result;nvmeq = dev->queues[0];if (!nvmeq) {nvmeq = nvme_alloc_queue(dev, 0, 64, 0);//admin 队列,深度为64if (!nvmeq)return -ENOMEM;dev->queues[0] = nvmeq; //保存nvmeq起始地址}aqa = nvmeq->q_depth - 1;aqa |= aqa << 16;//使能控制器状态dev->ctrl_config = NVME_CC_ENABLE | NVME_CC_CSS_NVM;//设置一个page的大小dev->ctrl_config |= (PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT;dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE;//配置cqe和sqe元素的大小,2^4,2^6dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES; writel(aqa, &dev->bar->aqa);//配置cq队列和sq队列深度writeq(nvmeq->sq_dma_addr, &dev->bar->asq); //配置sq队列dma起始地址writeq(nvmeq->cq_dma_addr, &dev->bar->acq); //配置cq队列dma起始地址/*cc->Controller Configuration31-24 : Reserved23-20 : I/O Completion queue entry size19-16 : I/O Submission Queue Entry Size15-14 : Shutdown Notification13-11 : Arbitration Mechanism Selected10-7 : Memory Page size6-4 : IO Command Set Selected3-1 : Reserved0 : Enable*/writel(dev->ctrl_config, &dev->bar->cc);//这里使能了控制器//读Controller Configuration的第一个bit,判断控制器是否已经enableresult = nvme_enable_ctrl(dev, cap);if (result)return result;result = queue_request_irq(dev, nvmeq, "nvme admin");if (result)return result;spin_lock(&nvmeq->q_lock);nvme_init_queue(nvmeq, 0); //admin queue初始化spin_unlock(&nvmeq->q_lock);return result;
}

注释都解释的比较清楚了,这里在贴一些相关函数调用的代码。

enable/disable控制器相关的。起始就是对寄存器的一些值的判断。

static int nvme_wait_ready(struct nvme_dev *dev, u64 cap, bool enabled)
{unsigned long timeout;u32 bit = enabled ? NVME_CSTS_RDY : 0;timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;while ((readl(&dev->bar->csts) & NVME_CSTS_RDY) != bit) {msleep(100);if (fatal_signal_pending(current))//可以被一些fatal的信号打断return -EINTR;if (time_after(jiffies, timeout)) { //jiffies比timeout靠后返回1dev_err(&dev->pci_dev->dev, "Device not ready; aborting initialisation\n");return -ENODEV;}}return 0;
}static int nvme_disable_ctrl(struct nvme_dev *dev, u64 cap)
{u32 cc = readl(&dev->bar->cc);if (cc & NVME_CC_ENABLE)writel(cc & ~NVME_CC_ENABLE, &dev->bar->cc);//取反使能bitreturn nvme_wait_ready(dev, cap, false);
}static int nvme_enable_ctrl(struct nvme_dev *dev, u64 cap)
{return nvme_wait_ready(dev, cap, true);
}

队列的alloc:

static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, int depth, int vector)
{struct device *dmadev = &dev->pci_dev->dev;unsigned extra = nvme_queue_extra(depth);struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL);if (!nvmeq)return NULL;nvmeq->cqes = dma_alloc_coherent(dmadev, CQ_SIZE(depth), &nvmeq->cq_dma_addr, GFP_KERNEL);if (!nvmeq->cqes)goto free_nvmeq;memset((void *)nvmeq->cqes, 0, CQ_SIZE(depth));nvmeq->sq_cmds = dma_alloc_coherent(dmadev, SQ_SIZE(depth), &nvmeq->sq_dma_addr, GFP_KERNEL);if (!nvmeq->sq_cmds)goto free_cqdma;nvmeq->q_dmadev = dmadev;nvmeq->dev = dev;spin_lock_init(&nvmeq->q_lock);//队列的自旋锁nvmeq->cq_head = 0;//cq head位置nvmeq->cq_phase = 1;//后续需要它确定cq tail的位置init_waitqueue_head(&nvmeq->sq_full);//初始化等待队列头init_waitqueue_entry(&nvmeq->sq_cong_wait, nvme_thread);//初始化等待队列bio_list_init(&nvmeq->sq_cong);//初始化bio 链表(单向链表)nvmeq->q_db = &dev->dbs[qid << (dev->db_stride + 1)];//qid队列的门铃寄存器基地址nvmeq->q_depth = depth;//队列深度nvmeq->cq_vector = vector;//队列的中断向量nvmeq->q_suspended = 1;//还没初始化,所以这个先设置为1dev->queue_count++;//总的队列个数加一return nvmeq;
free_cqdma:dma_free_coherent(dmadev, CQ_SIZE(depth), (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
free_nvmeq:kfree(nvmeq);return NULL;
}static unsigned nvme_queue_extra(int depth)
{/*DIV_ROUND_UP(depth, 8)用来记录bit位(一共有depth个bit)depth * sizeof(struct nvme_cmd_info)用来记录depth个bit的struct nvme_cmd_info信息*/return DIV_ROUND_UP(depth, 8) + (depth * sizeof(struct nvme_cmd_info));
}

队列的init:

static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)
{struct nvme_dev *dev = nvmeq->dev;unsigned extra = nvme_queue_extra(nvmeq->q_depth);nvmeq->sq_tail = 0;nvmeq->cq_head = 0;nvmeq->cq_phase = 1;nvmeq->q_db = &dev->dbs[qid << (dev->db_stride + 1)];memset(nvmeq->cmdid_data, 0, extra);memset((void *)nvmeq->cqes, 0, CQ_SIZE(nvmeq->q_depth));nvme_cancel_ios(nvmeq, false);nvmeq->q_suspended = 0;
}
static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout)
{int depth = nvmeq->q_depth - 1;struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);unsigned long now = jiffies;int cmdid;for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) { //从第一个bit开始遍历每一个bit,返回被置位(值为1)的位置void *ctx;nvme_completion_fn fn;static struct nvme_completion cqe = {.status = cpu_to_le16(NVME_SC_ABORT_REQ << 1), //7 << 1};/*初始化时timeout为0,所以流程不会走到下面now比info[cmdid].timeout大,函数返回1,所以流程要往下走,time_afte函数要返回1(即时间超时?now大于timeout)*/if (timeout && !time_after(now, info[cmdid].timeout))continue;if (info[cmdid].ctx == CMD_CTX_CANCELLED) //也有可能是这个(提交的命令出现错误的情况?),因为bit还没清,所以遍历的时候是有这种情况的continue;dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d\n", cmdid)ctx = cancel_cmdid(nvmeq, cmdid, &fn);fn(nvmeq->dev, ctx, &cqe);}
}

io队列的配置

static int nvme_setup_io_queues(struct nvme_dev *dev)
{struct pci_dev *pdev = dev->pci_dev;int result, cpu, i, vecs, nr_io_queues, size, q_depth;nr_io_queues = num_online_cpus();result = set_queue_count(dev, nr_io_queues);if (result < 0)return result;if (result < nr_io_queues)nr_io_queues = result;size = db_bar_size(dev, nr_io_queues);if (size > 8192) { //因为dev_map函数里map的size是8192,大于8192需要重新映射iounmap(dev->bar);//先解除映射do {dev->bar = ioremap(pci_resource_start(pdev, 0), size);//再重新映射if (dev->bar)//如果一次性映射成功是最好的break;if (!--nr_io_queues)//如果一次性映射不成功,那么就逐步减少nr_io_queues的值,直到映射成功return -ENOMEM;size = db_bar_size(dev, nr_io_queues);//重新计算nr_io_queues减少以后的size,直到map成功} while (1);//管理队列的需要重新赋值dev->dbs = ((void __iomem *)dev->bar) + 4096;dev->queues[0]->q_db = dev->dbs;}//注销管理队列的中断free_irq(dev->entry[0].vector, dev->queues[0]);vecs = nr_io_queues;//entry 初始化for (i = 0; i < vecs; i++)dev->entry[i].entry = i;for (;;) {//请求分配vecs个中断,返回0表示成功result = pci_enable_msix(pdev, dev->entry, vecs);if (result <= 0)break;vecs = result;}if (result < 0) { //考虑result小于0的情况vecs = nr_io_queues;if (vecs > 32)vecs = 32;for (;;) {result = pci_enable_msi_block(pdev, vecs);if (result == 0) {for (i = 0; i < vecs; i++)dev->entry[i].vector = i + pdev->irq;break;} else if (result < 0) {vecs = 1;break;}vecs = result;}}//应该调查分配比中断向量更多的队列是否有性能优势;它可能允许提交路径更好地扩展,即使接收路径受到中断数量的限制。nr_io_queues = vecs;result = queue_request_irq(dev, dev->queues[0], "nvme admin");if (result) {dev->queues[0]->q_suspended = 1;goto free_queues;}/*释放以前分配的不再可用的队列,即队列id比nr_io_queues还大的队列不过这里感觉应该不会进来,因为前面只创建了管理队列*/spin_lock(&dev_list_lock);for (i = dev->queue_count - 1; i > nr_io_queues; i--) {struct nvme_queue *nvmeq = dev->queues[i];spin_lock(&nvmeq->q_lock);nvme_cancel_ios(nvmeq, false);spin_unlock(&nvmeq->q_lock);nvme_free_queue(nvmeq);dev->queue_count--;dev->queues[i] = NULL;}spin_unlock(&dev_list_lock);/*将不同的队列和cpu进行绑定*/cpu = cpumask_first(cpu_online_mask);for (i = 0; i < nr_io_queues; i++) {//https://zhuanlan.zhihu.com/p/163850501irq_set_affinity_hint(dev->entry[i].vector, get_cpu_mask(cpu));cpu = cpumask_next(cpu, cpu_online_mask);}/*page45Maximum Queue Entries Supported (MQES):该字段表示控制器支持的最大单个队列大小。对于基于pcie实现的NVMe,此值适用于主机创建的I/O提交队列和I/O完成队列。对于基于fabricimplementation的NVMe,这个值只适用于主机创建的I/O提交队列。这是一个基于0的值。最小值为1h,即2条*/q_depth = min_t(int, NVME_CAP_MQES(readq(&dev->bar->cap)) + 1, NVME_Q_DEPTH);for (i = dev->queue_count - 1; i < nr_io_queues; i++) {dev->queues[i + 1] = nvme_alloc_queue(dev, i + 1, q_depth, i);if (!dev->queues[i + 1]) {result = -ENOMEM;goto free_queues;}}for (; i < num_possible_cpus(); i++) {//该函数是取数的最高二进制阶数,即将给定值四舍五入到最接近的二次方int target = i % rounddown_pow_of_two(dev->queue_count - 1);dev->queues[i + 1] = dev->queues[target + 1];}//创建io queue,索引从1开始for (i = 1; i < dev->queue_count; i++) {result = nvme_create_queue(dev->queues[i], i);if (result) {for (--i; i > 0; i--)nvme_disable_queue(dev, i);goto free_queues;}}return 0;
free_queues:nvme_free_queues(dev);return result;
}static int set_queue_count(struct nvme_dev *dev, int count)
{int status;u32 result, q_count = (count - 1) | ((count - 1) << 16);/*设置队列个数(不包括管理队列), page212:0-15bit:number of I/O submission queues Requested(NSQR)16:31bit:Number of I/O completion queue requested(NCQR)最大值65535,q_count设置为0说明配置的队列个数是1,如果配置65535将发生错误(要配置65534)*/status = nvme_set_features(dev, NVME_FEAT_NUM_QUEUES, q_count, 0, &result);if (status)return status < 0 ? -EIO : -EBUSY;return min(result & 0xffff, result >> 16) + 1;
}

这个函数也没啥说的,看注释吧。
差不多就总结到这里了。关于nvme_dev_start函数的总结。

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

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

相关文章

目标检测回归损失函数(看情况补...)

文章目录 L1 loss-平均绝对误差(Mean Absolute Error——MAE)L2 loss-均方误差(Mean Square Error——MSE)Smooth L1 LossMAE、MSE、Smooth L1对比IoU LossGIoU LossDIoU Loss、CIoU LossE-IoU Loss、Focal E-IoU LossReferenceL1 loss-平均绝对误差(Mean Absolute Error——…

ASP.NETCore6开启文件服务允许通过url访问附件(图片)

需求背景 最近在做一个工作台的文件上传下载功能&#xff0c;主要想实现上传图片之后&#xff0c;可以通过url直接访问。由于url直接访问文件不安全&#xff0c;所以需要手动开启文件服务。 配置 文件路径如下&#xff0c;其中Files是存放文件的目录&#xff1a; 那么&…

接口测试工具

接口测试的重要性 接口测试&#xff1a; 直接对后端服务的测试&#xff0c;是服务端性能测试的基础&#xff0c;是测试工程师的必备技能。 接口测试的概念 接口&#xff1a;系统之间数据交互的通道 接口测试&#xff1a;校验接口响应数据与预期数据是否一致 接口信息解析 …

缓存-基础理论和Guava Cache介绍

缓存-基础理论和Guava Cache介绍 缓存基础理论 缓存的容量和扩容 缓存初始容量、最大容量&#xff0c;扩容阈值以及相应的扩容实现。 缓存分类 本地缓存&#xff1a;运行于本进程中的缓存&#xff0c; 如Java的 concurrentHashMap, Ehcache&#xff0c;Guava Cache。 分布式缓…

Mac上好用的翻译软件推荐 兼容m

Mac翻译软件可以用在学习&#xff0c;工作&#xff0c;生活当中&#xff0c;一款好用的翻译软件&#xff0c;具有翻译准确&#xff0c;翻译快速等基本特点&#xff0c;能够帮您提高工作效率。Mac上有什么好用的翻译软件呢&#xff1f;今天小编为大家整理了6款好用的Mac翻译软件…

C语言--typedef的使用

前言 在C语言中使用结构体时必须加上struct这个关键字,那有没有办法省略这个呢?要想达到这个目的就 需要用到关键字typedef,顾名思义”类型定义”。 typedef 数据类型 新的别名; 它是用来操作数据类型。其主要作用有两个: 1.给一个较长较复杂的类型取一个简单的别名。 2.给类…

MySQL数据库入门到大牛_03_SQL概述、基本的SELECT语句、显示表结构、过滤数据

文章目录 1. SQL概述1.1 SQL背景知识1.2 SQL语言排行榜1.3 SQL 分类1.4 数据库内容 2. SQL语言的规则与规范2.1 基本规则2.2 SQL大小写规范 &#xff08;建议遵守&#xff09;2.3 注 释2.4 命名规则&#xff08;暂时了解&#xff09;2.5 数据导入指令2.5.1 source 文件全路径名…

零代码编程:用ChatGPT批量将Mp4视频转为Mp3音频

文件夹中有很多mp4视频文件&#xff0c;如何利用ChatGPT来全部转换为mp3音频呢&#xff1f; 在ChatGPT中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个批量将Mp4视频转为Mp3音频的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹&#xff1a;…

Springboot中解析JSON字符串(jackson库ObjectMapper解析JSON字符串)

1、ObjectMapper与JSONObject比较 1、ObjectMapper属于jackson库的一部分,JSONObject属于alibaba的fastjson&#xff0c;两者各有优劣&#xff0c;可根据自己的系统环境选择使用哪种技术。 2、目前来看&#xff0c;Jackson社区相对活跃&#xff0c;Spring MVC和Spring Boot都…

Spring中@Bean标注的方法是如何创建对象呢?

Bean 标注的方法如何创建对象呢&#xff1f; 参考文章&#xff1a;https://blog.csdn.net/qq_35971258/article/details/128241353 下边只讲一下 Bean 注解标注的方法&#xff0c;是如何去进行创建 bean&#xff0c;以及流程是怎样的&#xff0c;如果需要看源码具体执行流程&a…

Godot Best practices

Get Forward Vector transform.x # 等价手算 var rad node.rotation var forward Vector2(cos(rad), sin(rad))Await and Unity Style Coroutine func coroutine(on_update: Callable, duration: float 1):var elapse_time 0while elapse_time < 1:elapse_time get_p…

RabbitMQ常用命令(一)

启动和关闭 1、启动RabbitMQ rabbitmq-server start & 注意&#xff1a;这里可能会出现错误&#xff0c;错误原因是/var/lib/rabbitmq/.erlang.cookie文件权限不够。 解决方案对这个文件授权 chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie chmod 400 /va…

IDEA项目下不显示target目录或者target目录不完整没有新添加的资源,idea隐藏target目录

文章目录 一、前言二、idea隐藏target目录2.1、idea隐藏target目录2.2、git提交时隐藏target目录 三、idea下显示target目录3.1、解决idea下不显示target目录问题3.2、target显示目录不完整 一、前言 在idea-2020.1.4版本下讲解idea怎么显示或隐藏target目录。 需要知道:如果…

3-爬虫-搜索文档树(find和find_all)、bs4其它用法、css选择器、selenium基本使用以及其他、selenium(无头浏览器、搜索标签)

1 搜索文档树 1.1 find和find_all 1.2 爬取美女图片 2 bs4其它用法 3 css选择器 4 selenium基本使用 4.1 模拟登录 5 selenium其它用法 5.1 无头浏览器 5.2 搜索标签 遍历文档树 -1 request 使用代理proxies {https: 192.168.1.12:8090,}-2 代理的使用-高匿 透明-免费---》…

聊一聊 tcp/ip 在.NET故障分析的重要性

一&#xff1a;背景 1. 讲故事 这段时间分析了几个和网络故障有关的.NET程序之后&#xff0c;真的越来越体会到计算机基础课的重要&#xff0c;比如 计算机网络 课&#xff0c;如果没有对 tcpip协议 的深刻理解&#xff0c;解决这些问题真的很难&#xff0c;因为你只能在高层…

线性代数之 伪逆矩阵

目录 一、伪逆矩阵 ◼ A的伪逆矩阵与SVD ◼ 用Python代码计算A的伪逆矩阵 ◼ 笔算A的伪逆矩阵 一、伪逆矩阵 ◼ A的伪逆矩阵与SVD 逆矩阵并不总是存在&#xff0c;即使是方阵。然而&#xff0c;对于非正方形矩阵&#xff0c;存在一个伪逆矩阵&#xff0c;也叫摩尔-彭罗斯…

SpringIoC之Bean生命周期源码主要流程解析

文章目录 生成BeanDefinition合并BeanDefinition加载类实例化前实例化 生成BeanDefinition Spring启动的时候会进行扫描&#xff0c;会先调用 org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePacka…

OPCUA 行业配套标准:机器人

OPC UA 定义了对象&#xff0c;对象类型&#xff0c;结构化组织能力和定义对象之间关系的能力&#xff0c;利用这些基础和衍生类型及对象&#xff0c;用户还可以搭建出更复杂的类型&#xff0c;关系和对象。 如果不同的厂商或者用户定义的信息模型不同&#xff0c;将会影响系统…

qml添加滚动条

import QtQuick.Controls 2.15ScrollBar.vertical: ScrollBar {visible: flick1.contentHeight > flick1.heightanchors.right: parent.rightanchors.rightMargin: 40width: 10active: truecontentItem: Rectangle {radius: 6opacity: 0.5color: "#7882A0"} }

Linux 安装 Nginx 并配置为系统服务(超详细)

目录 前言安装 Nginx安装依赖项下载Nginx解压Nginx编译和安装防火墙设置启动Nginx 配置 Nginx 为系统服务配置 Nginx 服务文件启动 Nginx 服务设置开机自启动检查 Nginx 状态停止 Nginx 服务重启 Nginx 服务 卸载 Nginx结语 前言 Nginx是一款卓越的高性能Web服务器&#xff0c…