这篇文章主要从virtio和ADMA的角度讲述一下iommu中的一些逻辑。
做虚拟化或者网络的人对virtio,iommu或者DMA这些概念都不陌生,但是其中的关联却又有很多人不是很明白,比如在裸金属或物理机上支持虚拟机或安全容器需要开启iommu,那虚拟机前端不支持VIRTIO_F_ACCESS_PLATFORM是否有影响呢?这里就是把iommu和viommu搞混了。又比如物理机开启或关闭iommu对应virtio设备的处理逻辑有什么影响?这篇文章主要就是把这些问题讨论清楚。
问题1:如果使用设备直通方式在物理机上启动虚拟机,为什么需要物理机开启iommu?
这个问题比较简单,物理机开启iommu主要是为了避免直通给虚拟机A的外设DMA到虚拟机B的内存,所以不直接使用hpa,而使用iova(gpa)进行DMA,这样每个虚拟机用自己的iova,iommu确保其转换后之会访问自己对应的内存。
问题2:iommu是否开启对前端驱动的处理逻辑有什么影响?
我们知道开启iommu后,设备发起DMA操作会经过如下图流程,根据TLP中的bdf找到应对的context entry进行地址转换。那么对于前端的驱动软件处理行为有什么差异呢?
(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
其实,iommu对于驱动软硬的影响主要是在进行DMA操作或者说进行DMA地址映射时使用的地址差异(直接使用物理地址还是iova)。 我们以ixgbe的发送函数ixgbe_tx_map为例:
-
static void ixgbe_tx_map(struct ixgbe_ring *tx_ring,
-
struct ixgbe_tx_buffer *first,
-
const u8 hdr_len)
-
{
-
//...
-
dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
-
for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
-
tx_desc->read.buffer_addr = cpu_to_le64(dma);
-
while (unlikely(size > IXGBE_MAX_DATA_PER_TXD)) {
-
i++;
-
tx_desc++;
-
tx_desc->read.buffer_addr = cpu_to_le64(dma);
-
}
-
//...
-
}
核心逻辑就是先把skb的地址做一下dma map,然后让硬件可以直接dma这段数据,其具体后续调用过程如下图:
之前iommu文章中介绍过在intel环境iommu初始化会把pci bus的iommu ops设置成intel_dma_ops,所以map_page函数{BANNED}最佳终会调用到intel_map_page。
l __intel_map_single
-
static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
-
size_t size, int dir, u64 dma_mask)
-
{
-
struct dmar_domain *domain;
-
phys_addr_t start_paddr;
-
unsigned long iova_pfn;
-
int prot = 0;
-
int ret;
-
struct intel_iommu *iommu;
-
unsigned long paddr_pfn = paddr >> PAGE_SHIFT;
-
BUG_ON(dir == DMA_NONE);
-
if (iommu_no_mapping(dev))
-
return paddr;
-
domain = get_valid_domain_for_dev(dev);
-
if (!domain)
-
return 0;
-
iommu = domain_get_iommu(domain);
-
size = aligned_nrpages(paddr, si