]> git.baikalelectronics.ru Git - kernel.git/commitdiff
KVM: x86/xen: Make kvm_xen_set_evtchn() reusable from other places
authorDavid Woodhouse <dwmw@amazon.co.uk>
Thu, 3 Mar 2022 15:41:17 +0000 (15:41 +0000)
committerPaolo Bonzini <pbonzini@redhat.com>
Sat, 2 Apr 2022 09:41:14 +0000 (05:41 -0400)
Clean it up to return -errno on error consistently, while still being
compatible with the return conventions for kvm_arch_set_irq_inatomic()
and the kvm_set_irq() callback.

We use -ENOTCONN to indicate when the port is masked. No existing users
care, except that it's negative.

Also allow it to optimise the vCPU lookup. Unless we abuse the lapic
map, there is no quick lookup from APIC ID to a vCPU; the logic in
kvm_get_vcpu_by_id() will just iterate over all vCPUs till it finds
the one it wants. So do that just once and stash the result in the
struct kvm_xen_evtchn for next time.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20220303154127.202856-8-dwmw2@infradead.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
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 65ffba89441ad5ae881349b4b309b194714ba58a..9c87263a5be29f51ff4fb8ba21c5b27b3d342520 100644 (file)
@@ -859,13 +859,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;
@@ -873,18 +876,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_cache.active)
-               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;
 
@@ -898,12 +906,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;
        }
 
        /*
@@ -913,10 +921,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 to the bitmap in shared_info. */
                /* Now switch to the vCPU's vcpu_info to set the index and pending_sel */
@@ -962,17 +970,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;
 
@@ -1016,7 +1019,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;
 
@@ -1033,11 +1036,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;
 
@@ -1045,8 +1064,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 7dd0590f93e17e4b74a1ca44b99800adb6406d37..e28feb32add6414b6a9aaf6488a9008211e88383 100644 (file)
@@ -25,7 +25,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);
 void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu);
-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 3f9b22c4983a85704667a89e274c364326980574..252ee4a61b58b622f1f69380a84bd8d6f2575c50 100644 (file)
@@ -611,7 +611,8 @@ struct kvm_hv_sint {
 
 struct kvm_xen_evtchn {
        u32 port;
-       u32 vcpu;
+       u32 vcpu_id;
+       int vcpu_idx;
        u32 priority;
 };