[Linux] VFIO

参考文章:VFIO Introductionkernel-doc/vfio.rstintel vt-dkernel-doc/intel-iommu.txt笔记intel vt-d

VFIO - "Virtual Function I/O"

许多硬件平台提供DMA和中断重映射功能,以确保I/O设备只在分配给它们的域内访问内存,一般这样的硬件单元称为IOMMU。VFIO驱动程序是一个与IOMMU具体硬件无关的框架,可以支持x86架构的VT-D、AMD-Vi,POWER PE,ARM等各种IOMMU硬件架构。使用VFIO可以实现安全的,非特权的,用户空间驱动程序。相较于VFIO,用UIO框架实现的用户空间驱动,将不受IOMMU的保护,且中断支持有限,并且需要root权限运行。

Groups, Devices, and IOMMUs

VFIO层有Group,Device及IOMMU的概念,以下分别对他们进行介绍。Device是所有IO驱动的主体。通常对它创建IO访问、中断和DMA的编程接口。如果对设备的DMA可以访问的内存空间不加限制;这将非常危险。IOMMU硬件可以将设备的可访问物理内存空间进行彼此的隔离,当在IOMMU硬件中创建好IO虚拟地址到物理地址的映射后,设备可使用IO虚拟地址访问物理内存。

有时候,相关的一组设备可能会使用同一块内存空间。因此IOMMU给内存创建隔离区的最小粒度不是Device,而是group。因此,group也是VFIO使用的最小粒度。虽然group是确保用户访问所必须使用的最小粒度,但它不一定是首选粒度。在使用页表的IOMMU中,可以在不同group之间共享一组页表,从而减少平台(减少TLB抖动、减少重复页表)和用户(仅编程一组地址转换)的开销。因此,VFIO使用container 类,该类可以包含一个或多个group。只需打开/dev/vfio/vfio字符设备即可创建container。

容器本身提供的功能很少,除了一对版本和扩展查询接口外,其他所有接口都被锁定。用户需要在容器中添加一个group以获得下一级功能,即IOMMU,不同架构的IOMMU可能提供的接口不同,因此container并未提供统一接口访问IOMMU。为此,用户首先需要标识(这个标识即后文的/dev/vfio/{group})与所需设备关联的group。这可以使用下面示例中描述的sysfs链接来查找。通过绑定设备到VFIO驱动程序,为该组添加一个新的VFIO组/dev/VFIO/{group}字符设备文件接口,其中{group}是设备所属的IOMMU组号。

一旦组准备好,就可以通过打开VFIO组字符设备(/dev/VFIO/$group)并使用VFIO_GROUP_SET_CONTAINER ioctl,传递先前打开的容器文件的文件描述符,将其添加到容器中。如果需要,并且IOMMU驱动程序支持在组之间共享IOMMU上下文,则可以将多个组设置为同一容器。如果组不能设置为具有现有组的容器,则需要使用新的空容器。将一个组(或多个组)附加到容器后,剩余的ioctl将变为可用,从而可以访问VFIO IOMMU接口。此外,现在可以使用VFIO组文件描述符上的ioctl为组中的每个设备获取文件描述符。
VFIO操作device的API包括用于设备的描述(PCI配置空间)、IO区域(BAR空间)及其在设备描述符上的读/写/mmap偏移量的ioctl,以及用于描述和注册中断通知的机制。

VFIO使用示例

以Intel平台下PCI 设备号0000:06:0.0举例。
某个设备所属的iommu组号由内核指定(?不会改变?)
使用如下命令获取设备的组id(该设备为26)

$ readlink /sys/bus/pci/devices/0000:06:0d.0/iommu_group
../../../../kernel/iommu_groups/26

对于PCI设备,需要加载内核模块vfio-pci,并将设备绑定到该驱动上,方法如下

$ modprobe vfio-pci
$ lspci -n -s 0000:06:0d.0 (查看设备的PCI vendor id和device id号)
06:0d.0 0401: 1102:0002 (rev 08)
$ echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
$ echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id

注意: iommu_group 26下的所有设备必须都绑定到vfio或者部分设备不绑定任何驱动。

最后修改字符设备/dev/vfio/26的owner。并确保/dev/vfio/vfio的权限是0666;保证非root用户有读写权限。之前的步骤都需要在root下执行,在此之后可以在非root用户下执行了

$ chown user:user /dev/vfio/26

在此之后,user用户就可以访问属于iommu group 26的所有设备了。

通常,用户态程序使用如下步骤:

  1. 创建新的container,并检查版本(保证兼容性),以及支持的iommu驱动类型
int 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. */
  1. 打开iommu group 26并添加到刚才创建的container中
int group;
struct vfio_group_status group_status =
                                { .argsz = sizeof(group_status) };

/* 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);
  1. 因为不同硬件的iommu实现不同,因此内核支持多种iommu的类型。在使用iommu之前需要设置iommu的类型。也可以查询该iommu的硬件信息(例如iommu的page size)
struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };

/* 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);
  1. 现在可以使用vfio的iommu做映射了。通常首先使用mmap的匿名映射方式在内核中分配内存,并完成映射。注意:匿名内存映射方式创建的页表项位于内核的全局页表中,在iommu映射的时候,使用iommu返回给进程的虚拟地址(vaddr)来查找物理页框号(pfn)。
/* 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;

/* intel平台在内核中使用函数vfio_iommu_type1_ioctl处理,
 * 其调用函数vfio_dma_do_map完成具体工作,过程分两步:
 * 1. 使用vaddr在全局页表中找到其第一个页的页框号pfn
 * 2. 使用vfio_iommu_map建立iommu的映射,映射iova<-->页框号的对应关系
 * 映射完成后,进程就可以通过vaddr,设备可以通过iova来访问pfn中的内存了。
 */
ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
  1. 在第四步中申请和映射了iommu的DMA内存。这些内存必须要给设备使用才有意义。因此首先获取VFIO的设备文件描述符;并通过设备的文件描述符获取设备的PCI BAR信息和IRQ信息。当然也可以对设备做复位操作
int device, i;
struct vfio_device_info device_info = { .argsz = sizeof(device_info) };

/* Get a file descriptor for the device */
device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.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, &reg);

        /* 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);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容

  • 一、Docker 简介 Docker 两个主要部件:Docker: 开源的容器虚拟化平台Docker Hub: 用...
    R_X阅读 4,385评论 0 27
  • 首先要明确两个概念:Linux内核 PCI设备驱动和设备本身驱动两部分。工作中所谓的编写设备驱动,其实就是编写设备...
    Leon_Geo阅读 3,264评论 0 6
  • 本文开启 linux 内核 V4L2 框架部分的学习之旅,本文仅先对 V4L2 的框架做一个综述性的概括介绍,然后...
    yellowmax阅读 7,574评论 0 13
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,844评论 0 5
  • 1.DPDK 简介 DPDK(Data Plane Development Kit)是数据平面开发工具包,由用于加...
    古埃尔公园阅读 8,349评论 1 8