[PATCH v3 4/6] KVM: Add KVM_(UN)REGISTER_COALESCED_MMIO2 ioctls

Ilias Stamatis
2024-08-20T13:36:31Z

Add 2 new ioctls, KVM_REGISTER_COALESCED_MMIO2 and
KVM_UNREGISTER_COALESCED_MMIO2. These do the same thing as their v1
equivalents except an fd returned by KVM_CREATE_COALESCED_MMIO_BUFFER
needs to be passed as an argument to them.

The fd representing a ring buffer is associated with an MMIO region
registered for coalescing and all writes to that region are accumulated
there. This is in contrast to the v1 API where all regions have to share
the same buffer. Nevertheless, userspace code can still use the same
ring buffer for multiple zones if it wishes to do so.

Userspace can check for the availability of the new API by checking if
the KVM_CAP_COALESCED_MMIO2 capability is supported.

Signed-off-by: Ilias Stamatis <ilstam@amazon.com>
Reviewed-by: Paul Durrant <paul@xen.org>
---

v2->v3:
- Changed type of buffer_fd from int to __u32
- Removed 0 initialisation of ret in
kvm_vm_ioctl_register_coalesced_mmio()

include/uapi/linux/kvm.h | 16 ++++++++++++++++
virt/kvm/coalesced_mmio.c | 36 +++++++++++++++++++++++++++++++-----
virt/kvm/coalesced_mmio.h | 7 ++++---
virt/kvm/kvm_main.c | 34 +++++++++++++++++++++++++++++++++-
4 files changed, 84 insertions(+), 9 deletions(-)

diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 87f79a820fc0..5e9fcc560cc1 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -480,6 +480,16 @@ struct kvm_coalesced_mmio_zone {
};
};

+struct kvm_coalesced_mmio_zone2 {
+ __u64 addr;
+ __u32 size;
+ union {
+ __u32 pad;
+ __u32 pio;
+ };
+ __u32 buffer_fd;
+};
+
struct kvm_coalesced_mmio {
__u64 phys_addr;
__u32 len;
@@ -933,6 +943,7 @@ struct kvm_enable_cap {
#define KVM_CAP_PRE_FAULT_MEMORY 236
#define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237
#define KVM_CAP_X86_GUEST_MODE 238
+#define KVM_CAP_COALESCED_MMIO2 239

struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1573,6 +1584,11 @@ struct kvm_pre_fault_memory {
__u64 padding[5];
};

+/* Available with KVM_CAP_COALESCED_MMIO2 */
#define KVM_CREATE_COALESCED_MMIO_BUFFER _IO(KVMIO, 0xd6)
+#define KVM_REGISTER_COALESCED_MMIO2 \
+ _IOW(KVMIO, 0xd7, struct kvm_coalesced_mmio_zone2)
+#define KVM_UNREGISTER_COALESCED_MMIO2 \
+ _IOW(KVMIO, 0xd8, struct kvm_coalesced_mmio_zone2)

#endif /* __LINUX_KVM_H */
diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c
index 039c6ffcb2a8..4e237ee66711 100644
--- a/virt/kvm/coalesced_mmio.c
+++ b/virt/kvm/coalesced_mmio.c
@@ -17,6 +17,7 @@
#include <linux/kvm.h>
#include <linux/anon_inodes.h>
#include <linux/poll.h>
+#include <linux/file.h>

#include "coalesced_mmio.h"

@@ -278,19 +279,40 @@ int kvm_vm_ioctl_create_coalesced_mmio_buffer(struct kvm *kvm)
}

int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
- struct kvm_coalesced_mmio_zone *zone)
+ struct kvm_coalesced_mmio_zone2 *zone,
+ bool use_buffer_fd)
{
int ret;
+ struct file *file;
struct kvm_coalesced_mmio_dev *dev;
struct kvm_coalesced_mmio_buffer_dev *buffer_dev = NULL;

if (zone->pio != 1 && zone->pio != 0)
return -EINVAL;

+ if (use_buffer_fd) {
+ file = fget(zone->buffer_fd);
+ if (!file)
+ return -EBADF;
+
+ if (file->f_op != &coalesced_mmio_buffer_ops) {
+ fput(file);
+ return -EINVAL;
+ }
+
+ buffer_dev = file->private_data;
+ if (!buffer_dev->ring) {
+ fput(file);
+ return -ENOBUFS;
+ }
+ }
+
dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev),
GFP_KERNEL_ACCOUNT);
- if (!dev)
- return -ENOMEM;
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_free_file;
+ }

kvm_iodevice_init(&dev->dev, &coalesced_mmio_ops);
dev->kvm = kvm;
@@ -306,17 +328,21 @@ int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
list_add_tail(&dev->list, &kvm->coalesced_zones);
mutex_unlock(&kvm->slots_lock);

- return 0;
+ ret = 0;
+ goto out_free_file;

out_free_dev:
mutex_unlock(&kvm->slots_lock);
kfree(dev);
+out_free_file:
+ if (use_buffer_fd)
+ fput(file);

return ret;
}

int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
- struct kvm_coalesced_mmio_zone *zone)
+ struct kvm_coalesced_mmio_zone2 *zone)
{
struct kvm_coalesced_mmio_dev *dev, *tmp;
int r;
diff --git a/virt/kvm/coalesced_mmio.h b/virt/kvm/coalesced_mmio.h
index d1807ce26464..32792adb7cb4 100644
--- a/virt/kvm/coalesced_mmio.h
+++ b/virt/kvm/coalesced_mmio.h
@@ -19,7 +19,7 @@ struct kvm_coalesced_mmio_dev {
struct list_head list;
struct kvm_io_device dev;
struct kvm *kvm;
- struct kvm_coalesced_mmio_zone zone;
+ struct kvm_coalesced_mmio_zone2 zone;
struct kvm_coalesced_mmio_buffer_dev *buffer_dev;
};

@@ -34,9 +34,10 @@ struct kvm_coalesced_mmio_buffer_dev {
int kvm_coalesced_mmio_init(struct kvm *kvm);
void kvm_coalesced_mmio_free(struct kvm *kvm);
int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
- struct kvm_coalesced_mmio_zone *zone);
+ struct kvm_coalesced_mmio_zone2 *zone,
+ bool use_buffer_fd);
int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
- struct kvm_coalesced_mmio_zone *zone);
+ struct kvm_coalesced_mmio_zone2 *zone);
int kvm_vm_ioctl_create_coalesced_mmio_buffer(struct kvm *kvm);

#else
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 9f6ad6e03317..0850f151ef16 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4890,6 +4890,7 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
#ifdef CONFIG_KVM_MMIO
case KVM_CAP_COALESCED_MMIO:
return KVM_COALESCED_MMIO_PAGE_OFFSET;
+ case KVM_CAP_COALESCED_MMIO2:
case KVM_CAP_COALESCED_PIO:
return 1;
#endif
@@ -5228,15 +5229,46 @@ static long kvm_vm_ioctl(struct file *filp,
#ifdef CONFIG_KVM_MMIO
case KVM_REGISTER_COALESCED_MMIO: {
struct kvm_coalesced_mmio_zone zone;
+ struct kvm_coalesced_mmio_zone2 zone2;

r = -EFAULT;
if (copy_from_user(&zone, argp, sizeof(zone)))
goto out;
- r = kvm_vm_ioctl_register_coalesced_mmio(kvm, &zone);
+
+ zone2.addr = zone.addr;
+ zone2.size = zone.size;
+ zone2.pio = zone.pio;
+
+ r = kvm_vm_ioctl_register_coalesced_mmio(kvm, &zone2, false);
+ break;
+ }
+ case KVM_REGISTER_COALESCED_MMIO2: {
+ struct kvm_coalesced_mmio_zone2 zone;
+
+ r = -EFAULT;
+ if (copy_from_user(&zone, argp, sizeof(zone)))
+ goto out;
+
+ r = kvm_vm_ioctl_register_coalesced_mmio(kvm, &zone, true);
break;
}
case KVM_UNREGISTER_COALESCED_MMIO: {
struct kvm_coalesced_mmio_zone zone;
+ struct kvm_coalesced_mmio_zone2 zone2;
+
+ r = -EFAULT;
+ if (copy_from_user(&zone, argp, sizeof(zone)))
+ goto out;
+
+ zone2.addr = zone.addr;
+ zone2.size = zone.size;
+ zone2.pio = zone.pio;
+
+ r = kvm_vm_ioctl_unregister_coalesced_mmio(kvm, &zone2);
+ break;
+ }
+ case KVM_UNREGISTER_COALESCED_MMIO2: {
+ struct kvm_coalesced_mmio_zone2 zone;

r = -EFAULT;
if (copy_from_user(&zone, argp, sizeof(zone)))
--
2.34.1