[RFC PATCH v2 03/28] KVM, vfio: remove symbol_get(kvm_put_kvm) from vfio

Steffen Eiden
2026-04-28T16:06:05Z

From: Paolo Bonzini <pbonzini@redhat.com>

Right now, KVM and VFIO are using symbol_get to access each other's
symbols because of a circular reference between the modules, as well
as to avoid loading them unnecessarily.

The remaining use in VFIO is for kvm_put_kvm, which is not inline
because it needs to call kvm_destroy_vm. However, storing the
address of kvm_destroy_vm in the "struct kvm" is enough to remove
the dependency from VFIO.

This also makes it possible to direct kvm_put_kvm to either the arm64
or the s390 implementation of kvm_destroy_vm.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Alex Williamson <alex@shazbot.org>
---
drivers/vfio/vfio_main.c | 24 +++---------------------
include/linux/kvm_host.h | 1 -
include/linux/kvm_types.h | 8 ++++++++
include/linux/vfio.h | 1 -
virt/kvm/kvm_main.c | 9 ++-------
5 files changed, 13 insertions(+), 30 deletions(-)

diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index cb6eaabd64ce..f27a1d00c21e 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -17,7 +17,7 @@
#include <linux/idr.h>
#include <linux/iommu.h>
#if IS_ENABLED(CONFIG_KVM)
-#include <linux/kvm_host.h>
+#include <linux/kvm_types.h>
#endif
#include <linux/list.h>
#include <linux/miscdevice.h>
@@ -435,9 +435,6 @@ EXPORT_SYMBOL_GPL(vfio_unregister_group_dev);
#if IS_ENABLED(CONFIG_KVM)
void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm, struct module *kvm_module)
{
- void (*pfn)(struct kvm *kvm);
- bool ret;
-
lockdep_assert_held(&device->dev_set->lock);

if (!kvm)
@@ -446,21 +443,13 @@ void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm, struc
if (!try_module_get(kvm_module))
return;

- pfn = symbol_get(kvm_put_kvm);
- if (WARN_ON(!pfn))
+ if (!kvm_get_kvm_safe(kvm))
goto out_put_mod;

- ret = kvm_get_kvm_safe(kvm);
- if (!ret)
- goto out_put_sym;
-
- device->put_kvm = pfn;
device->kvm = kvm;
device->kvm_module = kvm_module;
return;

-out_put_sym:
- symbol_put(kvm_put_kvm);
out_put_mod:
module_put(kvm_module);
}
@@ -472,14 +461,7 @@ void vfio_device_put_kvm(struct vfio_device *device)
if (!device->kvm)
return;

- if (WARN_ON(!device->put_kvm))
- goto clear;
-
- device->put_kvm(device->kvm);
- device->put_kvm = NULL;
- symbol_put(kvm_put_kvm);
-
-clear:
+ kvm_put_kvm(device->kvm);
device->kvm = NULL;
module_put(device->kvm_module);
device->kvm_module = NULL;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 5163b541c82d..ca06f9bda028 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1064,7 +1064,6 @@ static inline void kvm_irqfd_exit(void)
int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module);
void kvm_exit(void);

-void kvm_put_kvm(struct kvm *kvm);
bool file_is_kvm(struct file *file);
void kvm_put_kvm_no_destroy(struct kvm *kvm);

diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
index 4cb68c71a13c..8a9be07cb8dd 100644
--- a/include/linux/kvm_types.h
+++ b/include/linux/kvm_types.h
@@ -144,6 +144,7 @@ struct kvm_vcpu_stat_generic {

struct kvm_refcount {
refcount_t users_count;
+ void (*destroy)(struct kvm *kvm);
};

static inline void kvm_get_kvm(struct kvm *kvm)
@@ -162,6 +163,13 @@ static inline bool kvm_get_kvm_safe(struct kvm *kvm)
return refcount_inc_not_zero(&rc->users_count);
}

+static inline void kvm_put_kvm(struct kvm *kvm)
+{
+ struct kvm_refcount *rc = (struct kvm_refcount *)kvm;
+ if (refcount_dec_and_test(&rc->users_count))
+ rc->destroy(kvm);
+}
+
#endif /* !__ASSEMBLER__ */

#endif /* __KVM_TYPES_H__ */
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 69a8d527b0e8..5c69532d6127 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -65,7 +65,6 @@ struct vfio_device {
unsigned int open_count;
struct completion comp;
struct iommufd_access *iommufd_access;
- void (*put_kvm)(struct kvm *kvm);
struct inode *inode;
#if IS_ENABLED(CONFIG_IOMMUFD)
struct iommufd_device *iommufd_device;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 6e3796814da7..27a718a0bc01 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -120,6 +120,7 @@ static struct dentry *kvm_debugfs_dir;

static const struct file_operations stat_fops_per_vm;

+static void kvm_destroy_vm(struct kvm *kvm);
static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
unsigned long arg);
#ifdef CONFIG_KVM_COMPAT
@@ -1155,6 +1156,7 @@ static struct kvm *kvm_create_vm(unsigned long type, const char *fdname)

BUILD_BUG_ON(offsetof(struct kvm, rc) != 0);
refcount_set(&kvm->rc.users_count, 1);
+ kvm->rc.destroy = kvm_destroy_vm;

for (i = 0; i < kvm_arch_nr_memslot_as_ids(kvm); i++) {
for (j = 0; j < 2; j++) {
@@ -1317,13 +1319,6 @@ static void kvm_destroy_vm(struct kvm *kvm)
mmdrop(mm);
}

-void kvm_put_kvm(struct kvm *kvm)
-{
- if (refcount_dec_and_test(&kvm->rc.users_count))
- kvm_destroy_vm(kvm);
-}
-EXPORT_SYMBOL_GPL(kvm_put_kvm);
-
/*
* Used to put a reference that was taken on behalf of an object associated
* with a user-visible file descriptor, e.g. a vcpu or device, if installation
--
2.51.0