提交 58754e77 编写于 作者: L Longfang Liu 提交者: Zheng Zengkai

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: NLongfang Liu <liulongfang@huawei.com>
Reviewed-by: NHao Fang <fanghao11@huawei.com>
Reviewed-by: NMingqiang Ling <lingmingqiang@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 716406d9
...@@ -74,6 +74,61 @@ static inline bool vfio_vga_disabled(void) ...@@ -74,6 +74,61 @@ static inline bool vfio_vga_disabled(void)
#endif #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) static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)
{ {
switch (pdev->vendor) { switch (pdev->vendor) {
...@@ -914,7 +969,7 @@ static void vfio_pci_vf_token_user_add(struct vfio_pci_device *vdev, int val) ...@@ -914,7 +969,7 @@ static void vfio_pci_vf_token_user_add(struct vfio_pci_device *vdev, int val)
vfio_device_put(pf_dev); 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; struct vfio_pci_device *vdev = device_data;
...@@ -941,8 +996,9 @@ static void vfio_pci_release(void *device_data) ...@@ -941,8 +996,9 @@ static void vfio_pci_release(void *device_data)
module_put(THIS_MODULE); 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; struct vfio_pci_device *vdev = device_data;
int ret = 0; int ret = 0;
...@@ -967,6 +1023,7 @@ static int vfio_pci_open(void *device_data) ...@@ -967,6 +1023,7 @@ static int vfio_pci_open(void *device_data)
module_put(THIS_MODULE); module_put(THIS_MODULE);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(vfio_pci_open);
static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
{ {
...@@ -1161,7 +1218,7 @@ struct vfio_devices { ...@@ -1161,7 +1218,7 @@ struct vfio_devices {
int max_index; int max_index;
}; };
static long vfio_pci_ioctl(void *device_data, long vfio_pci_ioctl(void *device_data,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct vfio_pci_device *vdev = device_data; struct vfio_pci_device *vdev = device_data;
...@@ -1193,8 +1250,10 @@ static long vfio_pci_ioctl(void *device_data, ...@@ -1193,8 +1250,10 @@ static long vfio_pci_ioctl(void *device_data,
if (vdev->reset_works) if (vdev->reset_works)
info.flags |= VFIO_DEVICE_FLAGS_RESET; info.flags |= VFIO_DEVICE_FLAGS_RESET;
info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions; info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions +
info.num_irqs = VFIO_PCI_NUM_IRQS + vdev->num_ext_irqs; 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)) { if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV)) {
int ret = vfio_pci_info_zdev_add_caps(vdev, &caps); int ret = vfio_pci_info_zdev_add_caps(vdev, &caps);
...@@ -1819,6 +1878,7 @@ static long vfio_pci_ioctl(void *device_data, ...@@ -1819,6 +1878,7 @@ static long vfio_pci_ioctl(void *device_data,
return -ENOTTY; return -ENOTTY;
} }
EXPORT_SYMBOL_GPL(vfio_pci_ioctl);
static ssize_t vfio_pci_rw(void *device_data, char __user *buf, static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
size_t count, loff_t *ppos, bool iswrite) size_t count, loff_t *ppos, bool iswrite)
...@@ -1852,7 +1912,7 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, ...@@ -1852,7 +1912,7 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
return -EINVAL; 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) size_t count, loff_t *ppos)
{ {
if (!count) if (!count)
...@@ -1860,8 +1920,9 @@ static ssize_t vfio_pci_read(void *device_data, char __user *buf, ...@@ -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); 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) size_t count, loff_t *ppos)
{ {
if (!count) if (!count)
...@@ -1869,6 +1930,7 @@ static ssize_t vfio_pci_write(void *device_data, const char __user *buf, ...@@ -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); 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) */ /* 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) 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 = { ...@@ -2064,7 +2126,7 @@ static const struct vm_operations_struct vfio_pci_mmap_ops = {
.fault = vfio_pci_mmap_fault, .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 vfio_pci_device *vdev = device_data;
struct pci_dev *pdev = vdev->pdev; struct pci_dev *pdev = vdev->pdev;
...@@ -2133,8 +2195,9 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) ...@@ -2133,8 +2195,9 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
return 0; 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 vfio_pci_device *vdev = device_data;
struct pci_dev *pdev = vdev->pdev; struct pci_dev *pdev = vdev->pdev;
...@@ -2154,6 +2217,7 @@ static void vfio_pci_request(void *device_data, unsigned int count) ...@@ -2154,6 +2217,7 @@ static void vfio_pci_request(void *device_data, unsigned int count)
mutex_unlock(&vdev->igate); mutex_unlock(&vdev->igate);
} }
EXPORT_SYMBOL_GPL(vfio_pci_request);
static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev, static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev,
bool vf_token, uuid_t *uuid) bool vf_token, uuid_t *uuid)
...@@ -2250,7 +2314,7 @@ static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev, ...@@ -2250,7 +2314,7 @@ static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev,
#define VF_TOKEN_ARG "vf_token=" #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; struct vfio_pci_device *vdev = device_data;
bool vf_token = false; bool vf_token = false;
...@@ -2298,6 +2362,7 @@ static int vfio_pci_match(void *device_data, char *buf) ...@@ -2298,6 +2362,7 @@ static int vfio_pci_match(void *device_data, char *buf)
return 1; /* Match */ return 1; /* Match */
} }
EXPORT_SYMBOL_GPL(vfio_pci_match);
static const struct vfio_device_ops vfio_pci_ops = { static const struct vfio_device_ops vfio_pci_ops = {
.name = "vfio-pci", .name = "vfio-pci",
...@@ -2404,6 +2469,35 @@ static void vfio_pci_vga_uninit(struct vfio_pci_device *vdev) ...@@ -2404,6 +2469,35 @@ static void vfio_pci_vga_uninit(struct vfio_pci_device *vdev)
VGA_RSRC_LEGACY_MEM); 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) static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct vfio_pci_device *vdev; struct vfio_pci_device *vdev;
...@@ -2477,7 +2571,11 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -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); vfio_pci_set_power_state(vdev, PCI_D3hot);
} }
ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); 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) if (ret)
goto out_power; goto out_power;
return 0; return 0;
...@@ -2517,6 +2615,12 @@ static void vfio_pci_remove(struct pci_dev *pdev) ...@@ -2517,6 +2615,12 @@ static void vfio_pci_remove(struct pci_dev *pdev)
vfio_pci_set_power_state(vdev, PCI_D0); vfio_pci_set_power_state(vdev, PCI_D0);
mutex_destroy(&vdev->ioeventfds_lock); 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->region);
kfree(vdev->pm_save); kfree(vdev->pm_save);
kfree(vdev); kfree(vdev);
...@@ -2868,6 +2972,9 @@ static int __init vfio_pci_init(void) ...@@ -2868,6 +2972,9 @@ static int __init vfio_pci_init(void)
if (ret) if (ret)
return ret; return ret;
mutex_init(&vfio_pci.vendor_drivers_lock);
INIT_LIST_HEAD(&vfio_pci.vendor_drivers_list);
/* Register and scan for devices */ /* Register and scan for devices */
ret = pci_register_driver(&vfio_pci_driver); ret = pci_register_driver(&vfio_pci_driver);
if (ret) if (ret)
...@@ -2885,6 +2992,60 @@ static int __init vfio_pci_init(void) ...@@ -2885,6 +2992,60 @@ static int __init vfio_pci_init(void)
return ret; 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_init(vfio_pci_init);
module_exit(vfio_pci_cleanup); module_exit(vfio_pci_cleanup);
......
...@@ -112,6 +112,11 @@ struct vfio_pci_mmap_vma { ...@@ -112,6 +112,11 @@ struct vfio_pci_mmap_vma {
struct list_head vma_next; 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 vfio_pci_device {
struct pci_dev *pdev; struct pci_dev *pdev;
void __iomem *barmap[PCI_STD_NUM_BARS]; void __iomem *barmap[PCI_STD_NUM_BARS];
...@@ -127,6 +132,8 @@ struct vfio_pci_device { ...@@ -127,6 +132,8 @@ struct vfio_pci_device {
struct vfio_ext_irq *ext_irqs; struct vfio_ext_irq *ext_irqs;
int num_ext_irqs; int num_ext_irqs;
int num_regions; int num_regions;
int num_vendor_regions;
int num_vendor_irqs;
struct vfio_pci_region *region; struct vfio_pci_region *region;
u8 msi_qmax; u8 msi_qmax;
u8 msix_bar; u8 msix_bar;
...@@ -163,6 +170,8 @@ struct vfio_pci_device { ...@@ -163,6 +170,8 @@ struct vfio_pci_device {
struct mutex vma_lock; struct mutex vma_lock;
struct list_head vma_list; struct list_head vma_list;
struct rw_semaphore memory_lock; 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) #define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
......
...@@ -224,6 +224,16 @@ static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar) ...@@ -224,6 +224,16 @@ static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar)
return 0; 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, ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite) size_t count, loff_t *ppos, bool iswrite)
{ {
......
...@@ -214,4 +214,56 @@ extern int vfio_virqfd_enable(void *opaque, ...@@ -214,4 +214,56 @@ extern int vfio_virqfd_enable(void *opaque,
void *data, struct virqfd **pvirqfd, int fd); void *data, struct virqfd **pvirqfd, int fd);
extern void vfio_virqfd_disable(struct virqfd **pvirqfd); 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 */ #endif /* VFIO_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册