sriov_init初始化和pci_enable_sriov使能vf

在drivers/pci/probe.c 中的pci_init_capabilities函数中调用pci_iov_init 来进行sriov的初始化
int pci_iov_init(struct pci_dev *dev)
{
    int pos;

    if (!pci_is_pcie(dev))
        return -ENODEV;

    pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
    if (pos)
        return sriov_init(dev, pos);

    return -ENODEV;
}
在pci_iov_init 中首先通过pci_is_pcie 来判断是不是pcie设备,因为只有pcie设备有sriov,pci设备是没有的。
其次通过pci_find_ext_capability 来查找看这款设备是否具有sriov 这个feature。如果有的话,就调用sriov_init 来做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;

    pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
    if (ctrl & PCI_SRIOV_CTRL_VFE) {
        pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
        ssleep(1);
    }

    ctrl = 0;
    list_for_each_entry(pdev, &dev->bus->devices, bus_list)
        if (pdev->is_physfn)
            goto found;

    pdev = NULL;
    if (pci_ari_enabled(dev->bus))
        ctrl |= PCI_SRIOV_CTRL_ARI;

found:

    pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
    /从配置空间中找到当前支持几个vf
    pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
    if (!total)
        return 0;

    pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
    i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
    pgsz &= ~((1 << i) - 1);
    if (!pgsz)
        return -EIO;

    pgsz &= ~(pgsz - 1);
    pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
//申请一个struct pci_sriov *iov;
    iov = kzalloc(sizeof(*iov), GFP_KERNEL);
    if (!iov)
        return -ENOMEM;

    nres = 0;
//这个for循环是给vf的bar 空间赋值
    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);
        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++;
    }
//iov的其他成员赋值
    iov->pos = pos;
    iov->nres = nres;
    iov->ctrl = ctrl;
    iov->total_VFs = total;
    iov->pgsz = pgsz;
    iov->self = dev;
    pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
    pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
    if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
        iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);

    if (pdev)
        iov->dev = pci_dev_get(pdev);
    else
        iov->dev = dev;

    mutex_init(&iov->lock);
//将iov赋值给dev->sriov,这个dev代表当前的pcie设备也就是pf.
    dev->sriov = iov;
    dev->is_physfn = 1;
    rc = compute_max_vf_buses(dev);
    if (rc)
        goto fail_max_buses;

    return 0;

fail_max_buses:
    dev->sriov = NULL;
    dev->is_physfn = 0;
failed:
    for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
        res = &dev->resource[i + PCI_IOV_RESOURCES];
        res->flags = 0;
    }

    kfree(iov);
    return rc;
}

最后调用compute_max_vf_buses 给每个vf 的PCI_SRIOV_NUM_VF/PCI_SRIOV_VF_OFFSET/PCI_SRIOV_VF_STRIDE 赋值
static inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn)
{
    struct pci_sriov *iov = dev->sriov;

    pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
    pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &iov->offset);
    pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride);
}

/*
 * The PF consumes one bus number.  NumVFs, First VF Offset, and VF Stride
 * determine how many additional bus numbers will be consumed by VFs.
 *
 * Iterate over all valid NumVFs, validate offset and stride, and calculate
 * the maximum number of bus numbers that could ever be required.
 */
static int compute_max_vf_buses(struct pci_dev *dev)
{
    struct pci_sriov *iov = dev->sriov;
    int nr_virtfn, busnr, rc = 0;

    for (nr_virtfn = iov->total_VFs; nr_virtfn; nr_virtfn--) {
        pci_iov_set_numvfs(dev, nr_virtfn);
        if (!iov->offset || (nr_virtfn > 1 && !iov->stride)) {
            rc = -EIO;
            goto out;
        }

        busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
        if (busnr > iov->max_VF_buses)
            iov->max_VF_buses = busnr;
    }

out:
    pci_iov_set_numvfs(dev, 0);
    return rc;
}

总结协一下。sriov的初始化就是给vf的bar空间赋值,并决定当前pf支持几个vf,并给每个vf编个号,sriov_init 只是初始化,如果需要使用vf的话,还必须主动调用pci_enable_sriov来指定需要启动哪个vf


`pci_disable_sriov()` 是Linux内核中用于禁用单根I/O虚拟化(SR-IOV)功能的函数。SR-IOV是一种硬件虚拟化技术,允许一个物理PCI设备呈现多个虚拟设备给系统,这对于像虚拟化环境中的网络或存储适配器等硬件来说非常有用。当不再需要这些虚拟功能时,可以通过调用`pci_disable_sriov()`来禁用SR-IOV。 函数`pci_disable_sriov()`的实现通常涉及到以下几个步骤: 1. 检查PCI设备是否支持SR-IOV功能,如果支持,则继续操作。 2. 禁用SR-IOV功能,这通常涉及写入设备的SR-IOV控制寄存器。 3. 清理任何相关的资源,比如释放已经分配给虚拟函数(VF)的资源。 4. 更新设备的状态,确保后续的操作知道SR-IOV已经被禁用。 以下是`pci_disable_sriov()`函数的一个非常简化的伪代码示例: ```c int pci_disable_sriov(struct pci_dev *dev, int nr_vfs) { if (!dev->is_sriov_enabled) return -ENOSYS; // 如果SR-IOV没有启用,则不需要操作 // 禁用SR-IOV功能 pci_write_config_word(dev, dev->sriov_cap + PCI_SRIOV_TOTALVF, 0); // 清理虚拟函数资源 for (int i = 0; i < nr_vfs; ++i) { // 释放VF相关资源的代码 } // 更新设备状态 dev->is_sriov_enabled = false; dev->num_vfs = 0; return 0; } ``` 需要注意的是,这只是一个示例,实际的内核代码会更加复杂,涉及许多其他细节,例如错误处理、并发控制、以及与系统资源管理器(如udev)的交互等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值