您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件开发专栏 > 系统/运维 > 正文

Linux iommu和vfio概念空间解构

发表于:2017-08-08 作者:in nek 来源:

  最近和一些硬件和相关驱动设计的同学讨论SMMU的设计需求,双方讨论的空间不太一致,我写一个文档澄清一下这些概念。
  (不熟悉SMMU的同学可以跳过这一段,后面我会单独解释概念的)我们首先得厘清两个概念,当我们说SMMU的时候,硬件设计同学心中是那个进行设备地址翻译,做DMA操作的那个硬件。软件驱动同学心中是那个控制SMMU硬件的那个软件。你们之间唯一的接口是:中断,配置空间和内存,没有其他了。所以,当我们讨论这个问题的时候,一定首先分清楚对方在说的是哪个概念空间的概念,因为两者是不一样的。硬件的同学说“设备发出的VA”,包括Substream ID和软件指配给它VA地址,这个地址远远比64位长。但软件指配地址的时候,可不认为自己发出了Substream ID的。实际上,Substream ID很多时候是ASID(或者说PASID,后者是PCIE的概念),但对软件来说,ASID有特定的含义,把ASID作为Substream ID,那是硬件自己的决定,不是全系统的共识。
  本文要讨论的是软件的概念空间,所以,我们先抽象一下硬件的模型:
  SMMU的作用是把CPU提交给设备的VA地址(软件概念的VA),直接作为设备发出的地址,变成正确的物理地址,访问到物理内存上。
  MMU通过页表完成这个翻译,SMMU也一样,但SMMU的页表比MMU复杂得多,这受两个要素限制:
  第一,一个SMMU控制器可能有多个设备连着,两个设备互相之间可能不会复用同一个页表,需要区分。SMMU用stream id来做这个区分
  第二,一个设备可能被多个进程使用,两个进程有两个页表,这个设备需要进行区分。SMMU通过substream id来进行区分
  这样,设备发出一个地址请求,它的地址就包括stream id, substream id和VA三个部分。前两者定位一个类似MMU的页表体系,后者的执行一个类似MMU的翻译过程。
  要SMMU硬件正确动作,就需要CPU(就是软件了)给它提供正确的信息,用我以前提过的DFD方法就可以把对应数据模型的充要性建立出来:

  至于很多人想引入到这个模型的“虚拟化”的概念,我认为你急了,虚拟化的概念在MMU的页表翻译机制中是自恰的,根本没有必要引入到这里来。无论你用ARM的VA->IPA->PA的两级翻译,还是Intel的GVA->GPA->HVA->HPA的三级翻译,都和这层建模无关。
  好了,现在轮到软件。软件的概念空间就比这个概念复杂得多了,这是因为软件要扭合多个硬件平台的意见,有人DMA是64位的,但还有人是32位的啊。而且,软件是有版本概念的,每个版本都和原来有偏离(我这里用主线Linux内核4.11来作为分析对象),而且都有多个硬件厂商的角力,我们不但要看到现在如何,还要看到未来的走向。
  我们先抛开VFIO,先建立iommu的概念空间:
  在Linux的概念空间中,硬件的设备,用device来代表。设备用总线类型bus_type来区分。比如众所皆知的pci_device,或者没爹没娘的platform_device。smmu控制器,本身是一种device,对于ARM来说,它现在是一种平台设备。不同总线有什么设备,可以从/sys/bus//devices/目录中全部列出来。
  bus_type说明的是发现设备的方法,不表明设备的功能,设备的功能用class来表达,/sys/class列出了从功能角度对设备的表达。从这个角度来收,smmu是一种iommu class的设备(之所以不叫smmu而叫iommu,因为intel先上的主线,所以用intel的名字,现在开源,主要是抢名称空间,因为名称空间意味着整个行业为谁服务)
  所以我们现在有了device和iommu两个概念了。下一步是device和iommu的关联,从硬件的逻辑空间,应该是每个device有一个指向iommu的指针,但如果软件也这样做,就掩盖了一个事实:如果两个设备共享同一个streamid,那么修改其中一个设备的页表体系,也就相当于修改了另一个设备的页表体系。所以,修改页表的最小单位不是设备,而是stream id。为此,Linux的模型是增加一个iommu_group的概念,iommu_group代表共享同一个streamid的一组device(表述在/sys/kernel/iommu_group中)。
  所以,整个概念空间就是:
  SMMU控制器自身的发现:总线扫描发现设备(device)(ARM是平台设备),设备匹配SMMU驱动(ARM现在是drivers/iommu/arm-smmu*.c),SMMU驱动probe的时候给自己支持的总线设置bus->iommu_ops,让总线具有了iommu attach的能力。
  设备的SMMU关联:总线扫描发现了一般的设备,总线的发现流程负责调用iommu_ops给这个设备加上iommu_group,然后让iommu_group指向对应的iommu控制器。
  这个过程实际要求SMMU驱动认识设备的SMMU Topo,很明显这个Topo是硬件设计的结果,要不是硬件提供自动发现机制(例如PCIE),要不就只能在固件中描述(比如DTS或者ACPI),这个固件,称为iommu_fwspec,属于device,在device发现的时候就可以生成。
  这样整个框架的模型就是这样的:

  IOMMU的整个框架,首先提供的是针对设备的DMA能力,也就是说,当我们发起dma_map的时候,设备定位的streamid,进程定位了substreamid(这个概念是iommu_device通过iommu_domain种入),VA送下来的时候,从设备定位group,group匹配的iommu_device,device决定了iommu_domain,驱动就可以综合全部的信息写到SMMU硬件中了。
  然后我们看VFIO。
  VFIO的目的(从SMMU的角度来说)是把设备的DMA能力直接暴露到用户态。todo: 太晚了,明天接着来。
  todo:还要做一次全局的概念对照和整理。