kvm and VFIO need to be coupled together however neither is willing to tolerate a direct module dependency. Instead when kvm is given a VFIO FD it uses many symbol_get()'s to access VFIO. Provide a single VFIO function vfio_file_get_ops() which validates the given struct file * is a VFIO file and then returns a struct of ops. Following patches will redo each of the symbol_get() calls into an indirection through this ops struct. Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx> --- drivers/vfio/vfio.c | 19 +++++++++++++++++++ include/linux/vfio.h | 3 +++ virt/kvm/vfio.c | 14 ++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index a4555014bd1e72..93508f6a8beda5 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -2013,6 +2013,25 @@ long vfio_external_check_extension(struct vfio_group *group, unsigned long arg) } EXPORT_SYMBOL_GPL(vfio_external_check_extension); +static const struct vfio_file_ops vfio_file_group_ops = { +}; + +/** + * vfio_file_get_ops - Return a struct of function pointers to use with the file + * @filep: VFIO file to return pointers for + * + * This API exists to allow KVM to call into VFIO without tightly coupling the + * VFIO and KVM modules together. KVM will call this using symbol_get() and from + * then on will call VFIO through the returned function pointers. + */ +const struct vfio_file_ops *vfio_file_get_ops(struct file *filep) +{ + if (filep->f_op != &vfio_group_fops) + return ERR_PTR(-EINVAL); + return &vfio_file_group_ops; +} +EXPORT_SYMBOL_GPL(vfio_file_get_ops); + /* * Sub-module support */ diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 66dda06ec42d1b..409bbf817206cc 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -138,6 +138,8 @@ int vfio_mig_get_next_state(struct vfio_device *device, /* * External user API */ +struct vfio_file_ops { +}; extern struct vfio_group *vfio_group_get_external_user(struct file *filep); extern void vfio_group_put_external_user(struct vfio_group *group); extern struct vfio_group *vfio_group_get_external_user_from_dev(struct device @@ -147,6 +149,7 @@ extern bool vfio_external_group_match_file(struct vfio_group *group, extern int vfio_external_user_iommu_id(struct vfio_group *group); extern long vfio_external_check_extension(struct vfio_group *group, unsigned long arg); +const struct vfio_file_ops *vfio_file_get_ops(struct file *filep); #define VFIO_PIN_PAGES_MAX_ENTRIES (PAGE_SIZE/sizeof(unsigned long)) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index 7e1793a1f5f1fd..254d8c18378163 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -34,6 +34,7 @@ static void kvm_spapr_tce_release_iommu_group(struct kvm *kvm, struct kvm_vfio_group { struct list_head node; struct file *filp; + const struct vfio_file_ops *ops; struct vfio_group *vfio_group; }; @@ -196,6 +197,7 @@ static void kvm_vfio_update_coherency(struct kvm_device *dev) static int kvm_vfio_group_add(struct kvm_device *dev, unsigned int fd) { + const struct vfio_file_ops *(*fn)(struct file *filep); struct kvm_vfio *kv = dev->private; struct vfio_group *vfio_group; struct kvm_vfio_group *kvg; @@ -221,6 +223,18 @@ static int kvm_vfio_group_add(struct kvm_device *dev, unsigned int fd) goto err_unlock; } + fn = symbol_get(vfio_file_get_ops); + if (!fn) { + ret = -EINVAL; + goto err_free; + } + kvg->ops = fn(filp); + symbol_put(vfio_file_get_ops); + if (IS_ERR(kvg->ops)) { + ret = PTR_ERR(kvg->ops); + goto err_free; + } + vfio_group = kvm_vfio_group_get_external_user(filp); if (IS_ERR(vfio_group)) { ret = PTR_ERR(vfio_group); -- 2.35.1