References: bsc#963161

# Commit 3a6222bd57209d4c2f098d61ace042370a9b0a96
# Date 2016-04-26 11:52:22 +0200
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86/vMSI-X: avoid missing first unmask of vectors

Recent changes to Linux result in there just being a single unmask
operation prior to expecting the first interrupts to arrive. However,
we've had a chicken-and-egg problem here: Qemu invokes
xc_domain_update_msi_irq(), ultimately leading to
msixtbl_pt_register(), upon seeing that first unmask operation. Yet
for msixtbl_range() to return true (in order to msixtbl_write() to get
invoked at all) msixtbl_pt_register() must have completed.

Deal with this by snooping suitable writes in msixtbl_range() and
triggering the invocation of msix_write_completion() from
msixtbl_pt_register() when that happens in the context of a still in
progress vector control field write.

Note that the seemingly unrelated deletion of the redundant
irq_desc->msi_desc check in msixtbl_pt_register() is to make clear to
any compiler version used that the "msi_desc" local variable isn't
being used uninitialized. (Doing the same in msixtbl_pt_unregister() is
just for consistency reasons.)

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>

# Commit 9772c480a71ad38cc2c342e4c2e78c2475de7268
# Date 2016-04-26 16:53:36 +0200
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86/vMSI-X: write snoops should ignore hvm_mmio_internal() requests

Those aren't actual I/O requests (and hence are of no interest here
anyway). Since they don't get copied into struct vcpu, looking at that
copy reads whatever was left there. Use the state of the request to
determine its validity.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>

# Commit de8627767968d84385648b399317b1b55323bc15
# Date 2016-04-28 15:10:22 +0200
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86/vMSI-X: add further checks to snoop logic

msixtbl_range(), as any other MMIO ->check() handlers, may get called
with other than the base address of an access - avoid the snoop logic
considering those.

Also avoid considering vCPU-s not blocked in the hypervisor in
msixtbl_pt_register(), just to be on the safe side.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>

--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -110,6 +110,7 @@ static int hvmemul_do_io(
     if ( (p_data != NULL) && (dir == IOREQ_WRITE) )
     {
         memcpy(&value, p_data, size);
+        vio->io_data = value;
         p_data = NULL;
     }
 
@@ -184,6 +185,9 @@ static int hvmemul_do_io(
 
     vio->io_state =
         (p_data == NULL) ? HVMIO_dispatched : HVMIO_awaiting_completion;
+    vio->io_addr = addr;
+    vio->io_dir = dir;
+    vio->io_data_is_ptr = value_is_ptr;
     vio->io_size = size;
 
     /*
--- a/xen/arch/x86/hvm/vmsi.c
+++ b/xen/arch/x86/hvm/vmsi.c
@@ -377,7 +377,23 @@ static int msixtbl_range(struct vcpu *v,
 
     rcu_read_unlock(&msixtbl_rcu_lock);
 
-    return !!virt;
+    if ( virt )
+        return 1;
+
+    if ( (addr & (PCI_MSIX_ENTRY_SIZE - 1)) ==
+         PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET )
+    {
+        const struct hvm_vcpu_io *vio = &v->arch.hvm_vcpu.hvm_io;
+
+        if ( vio->io_state != HVMIO_dispatched || vio->io_addr != addr )
+            return 0;
+        if ( vio->io_dir == IOREQ_WRITE && vio->io_size == 4 &&
+             !vio->io_data_is_ptr &&
+             !(vio->io_data & PCI_MSIX_VECTOR_BITMASK) )
+            v->arch.hvm_vcpu.hvm_io.msix_snoop_address = addr;
+    }
+
+    return 0;
 }
 
 const struct hvm_mmio_handler msixtbl_mmio_handler = {
@@ -448,9 +464,6 @@ int msixtbl_pt_register(struct domain *d
         return r;
     }
 
-    if ( !irq_desc->msi_desc )
-        goto out;
-
     msi_desc = irq_desc->msi_desc;
     if ( !msi_desc )
         goto out;
@@ -475,6 +488,23 @@ found:
 out:
     spin_unlock_irq(&irq_desc->lock);
     xfree(new_entry);
+
+    if ( !r )
+    {
+        struct vcpu *v;
+
+        for_each_vcpu ( d, v )
+        {
+            if ( (v->pause_flags & VPF_blocked_in_xen) &&
+                 v->arch.hvm_vcpu.hvm_io.msix_snoop_address ==
+                 (gtable + msi_desc->msi_attrib.entry_nr *
+                           PCI_MSIX_ENTRY_SIZE +
+                  PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET) )
+                v->arch.hvm_vcpu.hvm_io.msix_unmask_address =
+                    v->arch.hvm_vcpu.hvm_io.msix_snoop_address;
+        }
+    }
+
     return r;
 }
 
@@ -492,9 +522,6 @@ void msixtbl_pt_unregister(struct domain
     if ( !irq_desc )
         return;
 
-    if ( !irq_desc->msi_desc )
-        goto out;
-
     msi_desc = irq_desc->msi_desc;
     if ( !msi_desc )
         goto out;
@@ -543,6 +570,8 @@ void msix_write_completion(struct vcpu *
 {
     unsigned long ctrl_address = v->arch.hvm_vcpu.hvm_io.msix_unmask_address;
 
+    v->arch.hvm_vcpu.hvm_io.msix_snoop_address = 0;
+
     if ( !ctrl_address )
         return;
 
--- a/xen/include/asm-x86/hvm/vcpu.h
+++ b/xen/include/asm-x86/hvm/vcpu.h
@@ -47,7 +47,10 @@ struct hvm_vcpu_asid {
 struct hvm_vcpu_io {
     /* I/O request in flight to device model. */
     enum hvm_io_state   io_state;
+    unsigned long       io_addr;
     unsigned long       io_data;
+    uint8_t             io_dir;
+    bool_t              io_data_is_ptr;
     int                 io_size;
 
     /*
@@ -77,6 +80,7 @@ struct hvm_vcpu_io {
     bool_t mmio_retry, mmio_retrying;
 
     unsigned long msix_unmask_address;
+    unsigned long msix_snoop_address;
 };
 
 #define VMCX_EADDR    (~0ULL)
