背景
1.1 SR-IOV Goals
- 物理功能(PFs):这些是完整的PCIe功能,包括SR-IOV扩展功能。该功能用于配置和管理SR-IOV功能。
- 虚拟功能(VFs):这些是“轻量级”PCIe函数,包含数据移动所需的资源,但配置资源的集合被精心最小化。
1.2 SR-IOV概述
支持SR-IOV的设备提供许多可配置的独立VFs,每个VFs都有自己的PCI配置空间。VMM将一个或多个VF分配给虚拟机。内存转换技术,如Intel®VT-x和Intel®VT-d提供硬件辅助技术,允许直接DMA传输到和从VM,从而绕过VMM的软件切换。
2.2 SR-IOV 的条件
硬件条件:
1. CPU 支持 Intel VT-x 和 VT-D (或者 AMD 的 SVM 和 IOMMU) 2. 有支持 SR-IOV 规范的设备:目前这种设备较多,比如Intel的很多中高端网卡等。 3. CPU必须支持IOMMU(比如英特尔的 VT-d 或者AMD的 AMD-Vi,Power8 处理器默认支持IOMMU),并且在BIOS中已开启。 4. 支持PCI-SIG* Single Root I/O Virtualization and Sharing(SR-IOV),并且在BIOS中已开启。
软件条件:
- 需要 QEMU/KAM 的支持。
- 安装软件包
yum install -y kvm virt-* libvirt bridge-utils qemu-img
kvm:软件包中含有KVM内核模块,它在默认linux内核中提供kvm管理程序
libvirts:安装虚拟机管理工具,使用virsh等命令来管理和控制虚拟机。
bridge-utils:设置网络网卡桥接。
virt-*:创建、克隆虚拟机命令,以及图形化管理工具virt-manager
qemu-img:安装qemu组件,使用qemu命令来创建磁盘等。
RedHat Linux 官方提供了详细的流程:
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/virtualization_host_configuration_and_guest_installation_guide/index
https://www.cnblogs.com/liuhongru/p/11068460.html
3.0 kernel 代码流程
查看是否支持SR-IOV:
通过下面的命令来enable SRIOV功能:
echo 2 > /sys/bus/pci/devices/0000:5e:00.0/sriov_numvfs
上面红色的是一个支持SR-IOV功能的pcie 网卡,2是enable的数量,从下图看,enable了2个VFs后,多出来了2个pcie设备。

enable SR-IOV的流程:
从上面命令上看,入口是每个pcie的sriov_numvfs节点,下面是实现节点的代码:
/* * num_vfs > 0; number of VFs to enable * num_vfs = 0; disable all VFs * * Note: SRIOV spec doesn't allow partial VF * disable, so it's all or none. */ static ssize_t sriov_numvfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
struct pci_dev *pdev = to_pci_dev(dev); int ret; u16 num_vfs; ret = kstrtou16(buf, 0, &num_vfs); if (ret < 0) return ret; if (num_vfs > pci_sriov_get_totalvfs(pdev)) return -ERANGE; device_lock(&pdev->dev); if (num_vfs == pdev->sriov->num_VFs) goto exit; /* is PF driver loaded w/callback */ if (!pdev->driver || !pdev->driver->sriov_configure) {
pci_info(pdev, "Driver doesn't support SRIOV configuration via sysfs\n"); ret = -ENOENT; goto exit; } if (num_vfs == 0) {
/* disable VFs */ ret = pdev->driver->sriov_configure(pdev, 0); goto exit; } /* enable VFs */ if (pdev->sriov->num_VFs) {
pci_warn(pdev, "%d VFs already enabled. Disable before enabling %d VFs\n", pdev->sriov->num_VFs, num_vfs); ret = -EBUSY; goto exit; } ret = pdev->driver->sriov_configure(pdev, num_vfs); if (ret < 0) goto exit; if (ret != num_vfs) pci_warn(pdev, "%d VFs requested; only %d enabled\n", num_vfs, ret); exit: device_unlock(&pdev->dev); if (ret < 0) return ret; return count; }
从上面看,支持SR-IOV的driver需要实现下面的函数接口
static struct pci_driver igb_driver = {
.name = igb_driver_name, .sriov_configure = `igb_pci_sriov_configure`, / 支持sriov功能需要实现的函数 / }; static int igb_pci_sriov_configure(struct pci_dev *dev, int num_vfs) {
#ifdef CONFIG_PCI_IOV if (num_vfs == 0) return igb_pci_disable_sriov(dev); else return igb_pci_enable_sriov(dev, num_vfs); #endif return 0; }
最终调用的是pci api: pci_enable_sriov
/ * pci_enable_sriov - enable the SR-IOV capability * @dev: the PCI device * @nr_virtfn: number of virtual functions to enable * * Returns 0 on success, or negative on failure. */ int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) {
might_sleep(); if (!dev->is_physfn) return -ENOSYS; return sriov_enable(dev, nr_virtfn); }
下面详细分析下sriov_enable
sriov_enable(struct pci_dev *dev, int nr_virtfn) pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 这里省略数行,主要是用来判断initial,total_VFs,nr_virtfn是否合法 bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1); //计算出来一个bus num分给VFs使用 dev->bus->number + ((dev->devfn + dev->sriov->offset + dev->sriov->stride * vf_id) >> 8); pci_enable_resources(dev, bars);//设置VFs 配置空间的PCI_COMMAND字段 pcibios_sriov_enable(dev, initial); pci_iov_set_numvfs(dev, nr_virtfn);/设置VFs number,读取offset,stride/ iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);/ VF Enable, VF Memory Space Enable/ sriov_add_vfs(dev, initial); for (i = 0; i < num_vfs; i++) //逐个初始化,add VFs rc = pci_iov_add_virtfn(dev, i); //和pcie初始化的流程基本一致,分配填充结构体,初始化设备,add 设备 virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); virtfn = pci_alloc_dev(bus); virtfn->devfn = pci_iov_virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; virtfn->device = iov->vf_device; virtfn->is_virtfn = 1; virtfn->physfn = pci_dev_get(dev); if (id == 0) pci_read_vf_config_common(virtfn); pci_setup_device(virtfn); //这两个函数就是按照capability初始化设备 中间省掉获取bar空间的代码,从PF上的SR-IVO的bar空间获取 pci_device_add(virtfn, virtfn->bus); sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); pci_bus_add_device(virtfn) kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203850.html原文链接:https://javaforall.net
