x86/hvm: add per-vcpu evtchn upcalls
authorPaul Durrant <paul.durrant@citrix.com>
Mon, 19 Jan 2015 11:18:03 +0000 (12:18 +0100)
committerJan Beulich <jbeulich@suse.com>
Mon, 19 Jan 2015 11:18:03 +0000 (12:18 +0100)
HVM guests have always been confined to using the domain callback
via (see HVM_PARAM_CALLBACK_IRQ) to receive event notifications.
This is usually an IOAPIC vector and is only used if the event
channel is bound to vcpu 0.

PVHVM Linux uses a pre-defined interrupt vector for the event
channel upcall, set using HVM_PARAM_CALLBACK_IRQ by ORing in a
special bit (bit 57) into the value (see params.h). However, it
does not assert the interrupt via the emulated local APIC.

This mechanism is not suitable in the general case since Windows
(and potentially other OSes) because they:

- cannot guarantee the same vector for all VCPUs
- do require the upcall to be asserted via the local APIC

This patch adds a new HVM op allowing a guest to specify a local
APIC vector to use as an upcall notification for a specific vcpu
therefore coping with the case of differing vector numbers.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/irq.c
xen/include/asm-x86/hvm/vcpu.h
xen/include/public/hvm/hvm_op.h

index 8b06bfdac869e354798ce92b85e01acf6ad6923b..b03bb101032b1f9797a1e8e912dedec9047da90a 100644 (file)
@@ -5514,6 +5514,31 @@ static int hvmop_destroy_ioreq_server(
     return rc;
 }
 
+static int hvmop_set_evtchn_upcall_vector(
+    XEN_GUEST_HANDLE_PARAM(xen_hvm_evtchn_upcall_vector_t) uop)
+{
+    xen_hvm_evtchn_upcall_vector_t op;
+    struct domain *d = current->domain;
+    struct vcpu *v;
+
+    if ( copy_from_guest(&op, uop, 1) )
+        return -EFAULT;
+
+    if ( !is_hvm_domain(d) )
+        return -EINVAL;
+
+    if ( op.vector < 0x10 )
+        return -EINVAL;
+
+    if ( op.vcpu >= d->max_vcpus || (v = d->vcpu[op.vcpu]) == NULL )
+        return -ENOENT;
+
+    printk(XENLOG_G_INFO "%pv: upcall vector %02x\n", v, op.vector);
+
+    v->arch.hvm_vcpu.evtchn_upcall_vector = op.vector;
+    return 0;
+}
+
 /*
  * Note that this value is effectively part of the ABI, even if we don't need
  * to make it a formal part of it: A guest suspended for migration in the
@@ -5573,6 +5598,11 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE_PARAM(void) arg)
             guest_handle_cast(arg, xen_hvm_destroy_ioreq_server_t));
         break;
     
+    case HVMOP_set_evtchn_upcall_vector:
+        rc = hvmop_set_evtchn_upcall_vector(
+            guest_handle_cast(arg, xen_hvm_evtchn_upcall_vector_t));
+        break;
+    
     case HVMOP_set_param:
     case HVMOP_get_param:
     {
index 35f4f948daa6676e4f684765b27f48e639910437..743429e59ca6c1fda6774c4b65b973089c6e1b8e 100644 (file)
@@ -218,7 +218,13 @@ void hvm_assert_evtchn_irq(struct vcpu *v)
         return;
     }
 
-    if ( is_hvm_pv_evtchn_vcpu(v) )
+    if ( v->arch.hvm_vcpu.evtchn_upcall_vector != 0 )
+    {
+        uint8_t vector = v->arch.hvm_vcpu.evtchn_upcall_vector;
+
+        vlapic_set_irq(vcpu_vlapic(v), vector, 0);
+    }
+    else if ( is_hvm_pv_evtchn_vcpu(v) )
         vcpu_kick(v);
     else if ( v->vcpu_id == 0 )
         hvm_set_callback_irq_level(v);
index 01e0665beb0cc29beed13d5017905fb3ccda9803..3d8f4dcac1ed71bed42e9c95d8b15944814e37a6 100644 (file)
@@ -169,6 +169,8 @@ struct hvm_vcpu {
     /* In mode delay_for_missed_ticks, VCPUs have differing guest times. */
     int64_t             stime_offset;
 
+    u8                  evtchn_upcall_vector;
+
     /* Which cache mode is this VCPU in (CR0:CD/NW)? */
     u8                  cache_mode;
 
index a4e5345bc7f1c3a080252491557109ea213480e8..cde35719e7937792baa0e90cf7d0e2ed9b2365db 100644 (file)
@@ -370,6 +370,25 @@ DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_ioreq_server_state_t);
 
 #endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
 
+#if defined(__i386__) || defined(__x86_64__)
+
+/*
+ * HVMOP_set_evtchn_upcall_vector: Set a <vector> that should be used for event
+ *                                 channel upcalls on the specified <vcpu>. If set,
+ *                                 this vector will be used in preference to the
+ *                                 domain global callback via (see
+ *                                 HVM_PARAM_CALLBACK_IRQ).
+ */
+#define HVMOP_set_evtchn_upcall_vector 23
+struct xen_hvm_evtchn_upcall_vector {
+    uint32_t vcpu;
+    uint8_t vector;
+};
+typedef struct xen_hvm_evtchn_upcall_vector xen_hvm_evtchn_upcall_vector_t;
+DEFINE_XEN_GUEST_HANDLE(xen_hvm_evtchn_upcall_vector_t);
+
+#endif /* defined(__i386__) || defined(__x86_64__) */
+
 #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
 
 /*