Commit 58754e77 authored by Longfang Liu's avatar Longfang Liu Committed by Zheng Zengkai
Browse files

vfio/pci: provide customized live migration VFIO driver framework

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I473Q4?from=project-issue



----------------------------------------------------------------------

vfio_pci_vendor_driver_ops includes these parts:
(1) .probe() and .remove() interface to be called by vfio_pci_probe()
and vfio_pci_remove().
(2) pointer to struct vfio_device_ops. It will be registered as ops of vfio
device if .probe() succeeds.
(3) vendor modules call macro module_vfio_pci_register_vendor_handler to
generate module_init and module_exit.
(4) export functions vfio_pci_vendor_data(), vfio_pci_irq_type(),
vfio_pci_num_regions(), vfio_pci_pdev(), and functions in vfio_pci_ops,
so they are able to be called from outside modules and make them a kind of
inherited by vfio_device_ops provided by vendor modules
(5) allows a simpler VFIO_DEVICE_GET_INFO ioctl in vendor driver,
let vfio_pci know number of vendor regions and vendor irqs
(6) allows vendor driver to read/write to bars directly which is useful
in security checking condition.
(7) allows vendor driver triggers this VFIO_IRQ_TYPE_REMAP_BAR_REGION
when it wants to notify userspace to remap PCI BARs.

Signed-off-by: default avatarLongfang Liu <liulongfang@huawei.com>
Reviewed-by: default avatarHao Fang <fanghao11@huawei.com>
Reviewed-by: default avatarMingqiang Ling <lingmingqiang@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 716406d9
Loading
Loading
Loading
Loading
+172 −11
Original line number Diff line number Diff line
@@ -74,6 +74,61 @@ static inline bool vfio_vga_disabled(void)
#endif
}

static struct vfio_pci {
	struct mutex		vendor_drivers_lock;
	struct list_head	vendor_drivers_list;
} vfio_pci;

struct pci_dev *vfio_pci_pdev(void *device_data)
{
	struct vfio_pci_device *vdev = device_data;

	return vdev->pdev;
}
EXPORT_SYMBOL_GPL(vfio_pci_pdev);

int vfio_pci_num_regions(void *device_data)
{
	struct vfio_pci_device *vdev = device_data;

	return vdev->num_regions;
}
EXPORT_SYMBOL_GPL(vfio_pci_num_regions);

int vfio_pci_irq_type(void *device_data)
{
	struct vfio_pci_device *vdev = device_data;

	return vdev->irq_type;
}
EXPORT_SYMBOL_GPL(vfio_pci_irq_type);

void *vfio_pci_vendor_data(void *device_data)
{
	struct vfio_pci_device *vdev = device_data;

	return vdev->vendor_data;
}
EXPORT_SYMBOL_GPL(vfio_pci_vendor_data);

int vfio_pci_set_vendor_regions(void *device_data, int num_vendor_regions)
{
	struct vfio_pci_device *vdev = device_data;

	vdev->num_vendor_regions = num_vendor_regions;
	return 0;
}
EXPORT_SYMBOL_GPL(vfio_pci_set_vendor_regions);

int vfio_pci_set_vendor_irqs(void *device_data, int num_vendor_irqs)
{
	struct vfio_pci_device *vdev = device_data;

	vdev->num_vendor_irqs = num_vendor_irqs;
	return 0;
}
EXPORT_SYMBOL_GPL(vfio_pci_set_vendor_irqs);

static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)
{
	switch (pdev->vendor) {
@@ -914,7 +969,7 @@ static void vfio_pci_vf_token_user_add(struct vfio_pci_device *vdev, int val)
	vfio_device_put(pf_dev);
}

static void vfio_pci_release(void *device_data)
void vfio_pci_release(void *device_data)
{
	struct vfio_pci_device *vdev = device_data;

@@ -941,8 +996,9 @@ static void vfio_pci_release(void *device_data)

	module_put(THIS_MODULE);
}
EXPORT_SYMBOL_GPL(vfio_pci_release);

static int vfio_pci_open(void *device_data)
int vfio_pci_open(void *device_data)
{
	struct vfio_pci_device *vdev = device_data;
	int ret = 0;
@@ -967,6 +1023,7 @@ static int vfio_pci_open(void *device_data)
		module_put(THIS_MODULE);
	return ret;
}
EXPORT_SYMBOL_GPL(vfio_pci_open);

static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
{
@@ -1161,7 +1218,7 @@ struct vfio_devices {
	int max_index;
};

static long vfio_pci_ioctl(void *device_data,
long vfio_pci_ioctl(void *device_data,
			   unsigned int cmd, unsigned long arg)
{
	struct vfio_pci_device *vdev = device_data;
@@ -1193,8 +1250,10 @@ static long vfio_pci_ioctl(void *device_data,
		if (vdev->reset_works)
			info.flags |= VFIO_DEVICE_FLAGS_RESET;

		info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
		info.num_irqs = VFIO_PCI_NUM_IRQS + vdev->num_ext_irqs;
		info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions +
						   vdev->num_vendor_regions;
		info.num_irqs = VFIO_PCI_NUM_IRQS + vdev->num_ext_irqs +
						vdev->num_vendor_irqs;

		if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV)) {
			int ret = vfio_pci_info_zdev_add_caps(vdev, &caps);
@@ -1819,6 +1878,7 @@ static long vfio_pci_ioctl(void *device_data,

	return -ENOTTY;
}
EXPORT_SYMBOL_GPL(vfio_pci_ioctl);

static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
			   size_t count, loff_t *ppos, bool iswrite)
@@ -1852,7 +1912,7 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
	return -EINVAL;
}

static ssize_t vfio_pci_read(void *device_data, char __user *buf,
ssize_t vfio_pci_read(void *device_data, char __user *buf,
			     size_t count, loff_t *ppos)
{
	if (!count)
@@ -1860,8 +1920,9 @@ static ssize_t vfio_pci_read(void *device_data, char __user *buf,

	return vfio_pci_rw(device_data, buf, count, ppos, false);
}
EXPORT_SYMBOL_GPL(vfio_pci_read);

static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
ssize_t vfio_pci_write(void *device_data, const char __user *buf,
			      size_t count, loff_t *ppos)
{
	if (!count)
@@ -1869,6 +1930,7 @@ static ssize_t vfio_pci_write(void *device_data, const char __user *buf,

	return vfio_pci_rw(device_data, (char __user *)buf, count, ppos, true);
}
EXPORT_SYMBOL_GPL(vfio_pci_write);

/* Return 1 on zap and vma_lock acquired, 0 on contention (only with @try) */
static int vfio_pci_zap_and_vma_lock(struct vfio_pci_device *vdev, bool try)
@@ -2064,7 +2126,7 @@ static const struct vm_operations_struct vfio_pci_mmap_ops = {
	.fault = vfio_pci_mmap_fault,
};

static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
{
	struct vfio_pci_device *vdev = device_data;
	struct pci_dev *pdev = vdev->pdev;
@@ -2133,8 +2195,9 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)

	return 0;
}
EXPORT_SYMBOL_GPL(vfio_pci_mmap);

static void vfio_pci_request(void *device_data, unsigned int count)
void vfio_pci_request(void *device_data, unsigned int count)
{
	struct vfio_pci_device *vdev = device_data;
	struct pci_dev *pdev = vdev->pdev;
@@ -2154,6 +2217,7 @@ static void vfio_pci_request(void *device_data, unsigned int count)

	mutex_unlock(&vdev->igate);
}
EXPORT_SYMBOL_GPL(vfio_pci_request);

static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev,
				      bool vf_token, uuid_t *uuid)
@@ -2250,7 +2314,7 @@ static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev,

#define VF_TOKEN_ARG "vf_token="

static int vfio_pci_match(void *device_data, char *buf)
int vfio_pci_match(void *device_data, char *buf)
{
	struct vfio_pci_device *vdev = device_data;
	bool vf_token = false;
@@ -2298,6 +2362,7 @@ static int vfio_pci_match(void *device_data, char *buf)

	return 1; /* Match */
}
EXPORT_SYMBOL_GPL(vfio_pci_match);

static const struct vfio_device_ops vfio_pci_ops = {
	.name		= "vfio-pci",
@@ -2404,6 +2469,35 @@ static void vfio_pci_vga_uninit(struct vfio_pci_device *vdev)
					      VGA_RSRC_LEGACY_MEM);
}

static int probe_vendor_drivers(struct vfio_pci_device *vdev)
{
	struct vfio_pci_vendor_driver *driver;
	int ret = -ENODEV;

	request_module("vfio-pci:%x-%x", vdev->pdev->vendor,
					 vdev->pdev->device);

	mutex_lock(&vfio_pci.vendor_drivers_lock);
	list_for_each_entry(driver, &vfio_pci.vendor_drivers_list, next) {
		void *data;

		if (!try_module_get(driver->ops->owner))
			continue;

		data = driver->ops->probe(vdev->pdev);
		if (IS_ERR(data)) {
			module_put(driver->ops->owner);
			continue;
		}
		vdev->vendor_driver = driver;
		vdev->vendor_data = data;
		ret = 0;
		break;
	}
	mutex_unlock(&vfio_pci.vendor_drivers_lock);
	return ret;
}

static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct vfio_pci_device *vdev;
@@ -2477,7 +2571,11 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
		vfio_pci_set_power_state(vdev, PCI_D3hot);
	}

	if (probe_vendor_drivers(vdev))
		ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
	else
		ret = vfio_add_group_dev(&pdev->dev,
				vdev->vendor_driver->ops->device_ops, vdev);
	if (ret)
		goto out_power;
	return 0;
@@ -2517,6 +2615,12 @@ static void vfio_pci_remove(struct pci_dev *pdev)
		vfio_pci_set_power_state(vdev, PCI_D0);

	mutex_destroy(&vdev->ioeventfds_lock);

	if (vdev->vendor_driver) {
		vdev->vendor_driver->ops->remove(vdev->vendor_data);
		module_put(vdev->vendor_driver->ops->owner);
	}

	kfree(vdev->region);
	kfree(vdev->pm_save);
	kfree(vdev);
@@ -2868,6 +2972,9 @@ static int __init vfio_pci_init(void)
	if (ret)
		return ret;

	mutex_init(&vfio_pci.vendor_drivers_lock);
	INIT_LIST_HEAD(&vfio_pci.vendor_drivers_list);

	/* Register and scan for devices */
	ret = pci_register_driver(&vfio_pci_driver);
	if (ret)
@@ -2885,6 +2992,60 @@ static int __init vfio_pci_init(void)
	return ret;
}

int __vfio_pci_register_vendor_driver(struct vfio_pci_vendor_driver_ops *ops)
{
	struct vfio_pci_vendor_driver *driver, *tmp;

	if (!ops || !ops->device_ops)
		return -EINVAL;

	driver = kzalloc(sizeof(*driver), GFP_KERNEL);
	if (!driver)
		return -ENOMEM;

	driver->ops = ops;

	mutex_lock(&vfio_pci.vendor_drivers_lock);

	/* Check for duplicates */
	list_for_each_entry(tmp, &vfio_pci.vendor_drivers_list, next) {
		if (tmp->ops->device_ops == ops->device_ops) {
			mutex_unlock(&vfio_pci.vendor_drivers_lock);
			kfree(driver);
			return -EINVAL;
		}
	}

	list_add(&driver->next, &vfio_pci.vendor_drivers_list);

	mutex_unlock(&vfio_pci.vendor_drivers_lock);

	if (!try_module_get(THIS_MODULE))
		return -ENODEV;

	return 0;
}
EXPORT_SYMBOL_GPL(__vfio_pci_register_vendor_driver);

void vfio_pci_unregister_vendor_driver(struct vfio_device_ops *device_ops)
{
	struct vfio_pci_vendor_driver *driver, *tmp;

	mutex_lock(&vfio_pci.vendor_drivers_lock);
	list_for_each_entry_safe(driver, tmp,
				 &vfio_pci.vendor_drivers_list, next) {
		if (driver->ops->device_ops == device_ops) {
			list_del(&driver->next);
			mutex_unlock(&vfio_pci.vendor_drivers_lock);
			kfree(driver);
			module_put(THIS_MODULE);
			return;
		}
	}
	mutex_unlock(&vfio_pci.vendor_drivers_lock);
}
EXPORT_SYMBOL_GPL(vfio_pci_unregister_vendor_driver);

module_init(vfio_pci_init);
module_exit(vfio_pci_cleanup);

+9 −0
Original line number Diff line number Diff line
@@ -112,6 +112,11 @@ struct vfio_pci_mmap_vma {
	struct list_head	vma_next;
};

struct vfio_pci_vendor_driver {
	const struct vfio_pci_vendor_driver_ops *ops;
	struct list_head                        next;
};

struct vfio_pci_device {
	struct pci_dev		*pdev;
	void __iomem		*barmap[PCI_STD_NUM_BARS];
@@ -127,6 +132,8 @@ struct vfio_pci_device {
	struct vfio_ext_irq	*ext_irqs;
	int			num_ext_irqs;
	int			num_regions;
	int			num_vendor_regions;
	int			num_vendor_irqs;
	struct vfio_pci_region	*region;
	u8			msi_qmax;
	u8			msix_bar;
@@ -163,6 +170,8 @@ struct vfio_pci_device {
	struct mutex		vma_lock;
	struct list_head	vma_list;
	struct rw_semaphore	memory_lock;
	void			*vendor_data;
	struct vfio_pci_vendor_driver	*vendor_driver;
};

#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
+10 −0
Original line number Diff line number Diff line
@@ -224,6 +224,16 @@ static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar)
	return 0;
}

void __iomem *vfio_pci_get_barmap(void *device_data, int bar)
{
	int ret;
	struct vfio_pci_device *vdev = device_data;

	ret = vfio_pci_setup_barmap(vdev, bar);
	return ret ? ERR_PTR(ret) : vdev->barmap[bar];
}
EXPORT_SYMBOL_GPL(vfio_pci_get_barmap);

ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
			size_t count, loff_t *ppos, bool iswrite)
{
+52 −0
Original line number Diff line number Diff line
@@ -214,4 +214,56 @@ extern int vfio_virqfd_enable(void *opaque,
			      void *data, struct virqfd **pvirqfd, int fd);
extern void vfio_virqfd_disable(struct virqfd **pvirqfd);

extern int vfio_pci_num_regions(void *device_data);
extern struct pci_dev *vfio_pci_pdev(void *device_data);
extern long vfio_pci_ioctl(void *device_data,
			  unsigned int cmd, unsigned long arg);
extern ssize_t vfio_pci_read(void *device_data, char __user *buf,
			     size_t count, loff_t *ppos);
extern ssize_t vfio_pci_write(void *device_data, const char __user *buf,
			      size_t count, loff_t *ppos);
extern int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma);
extern void vfio_pci_request(void *device_data, unsigned int count);
extern int vfio_pci_open(void *device_data);
extern void vfio_pci_release(void *device_data);
extern void *vfio_pci_vendor_data(void *device_data);
extern int vfio_pci_set_vendor_regions(void *device_data,
					int num_vendor_regions);

struct vfio_pci_vendor_driver_ops {
	char			*name;
	struct module		*owner;
	void			*(*probe)(struct pci_dev *pdev);
	void			(*remove)(void *vendor_data);
	struct vfio_device_ops *device_ops;
};
int __vfio_pci_register_vendor_driver(struct vfio_pci_vendor_driver_ops *ops);
void vfio_pci_unregister_vendor_driver(struct vfio_device_ops *device_ops);

#define vfio_pci_register_vendor_driver(__name, __probe, __remove,	\
					__device_ops)			\
static struct vfio_pci_vendor_driver_ops  __ops ## _node = {		\
	.owner		= THIS_MODULE,					\
	.name		= __name,					\
	.probe		= __probe,					\
	.remove		= __remove,					\
	.device_ops	= __device_ops,					\
};									\
__vfio_pci_register_vendor_driver(&__ops ## _node)

#define module_vfio_pci_register_vendor_handler(name, probe, remove,	\
						device_ops)		\
static int __init device_ops ## _module_init(void)			\
{									\
	vfio_pci_register_vendor_driver(name, probe, remove,		\
					device_ops);			\
	return 0;							\
};									\
static void __exit device_ops ## _module_exit(void)			\
{									\
	vfio_pci_unregister_vendor_driver(device_ops);			\
};									\
module_init(device_ops ## _module_init);				\
module_exit(device_ops ## _module_exit)

#endif /* VFIO_H */