KVM: VMX: speed up wildcard MMIO EVENTFD
authorMichael S. Tsirkin <mst@redhat.com>
Mon, 31 Mar 2014 18:50:44 +0000 (21:50 +0300)
committerMarcelo Tosatti <mtosatti@redhat.com>
Thu, 17 Apr 2014 17:01:43 +0000 (14:01 -0300)
With KVM, MMIO is much slower than PIO, due to the need to
do page walk and emulation. But with EPT, it does not have to be: we
know the address from the VMCS so if the address is unique, we can look
up the eventfd directly, bypassing emulation.

Unfortunately, this only works if userspace does not need to match on
access length and data.  The implementation adds a separate FAST_MMIO
bus internally. This serves two purposes:
    - minimize overhead for old userspace that does not use eventfd with lengtth = 0
    - minimize disruption in other code (since we don't know the length,
      devices on the MMIO bus only get a valid address in write, this
      way we don't need to touch all devices to teach them to handle
      an invalid length)

At the moment, this optimization only has effect for EPT on x86.

It will be possible to speed up MMIO for NPT and MMU using the same
idea in the future.

With this patch applied, on VMX MMIO EVENTFD is essentially as fast as PIO.
I was unable to detect any measureable slowdown to non-eventfd MMIO.

Making MMIO faster is important for the upcoming virtio 1.0 which
includes an MMIO signalling capability.

The idea was suggested by Peter Anvin.  Lots of thanks to Gleb for
pre-review and suggestions.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
arch/x86/kvm/vmx.c
include/linux/kvm_host.h
include/uapi/linux/kvm.h
virt/kvm/eventfd.c
virt/kvm/kvm_main.c

index 1f68c5831924d15dd741032cde2fafc46aae50ab..eb3f2b1b764c486ede13994e71e7a1802abf72df 100644 (file)
@@ -5528,6 +5528,10 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu)
        gpa_t gpa;
 
        gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
+       if (!kvm_io_bus_write(vcpu->kvm, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {
+               skip_emulated_instruction(vcpu);
+               return 1;
+       }
 
        ret = handle_mmio_page_fault_common(vcpu, gpa, true);
        if (likely(ret == RET_MMIO_PF_EMULATE))
index 7d21cf9f43806cae2c6b14363e83cd628366acdb..6c3c2eb96d0693f2c37964db739978f102423596 100644 (file)
@@ -163,6 +163,7 @@ enum kvm_bus {
        KVM_MMIO_BUS,
        KVM_PIO_BUS,
        KVM_VIRTIO_CCW_NOTIFY_BUS,
+       KVM_FAST_MMIO_BUS,
        KVM_NR_BUSES
 };
 
index 39098a61f41cca00638233883a2e6834aeef2e6f..d8a6ce4c2a83c8e3ae8e39fc86218b5fd4db0792 100644 (file)
@@ -515,6 +515,7 @@ enum {
        kvm_ioeventfd_flag_nr_pio,
        kvm_ioeventfd_flag_nr_deassign,
        kvm_ioeventfd_flag_nr_virtio_ccw_notify,
+       kvm_ioeventfd_flag_nr_fast_mmio,
        kvm_ioeventfd_flag_nr_max,
 };
 
index 2721996bb9c2437a97de1ac52c5cc9640e77b63a..912ec5a95e2c129fae74ca855b6dccf6f0c99f37 100644 (file)
@@ -770,6 +770,16 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        if (ret < 0)
                goto unlock_fail;
 
+       /* When length is ignored, MMIO is also put on a separate bus, for
+        * faster lookups.
+        */
+       if (!args->len && !(args->flags & KVM_IOEVENTFD_FLAG_PIO)) {
+               ret = kvm_io_bus_register_dev(kvm, KVM_FAST_MMIO_BUS,
+                                             p->addr, 0, &p->dev);
+               if (ret < 0)
+                       goto register_fail;
+       }
+
        kvm->buses[bus_idx]->ioeventfd_count++;
        list_add_tail(&p->list, &kvm->ioeventfds);
 
@@ -777,6 +787,8 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
 
        return 0;
 
+register_fail:
+       kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
 unlock_fail:
        mutex_unlock(&kvm->slots_lock);
 
@@ -816,6 +828,10 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
                        continue;
 
                kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
+               if (!p->length) {
+                       kvm_io_bus_unregister_dev(kvm, KVM_FAST_MMIO_BUS,
+                                                 &p->dev);
+               }
                kvm->buses[bus_idx]->ioeventfd_count--;
                ioeventfd_release(p);
                ret = 0;
index 56baae8c2f56baf0f41bb7bb24b0b12679267d20..96456ac888ba3822f5511773296bd9ea6853945d 100644 (file)
@@ -2922,6 +2922,7 @@ static int __kvm_io_bus_read(struct kvm_io_bus *bus, struct kvm_io_range *range,
 
        return -EOPNOTSUPP;
 }
+EXPORT_SYMBOL_GPL(kvm_io_bus_write);
 
 /* kvm_io_bus_read - called under kvm->slots_lock */
 int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,