Linux中nvme驱动详解

  • 时间:
  • 浏览:0
  • 来源:uu快3大小_uu快3网站_开奖历史

        nvme_dev_unmap(dev);

NVMe SPEC http://nvmexpress.org/

default:

……

        pci_bus_addr_t cmb_bus_addr;

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

                goto unmap;              

obj-$(CONFIG_BLK_DEV_NVME)      += nvme.o

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

        struct nvme_dev *dev;

        u8 cq_phase;

        .ioctl          = nvme_ioctl,

        .name                   = "pcie",

NVMe数据传输前会通过NVMe Command,而NVMe Command则存插进NVMe Queue中,其配置如下图。

                return -ENOMEM;          

        if (!dev)       

        unsigned max_qid;

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

        u32 *dbbuf_eis;

        init_completion(&dev->ioq_wait);

        result = nvme_setup_prp_pools(dev);

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

        u16 qid;

        if (node == NUMA_NO_NODE)

{

        .getgeo         = nvme_getgeo,

        .free_ctrl              = nvme_pci_free_ctrl,

                .pm     = &nvme_dev_pm_ops,

        put_device(dev->dev);

};

另5个多PCI bus上就多了5个多pci_driver nvme_driver。当读到5个多设备的class code是010302h时,就会调用你类似nvme_driver底部形态体的probe函数, 也只要说当设备和驱动匹配了另5个多,驱动的probe函数就会被调用,来实现驱动的加载。

        dma_addr_t host_mem_descs_dma;

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

                goto put_pci;            

                        sizeof(struct nvme_queue), GFP_KERNEL, node);

        .probe          = nvme_probe,

                set_dev_node(&pdev->dev, first_memory_node);

NVM Express driver

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

        return 0;

 free:

                        quirks);                 

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

        .revalidate_disk= nvme_revalidate_disk,

当Host的driver需用跟PCIe设备传输数据的另5个多,只需用告诉PCIe设备存放数据的地址就可不还都都能不能 。

        s16 cq_vector;



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

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

        u32 nr_host_mem_descs;

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

        .err_handler    = &nvme_err_handler,

        /* shadow doorbell buffer support: */

        int q_depth;

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

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

        pci_set_drvdata(pdev, dev);

        unsigned long quirks = id->driver_data;

        dma_addr_t sq_dma_addr;

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

{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },

        rm rf *.o *.ko

};

        if (result)     

        u32 *dbbuf_dbs;

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

        .module                 = THIS_MODULE,

};

        unsigned online_queues;

        /* host memory buffer support: */

        .flags                  = NVME_F_METADATA_SUPPORTED,

所以有,需用在驱动中指定class code为010302h,将010302h插进pci_driver nvme_driver的id_table。另5个多当nvme_driver注册到PCI Bus后,PCI Bus就知道你类似驱动是给class code=010302h的设备使用的。nvme_driver包含5个多probe函数,nvme_probe(),你类似函数才是真正加载设备的出理 函数。

2.设置admin queue;

        struct nvme_command *sq_cmds;

nvme:

        u32 *dbbuf_sq_db;

3.加进去去nvme namespace设备;

};

        spinlock_t q_lock;

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

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

        .reg_write32            = nvme_pci_reg_write32,

        u16 sq_tail;

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

        u32 cmbloc;

        u16 cq_head;

        .submit_async_event     = nvme_pci_submit_async_event,

static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {

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

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

        u32 *dbbuf_cq_db;

        .reg_read64             = nvme_pci_reg_read64,

        if (result)     

        void __iomem *cmb;

继续说nvme_probe函数,nvme_setup_prp_pools,主只要创建dma pool,底下可不还都都能不能 通过dma函数从dma pool中获得memory。主只要为了给4k128k的不同IO来做优化。

关于Makefile可不还都都能不能 参考如下:

        .release        = nvme_release,

        .open           = nvme_open,

        struct completion ioq_wait;

.我 知道首先是驱动需用注册到PCI总线。没人 nvme_driver是咋样注册的呢?

        .sriov_configure = nvme_pci_sriov_configure,

主要就5个多文件:nvme-core.cnvme-scsi.c

        INIT_WORK(&dev->remove_work, nvme_remove_dead_ctrl_work);

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

};

        u32 cmbsz;

        struct nvme_command __iomem *sq_cmds_io;

        nvme_reset_ctrl(&dev->ctrl);

        },              

       每个设备大慨 5个多队列,5个多是admin管理命令,5个多是给I/O命令,你类似队列概念和另5个多介绍块驱动中的磁盘队列5个多道理,只要那个驱动比较基础,所以有命令和IO并非区分队列,具体底部形态体如下。

        struct dma_pool *prp_page_pool;

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

       不过,最新的代码指在drivers/nvme/host,主只要core.cpci.c

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

        volatile struct nvme_completion *cqes;

        u16 q_depth;



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

        quirks |= check_vendor_combination_bug(pdev);

…….

时候直接make 即可生成nvme.ko文件。

        unsigned long bar_mapped_size;

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

        if (result)

        struct dma_pool *prp_small_pool;

        dma_addr_t dbbuf_dbs_dma_addr;

        .owner          = THIS_MODULE,

        struct mutex shutdown_lock;

        .remove         = nvme_remove,

        struct nvme_queue *queues;

        .reg_read32             = nvme_pci_reg_read32,

        u32 *dbbuf_cq_ei;

KERNELVER ?= $(shell uname -r)

struct nvme_dev {

PWD := $(shell pwd)

        .id_table       = nvme_id_table,

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

        .driver         = {      

        void __iomem *bar;

        if (!dev->queues)

        u32 *dbbuf_sq_ei;

        u64 host_mem_size;

       详见《NVM_Express_Revision》

其中队列包含Submission Queue,Completion Queue5个多。

        .name           = "nvme",

        struct blk_mq_tag_set tagset;

        int node, result = -ENOMEM;

        return result;

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

        struct device *dev;

                goto release_pools;      

release_pools:

static const struct pci_device_id nvme_id_table[] = {

nvme_init_ctrl函数会创建NVMe控制器底部形态体,另5个多在时候续probe阶段另5个多用初始化过的底部形态,其传入的操作函数集是nvme_pci_ctrl_ops

                goto free;               

        struct blk_mq_tags **tags;

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

        u8 cqe_seen;

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

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

Analysis of NVMe Driver Source Code in linux kernel 4.5

Queue有的概念,那只要队列厚度,表示其才能放哪多少个成员。在NVMe中,你类似队列厚度是由NVMe SSD决定的,存储在NVMe设备的BAR空间里。

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

不过需用注意的是,就算有所以有CPU发送请求,时候块层并非能保证都能出理 完,将来然前会绕过IO栈的块层,不然瓶颈只要操作系统三种了。

static struct pci_driver nvme_driver = {

        void **host_mem_desc_bufs;

        result = nvme_dev_map(dev);

#define PCI_CLASS_STORAGE_EXPRESS       0x010302

        node = dev_to_node(&pdev->dev);

        u32 __iomem *q_db;

        mutex_init(&dev->shutdown_lock);

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

static const struct block_device_operations nvme_fops = {

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

struct nvme_queue {

}

};

                        sizeof(struct nvme_queue), GFP_KERNEL, node);

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

        struct nvme_dev *dev;    

        struct work_struct remove_work;

        u64 cmb_size;

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

        .pr_ops         = &nvme_pr_ops,

PCIe有个寄存器位Bus Master Enable,你类似bit置1后,PCIe设备就可不还都都能不能 向Host发送DMA Read Memory和DMA Write Memory请求。

Linux Driver Information

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

        struct device *q_dmadev;

        struct nvme_host_mem_buf_desc *host_mem_descs;

        kfree(dev);

        .compat_ioctl   = nvme_ioctl,

        dma_addr_t dbbuf_eis_dma_addr;

        u32 db_stride;

        dma_addr_t cq_dma_addr;

 unmap:

clean:

        nvme_release_prp_pools(dev);

        u32 __iomem *dbs;

        bool subsystem;

        struct nvme_ctrl ctrl;

        .shutdown       = nvme_shutdown,

Improvements in the block layer

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

clean:

        struct blk_mq_tag_set admin_tagset;

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

        kfree(dev->queues);

 put_pci: