]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
KVM: x86/xen: Make kvm_xen_set_evtchn() reusable
authorDavid Woodhouse <dwmw@amazon.co.uk>
Mon, 7 Feb 2022 14:04:24 +0000 (14:04 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Mon, 7 Feb 2022 14:43:29 +0000 (14:43 +0000)
The return convention from kvm_arch_set_irq_inatomic() and a kvm_set_irq()
callback are slightly icky but not entirely incompatible. Document this
and clean up kvm_xen_set_evtchn() a little to make it easier to reuse it
internally from xen.c. Make it return negative errno in all cases, even
a semi-randomly-chosen -ENOTCONN when the port is masked. The existing
callers don't care, but when we start to invoke it more directly from
userspace it'll be good to be consistent.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
arch/x86/kvm/irq_comm.c
arch/x86/kvm/xen.c
arch/x86/kvm/xen.h
include/linux/kvm_host.h

index 6e0dab04320ea1984c9f362d0149ed24f0c90b0c..0687162c4f227d8ee9bfd45c70d2d5f7e3c70535 100644 (file)
@@ -181,7 +181,7 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
                if (!level)
                        return -1;
 
-               return kvm_xen_set_evtchn_fast(e, kvm);
+               return kvm_xen_set_evtchn_fast(&e->xen_evtchn, kvm);
 #endif
        default:
                break;
index bad57535fad0825608c91e9cdd590c9fc0040a42..635cbcd3eade8bb1817d9354d2f67a6b75467fd5 100644 (file)
@@ -854,13 +854,16 @@ static inline int max_evtchn_port(struct kvm *kvm)
 }
 
 /*
- * This follows the kvm_set_irq() API, so it returns:
+ * The return value from this function is propagated to kvm_set_irq() API,
+ * so it returns:
  *  < 0   Interrupt was ignored (masked or not delivered for other reasons)
  *  = 0   Interrupt was coalesced (previous irq is still pending)
  *  > 0   Number of CPUs interrupt was delivered to
+ *
+ * It is also called directly from kvm_arch_set_irq_inatomic(), where the
+ * only check on its return value is a comparison with -EWOULDBLOCK'.
  */
-int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
-                           struct kvm *kvm)
+int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 {
        struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
        struct kvm_vcpu *vcpu;
@@ -868,18 +871,23 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
        unsigned long flags;
        int port_word_bit;
        bool kick_vcpu = false;
-       int idx;
-       int rc;
+       int vcpu_idx, idx, rc;
 
-       vcpu = kvm_get_vcpu_by_id(kvm, e->xen_evtchn.vcpu);
-       if (!vcpu)
-               return -1;
+       vcpu_idx = READ_ONCE(xe->vcpu_idx);
+       if (vcpu_idx >= 0)
+               vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+       else {
+               vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id);
+               if (!vcpu)
+                       return -EINVAL;
+               WRITE_ONCE(xe->vcpu_idx, kvm_vcpu_get_idx(vcpu));
+       }
 
        if (!vcpu->arch.xen.vcpu_info_set)
-               return -1;
+               return -EINVAL;
 
-       if (e->xen_evtchn.port >= max_evtchn_port(kvm))
-               return -1;
+       if (xe->port >= max_evtchn_port(kvm))
+               return -EINVAL;
 
        rc = -EWOULDBLOCK;
        read_lock_irqsave(&gpc->lock, flags);
@@ -892,12 +900,12 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
                struct shared_info *shinfo = gpc->khva;
                pending_bits = (unsigned long *)&shinfo->evtchn_pending;
                mask_bits = (unsigned long *)&shinfo->evtchn_mask;
-               port_word_bit = e->xen_evtchn.port / 64;
+               port_word_bit = xe->port / 64;
        } else {
                struct compat_shared_info *shinfo = gpc->khva;
                pending_bits = (unsigned long *)&shinfo->evtchn_pending;
                mask_bits = (unsigned long *)&shinfo->evtchn_mask;
-               port_word_bit = e->xen_evtchn.port / 32;
+               port_word_bit = xe->port / 32;
        }
 
        /*
@@ -907,10 +915,10 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
         * already set, then we kick the vCPU in question to write to the
         * *real* evtchn_pending_sel in its own guest vcpu_info struct.
         */
-       if (test_and_set_bit(e->xen_evtchn.port, pending_bits)) {
+       if (test_and_set_bit(xe->port, pending_bits)) {
                rc = 0; /* It was already raised */
-       } else if (test_bit(e->xen_evtchn.port, mask_bits)) {
-               rc = -1; /* Masked */
+       } else if (test_bit(xe->port, mask_bits)) {
+               rc = -ENOTCONN; /* Masked */
        } else {
                rc = 1; /* Delivered. But was the vCPU waking already? */
                if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
@@ -929,17 +937,12 @@ int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
        return rc;
 }
 
-/* This is the version called from kvm_set_irq() as the .set function */
-static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
-                        int irq_source_id, int level, bool line_status)
+static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
 {
        bool mm_borrowed = false;
        int rc;
 
-       if (!level)
-               return -1;
-
-       rc = kvm_xen_set_evtchn_fast(e, kvm);
+       rc = kvm_xen_set_evtchn_fast(xe, kvm);
        if (rc != -EWOULDBLOCK)
                return rc;
 
@@ -983,7 +986,7 @@ static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm
                struct gfn_to_pfn_cache *gpc = &kvm->arch.xen.shinfo_cache;
                int idx;
 
-               rc = kvm_xen_set_evtchn_fast(e, kvm);
+               rc = kvm_xen_set_evtchn_fast(xe, kvm);
                if (rc != -EWOULDBLOCK)
                        break;
 
@@ -1001,11 +1004,27 @@ static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm
        return rc;
 }
 
+/* This is the version called from kvm_set_irq() as the .set function */
+static int evtchn_set_fn(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
+                        int irq_source_id, int level, bool line_status)
+{
+       if (!level)
+               return -EINVAL;
+
+       return kvm_xen_set_evtchn(&e->xen_evtchn, kvm);
+}
+
+/*
+ * Set up an event channel interrupt from the KVM IRQ routing table.
+ * Used for e.g. PIRQ from passed through physical devices.
+ */
 int kvm_xen_setup_evtchn(struct kvm *kvm,
                         struct kvm_kernel_irq_routing_entry *e,
                         const struct kvm_irq_routing_entry *ue)
 
 {
+       struct kvm_vcpu *vcpu;
+
        if (ue->u.xen_evtchn.port >= max_evtchn_port(kvm))
                return -EINVAL;
 
@@ -1013,8 +1032,22 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
        if (ue->u.xen_evtchn.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL)
                return -EINVAL;
 
+       /*
+        * Xen gives us interesting mappings from vCPU index to APIC ID,
+        * which means kvm_get_vcpu_by_id() has to iterate over all vCPUs
+        * to find it. Do that once at setup time, instead of every time.
+        * But beware that on live update / live migration, the routing
+        * table might be reinstated before the vCPU threads have finished
+        * recreating their vCPUs.
+        */
+       vcpu = kvm_get_vcpu_by_id(kvm, ue->u.xen_evtchn.vcpu);
+       if (vcpu)
+               e->xen_evtchn.vcpu_idx = kvm_vcpu_get_idx(vcpu);
+       else
+               e->xen_evtchn.vcpu_idx = -1;
+
        e->xen_evtchn.port = ue->u.xen_evtchn.port;
-       e->xen_evtchn.vcpu = ue->u.xen_evtchn.vcpu;
+       e->xen_evtchn.vcpu_id = ue->u.xen_evtchn.vcpu;
        e->xen_evtchn.priority = ue->u.xen_evtchn.priority;
        e->set = evtchn_set_fn;
 
index adbcc9ed59dbc0184ed113b2e557107765109b6f..6656afda00d6745efd81bb414efd8127d94a1001 100644 (file)
@@ -24,7 +24,7 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc);
 void kvm_xen_init_vm(struct kvm *kvm);
 void kvm_xen_destroy_vm(struct kvm *kvm);
 
-int kvm_xen_set_evtchn_fast(struct kvm_kernel_irq_routing_entry *e,
+int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
                            struct kvm *kvm);
 int kvm_xen_setup_evtchn(struct kvm *kvm,
                         struct kvm_kernel_irq_routing_entry *e,
index 06912d6b39d051013b731ae04b3b26c9f68a5efb..5142b5795feaf8bb51e257c8b05d2a642da84fb1 100644 (file)
@@ -496,7 +496,8 @@ struct kvm_hv_sint {
 
 struct kvm_xen_evtchn {
        u32 port;
-       u32 vcpu;
+       u32 vcpu_id;
+       int vcpu_idx;
        u32 priority;
 };