目前在日本从事SSD的开发,
日本太拘泥于硬件,导致软件开发的效率和品质比较低下。
(写完代码之后Debug和Test全凭DebugLog和MemoryDump。。。)
于是就有了一个想法,
是不是能写一个一个便于SSD开发的模拟环境呢?
同时自己也想试试全面的学习整个存储的stack(也太贪心了。。)
层次 | 构成 | 备考 |
---|---|---|
Application/Test | FIO | linux下主流的IQ测试软件,开源 |
Kernel | Linux | 。。。 |
Driver | 标准的 linux NVMe Driver | 。。。 |
虚拟机 | Qemu | 可以参考LightNvm的Qemu |
虚拟设备 | Qemu | 可以参考VSSIM,以及LightNvm的Qemu |
FLT | 也许把现在的工作中写的简化一下拿过来也ok | 。。。 |
虚拟NAND | SLC,MLC,TLC,3D | EagleTree可以参考 |
由于懂得不多,就一点一点学习研究,一点一点构建吧
首先从搭建一个可以用GDB来调试 NVMe Driver 以及 虚拟的NVMe 设备的环境开始。
目前做到的是:
1. 成功的编译支持nvme的qemu
2. 成功的编译linux(带nvme driver,带debug info)
3. qemu上面成功运行linux,成功的挂载了虚拟的nvme设备。
4. 成功的用gdb来debug NVMe Driver
5. 成功的用gdb来debug NVMe 虚拟设备
接下来:
6. 分析NVMe Driver的代码
7. 分析NVMe 虚拟设备的代码
https://github.com/OpenChannelSSD/qemu-nvme.git
TBD(用的是lightNvm的qemu环境)
http://www.linux-magazine.com/Online/Features/Qemu-and-the-Kernel
本来是尝试在64位的linux上面进行调试的,但是试了一下发现gdb还不能支持qemu上的64位的linux的调试(从start_kernel开始的调试)
换成了32bit的linux之后问题就解决了
make qemu_x86_64_defconfig
使用时遇上了 “gdb replay g packet is too long”的问题,查了一下面的内容,发现好像是gdb的问题,还没得到解决。
http://stackoverflow.com/questions/8662468/remote-g-packet-reply-is-too-long
https://lists.nongnu.org/archive/html/qemu-discuss/2014-10/msg00069.html
http://stackoverflow.com/questions/8662468/remote-g-packet-reply-is-too-long
http://augustl.com/blog/2014/gdb_progress/
https://sourceware.org/bugzilla/show_bug.cgi?id=13984
详细的分析可以参考:
http://blog.csdn.net/panzhenjie/article/details/51581063
这里只是列出基本的Function Tree及动作Sequence
/proc/devices : 代表某种设备对应的主设备号,而不是设备的实例
/proc/iomem:IO空间映射
/dev: 代表某种设备的实例
lspci -k
nvme_init
├nvme_workq = alloc_workqueue : 创建一个全局的workqueue
├nvme_core_init
│├register_blkdev : 注册一个名字叫nvme的块设备
│├__register_chrdev : 注册一个名字叫nvme的字符设备
│└class_create(THIS_MODULE, “nvme”);
└pci_register_driver :
注册了一个pci驱动(属性和callback表)
- 包括:name:“nvme”,
- id_table
- nvme_probe函数
- nvme_remove函数
- nvme_remove函数
- pm ops:(nvme_suspend,nvme_resume)
- error handler:(nvme_error_detect,nvme_slot_reset,nvme_error_resume,nvme_reset_notify)
nvme_exit
├pci_unregister_driver
├nvme_core_exit
└destory_workqueue
nvme_probe
├dev_to_node
├kzalloc_node(sizeof( * dev))
├kzalloc_node(num_possible_cpus() * sizeof( * dev->entry)):msi相关信息(cpu core个)
├kzalloc_node((num_possible_cpus() + 1) * sizeof(void * )):admin queue(1个)+ io queue(cpu core个)的相关信息
├get_device
├pci_set_drvdata
├nvme_dev_map
│├bars = pci_select_bars:pci设备的配置空间里有6个32位的bar寄存器,每一位代表一个bar(base address register)是否被置位
│├pci_request_selected_regions(pdev, bars, “nvme”):把对应的这个几个bar保留起来,不让别人使用。 (结果可以在/proc/iomem中确认)
│└dev->bar = ioremap:把pci bar的物理地址映射到虚拟地址
├INIT_WORK(&dev->scan_work, nvme_dev_scan);
├INIT_WORK(&dev->reset_work, nvme_reset_work);
├INIT_WORK(&dev->remove_work, nvme_remove_dead_ctrl_work);
├init_completion(&dev->ioq_wait);
├nvme_setup_prp_pools:创建dma pool,后面就可以通过其他dma函数从dma pool中获得memory
│├dev->prp_page_pool = dma_pool_create(“prp list page”, dev->dev,PAGE_SIZE, PAGE_SIZE, 0);
│└dev->prp_small_pool = dma_pool_create(“prp list 256”, dev->dev,256, 256, 0);
├nvme_init_ctrl
│├nvme_set_instance
│├device_create_with_group
│├get_device
│├dev_set_drvdata
│├ida_init
│├spin_lock
│├
│├
└queue_work(nvme_workq):调度nvme_reset_work
目前在看下面的文章补补PCIe相关知识:
http://blog.sina.com.cn/s/articlelist_1685243084_3_1.html
http://free-electrons.com/doc/pci-drivers.pdf