[PCIe] SR-IOV (单根虚拟化) 及linux驱动浅析(device的PF和VF及其驱动)

2023-10-26

网上从服务器和虚拟化层面介绍SR-IOV应用的文章很多了。

本文重点从支持SR-IOV的设备(EP)及其驱动来讨论。

对于SR-IOV的设备(EP)来说,无非就是一个device通过物理功能(PF)虚拟出关联的若干个虚拟功能(VF)。

host的驱动通过SR-IOV Extended Capability识别并配置VF使能,加载VF驱动,使得只有一个物理端口的pcie设备在软件层面

体现出多个pcie设备。在虚拟化应用上可以将虚拟功能VF分配给不同的客户机进行使用。

 

q0, linux应用层面如何使用打开VF,并使用VF

sysfs下,支持SR-IOV的device提供sriov_numvfs来控制VF的打开和关闭.VF打开后一般需要加载单独的VF驱动。

rom sysfs:

'nr_virtfn' is number of VFs to be enabled

echo 'nr_virtfn' > \ /sys/bus/pci/devices/<DOMAIN:BUS:DEVICE.FUNCTION>/sriov_numvfs

 关闭VF

echo  0 > \ /sys/bus/pci/devices/<DOMAIN:BUS:DEVICE.FUNCTION>/sriov_numvfs

实例

检查、配置vf数量,因为还没配置,所以是0

[root@g1 ~]# cat /sys/class/infiniband/mlx5_1/device/mlx5_num_vfs
0

这里我测试把OFED驱动中的vf数量配置成1

echo 1>  /sys/class/infiniband/mlx5_1/device/mlx5_num_vfs

在PCI总线上,用lspci可以看到ConnectX-6对应的设备出现了多次,其中Function 1是刚才配置好的Virtual Function

[root@g1 ~]# lspci|grep Mellanox
04:00.0 Infiniband controller: Mellanox Technologies MT28908 Family [ConnectX-6]
04:00.1 Infiniband controller: Mellanox Technologies MT28908 Family [ConnectX-6 Virtual Functio

 

q1:VF的bdf number如何分配(vf打开后是如何被识别到的)

首先回顾一下Routing ID,Routing ID就是BDF number,即采用Bus Number、Device Number和Function Number来确定目标设备的位置的id。

SR-IOV Extended Capability中用FirstVF Offset和VF Stride来标记VF的Routing ID。VF的Routing ID是以PF的Routing ID值为参考来计算的。

FirstVF Offset:第一个VF相对PF的Routing ID的偏移量

VF Stride: 相邻VF之间的Routing ID的偏移量(步进值)

PF的Routing ID在PF枚举之后就已经分配好了。PF的驱动程序通过配置SR-IOV Extended Capability,打开这个PF关联的VF之后,通过FirstVF Offset和VF Stride就能计算出VF们的Routing ID

这个计算也非常简单,按照BDF的位域规则累加就可以了,linux下pcie的device 和 function number是一起编码的(就是devfn),计算代码如下:

/* vf_bus = pf_bus + (pf_devfn + offset + stride * vf_id) >> 8 */
int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id)
{
	if (!dev->is_physfn)
		return -EINVAL;
	return dev->bus->number + ((dev->devfn + dev->sriov->offset +
				    dev->sriov->stride * vf_id) >> 8);
}
/* vf_devfn = (pf_devfn + offset + stride * vf_id) & 0xff */
int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id)
{
	if (!dev->is_physfn)
		return -EINVAL;
	return (dev->devfn + dev->sriov->offset +
		dev->sriov->stride * vf_id) & 0xff;
}

 

q2:VF的BAR空间如何分配

VF的BAR[n]空间是通过PF的SR-IOV Capability中每个VF_BAR[n]来分配的,和VF Configuration Space的BAR无关。

SR-IOV Capability中VF_BAR[n]的介绍如下:

9.3.3.14 VF BAR0 (Offset 24h), VF BAR1 (Offset 28h), VF BAR2 (Offset 2Ch), VF BAR3 (Offset
30h), VF BAR4 (Offset 34h), VF BAR5 (Offset 38h)

These fields must define the VF’s Base Address Registers (BARs). These fields behave as normal PCI BARs, as described in Section 7.5.1 . They can be sized by writing all 1s and reading back the contents of the BARs as described in Section 7.5.1.2.1 , complying with the low order bits that define the BAR type fields.
These fields may have their attributes affected by the VF Resizable BAR Extended Capability (see Section 9.3.7.5 ) if it is implemented.
The amount of address space decoded by each BAR shall be an integral multiple of System Page Size.

这些字段必须定义VF的基地址寄存器(BARs)。这些字段的作用与正常的PCI BARs相似,如7.5.1节所述。它们可以通过写入全1并按照第7.5.1.2.1节中描述的那样读入BAR的内容来调整大小,通过低BIT可以判断BAR的类型和属性。
如果实现了VF可调整大小的BAR扩展功能,这些字段的属性可能会受到影响(参见9.3.7.5节)

Each VF BARn, when “sized” by writing 1s and reading back the contents, describes the amount of address space consumed and alignment required by a single Virtual Function, per BAR. When written with an actual address value, and VF Enable and VF MSE are Set, the BAR maps NumVFs BARs. In other words, the base address is the address of the first VF BARn associated with this PF and all subsequent VF BARn address ranges follow as described below.

每个VF BARn通过写入1并读取内容来“调整大小”时,每个VF BAR都描述了单个VF所消耗的地址空间量和所需的对齐方式。当使用实际的地址值写入时,并且设置了VF Enable和VF MSE,则VF BAR映射了NumVFs个 BAR空间。换句话说,VF BAR的起始地址是与这个PF关联的第一个VF BAR的起始地址,所有后续VF的 BAR地址空间则依次向后排列。


VF BARs shall only support 32-bit and 64-bit memory space. PCI I/O Space is not supported in VFs. Bit 0 of any
implemented VF BARx must be RO 0b except for a VF BARx used to map the upper 32 bits of a 64-bit memory VF BAR pair.
The alignment requirement and size read is for a single VF, but when VF Enable is Set and VF MSE is Set, the BAR contains the base address for all (NumVFs) VF BARn

VF BARs只能支持32位和64位内存空间映射。VFs中不支持PCI I/O空间。所有VF BARx的bit0必须是只读的0值,除了用于映射64位内存VF BAR上32位的VF BARx。
对齐要求和大小读取是针对单个VF的,但是当使能VF Enable和VF MSE时,该BAR实际上包含所有(NumVFs多个) VF BAR的BAR地址空间

简单的总结一下:

1. PF 的VF_BAR[n]行为上是和常规的BAR是一样的(全写1来确定大小...等等分配机制)

2. 但是PF 的VF_BAR 地址空间分配之后,代表的含义与PF自己的BAR不同。VF_BAR对应的是PF关联的每个VF的BAR空间。

3. VF1的BAR空间完全与PF 的VF_BAR 地址空间相同,也就是PF 的VF_BAR[0-5]空间刚好就是对应第一个VF的bar[0-5]

4. 那后续的VFn的空间在哪呢,VFn的每一个BAR[n]空间都依次在VF1的bar[n]后依次排列(大小是相同的)

5. 也就是说,虽然VF_BAR只显式的看到一份VF的BAR空间,但实际上有NumVFs份 BAR空间在每个VF_BAR后依次存在,然后对应就是每个VF的bar。(类似一个矩阵,PF有关联的NumVFs个VF,每个VF有6个BAR。)

(这个讲的有点绕了,看图或者代码会好理解一点)

下面这张图展示了PF的BAR0, VF BAR0,和每个VF的BAR0空间的关系。其他的BARn也都是一样的。

linux 驱动分配VF BAR空间的代码如下:

PF驱动调用sriov_init的时候先按照SR-IOV Capability中每个VF_BAR[n] * TotalVF 来分配PCI空间的resource放在PF 的dev->resource[i + PCI_IOV_RESOURCES]中,这个时候VF还没使能和枚举。

static int sriov_init(struct pci_dev *dev, int pos)
{
	int i, bar64;
	int rc;
	int nres;
	u32 pgsz;
	u16 ctrl, total;
	struct pci_sriov *iov;
	struct resource *res;
	struct pci_dev *pdev;

...
	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
		res = &dev->resource[i + PCI_IOV_RESOURCES];
		/*
		 * If it is already FIXED, don't change it, something
		 * (perhaps EA or header fixups) wants it this way.
		 */
		if (res->flags & IORESOURCE_PCI_FIXED)
			bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
		else
			bar64 = __pci_read_base(dev, pci_bar_unknown, res,
						pos + PCI_SRIOV_BAR + i * 4);
		if (!res->flags)
			continue;
		if (resource_size(res) & (PAGE_SIZE - 1)) {
			rc = -EIO;
			goto failed;
		}
		iov->barsz[i] = resource_size(res);
        /* 重点在这一行,驱动按照VF_BAR分配的空间又强行乘以VF的total数量  */
		res->end = res->start + resource_size(res) * total - 1;
		dev_info(&dev->dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n",
			 i, res, i, total);
		i += bar64;
		nres++;
	}

...
}

然后在使能VF的时候sriov_enable->pci_iov_add_virtfn,再从PF之前申请到的dev->resource[i + PCI_IOV_RESOURCES]依次把对应的resource空间拿出去赋给每个VF 的dev->resource,然后再进行request_resource

int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset)
{
	int i;
	int rc = -ENOMEM;
	u64 size;
	char buf[VIRTFN_ID_LEN];
	struct pci_dev *virtfn;
	struct resource *res;
	struct pci_sriov *iov = dev->sriov;
	struct pci_bus *bus;

...

	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
		res = &dev->resource[i + PCI_IOV_RESOURCES];
		if (!res->parent)
			continue;
		virtfn->resource[i].name = pci_name(virtfn);
		virtfn->resource[i].flags = res->flags;
		size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES);
		virtfn->resource[i].start = res->start + size * id;
		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
		rc = request_resource(res, &virtfn->resource[i]);
		BUG_ON(rc);
	}

...

	return rc;
}

q3:VF的Configuration Space有何含义

打开VF之后,通过VF的BDF number访问VF的配置空间(和PF一样的)。

VF作为一个function也有自己Type 0 Configuration Space Header以及各种Capability存放在配置空间。

但是作为一个虚拟function,VF配置空间中的很多字段都是没用的,软件上应该直接采用PF的值。

因为配置空间主要是存放PCI的功能寄存器,而VF的电源,link,错误管理,BAR等其实完全是交给PF来管理的(VF和PF共享一个PCIe端口)。只有与VF独立功能相关的才会有VF单独的实现(例如MSI-X中断)。

 

例如:VF的Vendor ID和Device ID就是全F的无效值,软件上VF的Vendor ID应该直接采用PF的值,

Device ID应该从PF配置空间中的VF Device ID字段来获取。

pci_iov_add_virtfn中有这样一行代码:

virtfn->vendor = dev->vendor;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);

其中dev就是 virtfn关联的PF。

目前看来,配置空间涉及的VF独立的功能有:

FLR(VF可以单独进行复位)

MSI/MSI-X(VF应该单独实现自己独立的MSI/X capabilities,从而每个VF在系统中都有独立的中断向量,但VF无法支持INTx)

 

总结:VF通过共享PF的PCIe端口,节省了很多连接上的开销。在独立功能的支持方面,VF主要需要实现其独立的BAR空间以及中断资源,用于实现VF的独立功能,以达到从PCIe层面的虚拟化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[PCIe] SR-IOV (单根虚拟化) 及linux驱动浅析(device的PF和VF及其驱动) 的相关文章

  • 嵌入式linux内存分析

    在linux的桌面发行版中 一般都会有一个swap分区 然而在用FLASH做存储介质的嵌入式设备中 是没有交换分区的 这主要的有如下原因 1 一旦使用了交换分区 系统的性能将下降得很快 不可接受 2 FLASH的写次数有限的 大概在几十万次
  • 2020-11-30

    嵌入式MQTT库移植 基于mosquitto库 一 交叉编译OpenSSL 下载源码到Linux虚拟机环境 https www openssl org source openssl 1 1 1f tar gz 放入非windows共享文件夹
  • PCIe专题学习——2.1

    之前我们对PCIe的一些基础概念做了一个宏观的介绍 了解了PCIe是一种封装分层协议 packet based layered protocol 主要包括事务层 Transaction layer 数据链路层 Data link layer
  • qwt6.0.0交叉编译,在mini2440完美运行

    qwt6 0 0交叉编译 在mini2440完美运行 QWT简介 QWT 全称是Qt Widgets for Technical Applications 是一个基于LGPL版权协议的开源项目 可生成各种统计图 它为具有技术专业背景的程序提
  • VMware安装Ubuntu 18.04虚拟机(镜像下载、硬盘分区、创建虚拟机、安装系统、桥接模式网络配置)

    1 VMware安装 安装VMware Player免费 而且功能足够个人开发使用 Pro的主要区别在于可以同时运行多个虚拟机 而Player只能同时运行一个 VMware Workstation Player 16 下载 VMware W
  • 【WiFi】wifi芯片架构与原理说明

    目录 1 概述 2 芯片架构 3 工作原理 4 总结 1 概述 WiFi芯片是一种用于无线网络通信的集成电路 它负责将数据转换为无线信号并进行传输 下面是关于WiFi芯片架构和原理的简要说明 2 芯片架构 芯片架构 WiFi芯片通常由以下几
  • VMware player桥接模式不能联网的解决方法

    VMware虚拟机下主要使用两种网络连接方式 桥接模式 NAT模式 桥接模式 直接连接网络 虚拟机独立IP 并与宿主机处于同一网段内 相当于虚拟机是局域网内独立的一台电脑 使用起来较为方便 NAT模式 网络地址转换 借由宿主机的IP访问网络
  • 【PCIe 5.0 - 8】PCIe 事务层详解4 - First/Last DW Byte Enables规则

    First Last DW Byte Enables规则 Byte Enables包含在Memory I O 和Configuration Requests中 本节定义了相应的规则 Byte Enables出现在Request header
  • 树莓派教程 - 1.2 树莓派GPIO库wiringPi 软件PWM

    Git例程源码仓库 https github com ZhiliangMa raspberry git 使用到的硬件 led 200 左右的电阻 杜邦线 上一节使用硬件PWM来控制led亮度 可树莓派的硬件PWM引脚只有1路 在实际应用中
  • 树莓派教程 - 1.5 树莓派GPIO库wiringPi 使用硬件串口ttyAMA0与ttyS0

    Git例程源码仓库 https github com ZhiliangMa raspberry git 上一篇介绍 ttyS0串口的用法 说到了此串口利弊 可能会出现乱码 但绝对能满足绝大部分的要求 本节使用 dev ttyAMA0 的方法
  • Tina填坑

    1 Tina3 5 编译全志X3报错 environment variable SOURCE DATE EPOCH must expand to a non negative integer less than or equal to 25
  • PCIe5.0的Add-in-Card(AIC)金手指layout建议(三)

    PCIe5 0的Add in Card AIC 金手指layout建议 一 PCIe5 0的Add in Card AIC 金手指layout建议 二 前面两篇文章介绍了第一种金手指的layout建议 适用速率在32 0 GT s 以下介绍
  • 嵌入式 Linux 入门 环境篇(四、必备开发工具安装)

    嵌入式 Linux入门 环境篇第四课 开发工具软件的安装与说明 by 矜辰所致 前言 前面的环境篇我们把开发的基本环境安装好了 Ubuntu虚拟机 网络配置 为了从 0 开始说明 还特意买了一台新电脑 就问是不是不将就 在我们接下来做嵌入式
  • 嵌入式Linux编译系统的设计——Bootloader, 内核,驱动,文件系统,升级镜像等自动化编译打包

    项目简介 嵌入式系统的开发过程较为复杂 编译 裁剪 定制等如果没有一套规范的流程将会难于管理和控制 本项目的目的是设计一个嵌入式Linux编译系统 实现代码的编译 定制和裁剪 Bootloader 内核 驱动 文件系统 升级镜像等都可以自动
  • FriendlyDesktop系统烧写,SOM-RK3399/NanoPC T4烧录系统

    SOM RK3399 SOM RK3399 V2 NanoPC T4的系统是通用的 由于Type C烧写有蛮大的概率会识别不成功 实测 所以建议使用SD卡脱机烧写 友善之臂提供了几种系统支持 android7 8 10 FriendlyCo
  • PCIe专题学习——3.2(数据链路层Ack/Nak机制解析)

    之前我们讲了对PCIe的一些基础概念作了一个宏观的介绍 了解了PCIe是一种封装分层协议 packet based layered protocol 主要包括事务层 Transaction layer 数据链路层 Data link lay
  • 如何为可缓存 PCIe BAR 进行 mmap

    我正在尝试编写一个自定义驱动程序mmap PCIe BAR 的函数 目标是使该 BAR 可缓存在处理器缓存中 我知道这不是实现最高带宽的最佳方法 并且写入顺序是不可预测的 本例中也不是问题 这类似于中所描述的如何阻止 MMAP 缓存值 处理
  • macOS DriverKit:制作 PCI dext 来替换内置驱动程序

    我正在尝试在 DriverKit 中编写一个用户空间 PCI 驱动程序 用于教育 研究目的 我找到了一个来自 WorthDoingBadly 的示例其中包含 PCI 设备 dext 的样板代码 我已删除了漏洞利用代码 我已将其修改为通过以下
  • 从 x86 CPU 生成 64 字节读取 PCIe TLP

    将数据写入 PCIe 设备时 可以使用写入组合映射来提示 CPU 应生成针对该设备的 64 字节 TLP 是否可以对读取做类似的事情 以某种方式提示 CPU 读取整个缓存行或更大的缓冲区 而不是一次读取一个字 英特尔有关于从视频 RAM 复
  • 使用 mmap 访问 PCI-e 内存空间

    我在 Freescale MPC8308 处理器 基于 PowerPC 架构 上使用 PCI e 端口 在尝试使用它时遇到一些问题 端点 PCI e 设备的内存空间等于 256 MB 我可以使用 pciutils 包轻松读取和写入端点设备的

随机推荐

  • 【1803. 统计异或值在范围内的数对有多少】

    来源 力扣 LeetCode 描述 给你一个整数数组 nums 下标 从 0 开始 计数 以及两个整数 low 和 high 请返回 漂亮数对 的数目 漂亮数对 是一个形如 i j 的数对 其中 0 lt i lt j lt nums le
  • 2019年黑马新版Java学习路线图(内含大纲+视频+工具+书籍+面试)面试必看!

    非常好的java学习路线 伴有配套资源 面试必看 黑马程序员 http bbs itheima com thread 386464 1 1 html
  • LEVELDB介绍

    基本信息 特性 keys 和 values 是任意的字节数组 数据按 key 值排序存储 调用者可以重载函数来重写排序顺序 提供基本的 Put key value Get key Delete key Batch 操作 多个更改可以在一个原
  • JWT

    1 常见的认证机制 1 1 HTTP Basic Auth HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password 简言之 Basic Auth是配合RESTful API 使用的最简单的
  • SpringBoot整合Shiro

    一 pom xml引入依赖 1 shiro依赖
  • python3+tkinter实践历程(四)——模仿CRT完成基于socket通信与tkinter的TCP串口客户端

    python3 tkinter实践历程 四 基于socket通信与tkinter的TCP串口客户端 仿CRT 文章目录 系列文章目录 分享背景 制作背景 最终功能 工具截图展示 代码详解 系列文章目录 python3 tkinter实践历程
  • 天龙八部手游服务器维护公告,天龙八部手游更新维护公告 龙腾迎春全新资料片来袭...

    天龙八部手游终于迎来全新资料片 龙腾迎春啦 本次更新将加入全新帮派副本决战少室山 并且玩家们可以觉醒独特的至尊武魂 玩家们可以凭自己的喜好改变武魂的外观 一起来了解一下详细更新内容吧 更新时间 1月31日4 00 8 00 更新奖励 300
  • 查看表被数据库中其他对象使用

    select from dba dependencies where referenced name upper xxx
  • java求六位数以内所有自幂数

    如果在一个固定的进制中 一个n位自然数等于自身各个数位上数字的n次幂之和 则称此数为自幂数 以下用java语言求六位数以内所有自幂数 独身数共有9个 1 2 3 4 5 6 7 8 9 水仙花数共有4个 153 370 371 407 四叶
  • angular.js中的复选框checkbox的用法

    首先在head里引入 页面部分 div div div div
  • RestfulTool插件使用详解

    1 全局搜索 2 提供了一个 Services tree 的API接口显示窗口 右侧会有RestServices侧边栏 点击后会显示当前项目所有请求地址 可以进行输入查询 然后会直接把请求方式 地址以及参数列出来 默认请求服务器为本机 lo
  • 【python】socket-传输多个文件、大文件

    socket 传输多个文件 大文件 0 前言 1 发送单个文件流程 2 关于发送大文件 本地读取时报错 MemoryError 3 关于粘包 问题背景 排错过程 解决方案 4 备注 换算表 0 前言 看过挺多个发文件的例子 但是基本都是发单
  • 每日博客 :>

    1 交换数组 define CRT SECURE NO WARNINGS 1 include
  • 计算机网络34-学习笔记-IP地址

    IP地址属于网络层 这里主要介绍IP地址作用 与MAC地址配合 主机H1将数据包发送给路由器R1 在网络层封装的IP数据报首部中 源IP地址应填写主机H1的IP地址IP1 目的IP地址应填写主机H2的IP地址IP2 在数据链路层中源MAC地
  • python装饰器

    装饰器是python一个重要的部分 由它的名称我们就可以大致了解到它的功能 拓展其他函数 装饰器可以让我们的代码更加简洁 也更加pythonic 首先 我们先回顾一下基础概念 一 在python中 如果调用一个函数不带括号时 调用的是这个函
  • R语言多任务处理与并行运算包——foreach

    作者简介Introduction 杜雨 EasyCharts团队成员 R语言中文社区专栏作者 兴趣方向为 Excel商务图表 R语言数据可视化 地理信息数据可视化 个人公众号 数据小魔方 微信ID datamofang 数据小魔方 创始人
  • SpringBoot原理

    1 SpringBoot实现原理 SpringBoot是由自动配置和启动器以及大量注解实现 Stater stater就是启动器 也就是我们在pom xml文件中引入的带stater的依赖 springboot框架会根据依赖加载与该启动器有
  • Shell脚本入门

    Shell脚本入门 1 基本概念 Shell是一门弱类型 解释型 非编译型语言 Shell中无数据类型 Shell的作用是解释执行用户的命令 Shell执行命令的方式有两种 1 交互式 用户输入一条命令 shell就解释执行一条 2 批处理
  • 名为dash的蓝色插嘴小机器人_全球最出色的十大教育机器人

    2016年 阿尔法狗战胜围棋世界冠军李世石 成为人工智能发展的标志性事件 万物互联的时代 人工智能正掀起一场影响深刻的技术革命 谷歌 苹果 BAT 华为巨头们纷纷布局人工智能 有人猜测 互联网 过后 我们可能会迎来机器人 听到这个消息 爸爸
  • [PCIe] SR-IOV (单根虚拟化) 及linux驱动浅析(device的PF和VF及其驱动)

    网上从服务器和虚拟化层面介绍SR IOV应用的文章很多了 本文重点从支持SR IOV的设备 EP 及其驱动来讨论 对于SR IOV的设备 EP 来说 无非就是一个device通过物理功能 PF 虚拟出关联的若干个虚拟功能 VF host的驱