* [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-09 15:00 ` Steffen Eiden
2026-04-09 18:59 ` Sean Christopherson
2026-04-07 18:01 ` [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio Paolo Bonzini
` (3 subsequent siblings)
4 siblings, 2 replies; 10+ 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] 10+ messages in thread* Re: [PATCH 1/3] VFIO: take reference to the KVM module
2026-04-07 18:01 ` [PATCH 1/3] VFIO: take reference to the KVM module Paolo Bonzini
@ 2026-04-09 15:00 ` Steffen Eiden
2026-04-09 18:59 ` Sean Christopherson
1 sibling, 0 replies; 10+ messages in thread
From: Steffen Eiden @ 2026-04-09 15:00 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: linux-kernel, kvm, Alex Williamson
On Tue, Apr 07, 2026 at 08:01:05PM +0200, Paolo Bonzini wrote:
> 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>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] VFIO: take reference to the KVM module
2026-04-07 18:01 ` [PATCH 1/3] VFIO: take reference to the KVM module Paolo Bonzini
2026-04-09 15:00 ` Steffen Eiden
@ 2026-04-09 18:59 ` Sean Christopherson
1 sibling, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-04-09 18:59 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: linux-kernel, kvm, Steffen Eiden, Alex Williamson
On Tue, Apr 07, 2026, Paolo Bonzini wrote:
> 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.
NAK? :-)
I really don't think we should do this. We're reinventing the wheel, and probably
doing so poorly. As Jason suggested, the proper way to handle this is to pass
a "struct file" so that e.g. fops_get() pins kvm.ko for us.
https://lore.kernel.org/all/20231203140756.GI1489931@ziepe.ca
^ permalink raw reply [flat|nested] 10+ 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-09 15:01 ` Steffen Eiden
2026-04-07 18:01 ` [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) " Paolo Bonzini
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ 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] 10+ messages in thread* Re: [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio
2026-04-07 18:01 ` [PATCH 2/3] KVM, vfio: remove symbol_get(kvm_get_kvm_safe) from vfio Paolo Bonzini
@ 2026-04-09 15:01 ` Steffen Eiden
0 siblings, 0 replies; 10+ messages in thread
From: Steffen Eiden @ 2026-04-09 15:01 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: linux-kernel, kvm, Alex Williamson
On Tue, Apr 07, 2026 at 08:01:06PM +0200, Paolo Bonzini wrote:
> 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>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
^ permalink raw reply [flat|nested] 10+ 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-09 15:02 ` Steffen Eiden
2026-04-07 20:16 ` [PATCH 0/3] KVM, vfio: remove exported KVM symbols Alex Williamson
2026-04-09 15:06 ` Steffen Eiden
4 siblings, 1 reply; 10+ 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] 10+ messages in thread* Re: [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) from vfio
2026-04-07 18:01 ` [PATCH 3/3] KVM, vfio: remove symbol_get(kvm_put_kvm) " Paolo Bonzini
@ 2026-04-09 15:02 ` Steffen Eiden
0 siblings, 0 replies; 10+ messages in thread
From: Steffen Eiden @ 2026-04-09 15:02 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: linux-kernel, kvm, Alex Williamson
On Tue, Apr 07, 2026 at 08:01:07PM +0200, Paolo Bonzini wrote:
> 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>
^ permalink raw reply [flat|nested] 10+ 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
2026-04-09 15:06 ` Steffen Eiden
4 siblings, 0 replies; 10+ 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] 10+ 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
` (3 preceding siblings ...)
2026-04-07 20:16 ` [PATCH 0/3] KVM, vfio: remove exported KVM symbols Alex Williamson
@ 2026-04-09 15:06 ` Steffen Eiden
4 siblings, 0 replies; 10+ messages in thread
From: Steffen Eiden @ 2026-04-09 15:06 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: linux-kernel, kvm, Alex Williamson
On Tue, Apr 07, 2026 at 08:01:04PM +0200, Paolo Bonzini 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
>
...
Thanks for sending/fixing the issue. LGTM.
For v2 of the arm-on-s390 series I'll drop the (first )three patches and
depend on this series. So we can merge them early. OK?
Steffen
^ permalink raw reply [flat|nested] 10+ messages in thread