在drivers/net/ethernet/intel/ixgbe/ixgbe_main.c 中会注册一个pci driver
static struct pci_driver ixgbe_driver = {
.name = ixgbe_driver_name,
.id_table = ixgbe_pci_tbl,
.probe = ixgbe_probe,
.remove = ixgbe_remove,
#ifdef CONFIG_PM
.suspend = ixgbe_suspend,
.resume = ixgbe_resume,
#endif
.shutdown = ixgbe_shutdown,
.sriov_configure = ixgbe_pci_sriov_configure,
.err_handler = &ixgbe_err_handler
};
每个pcie设备都有一个pci_driver。其中提供了一个sriov_configure 用于开启vf。
int ixgbe_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
{
if (num_vfs == 0)
return ixgbe_pci_sriov_disable(dev);
else
return ixgbe_pci_sriov_enable(dev, num_vfs);
}
ixgbe_pci_sriov_configure 接受两个参数,一个是struct pci_dev *dev 代表这个pcie 设备,也就是pf,int num_vfs 也就是需要开启的vf的序列号,因为一个pf可以支持多个vf,所以要选择启动哪个vf.pf支持的vf个数是在sriov_init 中就已经初始化好的.
如果num_vfs 不是0的话,就调用ixgbe_pci_sriov_enable
static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs)
{
#ifdef CONFIG_PCI_IOV
struct ixgbe_adapter *adapter = pci_get_drvdata(dev);
int err = 0;
int i;
int pre_existing_vfs = pci_num_vf(dev);
err = pci_enable_sriov(dev, num_vfs);
if (err) {
e_dev_warn("Failed to enable PCI sriov: %d\n", err);
return err;
}
return num_vfs;
#else
return 0;
#endif
}
要使用vf,kernel必须打开CONFIG_PCI_IOV。随后调用pci_enable_sriov来启动vf
pci_enable_sriov->sriov_enable->pci_iov_add_virtfn
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;
mutex_lock(&iov->dev->sriov->lock);
//得到vf的bus
bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id));
if (!bus)
goto failed;
//申请一个struct pci_dev *virtfn; 作为vf,在kernel中用pci_dev 表示一个pcie end point,从这里能看出vf和pf在kernel中的结构体是一样的
virtfn = pci_alloc_dev(bus);
if (!virtfn)
goto failed0;
virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
//初始化virtfn
rc = pci_setup_device(virtfn);
if (rc)
goto failed0;
virtfn->dev.parent = dev->dev.parent;
virtfn->physfn = pci_dev_get(dev);
virtfn->is_virtfn = 1;
virtfn->multifunction = 0;
//为vf准备bar资源,所以vf是有独立的bar资源的.
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);
}
if (reset)
__pci_reset_function(virtfn);
//添加device
pci_device_add(virtfn, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock);
pci_bus_add_device(virtfn);
sprintf(buf, "virtfn%u", id);
//为vf建立sysfs接口
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
goto failed1;
rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
if (rc)
goto failed2;
kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
return 0;
failed2:
sysfs_remove_link(&dev->dev.kobj, buf);
failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
failed0:
virtfn_remove_bus(dev->bus, bus);
failed:
mutex_unlock(&iov->dev->sriov->lock);
return rc;
}
总结一下,要使用vf必须通过pf中的sriov_configure,在kernel中vf和pf都是用pci_dev,且初始化的flow也都是一样的,也就是说vf是一个独立的pcie 设备.且vf本身自己拥有资源.
static struct pci_driver ixgbe_driver = {
.name = ixgbe_driver_name,
.id_table = ixgbe_pci_tbl,
.probe = ixgbe_probe,
.remove = ixgbe_remove,
#ifdef CONFIG_PM
.suspend = ixgbe_suspend,
.resume = ixgbe_resume,
#endif
.shutdown = ixgbe_shutdown,
.sriov_configure = ixgbe_pci_sriov_configure,
.err_handler = &ixgbe_err_handler
};
每个pcie设备都有一个pci_driver。其中提供了一个sriov_configure 用于开启vf。
int ixgbe_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
{
if (num_vfs == 0)
return ixgbe_pci_sriov_disable(dev);
else
return ixgbe_pci_sriov_enable(dev, num_vfs);
}
ixgbe_pci_sriov_configure 接受两个参数,一个是struct pci_dev *dev 代表这个pcie 设备,也就是pf,int num_vfs 也就是需要开启的vf的序列号,因为一个pf可以支持多个vf,所以要选择启动哪个vf.pf支持的vf个数是在sriov_init 中就已经初始化好的.
如果num_vfs 不是0的话,就调用ixgbe_pci_sriov_enable
static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs)
{
#ifdef CONFIG_PCI_IOV
struct ixgbe_adapter *adapter = pci_get_drvdata(dev);
int err = 0;
int i;
int pre_existing_vfs = pci_num_vf(dev);
err = pci_enable_sriov(dev, num_vfs);
if (err) {
e_dev_warn("Failed to enable PCI sriov: %d\n", err);
return err;
}
return num_vfs;
#else
return 0;
#endif
}
要使用vf,kernel必须打开CONFIG_PCI_IOV。随后调用pci_enable_sriov来启动vf
pci_enable_sriov->sriov_enable->pci_iov_add_virtfn
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;
mutex_lock(&iov->dev->sriov->lock);
//得到vf的bus
bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id));
if (!bus)
goto failed;
//申请一个struct pci_dev *virtfn; 作为vf,在kernel中用pci_dev 表示一个pcie end point,从这里能看出vf和pf在kernel中的结构体是一样的
virtfn = pci_alloc_dev(bus);
if (!virtfn)
goto failed0;
virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
//初始化virtfn
rc = pci_setup_device(virtfn);
if (rc)
goto failed0;
virtfn->dev.parent = dev->dev.parent;
virtfn->physfn = pci_dev_get(dev);
virtfn->is_virtfn = 1;
virtfn->multifunction = 0;
//为vf准备bar资源,所以vf是有独立的bar资源的.
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);
}
if (reset)
__pci_reset_function(virtfn);
//添加device
pci_device_add(virtfn, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock);
pci_bus_add_device(virtfn);
sprintf(buf, "virtfn%u", id);
//为vf建立sysfs接口
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
goto failed1;
rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
if (rc)
goto failed2;
kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
return 0;
failed2:
sysfs_remove_link(&dev->dev.kobj, buf);
failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
failed0:
virtfn_remove_bus(dev->bus, bus);
failed:
mutex_unlock(&iov->dev->sriov->lock);
return rc;
}
总结一下,要使用vf必须通过pf中的sriov_configure,在kernel中vf和pf都是用pci_dev,且初始化的flow也都是一样的,也就是说vf是一个独立的pcie 设备.且vf本身自己拥有资源.