mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
KVM/vfio: Use file-based reference counting for KVM
Replace manual module reference counting with file-based reference counting for KVM integration. Previously, VFIO used symbol_get() to obtain function pointers for kvm_get_kvm_safe() and kvm_put_kvm(), then manually tracked module references through these symbols. This approach required storing the put_kvm function pointer in each device and carefully managing symbol references. Pass struct file pointers instead of struct kvm pointers throughout the VFIO-KVM interface, leveraging the kernel's existing file reference counting via get_file()/get_file_active() and fput(). Convert the x86 page-track API and update s390 vfio to use file_to_kvm_<arch>(). This simplifies the code. Suggested-by: Jason Gunthorpe <jgg@nvidia.com> Co-developed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> Acked-by: Alex Williamson <alex@shazbot.org> Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com> Acked-by: Janosch Frank <frankja@linux.ibm.com> Acked-by: Sean Christopherson <seanjc@google.com> [borntraeger@linux.ibm.com remove _safe from vfio_device_get_kvm_safe] Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
This commit is contained in:
parent
77037cf77a
commit
5c3ea6d3e8
|
|
@ -722,7 +722,7 @@ static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
|||
void kvm_arch_free_vm(struct kvm *kvm);
|
||||
|
||||
struct zpci_kvm_hook {
|
||||
int (*kvm_register)(void *opaque, struct kvm *kvm);
|
||||
int (*kvm_register)(void *opaque, struct file *kvm_file);
|
||||
void (*kvm_unregister)(void *opaque);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -498,17 +498,22 @@ static void kvm_s390_pci_dev_release(struct zpci_dev *zdev)
|
|||
* available, enable them and let userspace indicate whether or not they will
|
||||
* be used (specify SHM bit to disable).
|
||||
*/
|
||||
static int kvm_s390_pci_register_kvm(void *opaque, struct kvm *kvm)
|
||||
static int kvm_s390_pci_register_kvm(void *opaque, struct file *kvm_file)
|
||||
{
|
||||
struct zpci_dev *zdev = opaque;
|
||||
struct kvm *kvm;
|
||||
int rc;
|
||||
|
||||
if (!zdev)
|
||||
return -EINVAL;
|
||||
|
||||
kvm = file_to_kvm_s390(kvm_file);
|
||||
if (!kvm)
|
||||
return -ENOENT;
|
||||
|
||||
mutex_lock(&zdev->kzdev_lock);
|
||||
|
||||
if (zdev->kzdev || zdev->gisa != 0 || !kvm) {
|
||||
if (zdev->kzdev || zdev->gisa != 0) {
|
||||
mutex_unlock(&zdev->kzdev_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,13 +44,15 @@ struct kvm_page_track_notifier_node {
|
|||
struct kvm_page_track_notifier_node *node);
|
||||
};
|
||||
|
||||
int kvm_page_track_register_notifier(struct kvm *kvm,
|
||||
struct file;
|
||||
|
||||
int kvm_page_track_register_notifier(struct file *file,
|
||||
struct kvm_page_track_notifier_node *n);
|
||||
void kvm_page_track_unregister_notifier(struct kvm *kvm,
|
||||
void kvm_page_track_unregister_notifier(struct file *file,
|
||||
struct kvm_page_track_notifier_node *n);
|
||||
|
||||
int kvm_write_track_add_gfn(struct kvm *kvm, gfn_t gfn);
|
||||
int kvm_write_track_remove_gfn(struct kvm *kvm, gfn_t gfn);
|
||||
int kvm_write_track_add_gfn(struct file *file, gfn_t gfn);
|
||||
int kvm_write_track_remove_gfn(struct file *file, gfn_t gfn);
|
||||
#else
|
||||
/*
|
||||
* Allow defining a node in a structure even if page tracking is disabled, e.g.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <linux/rculist.h>
|
||||
|
||||
#include <asm/kvm_page_track.h>
|
||||
|
||||
#include "mmu.h"
|
||||
#include "mmu_internal.h"
|
||||
#include "page_track.h"
|
||||
|
|
@ -237,10 +239,11 @@ static int kvm_enable_external_write_tracking(struct kvm *kvm)
|
|||
* register the notifier so that event interception for the tracked guest
|
||||
* pages can be received.
|
||||
*/
|
||||
int kvm_page_track_register_notifier(struct kvm *kvm,
|
||||
int kvm_page_track_register_notifier(struct file *file,
|
||||
struct kvm_page_track_notifier_node *n)
|
||||
{
|
||||
struct kvm_page_track_notifier_head *head;
|
||||
struct kvm *kvm = file_to_kvm_x86(file);
|
||||
int r;
|
||||
|
||||
if (!kvm || kvm->mm != current->mm)
|
||||
|
|
@ -252,7 +255,7 @@ int kvm_page_track_register_notifier(struct kvm *kvm,
|
|||
return r;
|
||||
}
|
||||
|
||||
kvm_get_kvm(kvm);
|
||||
get_file(file);
|
||||
|
||||
head = &kvm->arch.track_notifier_head;
|
||||
|
||||
|
|
@ -267,10 +270,11 @@ EXPORT_SYMBOL_GPL(kvm_page_track_register_notifier);
|
|||
* stop receiving the event interception. It is the opposed operation of
|
||||
* kvm_page_track_register_notifier().
|
||||
*/
|
||||
void kvm_page_track_unregister_notifier(struct kvm *kvm,
|
||||
void kvm_page_track_unregister_notifier(struct file *file,
|
||||
struct kvm_page_track_notifier_node *n)
|
||||
{
|
||||
struct kvm_page_track_notifier_head *head;
|
||||
struct kvm *kvm = file_to_kvm_x86(file);
|
||||
|
||||
head = &kvm->arch.track_notifier_head;
|
||||
|
||||
|
|
@ -279,7 +283,7 @@ void kvm_page_track_unregister_notifier(struct kvm *kvm,
|
|||
write_unlock(&kvm->mmu_lock);
|
||||
synchronize_srcu(&head->track_srcu);
|
||||
|
||||
kvm_put_kvm(kvm);
|
||||
fput(file);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier);
|
||||
|
||||
|
|
@ -336,11 +340,12 @@ void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
|
|||
* add guest page to the tracking pool so that corresponding access on that
|
||||
* page will be intercepted.
|
||||
*
|
||||
* @kvm: the guest instance we are interested in.
|
||||
* @file: the VM file of the guest instance we are interested in.
|
||||
* @gfn: the guest page.
|
||||
*/
|
||||
int kvm_write_track_add_gfn(struct kvm *kvm, gfn_t gfn)
|
||||
int kvm_write_track_add_gfn(struct file *file, gfn_t gfn)
|
||||
{
|
||||
struct kvm *kvm = file_to_kvm_x86(file);
|
||||
struct kvm_memory_slot *slot;
|
||||
int idx;
|
||||
|
||||
|
|
@ -366,11 +371,12 @@ EXPORT_SYMBOL_GPL(kvm_write_track_add_gfn);
|
|||
* remove the guest page from the tracking pool which stops the interception
|
||||
* of corresponding access on that page.
|
||||
*
|
||||
* @kvm: the guest instance we are interested in.
|
||||
* @file: the VM file of the guest instance we are interested in.
|
||||
* @gfn: the guest page.
|
||||
*/
|
||||
int kvm_write_track_remove_gfn(struct kvm *kvm, gfn_t gfn)
|
||||
int kvm_write_track_remove_gfn(struct file *file, gfn_t gfn)
|
||||
{
|
||||
struct kvm *kvm = file_to_kvm_x86(file);
|
||||
struct kvm_memory_slot *slot;
|
||||
int idx;
|
||||
|
||||
|
|
|
|||
|
|
@ -1822,17 +1822,27 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
|
|||
|
||||
/**
|
||||
* vfio_ap_mdev_set_kvm - sets all data for @matrix_mdev that are needed
|
||||
* to manage AP resources for the guest whose state is represented by @kvm
|
||||
* to manage AP resources for the guest whose state is represented by
|
||||
* @kvm_file
|
||||
*
|
||||
* @matrix_mdev: a mediated matrix device
|
||||
* @kvm: reference to KVM instance
|
||||
* @kvm_file: the KVM VM file this vfio device is associated with
|
||||
*
|
||||
* Return: 0 if no other mediated matrix device has a reference to @kvm;
|
||||
* Return: 0 if no other mediated matrix device has a reference to the VM;
|
||||
* otherwise, returns an -EPERM.
|
||||
*/
|
||||
static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
|
||||
struct kvm *kvm)
|
||||
struct file *kvm_file)
|
||||
{
|
||||
struct kvm *kvm;
|
||||
|
||||
if (!kvm_file)
|
||||
return -ENOENT;
|
||||
|
||||
kvm = file_to_kvm_s390(kvm_file);
|
||||
if (!kvm)
|
||||
return -ENOENT;
|
||||
|
||||
if (kvm->arch.crypto.crycbd) {
|
||||
get_update_locks_for_kvm(kvm);
|
||||
if (kvm->arch.crypto.pqap_hook) {
|
||||
|
|
@ -1841,7 +1851,6 @@ static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
|
|||
}
|
||||
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
|
||||
|
||||
kvm_get_kvm(kvm);
|
||||
matrix_mdev->kvm = kvm;
|
||||
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
|
||||
release_update_locks_for_kvm(kvm);
|
||||
|
|
@ -1894,7 +1903,6 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
|
|||
matrix_mdev->kvm = NULL;
|
||||
|
||||
release_update_locks_for_kvm(kvm);
|
||||
kvm_put_kvm(kvm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,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(df->device, df->kvm);
|
||||
spin_unlock(&df->kvm_ref_lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,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(device, device->group->kvm);
|
||||
spin_unlock(&device->group->kvm_ref_lock);
|
||||
}
|
||||
|
||||
|
|
@ -860,11 +860,20 @@ 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 file *kvm)
|
||||
{
|
||||
struct file *old;
|
||||
|
||||
if (kvm)
|
||||
get_file(kvm);
|
||||
|
||||
spin_lock(&group->kvm_ref_lock);
|
||||
old = group->kvm;
|
||||
group->kvm = kvm;
|
||||
spin_unlock(&group->kvm_ref_lock);
|
||||
|
||||
if (old)
|
||||
fput(old);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ struct vfio_device_file {
|
|||
u8 access_granted;
|
||||
u32 devid; /* only valid when iommufd is valid */
|
||||
spinlock_t kvm_ref_lock; /* protect kvm field */
|
||||
struct kvm *kvm;
|
||||
struct file *kvm;
|
||||
struct iommufd_ctx *iommufd; /* protected by struct vfio_device_set::lock */
|
||||
};
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ struct vfio_group {
|
|||
#endif
|
||||
enum vfio_group_type type;
|
||||
struct mutex group_lock;
|
||||
struct kvm *kvm;
|
||||
struct file *kvm;
|
||||
struct file *opened_file;
|
||||
struct iommufd_ctx *iommufd;
|
||||
spinlock_t kvm_ref_lock;
|
||||
|
|
@ -107,7 +107,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 file *kvm);
|
||||
bool vfio_device_has_container(struct vfio_device *device);
|
||||
int __init vfio_group_init(void);
|
||||
void vfio_group_cleanup(void);
|
||||
|
|
@ -165,7 +165,7 @@ 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 file *kvm)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -429,11 +429,11 @@ 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(struct vfio_device *device, struct file *kvm);
|
||||
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)
|
||||
static inline void vfio_device_get_kvm(struct vfio_device *device,
|
||||
struct file *kvm)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -472,36 +472,14 @@ 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(struct vfio_device *device, struct file *kvm)
|
||||
{
|
||||
void (*pfn)(struct kvm *kvm);
|
||||
bool (*fn)(struct kvm *kvm);
|
||||
bool ret;
|
||||
|
||||
lockdep_assert_held(&device->dev_set->lock);
|
||||
|
||||
if (!kvm)
|
||||
return;
|
||||
|
||||
pfn = symbol_get(kvm_put_kvm);
|
||||
if (WARN_ON(!pfn))
|
||||
return;
|
||||
|
||||
fn = symbol_get(kvm_get_kvm_safe);
|
||||
if (WARN_ON(!fn)) {
|
||||
symbol_put(kvm_put_kvm);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = fn(kvm);
|
||||
symbol_put(kvm_get_kvm_safe);
|
||||
if (!ret) {
|
||||
symbol_put(kvm_put_kvm);
|
||||
return;
|
||||
}
|
||||
|
||||
device->put_kvm = pfn;
|
||||
device->kvm = kvm;
|
||||
device->kvm = get_file(kvm);
|
||||
}
|
||||
|
||||
void vfio_device_put_kvm(struct vfio_device *device)
|
||||
|
|
@ -511,14 +489,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:
|
||||
fput(device->kvm);
|
||||
device->kvm = NULL;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1544,9 +1515,13 @@ 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 file *kvm)
|
||||
{
|
||||
struct vfio_device_file *df = file->private_data;
|
||||
struct file *old;
|
||||
|
||||
if (kvm)
|
||||
get_file(kvm);
|
||||
|
||||
/*
|
||||
* The kvm is first recorded in the vfio_device_file, and will
|
||||
|
|
@ -1554,28 +1529,32 @@ static void vfio_device_file_set_kvm(struct file *file, struct kvm *kvm)
|
|||
* iommufd successfully in the vfio device cdev path.
|
||||
*/
|
||||
spin_lock(&df->kvm_ref_lock);
|
||||
old = df->kvm;
|
||||
df->kvm = kvm;
|
||||
spin_unlock(&df->kvm_ref_lock);
|
||||
|
||||
if (old)
|
||||
fput(old);
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_file_set_kvm - Link a kvm with VFIO drivers
|
||||
* @file: VFIO group file or VFIO device file
|
||||
* @kvm: KVM to link
|
||||
* @vfio_file: VFIO group file or VFIO device file
|
||||
* @kvm: KVM file to link
|
||||
*
|
||||
* 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 *vfio_file, struct file *kvm)
|
||||
{
|
||||
struct vfio_group *group;
|
||||
|
||||
group = vfio_group_from_file(file);
|
||||
group = vfio_group_from_file(vfio_file);
|
||||
if (group)
|
||||
vfio_group_set_kvm(group, kvm);
|
||||
|
||||
if (vfio_device_from_file(file))
|
||||
vfio_device_file_set_kvm(file, kvm);
|
||||
if (vfio_device_from_file(vfio_file))
|
||||
vfio_device_file_set_kvm(vfio_file, kvm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfio_file_set_kvm);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ struct vfio_device {
|
|||
struct list_head dev_set_list;
|
||||
unsigned int migration_flags;
|
||||
u8 precopy_info_v2;
|
||||
struct kvm *kvm;
|
||||
struct file *kvm;
|
||||
|
||||
/* Members below here are private, not for driver use */
|
||||
unsigned int index;
|
||||
|
|
@ -66,7 +66,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;
|
||||
|
|
@ -378,7 +377,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 *vfio_file, struct file *kvm);
|
||||
|
||||
#define VFIO_PIN_PAGES_MAX_ENTRIES (PAGE_SIZE/sizeof(unsigned long))
|
||||
|
||||
|
|
|
|||
|
|
@ -35,15 +35,15 @@ struct kvm_vfio {
|
|||
bool noncoherent;
|
||||
};
|
||||
|
||||
static void kvm_vfio_file_set_kvm(struct file *file, struct kvm *kvm)
|
||||
static void kvm_vfio_file_set_kvm(struct file *vfio_file, struct file *kvm)
|
||||
{
|
||||
void (*fn)(struct file *file, struct kvm *kvm);
|
||||
void (*fn)(struct file *vfio_file, struct file *kvm);
|
||||
|
||||
fn = symbol_get(vfio_file_set_kvm);
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
fn(file, kvm);
|
||||
fn(vfio_file, kvm);
|
||||
|
||||
symbol_put(vfio_file_set_kvm);
|
||||
}
|
||||
|
|
@ -144,6 +144,7 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd)
|
|||
{
|
||||
struct kvm_vfio *kv = dev->private;
|
||||
struct kvm_vfio_file *kvf;
|
||||
struct file *kvm_file __free(fput) = NULL;
|
||||
struct file *filp __free(fput) = NULL;
|
||||
|
||||
filp = fget(fd);
|
||||
|
|
@ -154,6 +155,10 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd)
|
|||
if (!kvm_vfio_file_is_valid(filp))
|
||||
return -EINVAL;
|
||||
|
||||
kvm_file = get_file_active(&dev->kvm->file);
|
||||
if (!kvm_file)
|
||||
return -ENOENT;
|
||||
|
||||
guard(mutex)(&kv->lock);
|
||||
|
||||
list_for_each_entry(kvf, &kv->file_list, node) {
|
||||
|
|
@ -168,7 +173,7 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd)
|
|||
kvf->file = get_file(filp);
|
||||
list_add_tail(&kvf->node, &kv->file_list);
|
||||
|
||||
kvm_vfio_file_set_kvm(kvf->file, dev->kvm);
|
||||
kvm_vfio_file_set_kvm(kvf->file, kvm_file);
|
||||
kvm_vfio_update_coherency(dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user