KVM: support any-length wildcard ioeventfd
authorMichael S. Tsirkin <mst@redhat.com>
Mon, 31 Mar 2014 18:50:38 +0000 (21:50 +0300)
committerMarcelo Tosatti <mtosatti@redhat.com>
Thu, 17 Apr 2014 17:01:42 +0000 (14:01 -0300)
It is sometimes benefitial to ignore IO size, and only match on address.
In hindsight this would have been a better default than matching length
when KVM_IOEVENTFD_FLAG_DATAMATCH is not set, In particular, this kind
of access can be optimized on VMX: there no need to do page lookups.
This can currently be done with many ioeventfds but in a suboptimal way.

However we can't change kernel/userspace ABI without risk of breaking
some applications.
Use len = 0 to mean "ignore length for matching" in a more optimal way.

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

index 8b8fc0b792baeddf76a1b0ee3dac3a6a9b5cd0de..bc4aaf68190cf68d40b5bac74a3783e1160f882d 100644 (file)
@@ -2644,6 +2644,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_IRQ_INJECT_STATUS:
        case KVM_CAP_IRQFD:
        case KVM_CAP_IOEVENTFD:
+       case KVM_CAP_IOEVENTFD_NO_LENGTH:
        case KVM_CAP_PIT2:
        case KVM_CAP_PIT_STATE2:
        case KVM_CAP_SET_IDENTITY_MAP_ADDR:
index a8f4ee5d2e8242507601d0f3cb81417e39daf40b..39098a61f41cca00638233883a2e6834aeef2e6f 100644 (file)
@@ -529,7 +529,7 @@ enum {
 struct kvm_ioeventfd {
        __u64 datamatch;
        __u64 addr;        /* legal pio/mmio address */
-       __u32 len;         /* 1, 2, 4, or 8 bytes    */
+       __u32 len;         /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
        __s32 fd;
        __u32 flags;
        __u8  pad[36];
@@ -743,6 +743,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
 #define KVM_CAP_ENABLE_CAP_VM 98
 #define KVM_CAP_S390_IRQCHIP 99
+#define KVM_CAP_IOEVENTFD_NO_LENGTH 100
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
index 29c2a04e036e8a30613133b11510eee9232f5213..2721996bb9c2437a97de1ac52c5cc9640e77b63a 100644 (file)
@@ -600,7 +600,15 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
 {
        u64 _val;
 
-       if (!(addr == p->addr && len == p->length))
+       if (addr != p->addr)
+               /* address must be precise for a hit */
+               return false;
+
+       if (!p->length)
+               /* length = 0 means only look at the address, so always a hit */
+               return true;
+
+       if (len != p->length)
                /* address-range must be precise for a hit */
                return false;
 
@@ -671,9 +679,11 @@ ioeventfd_check_collision(struct kvm *kvm, struct _ioeventfd *p)
 
        list_for_each_entry(_p, &kvm->ioeventfds, list)
                if (_p->bus_idx == p->bus_idx &&
-                   _p->addr == p->addr && _p->length == p->length &&
-                   (_p->wildcard || p->wildcard ||
-                    _p->datamatch == p->datamatch))
+                   _p->addr == p->addr &&
+                   (!_p->length || !p->length ||
+                    (_p->length == p->length &&
+                     (_p->wildcard || p->wildcard ||
+                      _p->datamatch == p->datamatch))))
                        return true;
 
        return false;
@@ -697,8 +707,9 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        int                       ret;
 
        bus_idx = ioeventfd_bus_from_flags(args->flags);
-       /* must be natural-word sized */
+       /* must be natural-word sized, or 0 to ignore length */
        switch (args->len) {
+       case 0:
        case 1:
        case 2:
        case 4:
@@ -716,6 +727,12 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        if (args->flags & ~KVM_IOEVENTFD_VALID_FLAG_MASK)
                return -EINVAL;
 
+       /* ioeventfd with no length can't be combined with DATAMATCH */
+       if (!args->len &&
+           args->flags & (KVM_IOEVENTFD_FLAG_PIO |
+                          KVM_IOEVENTFD_FLAG_DATAMATCH))
+               return -EINVAL;
+
        eventfd = eventfd_ctx_fdget(args->fd);
        if (IS_ERR(eventfd))
                return PTR_ERR(eventfd);