Linux中nvme驱动详解

  • 时间:
  • 浏览:0
  • 来源:5分3DAPP下载_5分3DAPP官网

        u32 __iomem *dbs;

        u32 *dbbuf_cq_ei;

NVMe驱动解析——关键的BAR空间

NVMe SPEC http://nvmexpress.org/

        struct work_struct remove_work;

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

3.上加nvme namespace设备;

default:

                return -ENOMEM;          

                goto unmap;              

主要就另还有一个 文件:nvme-core.cnvme-scsi.c

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

};

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

        .pr_ops         = &nvme_pr_ops,

        void __iomem *bar;

        .ioctl          = nvme_ioctl,

Queue有的概念,那倘若队列深度,表示其还里能放几条个成员。在NVMe中,你这名队列深度是由NVMe SSD决定的,存储在NVMe设备的BAR空间里。

        .shutdown       = nvme_shutdown,

        u8 cq_phase;

        if (result)     

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

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

        u32 db_stride;

当Host的driver前要跟PCIe设备传输数据的完后 ,只前要告诉PCIe设备存放数据的地址就可不前要。

        kfree(dev);

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

        .driver         = {      

        .probe          = nvme_probe,

        u64 host_mem_size;

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

        s16 cq_vector;

…….

否则 直接make 即可生成nvme.ko文件。

        return 0;

        u64 cmb_size;

        return result;

                .pm     = &nvme_dev_pm_ops,

        rm rf *.o *.ko

       每个设备大约另还有一个 队列,另还有一个 是admin管理命令,另还有一个 是给I/O命令,你这名队列概念和完后 介绍块驱动中的磁盘队列另还有一个 道理,倘若那个驱动比较基础,所以命令和IO暂且区分队列,具体形态体如下。

        nvme_reset_ctrl(&dev->ctrl);

        u32 nr_host_mem_descs;

        struct device *dev;

        int q_depth;

        dma_addr_t cq_dma_addr;

        dev->queues = kcalloc_node(num_possible_cpus() + 1,

        struct completion ioq_wait;

   $(MAKE) -C $(KERNROOT) M=`pwd`/drivers/block clean

        result = nvme_setup_prp_pools(dev);

        quirks |= check_vendor_combination_bug(pdev);

        dma_addr_t dbbuf_dbs_dma_addr;

#define PCI_CLASS_STORAGE_EXPRESS       0x0101002

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

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

{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },

obj-$(CONFIG_BLK_DEV_NVME)      += nvme.o

};

接着来看下nvme_driver形态体中的.probe函数nvme_probe。

};

        unsigned long quirks = id->driver_data;

        nvme_release_prp_pools(dev);

        put_device(dev->dev);

Linux Driver Information

        .owner          = THIS_MODULE,

        unsigned max_qid;

        dev->dev = get_device(&pdev->dev);

        nvme_dev_unmap(dev);

        .open           = nvme_open,

static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)

       另外NVMe磁盘的操作函数集,类事打开,释放等,形态体如下:

};

};

        .free_ctrl              = nvme_pci_free_ctrl,

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

static const struct pci_device_id nvme_id_table[] = {

        struct nvme_command __iomem *sq_cmds_io;

……

不过前要注意的是,就算有所以CPU发送请求,否则 块层暂且能保证都能出理 完,将来原因 要绕过IO栈的块层,不然瓶颈倘若操作系统一种生活了。

NVMe离不开PCIe,NVMe SSD是PCIe的endpoint。PCIe是x86平台上一种生活流行的bus总线,原因 其Plug and Play的形态,目前所以外设都通过PCI Bus与Host通信,甚至不少CPU的集成外设都通过PCI Bus连接,如APIC等。

        mutex_init(&dev->shutdown_lock);

        .getgeo         = nvme_getgeo,

        u8 cqe_seen;

        init_completion(&dev->ioq_wait);

        u32 *dbbuf_eis;

clean:

KERNELVER ?= $(shell uname -r)

        .revalidate_disk= nvme_revalidate_disk,

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

        u32 __iomem *q_db;

        int node, result = -ENOMEM;

        if (!dev)       

                goto free;               

       PCIe的Header空间和BAR空间是PCIe的关键形态。Header空间是PCIe设备的通有属性,所有的PCIe Spec功能和规范不会这里实现;BAR空间则是设备差异化的具体体现,BAR空间的定义决定了你这名设备是网卡,SSD还是虚拟设备。BAR空间是Host和PCIe设备进行信息交互的重要介质,BAR空间的数据实际存储在PCIe设备上。Host这边给PCIe设备分配的地址资源,暂且占用Host的内存资源。当读写BAR空间时,都前要通过PCIe接口(通过PCI TLP消息)进行实际的数据传输。

        void __iomem *cmb;

        },              



        dma_addr_t host_mem_descs_dma;

        INIT_WORK(&dev->remove_work, nvme_remove_dead_ctrl_work);

        struct nvme_queue *queues;

dev->queues = kcalloc_node(num_possible_cpus() + 1,

        void **host_mem_desc_bufs;

        struct blk_mq_tags **tags;

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

PWD := $(shell pwd)

                goto release_pools;      

       不过,最新的代码处于drivers/nvme/host,主倘若core.cpci.c

        .sriov_configure = nvme_pci_sriov_configure,

        u16 qid;

       详见《NVM_Express_Revision》

        u32 cmbsz;

nvme_init_ctrl函数会创建NVMe控制器形态体,原先在以后续probe阶段完后 用初始化过的形态,其传入的操作函数集是nvme_pci_ctrl_ops

        .compat_ioctl   = nvme_ioctl,

        u32 *dbbuf_dbs;

NVMe数据传输不会通过NVMe Command,而NVMe Command则存里装去NVMe Queue中,其配置如下图。

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

        pci_bus_addr_t cmb_bus_addr;

                        sizeof(struct nvme_queue), GFP_KERNEL, node);

        .err_handler    = &nvme_err_handler,

        .id_table       = nvme_id_table,

所以人知道首先是驱动前要注册到PCI总线。不还里能 nvme_driver是何如注册的呢?



release_pools:

        u32 *dbbuf_cq_db;

Probe函数主要完成还有一个工作:

        pci_set_drvdata(pdev, dev);

        dma_addr_t dbbuf_eis_dma_addr;

        struct blk_mq_tag_set admin_tagset;

        .reg_read32             = nvme_pci_reg_read32,

        if (!dev->queues)

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

        u32 *dbbuf_sq_db;

所以,前要在驱动中指定class code为0101002h,将0101002h里装去pci_driver nvme_driver的id_table。完后 当nvme_driver注册到PCI Bus后,PCI Bus就知道你这名驱动是给class code=0101002h的设备使用的。nvme_driver中有 另还有一个 probe函数,nvme_probe(),你这名函数才是真正加载设备的出理 函数。

        volatile struct nvme_completion *cqes;

       驱动中的队列创建,通过函数kcalloc_node如下,可不前要就看队列数量是和系统中所拥有的cpu数量有关。

}

        u16 sq_tail;

        .name           = "nvme",

        struct blk_mq_tag_set tagset;

        u16 q_depth;

struct nvme_queue {

 unmap:

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

                        quirks);                 

 free:

Improvements in the block layer

        kfree(dev->queues);

        struct nvme_command *sq_cmds;

        struct device *q_dmadev;

原先PCI bus上就多了另还有一个 pci_driver nvme_driver。当读到另还有一个 设备的class code是0101002h时,就会调用你这名nvme_driver形态体的probe函数, 也倘若说当设备和驱动匹配了完后 ,驱动的probe函数就会被调用,来实现驱动的加载。

        if (result)     

        .name                   = "pcie",

       nvme_probe函数会通过nvme_dev_map函数(层层调用完后 )映射设备的bar空间到内核的虚拟地址空间当中, pci协议里规定了pci设备的配置空间里有6个32位的bar寄存器,代表了pci设备上的一段内存空间,可不前要通过writel, readl类事函数直接读写寄存器。

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

Analysis of NVMe Driver Source Code in linux kernel 4.5

        result = nvme_init_ctrl(&dev->ctrl, &pdev->dev, &nvme_pci_ctrl_ops,

        /* shadow doorbell buffer support: */

        .release        = nvme_release,

        u16 cq_head;

nvme:

继续说nvme_probe函数,nvme_setup_prp_pools,主倘若创建dma pool,里面可不前要通过dma函数从dma pool中获得memory。主倘若为了给4k128k的不同IO来做优化。

static struct pci_driver nvme_driver = {

其中队列中有 Submission Queue,Completion Queue另还有一个 。

2.设置admin queue;

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

                goto put_pci;            

};

static const struct block_device_operations nvme_fops = {

        dma_addr_t sq_dma_addr;

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

        struct nvme_host_mem_buf_desc *host_mem_descs;

        /* host memory buffer support: */

        .module                 = THIS_MODULE,

        .flags                  = NVME_F_METADATA_SUPPORTED,

        u32 *dbbuf_sq_ei;

        bool subsystem;

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

        node = dev_to_node(&pdev->dev);

        result = nvme_dev_map(dev);

        .submit_async_event     = nvme_pci_submit_async_event,

        unsigned long bar_mapped_size;

        if (result)

                        sizeof(struct nvme_queue), GFP_KERNEL, node);

clean:

                set_dev_node(&pdev->dev, first_memory_node);

        dev_info(dev->ctrl.device, "pci function %s\n", dev_name(&pdev->dev));

struct nvme_dev {

NVM Express driver

        struct nvme_ctrl ctrl;

        struct nvme_dev *dev;

        if (node == NUMA_NO_NODE)

    $(MAKE) -C $(KERNROOT) M=`pwd`/drivers/block                                            

        .remove         = nvme_remove,

        INIT_WORK(&dev->ctrl.reset_work, nvme_reset_work);

        dev = kzalloc_node(sizeof(*dev), GFP_KERNEL, node);

        unsigned online_queues;

        struct dma_pool *prp_page_pool;

        .reg_write32            = nvme_pci_reg_write32,

        u32 cmbloc;

        make -C /usr/src/kernels/3.10.0-327.x86_64/ M=$(PWD) modules

关于Makefile可不前要参考如下:

static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {

        .reg_read64             = nvme_pci_reg_read64,

        struct nvme_dev *dev;    

        struct dma_pool *prp_small_pool;

        struct mutex shutdown_lock;

{

 put_pci:

        spinlock_t q_lock;

猜你喜欢

中行网银怎么在网上注册

毕业于青岛农业大学海都学院学士学位有三年网络工作经验首次登录要下载安全控件,扫描二维码下载二、另有本身是具有多种功能的网上银行前要在银行柜台开通才不用 使用,在银行开办网

2020-02-19

网银到底有用吗?在淘宝上买东西直接快捷支付就可以了,还要网银干嘛

网银安全换一换收起更多回答(3)展开完全展开完全为什会 可能没法 用,许多不要再支付宝的网购是需要网银的你对这个 回答的评价是?你对这个 回答的评价是?你对这个

2020-02-18

招商银行网上银行怎么开通

为你推荐:展开删剪招商银行成立于1987年,目前已发展成为了资本净额超过3500亿、资产总额超过4.十五万亿、全国设有超过150家网点、员工超过十五万人的全国性股份制商业银行,

2020-02-18

是不是银行卡没有开通网银就不能支付宝转账

以在手机上用支付宝钱包转账为例,具体转账步骤如下:3、输入收款人姓名、卡号、银行名称、转账金额、挑选付款辦法 ,这里挑选绑定支付宝的没开通网银的那张银行卡支付,怎么让点下一步

2020-02-18

阿里巴巴数据中心双11守夜人:把机器当“媳妇”,愿做亿万网友背后的男人

相比跟人的交流,布赫与机器“对话”的时间更久。虽说有了“智能设备大脑”随时监控情况汇报,但布赫还是老会 会用最古老的“望布赫看不都都可否 互联网盛宴肩头的亿万网民,甚至体

2020-02-18