NVMe离不开PCIe,NVMe SSD是PCIe的endpoint。PCIe是x86平台上一种流行的bus总线,由于其Plug and Play的特性,目前很多外设都通过PCI Bus与Host通信,甚至不少CPU的集成外设都通过PCI Bus连接,如APIC等。

NVMe SSD在PCIe接口上使用新的标准协议NVMe,由大厂Intel推出并交由nvmexpress组织推广,现在被全球大部分存储企业采纳

1.NVMe Command

NVMe Host(Server)和NVMe Controller(SSD)通过NVMe Command进行信息交互。NVMe Spec中定义了NVMe Command的格式,占用64字节。

NVMe Command分为Admin Command和IO Command两大类,前者主要是用于配置,后者用于数据传输。

NVMe Command是Host与SSD Controller交流的基本单元,应用的I/O请求也要转化成NVMe Command。

2.PCI总线

在系统启动时,BIOS会枚举整个PCI的总线,之后将扫描到的设备通过ACPI tables传给操作系统。当操作系统加载时,PCI Bus驱动则会根据此信息读取各个PCI设备的Header Config空间,从class code寄存器获得一个特征值。

class code是PCI bus用来选择哪个驱动加载设备的唯一根据。NVMe Spec定义的class code是010802h。NVMe SSD内部的Controller PCIe Header中class code都会设置成010802h。

所以,需要在驱动中指定class code为010802h,将010802h放入pci_driver nvme_driver的id_table。之后当nvme_driver注册到PCI Bus后,PCI Bus就知道这个驱动是给class code=010802h的设备使用的。nvme_driver中有一个probe函数,nvme_probe(),这个函数才是真正加载设备的处理函数。

0x010802

staticconststruct

…….

0xffffff) },

……

};

3.单独编译NVME驱动

在老版本的源码中,可以在源码路径drivers/block中,增加Makefile内容如下,进行编译:

nvme-objs := nvme-core.o nvme-scsi.o

PWD := $(shell pwd)

default:

clean:

rf *.o *.ko

然后直接即可生成nvme.ko文件。

关于Makefile可以参考如下:

KERNELVER ?= $(shell uname -r)

KERNROOT = /lib/modules/$(KERNELVER)/build

nvme:

clean:

主要就两个文件:nvme-core.c和nvme-scsi.c。

不过,最新的代码位于drivers/nvme/host中,主要是core.c和pci.c。

4.注册和初始化

我们知道首先是驱动需要注册到PCI总线。那么nvme_driver是如何注册的呢?

当驱动被加载时就会调用nvme_init(drivers/nvme/host/pci.c)函数。在这个函数中,调用了kernel的函数pci_register_driver,注册nvme_driver,其结构体如下。

staticstruct

"nvme",

};

这样PCI bus上就多了一个pci_driver nvme_driver。当读到一个设备的class code是010802h时,就会调用这个nvme_driver结构体的probe函数, 也就是说当设备和驱动匹配了之后,驱动的probe函数就会被调用,来实现驱动的加载。

Probe函数主要完成四个工作:

1.映射设备的bar空间到内存虚拟地址空间

2.设置admin queue;

3.添加nvme namespace设备;

4.添加nvme Controller,提供ioctl接口。

接着来看下nvme_driver结构体中的.probe函数nvme_probe。

staticintstructconststruct

{

intENOMEM;

struct

unsignedlong

if

sizeof(*dev), GFP_KERNEL, node);

if

returnENOMEM

1,

sizeof(struct

if

goto

if

goto

if

goto

if

goto

%s\n", dev_name(&pdev->dev));

return0;

release_pools:

unmap:

put_pci:

free:

return

}

并分配设备数据结构nvme_dev,队列nvme_queue等,结构体如下。

structnvme_dev

struct

struct

struct

struct

struct

struct

unsigned

unsigned

int

void

unsignedlong

struct

struct

bool

void

struct

struct

/* shadow doorbell buffer support: */

/* host memory buffer support: */

struct

void

};

structnvme_queue

struct

struct

struct

struct

volatilestruct

struct

};

继续说nvme_probe函数,nvme_setup_prp_pools,主要是创建dma pool,后面可以通过dma函数从dma pool中获得memory。主要是为了给4k和128k的不同IO来做优化。

nvme_init_ctrl函数会创建NVMe控制器结构体,这样在后后续probe阶段时候用初始化过的结构,其传入的操作函数集是nvme_pci_ctrl_ops。

staticconststruct

"pcie",

};

staticconststructnvme_fops

};

5.NVMe的IO

机械硬盘时代,由于其随机访问性能差,内核开发者主要放在缓存I/O、合并I/O等方面,并没有考虑多队列的设计;而Flash的出现,性能出现了戏剧性的反转,因为单个CPU每秒发出IO请求数量是有限的,所以促进了IO多队列开发。

kcalloc_node如下,可以看到队列数量是和系统中所拥有的cpu数量有关。

1,

sizeof(struct

Queue有的概念,那就是队列深度,表示其能够放多少个成员。在NVMe中,这个队列深度是由NVMe SSD决定的,存储在NVMe设备的BAR空间里。

队列用来存放NVMe Command,NVMe Command是主机与SSD控制器交流的基本单元,应用的I/O请求也要转化成NVMe Command。

不过需要注意的是,就算有很多CPU发送请求,但是块层并不能保证都能处理完,将来可能要绕过IO栈的块层,不然瓶颈就是操作系统本身了。

当前Linux内核提供了blk_queue_make_request函数,调用这个函数注册自定义的队列处理方法,可以绕过io调度和io队列,从而缩短io延时。Block层收到上层发送的IO请求,就会选择该方法处理,如下图:

从图中可 以看出NVMe SSD I/O路径并不经传统的块层。

6.DMA

PCIe有个寄存器位Bus Master Enable,这个bit置1后,PCIe设备就可以向Host发送DMA Read Memory和DMA Write Memory请求。

当Host的driver需要跟PCIe设备传输数据的时候,只需要告诉PCIe设备存放数据的地址就可以。

NVMe Command占用64个字节,另外其PCIe BAR空间被映射到虚拟内存空间(其中包括用来通知NVMe SSD Controller读取Command的Doorbell寄存器)。

NVMe数据传输都是通过NVMe Command,而NVMe Command则存放在NVMe Queue中,其配置如下图。

其中队列中有Submission Queue,Completion Queue两个。

7.参考

linux内核nvme驱动程序,Linux中nvme驱动详解相关推荐

  1. Linux内核学习笔记(6)-- 进程优先级详解(prio、static_prio、normal_prio、rt_priority)...

    Linux 中采用了两种不同的优先级范围,一种是 nice 值,一种是实时优先级.在上一篇粗略的说了一下 nice 值和实时优先级,仍有不少疑问,本文来详细说明一下进程优先级.linux 内核版本为 ...

  2. linux 系统调用 优先级,Linux内核学习笔记(6)-- 进程优先级详解(prio、static_prio、normal_prio、rt_priority)...

    Linux 中采用了两种不同的优先级范围,一种是 nice 值,一种是实时优先级.在上一篇粗略的说了一下 nice 值和实时优先级,仍有不少疑问,本文来详细说明一下进程优先级.linux 内核版本为 ...

  3. linux内核epub,Android底层开发技术实战详解——内核、移植和驱动(第2版)[EPUB][MOBI][AZW3][42.33MB]...

    内容简介 本书从底层原理开始讲起,结合真实的案例向读者详细介绍了Android内核.移植和驱动开发的整个流程.全书分为21章,依次讲解驱动移植的必要性, Goldfish.OMAP内核和驱动解析,显示 ...

  4. linux内核(4.17.10)配置项详解(x86)

    64-bit kernel--支持64位 General setup--通用设置 Cross-compiler tool prefix--交叉编译工具前缀 Local version-append t ...

  5. Linux MTD架构下的nand flash驱动详解

    转载自:http://blog.csdn.net/wang_zheng_kai/article/details/18988521 有了前面的基础(Nandflash详解:https://blog.cs ...

  6. 【学习笔记】编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译

    本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录,就可以发现源码目录及其子目录中有很多的KConfig文件和Makefile文件.这些文件什么作用呢?正是这些文件组成了Linux ...

  7. 编译arm linux内核,编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译...

    转载自:http://blog.csdn.net/newthinker_wei/article/details/8022696 本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录, ...

  8. linux内核 块驱动程序,linux – 为什么内核使用默认的块驱动程序而不是我的驱动程序代码?...

    我写了一个块驱动程序,它创建了一个虚拟块设备(sbd0).我为该块设备注册了所有设备操作:(请参阅2.6.32内核源代码中的include / linux / blkdev.h) static str ...

  9. 16.Linux-LCD驱动(详解)

    在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构体: framebuffer_alloc(); 2) 设置fb_info 3) 设置硬件相关的操作 ...

  10. Linux内核 LCD 驱动程序框架

    Linux 内核 LCD 驱动程序框架 1. framebuffer 简介 1.1 什么是 framebuffer 1.2 framebuffer的作用 2. framebuffer 驱动的框架 3. ...

最新文章

  1. IT痴汉的工作现状25-技术之养成
  2. java语言提供结构_java学习之语句结构
  3. 七阶拉丁方阵_最强大脑七阶立方之战规则解析 清华天才对其了如指掌
  4. Java 9中什么是私有的?
  5. textcnn文本词向量_文本分类模型之TextCNN
  6. LeetCode-MySQL196. 删除重复的电子邮箱
  7. gps测速仪手机版下载_银豹收银系统手机版下载-银豹收银系统手机安卓版APP下载v2.0.1.2...
  8. php裁剪图片白边,php生成缩略图自动填充白边例子
  9. sql azure 语法_如何将SQL Server数据库备份到Microsoft Azure
  10. 敏捷开发般若敏捷系列之一:序言
  11. 测量不确定度matlab,基于MATLAB用蒙特卡洛法评估测量不确定度简介,目录书摘
  12. JSF使用HTML5的custom attribute
  13. 团队项目——测试心得
  14. latex导数_使用LaTeX语法编写数学公式(持续更新)
  15. gradle教程java_gradle入门到精通视频教程 下载
  16. 互联网金融学习总结(1)——互联网金融(ITFIN)概念相关学习
  17. L1-009 N个数求和(解决测试点三错误)
  18. android粘性广播何时结束,Android之粘性广播理解
  19. Vue学习记录07--vue路由的使用
  20. 在html中引入谷歌地图

热门文章

  1. Linux-完美解决linux系统镜像下载速度慢的问题
  2. fortan程序设计
  3. 图书管理系统源码,图书管理系统开发,图书借阅系统源码一
  4. Navicat for mysql 破解版,安装与破解
  5. Ubuntu下Vim的常用操作命令——vi编辑器常用命令
  6. gns3虚拟机服务器集群,GNS3中如何实现与Vmware Workstation连接
  7. SIFT算法论文:译文、详解
  8. modelsim安装_Modelsim10.5安装教程
  9. 软件系统设计-13-质量属性
  10. 双系统格式化Ubuntu分区后Windows 开机出现grub rescue问题