阿呆实战NVMe之八

原创内容,转载请注明:  [http://www.ssdfans.com]  谢谢!

 

提要

 

本系列文章,旨在带你开发一个NVMe SSD控制器的前端协议逻辑,只不过是在QEMU虚拟机环境中。

 

澄清关于PCIe BAR空间的误解

 

前面我们说过了NVMe的BAR空间是个什么东东,结果在群里就被山哥指出来理解错误,BAR寄存器里面的地址并不是CPU的存储器域空间,而是PCI域,只是因为X86架构这两个用一样的地址,所以给人一种误解。到了PowerPC处理器,就是不一样的地址,需要Host PCI桥或者PCIe的RC做地址转换。

 

 

上图所示的处理器系统由一个CPU,一个DRAM控制器和两个HOST主桥组成。在这个处理器系统中,包含CPU域、DRAM域、存储器域和PCI总线域地址空间。其中HOST主桥x和HOST主桥y分别管理PCI总线x域与PCI总线y域。PCI设备访问存储器域时,也需要通过HOST主桥,并由HOST主桥进行PCI总线域到存储器域的地址转换;CPU访问PCI设备时,同样需要通过HOST主桥进行存储器域到PCI总线域的地址转换。

 

Memory Address和IO Address

 

再来回顾一下之前的两个概念,PCIe设备可以申请两类地址空间,memory space和I/O space,它们用BAR的最后一位区别开来。

 

说到地址空间,计算机系统中,除了我们常说的memory address(包括逻辑地址、虚拟地址(线性地址)、CPU地址(物理地址)),还有I/O address,这是为了访问I/O设备(主要是设备中的寄存器)而设立的,大部分体系结构中,memory address和I/O address都是分别编址的,且使用不同的寻址指令,构成了两套地址空间,也有少数体系结构将memory address和I/O address统一编址(如ARM)。

 

有两套地址空间并不意味着计算机系统中需要两套地址总线,实际上,memory address和I/O address是共用一套地址总线,但通过控制总线上的信号区别当前地址总线上的地址是memory address还是I/O address。

 

QEMU中NVMe BAR空间初始化

 

我们知道QEMU虚拟机其实本没有物理内存,他的内存是QEMU从宿主机的内存中虚拟出来的。我们虚拟的设备要获得内存地址或者IO地址,就需要向QEMU注册申请。我们这里NVMe是需要IO地址,所以首先通过cpu_register_io_memory注册,获得io_mem_write/io_mem_read的索引。该索引还有需要的IO空间大小等参数传给cpu_register_physical_memory函数,获得QEMU虚拟机的IO地址空间。

 

对于PCI设备来说,IO地址注册就要多一步,因为要进行PCI bar地址与IO的映射,所以必须先调用下面函数来给bar注册PCI地址。关键参数说明:第一个是PCI设备指针,第三个是我们需要注册IO地址的空间长度,最后一个是我们要进行IO操作映射的初始化函数指针。

1     /* Register BAR 0 (and bar 1 as it is 64bit). */
2     pci_register_bar((struct PCIDevice *)&n->dev,
3         0, ((n->dev.cap_present & QEMU_PCI_CAP_MSIX) ?
4         n->dev.msix_bar_size : n->bar0_size),
5         (PCI_BASE_ADDRESS_SPACE_MEMORY ,
6         PCI_BASE_ADDRESS_MEM_TYPE_64),
7         nvme_mmio_map);

IO初始化函数如下,关键参数说明:第一个依然是PCI设备指针,第三个是PCI地址映射的PIO起始地址,这个起始地址是在上面的函数注册PCI地址的时候,PCI总线通过计算比较PIO地址空间得到的一个PIO地址起始空间。所以在我们注册设备PIO空间的时候必须将这个地址作为注册IO空间的起始地址。这个函数是在更新bar映射的时候被调用的,里面调用了前面说的cpu_register_physical_memory,其实就是QEMU从宿主机申请一段内存用来映射到虚拟机。

1 static void nvme_mmio_map(PCIDevice *pci_dev, int reg_num, pcibus_t addr,
2                             pcibus_t size, int type)

在阿呆看的版本中,MSIX中断向量数为32个,BAR 0空间大小为8KB,NVMe队列为64个(包括Admin队列)。

 

NVMe设备参数初始化

 

关键的工作搞定,后面的工作就简单了。首先是类似于PCI设备,从NVME_device_NVME_config文件读取NVMe相关寄存器的参数给对应变量赋值。例如:

 1 <REG>
 2     CFG_NAME = CNTRLREG
 3     NAME = "AQA"
 4     OFFSET = 0x24
 5     LENGTH = 0x04
 6     VALUE = 0x00000000
 7     RO_MASK = 0xF000F000
 8     RW_MASK = 0x0FFF0FFF
 9     RWC_MASK = 0x00000000
10     RWS_MASK = 0x00000000
11     DESC = "Admin Queue Attributes"
12 </REG>

接下来的内容比较杂,主要是:

  • 是各种配置参数初始化,比如温度的阈值,队列数。
  • 给每个namespace的Identify页赋值。
  • 一些Log页的赋值。
  • SQ处理和Async事件处理两个Timer的初始化。

 

Timer初始化之后,其实NVMe设备就开始工作了,能响应主机的需求。

 

SSD模拟器初始化

 

SSD其实就是个存储空间,所以QEMU用了文件来模拟。读写SSD相当于读写宿主机上的一些文件。但是读写文件不太方便,所以这里使用了Linux的mmap函数,这个函数把文件映射到一个地址,后来写这个文件就跟写内存一样方便,可以按照偏移地址去写。

 

引用

 

https://github.com/nvmeqemu

sailing, 《浅谈PCIe体系结构》, http://blog.sina.com.cn/s/blog_6472c4cc0100qfau.html

http://blog.csdn.net/yearn520/article/details/6560851

http://people.cs.nctu.edu.tw/~chenwj/dokuwiki/doku.php?id=qemu

分类目录 SSD, 技术文章.
扫一扫二维码或者微信搜索公众号ssdfans关注(添加朋友->点最下面的公众号->搜索ssdfans),可以经常看到SSD技术和产业的文章(SSD Fans只推送干货)。
ssdfans微信群介绍
技术讨论群 覆盖2000多位中国和世界华人圈SSD以及存储技术精英
固件、软件、测试群 固件、软件和测试技术讨论
异构计算群 讨论人工智能和GPU、FPGA、CPU异构计算
ASIC-FPGA群 芯片和FPGA硬件技术讨论群
闪存器件群 NAND、3D XPoint等固态存储介质技术讨论
企业级 企业级SSD、企业级存储
销售群 全国SSD供应商都在这里,砍砍价,会比某东便宜20%
工作求职群 存储行业换工作,发招聘,要关注各大公司招聘信息,赶快来
高管群 各大SSD相关存储公司高管和创始人、投资人

想加入这些群,请微信扫描下面二维码,或搜索nanoarchplus,加阿呆为微信好友,介绍你的昵称-单位-职务,注明群名,拉你进群。SSD业界需要什么帮助,也可以找阿呆聊。