[PATCH 04/10] vfio: Use a struct of function pointers instead of a many symbol_get()'s

Jason Gunthorpe
2022-04-14T18:46:30Z

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@nvidia.com>
---
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