0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

VFIO将DMA映射暴露给用户态?

Linux阅码场 来源:Linuxer 作者:Linuxer 2020-12-26 09:39 次阅读

在开始之前我们先要说一个东西就是 DMA,直接让设备访问内存,可以不通过 CPU 搬运数据。

48963e3a-4422-11eb-8b86-12bb97331649.jpg

这是一个比较简单的体系结构图,设备 和 CPU 通过存储控制器访问存储器。一个简单的 case 是 CPU 向存储器写数据,然后设备从存储器读数据。这么快来一切都很正常。但是实际上 CPU 是有一层缓存的,例如下面这样的。

48b96504-4422-11eb-8b86-12bb97331649.jpg

CPU 想内存写数据,但是先要清空到不一致的缓存,然后设备再去读数据,不然设备读到的数据和 CPU 实际的数据会不一致(因为缓存里的数据可能和存储器的不一致),而且实际上缓存也不只是一层,所以需要一个中间层来保证 从 CPU 的角度和从设备的角度内存都是一致的,所以就有了下面这个结构。

48db9c32-4422-11eb-8b86-12bb97331649.jpg

CPU 和 设备都会走缓存验证一遍以后,再落到存储器上,这样带上缓存以后大家的一致性都是一样的了。所以从设备的角度,设备也拥有了缓存,实际上这个和 IOMMU 关系不是很大,接下来设备其实也可以和 CPU 一样有一层 MMU,也就是地址到存储器物理地址的转换。注意,这里我用了地址,因为对 CPU 来说是虚拟地址,但是对设备来说是一个总线域的地址。这里要明确区分一下,一个是总线地址,是从设备的角度来看的,一个是 CPU 的虚拟地址,这是从 CPU 角度来看的,两个是不同的东西。将总线域地址转换成存储器物理地址的设备就叫 IOMMU。

495b4f04-4422-11eb-8b86-12bb97331649.jpg

如果没有 IOMMU,DMA 也能照常工作,IOMMU 的主要作用就是保护功能,防止使用 DMA 的设备访问任意存储器的物理地址。

IOMMU 在不同架构上名字不太一样,AMD 叫 AMD-Vi,最开始针对的设备只是显卡,Intel 叫 VT-d,arm 叫 SMMU,具体对应的手册也不太一样,但是主要解决的问题是一致的。在 VTd 中,dmar (DMA remapping) 就是那个 IOMMU 设备,通过中断的方式实现类似 page fault 一样的内存分配行为。DMA 传输是由 CPU 发起的:CPU 会告诉 DMA 控制器,帮忙将 xxx 地方的数据搬到 xxx 地方。CPU 发完指令之后,就当甩手掌柜了。IOMMU 有点像 MMU 是一个将设备地址翻译到内存地址的页表体系,也会有对应的页表,这个东西在虚拟化中也非常有用,可以将原本有软件模拟的设备,用直接的硬件替代,而原本的隔离通过 IOMMU 来完成。如下图所示,原本需要通过软件模拟的驱动设备可以通过 IOMMU 以安全的方式来直接把硬件设备分配个用户态的 Guest OS。

49842d16-4422-11eb-8b86-12bb97331649.jpg

理论上讲没有 IOMMU 实际上是可以工作的,但是硬件的角度,设备就拥有了整个存储器的全局视图,这是无论如何都非常不合理的事情,不应该让设备拥有访问任意物理内存的能力。

这里要提的另外一个功能就是对中断的隔离,类似于下面的通过在中断请求中添加标识来重定向中断到对应的中断回调上。

49faa900-4422-11eb-8b86-12bb97331649.jpg

VFIO 的作用就是通过 IOMMU 以安全的方式来将设备的访问直接暴露到用户空间,而不用专门完成某个驱动等待合并到上游或者使用之前的对 IOMMU 没有感知的 UIO 的框架。通过 VFIO 向用户态开放 IOMMU 的功能,编写用户态的驱动。

对于 IOMMU 来说,隔离的级别不一定是单个设备,比如一个后面有几个设备的 PCI 桥,从 PCI 桥角度来说,都是来自 PCI 桥的总线事务。所以 IOMMU 有一个iommu_group的概念,代表一组与其他设备隔离的设备的集合。

IOMMU 根据手册上讲还有一个域的概念,可以简单理解为一段物理地址的抽象。

在iommu_group的层级上,VFIO 封装了一层container class,这个的作用对应于希望能够在不同的iommu_group之间共享TLB和page tables,这个就是一个集合的概念,跟容器的那个概念没啥关系,一个集合总归要有个名字。通过把 host 的 device 和 driver 解绑,然后绑定到 VFIO 的 driver 上,就会有个/dev/vfio/$GROUP/出现,然后这个$GROUP代表的就是这个 device 的iommu_group号,如果要使用 VFIO 就要把这个 group 下的所有 device 都解绑才可以。

通过打开/dev/vfio/vfio就能创建一个 VFIO 的 container,然后再打开/dev/vfio/$GROUP用VFIO_GROUP_SET_CONTAINERioctl 把文件描述传进去,就把 group 加进去了,如果支持多个 group 共享页表等结构,还可以把相应的 group 也加进去。(再强调一遍这个页表是总线地址到存储器物理地址,IOMMU 管理的那个页表)。

下面举个官方的栗子,获取 PCI 设备 00000d.0 的group_id(PCI 命名的规则是domainslot.func)

$ readlink /sys/bus/pci/devices/00000d.0/iommu_group../../../../kernel/iommu_groups/26

使用之前需要你已经加载了 VFIO 模块

modprobevfio-pci

解绑 PCI 设备,然后创建一个 container id

$ lspci -n -s 00000d.006:0d.0 0401: 1102:0002 (rev 08)# echo 00000d.0 > /sys/bus/pci/devices/00000d.0/driver/unbind# echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id

然后寻找其他同属于一个 group 的设备

$ ls -l /sys/bus/pci/devices/0000:06:0d.0/iommu_group/devicestotal 0lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:00:1e.0 -> ../../../../devices/pci0000:00/0000:00:1e.0lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.0 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.1 ->../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1PCI 桥00001e.0后面挂了两个设备,一个是刚才加进去的00000d.0,还有一个是00000d.1,通过上面的步奏加进去就可以。

最后一步是让用户有权限使用这个 group。

#chownuser:user/dev/vfio/26

下面就是一个样例,从用户态使用 VFIO,整个的使用方式是通过ioctl来获取中断相关信息,以及注册中断处理函数,然后也是通过ioctl来获取region信息,然后调用相应的mmap函数,让 CPU 可以访问内存。

int container, group, device, i;struct vfio_group_status group_status ={ .argsz = sizeof(group_status) };struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; /* Create a new container */container = open("/dev/vfio/vfio", O_RDWR); if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION) /* Unknown API version */ if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) /* Doesn't support the IOMMU driver we want. */ /* Open the group */group = open("/dev/vfio/26", O_RDWR); /* Test the group is viable and available */ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) /* Group is not viable (ie, not all devices bound for vfio) */ /* Add the group to the container */ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); /* Enable the IOMMU model we want */ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); /* Get addition IOMMU info */ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info); /* Allocate some space and setup a DMA mapping */dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);dma_map.size = 1024 * 1024;dma_map.iova = 0; /* 1MB starting at 0x0 from device view */dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); /* Get a file descriptor for the device */device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "00000d.0"); /* Test and setup the device */ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); for (i = 0; i < device_info.num_regions; i++) { struct vfio_region_info reg = { .argsz = sizeof(reg) }; reg.index = i; ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®); /* Setup mappings... read/write offsets, mmaps * For PCI devices, config space is a region */} for (i = 0; i < device_info.num_irqs; i++) { struct vfio_irq_info irq = { .argsz = sizeof(irq) }; irq.index = i; ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, &irq); /* Setup IRQs... eventfds, VFIO_DEVICE_SET_IRQS */} /* Gratuitous device reset and go... */ioctl(device, VFIO_DEVICE_RESET);

在include/linux/vfio.h里面有完整的 API,这里就简单略过。

在理解了一些基本原理和使用方式之后再来看 VFIO 的代码应该叫就比较容易理解了。

首先是作为 PCI 设备的probe。主要是通过vfio_iommu_group_get分配iommu_group,然后调用vfio_add_group_dev初始化设备回调接口vfio_pci_ops,而remove就是反过来把对应的结构释放掉就可以。然后再看注册的回调函数结构体。

static const struct vfio_device_ops vfio_pci_ops = { .name = "vfio-pci", .open = vfio_pci_open, .release = vfio_pci_release, .ioctl = vfio_pci_ioctl, .read = vfio_pci_read, .write = vfio_pci_write, .mmap = vfio_pci_mmap, .request = vfio_pci_request,};

这里分析几个关键的函数,他们会通过file_operations vfio_fops被间接的调用。

首先是 mmap,就是在调用vfio_pci_mmap的时候最终调用remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, req_len, vma->vm_page_prot);来将物理内存映射到用户态空间,这就是上面的栗子中 mmap 系统调用的入口,而具体要映射的物理内存是通过一系列pci_resource_xxx宏从 PCI bar 空间读出来的配置。

然后是 ioctl 接口,这个接口比较丰富,也简单的看一下。比如VFIO_DEVICE_SET_IRQS会通过使用用户态传进来的结构体,调用vfio_pci_set_irqs_ioctl注册中断处理函数。而通过vfio_ioctl_set_iommu会设置 container 的 iommu_group 以及对应的 driver。read/write接口都是用于修改 PCI 配置信息的。

简单的来说,VFIO 的主要工作是把设备通过 IOMMU 映射的 DMA 物理内存地址映射到用户态中,让用户态程序可以自行操纵设备的传输,并且可以保证一定程度的安全,另外可以自行注册中断处理函数,从而在用户态实现设备的驱动程序,通过这样的框架,可以在 DPDK 中充分发挥用户态协议栈的威力。

原文标题:VFIO —将 DMA 映射暴露给用户态

文章出处:【微信公众号:Linuxer】欢迎添加关注!文章转载请注明出处。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

原文标题:VFIO —将 DMA 映射暴露给用户态

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    雅特力AT32F402/F405 DMA使用指南

    通道都支持外设的DMA请求映射到任意通道上。图1.DMA控制器架构DMAMUX简介对于如何外设的DMA请求
    的头像 发表于 11-20 01:03 85次阅读
    雅特力AT32F402/F405 <b class='flag-5'>DMA</b>使用指南

    如何设置内网IP的端口映射到公网

    在现代网络环境中,端口映射(Port Mapping)是一项非常实用的技术,它允许用户内网设备的服务端口映射到公网,使外网用户可以访问内网
    的头像 发表于 11-14 14:23 248次阅读

    DMA与C64x的框架组件结合使用

    电子发烧友网站提供《DMA与C64x的框架组件结合使用.pdf》资料免费下载
    发表于 10-16 10:29 0次下载
    <b class='flag-5'>将</b><b class='flag-5'>DMA</b>与C64x的框架组件结合使用

    揭秘车载VCU项目之外挂界的“大哥”DMA

    引脚配置此实例选择CAN0进行配置。三、外设配置对于DMA,其采用的固定映射,对于通道0至通道15,其映射一部分外设,通道16至通道31映射一部分外设,所以对于外设要使用
    的头像 发表于 07-30 08:11 516次阅读
    揭秘车载VCU项目之外挂界的“大哥”<b class='flag-5'>DMA</b>

    AN75664是否可以DMA_WM_THn用作DR_DATA状态下的转换方程?

    DMA_RDY_THn 事件。 但是,没有记录显示 DR_DATA 可以产生 DMA_WM_THn 事件。 因此,我的问题是,是否可以 DMA_WM_THn 用作 DR_DATA
    发表于 06-04 11:57

    路由器映射是什么意思?路由器端口映射怎么设置?

    路由器映射是指一台主机的内网(LAN)IP地址映射成一个公网(WAN)IP地址,当用户访问提供映射端口主机的某个端口时,服务器
    的头像 发表于 05-10 13:42 1193次阅读

    请问STM32H750VB的DMA通道映射在哪里查看?

    准备用STM32H750VB这款芯片,在参考手册中发现找不到DMA通道的映射表,想问一下这个去哪里查看?
    发表于 04-11 07:15

    Xilinx高性能PCIe DMA控制器IP,8个DMA通道

    Scather Gather DMA,提供FIFO/AXI4-Stream用户接口。 基于PCI Express Integrated Block,Multi-Channel PCIe RDMA
    的头像 发表于 02-22 11:11 1359次阅读
    Xilinx高性能PCIe <b class='flag-5'>DMA</b>控制器IP,8个<b class='flag-5'>DMA</b>通道

    雅特力AT32F423 DMA使用指南

    通道都支持外设的DMA请求映射到任意通道上。图1.DMA控制器架构DMAMUX简介对于如何外设的DMA请求
    的头像 发表于 02-22 08:13 634次阅读
    雅特力AT32F423 <b class='flag-5'>DMA</b>使用指南

    TSMaster 信号映射的配置方法

    TSMaster信号映射模块可以数据库变量映射为系统变量,经过映射后的系统变量就等同于数据库中的变量,该系统变量的读写操作就等同于读写数据库变量。其在系统软件中的位置如下图所示:信号
    的头像 发表于 02-19 14:00 489次阅读
    TSMaster 信号<b class='flag-5'>映射</b>的配置方法

    拆解mmap内存映射的本质!

    mmap 内存映射里所谓的内存其实指的是虚拟内存,在调用 mmap 进行匿名映射的时候(比如进行堆内存的分配),是进程虚拟内存空间中的某一段虚拟内存区域与物理内存中的匿名内存页进行映射
    的头像 发表于 01-24 14:30 1475次阅读
    拆解mmap内存<b class='flag-5'>映射</b>的本质!

    如何共享代码映射到DSPR5以及将其映射到其他RAM区域?

    Scratch-Pad Sram(CPU5 DSPR)相关联。 在链接器脚本中,我已经映射了每个.text 部分移至其各自的 PSPR 内核。 我想知道如何共享代码映射到 DSPR5 以及如何将其
    发表于 01-24 07:26

    dma和串口直接发送的区别

    DMA(Direct Memory Access)和串口直接发送是两种不同的数据传输方式,它们在实现上有着明显的区别和优劣势。本文详细介绍DMA和串口直接发送的原理、优缺点以及适用场景。 首先
    的头像 发表于 01-07 17:43 2700次阅读

    电机的冷、热是怎样定义的?

    电机的冷、热是怎样定义的?两者如何判断?满负载时是热否则就是冷是这样吗?
    发表于 12-13 08:16

    STM32 DMA传输的问题分析

    用户使用STM32G473RET6芯片,开发环境STM32CubeMX+Keil(LL库)。使用DMA1通道1,在半传输中断和完全传输中断里,拷贝ADC采集的数据。在应用过程中发现DMA半传输中断和完全传输中断不能独立使用。
    的头像 发表于 12-01 09:19 2705次阅读
    STM32 <b class='flag-5'>DMA</b>传输的问题分析