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

Ilias Stamatis
2024-07-10T08:56:44Z

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>
---
include/uapi/linux/kvm.h | 16 ++++++++++++++++
virt/kvm/coalesced_mmio.c | 36 ++++++++++++++++++++++++++++++------
virt/kvm/coalesced_mmio.h | 7 ++++---
virt/kvm/kvm_main.c | 36 +++++++++++++++++++++++++++++++++++-
4 files changed, 85 insertions(+), 10 deletions(-)

diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6d6f132e6203..e49dda50b639 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -467,6 +467,16 @@ struct kvm_coalesced_mmio_zone {
};
};

+struct kvm_coalesced_mmio_zone2 {
+ __u64 addr;
+ __u32 size;
+ union {
+ __u32 pad;
+ __u32 pio;
+ };
+ int buffer_fd;
+};
+
struct kvm_coalesced_mmio {
__u64 phys_addr;
__u32 len;
@@ -917,6 +927,7 @@ struct kvm_enable_cap {
#define KVM_CAP_MEMORY_ATTRIBUTES 233
#define KVM_CAP_GUEST_MEMFD 234
#define KVM_CAP_VM_TYPES 235
+#define KVM_CAP_COALESCED_MMIO2 236

struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1548,6 +1559,11 @@ struct kvm_create_guest_memfd {
__u64 reserved[6];
};

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

#endif /* __LINUX_KVM_H */
diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c
index 00439e035d74..8d6d98c01f6e 100644
--- a/virt/kvm/coalesced_mmio.c
+++ b/virt/kvm/coalesced_mmio.c
@@ -277,19 +277,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;
+ int ret = 0;
+ 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;
@@ -305,17 +326,20 @@ 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;
+ 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 54df2e88d4f4..683b5d392b5f 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4815,6 +4815,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
@@ -5153,15 +5154,48 @@ 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;
+ zone2.buffer_fd = -1;
+
+ 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;
+ zone2.buffer_fd = -1;
+
+ 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