* [PATCH 0/3] KVM, vfio: remove exported KVM symbols
@ 2026-04-07 18:01 Paolo Bonzini
2026-04-07 18:01 ` [PATCH 1/3] VFIO: take reference to the KVM module Paolo Bonzini
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Paolo Bonzini @ 2026-04-07 18:01 UTC (permalink / raw)
To: linux-kernel, kvm; +Cc: Steffen Eiden, Alex Williamson
KVM right now exports three symbols for the whole kernel to manage
the reference count of "struct kvm"; these are used by VFIO just to
keep the "struct kvm" alive. This can mostly be replaced
by inlines, and in fact the entire definition of struct kvm can be
made opaque to VFIO.
Besides the cleanup of removing the sort-of-deprecated symbol_get() and
the bidirectional dependency between KVM and VFIO, this is useful for
the recently posted support for Arm VMs on s390. In that scenario each
KVM implementation module wants to have its own copy of kvm_put_kvm()
in order to call the "right" kvm_destroy_vm(). With multiple modules,
VFIO has no way to do a symbol_get() from the right module.
With this series, that problem is gone because kvm_put_kvm dispatches
through a function pointer, set by whichever implementation creates the VM.
The main issue is that symbol_get() was implicitly taking a reference
to the KVM module, and that has to be preserved. This is the purpose
of patch 1.
Thanks,
Paolo
Paolo Bonzini (3):
VFIO: take reference to the KVM module
KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio
KVM, vfio: remove symbol_get(kvm_put_kvm) from vfio
arch/x86/kvm/mmu/tdp_mmu.c | 2 +-
arch/x86/kvm/vmx/nested.h | 4 +--
drivers/vfio/device_cdev.c | 2 +-
drivers/vfio/group.c | 5 ++--
drivers/vfio/vfio.h | 15 +++++++----
drivers/vfio/vfio_main.c | 51 ++++++++++++++------------------------
include/linux/kvm_host.h | 9 +++----
include/linux/kvm_types.h | 30 ++++++++++++++++++++++
include/linux/vfio.h | 4 +--
virt/kvm/kvm_main.c | 34 ++++++-------------------
virt/kvm/vfio.c | 4 +--
11 files changed, 80 insertions(+), 80 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] VFIO: take reference to the KVM module
2026-04-07 18:01 [PATCH 0/3] KVM, vfio: remove exported KVM symbols Paolo Bonzini
@ 2026-04-07 18:01 ` Paolo Bonzini
2026-04-07 18:01 ` [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio Paolo Bonzini
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Paolo Bonzini @ 2026-04-07 18:01 UTC (permalink / raw)
To: linux-kernel, kvm; +Cc: Steffen Eiden, Alex Williamson
VFIO is implicitly taking a reference to the KVM module between
vfio_device_get_kvm_safe and vfio_device_put_kvm, thanks to
symbol_get and symbol_put.
In preparation for removing symbol_get and symbol_put themselves
from VFIO, actually store a pointer to the KVM module and use
module_get()/module_put() to keep KVM alive.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
drivers/vfio/device_cdev.c | 2 +-
drivers/vfio/group.c | 5 +++--
drivers/vfio/vfio.h | 15 ++++++++++-----
drivers/vfio/vfio_main.c | 39 +++++++++++++++++++++++---------------
include/linux/vfio.h | 3 ++-
virt/kvm/vfio.c | 4 ++--
6 files changed, 42 insertions(+), 26 deletions(-)
diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c
index 8ceca24ac136..a67d7215c239 100644
--- a/drivers/vfio/device_cdev.c
+++ b/drivers/vfio/device_cdev.c
@@ -56,7 +56,7 @@ int vfio_device_fops_cdev_open(struct inode *inode, struct file *filep)
static void vfio_df_get_kvm_safe(struct vfio_device_file *df)
{
spin_lock(&df->kvm_ref_lock);
- vfio_device_get_kvm_safe(df->device, df->kvm);
+ vfio_device_get_kvm_safe(df->device, df->kvm, df->kvm_module);
spin_unlock(&df->kvm_ref_lock);
}
diff --git a/drivers/vfio/group.c b/drivers/vfio/group.c
index 4f15016d2a5f..7d28f45fefaa 100644
--- a/drivers/vfio/group.c
+++ b/drivers/vfio/group.c
@@ -158,7 +158,7 @@ static int vfio_group_ioctl_set_container(struct vfio_group *group,
static void vfio_device_group_get_kvm_safe(struct vfio_device *device)
{
spin_lock(&device->group->kvm_ref_lock);
- vfio_device_get_kvm_safe(device, device->group->kvm);
+ vfio_device_get_kvm_safe(device, device->group->kvm, device->group->kvm_module);
spin_unlock(&device->group->kvm_ref_lock);
}
@@ -858,10 +858,11 @@ bool vfio_group_enforced_coherent(struct vfio_group *group)
return ret;
}
-void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm)
+void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm, struct module *kvm_module)
{
spin_lock(&group->kvm_ref_lock);
group->kvm = kvm;
+ group->kvm_module = kvm_module;
spin_unlock(&group->kvm_ref_lock);
}
diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h
index 50128da18bca..a0c38f89b30a 100644
--- a/drivers/vfio/vfio.h
+++ b/drivers/vfio/vfio.h
@@ -22,8 +22,9 @@ struct vfio_device_file {
u8 access_granted;
u32 devid; /* only valid when iommufd is valid */
- spinlock_t kvm_ref_lock; /* protect kvm field */
+ spinlock_t kvm_ref_lock; /* protect kvm and kvm_module fields */
struct kvm *kvm;
+ struct module *kvm_module;
struct iommufd_ctx *iommufd; /* protected by struct vfio_device_set::lock */
};
@@ -89,6 +90,7 @@ struct vfio_group {
enum vfio_group_type type;
struct mutex group_lock;
struct kvm *kvm;
+ struct module *kvm_module;
struct file *opened_file;
struct blocking_notifier_head notifier;
struct iommufd_ctx *iommufd;
@@ -108,7 +110,7 @@ void vfio_device_group_unuse_iommu(struct vfio_device *device);
void vfio_df_group_close(struct vfio_device_file *df);
struct vfio_group *vfio_group_from_file(struct file *file);
bool vfio_group_enforced_coherent(struct vfio_group *group);
-void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm);
+void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm, struct module *kvm_module);
bool vfio_device_has_container(struct vfio_device *device);
int __init vfio_group_init(void);
void vfio_group_cleanup(void);
@@ -171,7 +173,8 @@ static inline bool vfio_group_enforced_coherent(struct vfio_group *group)
return true;
}
-static inline void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm)
+static inline void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm,
+ struct module *kvm_module)
{
}
@@ -435,11 +438,13 @@ static inline void vfio_virqfd_exit(void)
#endif
#if IS_ENABLED(CONFIG_KVM)
-void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm);
+void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm,
+ struct module *kvm_module);
void vfio_device_put_kvm(struct vfio_device *device);
#else
static inline void vfio_device_get_kvm_safe(struct vfio_device *device,
- struct kvm *kvm)
+ struct kvm *kvm,
+ struct module *kvm_module)
{
}
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index 742477546b15..d1bbc42d484a 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -433,7 +433,7 @@ void vfio_unregister_group_dev(struct vfio_device *device)
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)
+void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm, struct module *kvm_module)
{
void (*pfn)(struct kvm *kvm);
bool (*fn)(struct kvm *kvm);
@@ -444,25 +444,31 @@ void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm)
if (!kvm)
return;
- pfn = symbol_get(kvm_put_kvm);
- if (WARN_ON(!pfn))
+ if (!try_module_get(kvm_module))
return;
+ pfn = symbol_get(kvm_put_kvm);
+ if (WARN_ON(!pfn))
+ goto out_put_mod;
+
fn = symbol_get(kvm_get_kvm_safe);
- if (WARN_ON(!fn)) {
- symbol_put(kvm_put_kvm);
- return;
- }
+ if (WARN_ON(!fn))
+ goto out_put_sym;
ret = fn(kvm);
symbol_put(kvm_get_kvm_safe);
- if (!ret) {
- symbol_put(kvm_put_kvm);
- return;
- }
+ 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);
}
void vfio_device_put_kvm(struct vfio_device *device)
@@ -481,6 +487,8 @@ void vfio_device_put_kvm(struct vfio_device *device)
clear:
device->kvm = NULL;
+ module_put(device->kvm_module);
+ device->kvm_module = NULL;
}
#endif
@@ -1483,7 +1491,7 @@ bool vfio_file_enforced_coherent(struct file *file)
}
EXPORT_SYMBOL_GPL(vfio_file_enforced_coherent);
-static void vfio_device_file_set_kvm(struct file *file, struct kvm *kvm)
+static void vfio_device_file_set_kvm(struct file *file, struct kvm *kvm, struct module *kvm_module)
{
struct vfio_device_file *df = file->private_data;
@@ -1494,6 +1502,7 @@ static void vfio_device_file_set_kvm(struct file *file, struct kvm *kvm)
*/
spin_lock(&df->kvm_ref_lock);
df->kvm = kvm;
+ df->kvm_module = kvm_module;
spin_unlock(&df->kvm_ref_lock);
}
@@ -1505,16 +1514,16 @@ static void vfio_device_file_set_kvm(struct file *file, struct kvm *kvm)
* When a VFIO device is first opened the KVM will be available in
* device->kvm if one was associated with the file.
*/
-void vfio_file_set_kvm(struct file *file, struct kvm *kvm)
+void vfio_file_set_kvm(struct file *file, struct kvm *kvm, struct module *kvm_module)
{
struct vfio_group *group;
group = vfio_group_from_file(file);
if (group)
- vfio_group_set_kvm(group, kvm);
+ vfio_group_set_kvm(group, kvm, kvm_module);
if (vfio_device_from_file(file))
- vfio_device_file_set_kvm(file, kvm);
+ vfio_device_file_set_kvm(file, kvm, kvm_module);
}
EXPORT_SYMBOL_GPL(vfio_file_set_kvm);
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index e90859956514..69a8d527b0e8 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -53,6 +53,7 @@ struct vfio_device {
struct list_head dev_set_list;
unsigned int migration_flags;
struct kvm *kvm;
+ struct module *kvm_module;
/* Members below here are private, not for driver use */
unsigned int index;
@@ -339,7 +340,7 @@ static inline bool vfio_file_has_dev(struct file *file, struct vfio_device *devi
#endif
bool vfio_file_is_valid(struct file *file);
bool vfio_file_enforced_coherent(struct file *file);
-void vfio_file_set_kvm(struct file *file, struct kvm *kvm);
+void vfio_file_set_kvm(struct file *file, struct kvm *kvm, struct module *kvm_module);
#define VFIO_PIN_PAGES_MAX_ENTRIES (PAGE_SIZE/sizeof(unsigned long))
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
index 9f9acb66cc1e..515ed445d8e1 100644
--- a/virt/kvm/vfio.c
+++ b/virt/kvm/vfio.c
@@ -37,13 +37,13 @@ struct kvm_vfio {
static void kvm_vfio_file_set_kvm(struct file *file, struct kvm *kvm)
{
- void (*fn)(struct file *file, struct kvm *kvm);
+ void (*fn)(struct file *file, struct kvm *kvm, struct module *kvm_module);
fn = symbol_get(vfio_file_set_kvm);
if (!fn)
return;
- fn(file, kvm);
+ fn(file, kvm, kvm ? THIS_MODULE : NULL);
symbol_put(vfio_file_set_kvm);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio
2026-04-07 18:01 [PATCH 0/3] KVM, vfio: remove exported KVM symbols Paolo Bonzini
2026-04-07 18:01 ` [PATCH 1/3] VFIO: take reference to the KVM module Paolo Bonzini
@ 2026-04-07 18:01 ` Paolo Bonzini
2026-04-07 18:01 ` [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) " Paolo Bonzini
2026-04-07 20:16 ` [PATCH 0/3] KVM, vfio: remove exported KVM symbols Alex Williamson
3 siblings, 0 replies; 5+ messages in thread
From: Paolo Bonzini @ 2026-04-07 18:01 UTC (permalink / raw)
To: linux-kernel, kvm; +Cc: Steffen Eiden, Alex Williamson
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.
However, usage of symbol_get is mostly deprecated and there are just a
handful of users left. In the case of VFIO, in particular, the
functions it calls can be made inline. Start with kvm_get_kvm_safe,
for which it is trivial to do so.
While at it, move the function from kvm_host.h to kvm_types.h.
Unlike e.g. drivers/s390/crypto/vfio_ap_ops.c, there's no need for
VFIO to know any implementation details of KVM, and struct kvm
can be treated as an opaque type.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
arch/x86/kvm/mmu/tdp_mmu.c | 2 +-
arch/x86/kvm/vmx/nested.h | 4 ++--
drivers/vfio/vfio_main.c | 8 +-------
include/linux/kvm_host.h | 8 ++++----
include/linux/kvm_types.h | 22 ++++++++++++++++++++++
virt/kvm/kvm_main.c | 27 ++++++---------------------
6 files changed, 36 insertions(+), 35 deletions(-)
diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c
index 9c26038f6b77..a88686b5db24 100644
--- a/arch/x86/kvm/mmu/tdp_mmu.c
+++ b/arch/x86/kvm/mmu/tdp_mmu.c
@@ -1136,7 +1136,7 @@ void kvm_tdp_mmu_invalidate_roots(struct kvm *kvm,
* being destroyed in an error path of KVM_CREATE_VM.
*/
if (IS_ENABLED(CONFIG_PROVE_LOCKING) &&
- refcount_read(&kvm->users_count) && kvm->created_vcpus)
+ refcount_read(&kvm->rc.users_count) && kvm->created_vcpus)
lockdep_assert_held_write(&kvm->mmu_lock);
/*
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index 213a448104af..2c83fc905698 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -58,7 +58,7 @@ bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, unsigned int port,
static inline struct vmcs12 *get_vmcs12(struct kvm_vcpu *vcpu)
{
lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
- !refcount_read(&vcpu->kvm->users_count));
+ !refcount_read(&vcpu->kvm->rc.users_count));
return to_vmx(vcpu)->nested.cached_vmcs12;
}
@@ -66,7 +66,7 @@ static inline struct vmcs12 *get_vmcs12(struct kvm_vcpu *vcpu)
static inline struct vmcs12 *get_shadow_vmcs12(struct kvm_vcpu *vcpu)
{
lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
- !refcount_read(&vcpu->kvm->users_count));
+ !refcount_read(&vcpu->kvm->rc.users_count));
return to_vmx(vcpu)->nested.cached_shadow_vmcs12;
}
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index d1bbc42d484a..cb6eaabd64ce 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -436,7 +436,6 @@ EXPORT_SYMBOL_GPL(vfio_unregister_group_dev);
void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm, struct module *kvm_module)
{
void (*pfn)(struct kvm *kvm);
- bool (*fn)(struct kvm *kvm);
bool ret;
lockdep_assert_held(&device->dev_set->lock);
@@ -451,12 +450,7 @@ void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm, struc
if (WARN_ON(!pfn))
goto out_put_mod;
- fn = symbol_get(kvm_get_kvm_safe);
- if (WARN_ON(!fn))
- goto out_put_sym;
-
- ret = fn(kvm);
- symbol_put(kvm_get_kvm_safe);
+ ret = kvm_get_kvm_safe(kvm);
if (!ret)
goto out_put_sym;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 6b76e7a6f4c2..5163b541c82d 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -767,6 +767,9 @@ struct kvm_memslots {
};
struct kvm {
+ /* Must be the first field, see function definitions in kvm_types.h. */
+ struct kvm_refcount rc;
+
#ifdef KVM_HAVE_MMU_RWLOCK
rwlock_t mmu_lock;
#else
@@ -830,7 +833,6 @@ struct kvm {
struct list_head ioeventfds;
struct kvm_vm_stat stat;
struct kvm_arch arch;
- refcount_t users_count;
#ifdef CONFIG_KVM_MMIO
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
spinlock_t ring_lock;
@@ -1062,8 +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_get_kvm(struct kvm *kvm);
-bool kvm_get_kvm_safe(struct kvm *kvm);
void kvm_put_kvm(struct kvm *kvm);
bool file_is_kvm(struct file *file);
void kvm_put_kvm_no_destroy(struct kvm *kvm);
@@ -1073,7 +1073,7 @@ static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
as_id = array_index_nospec(as_id, KVM_MAX_NR_ADDRESS_SPACES);
return srcu_dereference_check(kvm->memslots[as_id], &kvm->srcu,
lockdep_is_held(&kvm->slots_lock) ||
- !refcount_read(&kvm->users_count));
+ !refcount_read(&kvm->rc.users_count));
}
static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm)
diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
index a568d8e6f4e8..4cb68c71a13c 100644
--- a/include/linux/kvm_types.h
+++ b/include/linux/kvm_types.h
@@ -33,6 +33,7 @@
#include <linux/mutex.h>
#include <linux/spinlock_types.h>
+#include <linux/refcount.h>
struct kvm;
struct kvm_async_pf;
@@ -140,6 +141,27 @@ struct kvm_vcpu_stat_generic {
};
#define KVM_STATS_NAME_SIZE 48
+
+struct kvm_refcount {
+ refcount_t users_count;
+};
+
+static inline void kvm_get_kvm(struct kvm *kvm)
+{
+ struct kvm_refcount *rc = (struct kvm_refcount *)kvm;
+ refcount_inc(&rc->users_count);
+}
+
+/*
+ * A safe version of kvm_get_kvm(), making sure the vm is not being destroyed.
+ * Return true if kvm referenced successfully, false otherwise.
+ */
+static inline bool kvm_get_kvm_safe(struct kvm *kvm)
+{
+ struct kvm_refcount *rc = (struct kvm_refcount *)kvm;
+ return refcount_inc_not_zero(&rc->users_count);
+}
+
#endif /* !__ASSEMBLER__ */
#endif /* __KVM_TYPES_H__ */
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 9093251beb39..6e3796814da7 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1099,7 +1099,7 @@ static inline struct kvm_io_bus *kvm_get_bus_for_destruction(struct kvm *kvm,
enum kvm_bus idx)
{
return rcu_dereference_protected(kvm->buses[idx],
- !refcount_read(&kvm->users_count));
+ !refcount_read(&kvm->rc.users_count));
}
static struct kvm *kvm_create_vm(unsigned long type, const char *fdname)
@@ -1153,7 +1153,8 @@ static struct kvm *kvm_create_vm(unsigned long type, const char *fdname)
if (r)
goto out_err_no_irq_routing;
- refcount_set(&kvm->users_count, 1);
+ BUILD_BUG_ON(offsetof(struct kvm, rc) != 0);
+ refcount_set(&kvm->rc.users_count, 1);
for (i = 0; i < kvm_arch_nr_memslot_as_ids(kvm); i++) {
for (j = 0; j < 2; j++) {
@@ -1223,7 +1224,7 @@ static struct kvm *kvm_create_vm(unsigned long type, const char *fdname)
out_err_no_disable:
kvm_arch_destroy_vm(kvm);
out_err_no_arch_destroy_vm:
- WARN_ON_ONCE(!refcount_dec_and_test(&kvm->users_count));
+ WARN_ON_ONCE(!refcount_dec_and_test(&kvm->rc.users_count));
for (i = 0; i < KVM_NR_BUSES; i++)
kfree(kvm_get_bus_for_destruction(kvm, i));
kvm_free_irq_routing(kvm);
@@ -1316,25 +1317,9 @@ static void kvm_destroy_vm(struct kvm *kvm)
mmdrop(mm);
}
-void kvm_get_kvm(struct kvm *kvm)
-{
- refcount_inc(&kvm->users_count);
-}
-EXPORT_SYMBOL_GPL(kvm_get_kvm);
-
-/*
- * Make sure the vm is not during destruction, which is a safe version of
- * kvm_get_kvm(). Return true if kvm referenced successfully, false otherwise.
- */
-bool kvm_get_kvm_safe(struct kvm *kvm)
-{
- return refcount_inc_not_zero(&kvm->users_count);
-}
-EXPORT_SYMBOL_GPL(kvm_get_kvm_safe);
-
void kvm_put_kvm(struct kvm *kvm)
{
- if (refcount_dec_and_test(&kvm->users_count))
+ if (refcount_dec_and_test(&kvm->rc.users_count))
kvm_destroy_vm(kvm);
}
EXPORT_SYMBOL_GPL(kvm_put_kvm);
@@ -1348,7 +1333,7 @@ EXPORT_SYMBOL_GPL(kvm_put_kvm);
*/
void kvm_put_kvm_no_destroy(struct kvm *kvm)
{
- WARN_ON(refcount_dec_and_test(&kvm->users_count));
+ WARN_ON(refcount_dec_and_test(&kvm->rc.users_count));
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_put_kvm_no_destroy);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) from vfio
2026-04-07 18:01 [PATCH 0/3] KVM, vfio: remove exported KVM symbols Paolo Bonzini
2026-04-07 18:01 ` [PATCH 1/3] VFIO: take reference to the KVM module Paolo Bonzini
2026-04-07 18:01 ` [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio Paolo Bonzini
@ 2026-04-07 18:01 ` Paolo Bonzini
2026-04-07 20:16 ` [PATCH 0/3] KVM, vfio: remove exported KVM symbols Alex Williamson
3 siblings, 0 replies; 5+ messages in thread
From: Paolo Bonzini @ 2026-04-07 18:01 UTC (permalink / raw)
To: linux-kernel, kvm; +Cc: Steffen Eiden, Alex Williamson
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>
---
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.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 0/3] KVM, vfio: remove exported KVM symbols
2026-04-07 18:01 [PATCH 0/3] KVM, vfio: remove exported KVM symbols Paolo Bonzini
` (2 preceding siblings ...)
2026-04-07 18:01 ` [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) " Paolo Bonzini
@ 2026-04-07 20:16 ` Alex Williamson
3 siblings, 0 replies; 5+ messages in thread
From: Alex Williamson @ 2026-04-07 20:16 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: alex, linux-kernel, kvm, Steffen Eiden
On Tue, 7 Apr 2026 20:01:04 +0200
Paolo Bonzini <pbonzini@redhat.com> wrote:
> KVM right now exports three symbols for the whole kernel to manage
> the reference count of "struct kvm"; these are used by VFIO just to
> keep the "struct kvm" alive. This can mostly be replaced
> by inlines, and in fact the entire definition of struct kvm can be
> made opaque to VFIO.
>
> Besides the cleanup of removing the sort-of-deprecated symbol_get() and
> the bidirectional dependency between KVM and VFIO, this is useful for
> the recently posted support for Arm VMs on s390. In that scenario each
> KVM implementation module wants to have its own copy of kvm_put_kvm()
> in order to call the "right" kvm_destroy_vm(). With multiple modules,
> VFIO has no way to do a symbol_get() from the right module.
>
> With this series, that problem is gone because kvm_put_kvm dispatches
> through a function pointer, set by whichever implementation creates the VM.
> The main issue is that symbol_get() was implicitly taking a reference
> to the KVM module, and that has to be preserved. This is the purpose
> of patch 1.
>
> Thanks,
>
> Paolo
>
> Paolo Bonzini (3):
> VFIO: take reference to the KVM module
> KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio
> KVM, vfio: remove symbol_get(kvm_put_kvm) from vfio
>
> arch/x86/kvm/mmu/tdp_mmu.c | 2 +-
> arch/x86/kvm/vmx/nested.h | 4 +--
> drivers/vfio/device_cdev.c | 2 +-
> drivers/vfio/group.c | 5 ++--
> drivers/vfio/vfio.h | 15 +++++++----
> drivers/vfio/vfio_main.c | 51 ++++++++++++++------------------------
> include/linux/kvm_host.h | 9 +++----
> include/linux/kvm_types.h | 30 ++++++++++++++++++++++
> include/linux/vfio.h | 4 +--
> virt/kvm/kvm_main.c | 34 ++++++-------------------
> virt/kvm/vfio.c | 4 +--
> 11 files changed, 80 insertions(+), 80 deletions(-)
>
Looks clean.
Acked-by: Alex Williamson <alex@shazbot.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-04-07 20:16 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-07 18:01 [PATCH 0/3] KVM, vfio: remove exported KVM symbols Paolo Bonzini
2026-04-07 18:01 ` [PATCH 1/3] VFIO: take reference to the KVM module Paolo Bonzini
2026-04-07 18:01 ` [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio Paolo Bonzini
2026-04-07 18:01 ` [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) " Paolo Bonzini
2026-04-07 20:16 ` [PATCH 0/3] KVM, vfio: remove exported KVM symbols Alex Williamson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox