KVM: PPC: Book3S HV: XIVE: add a control to initialize a source
authorCédric Le Goater <clg@kaod.org>
Thu, 18 Apr 2019 10:39:29 +0000 (12:39 +0200)
committerPaul Mackerras <paulus@ozlabs.org>
Tue, 30 Apr 2019 09:35:16 +0000 (19:35 +1000)
The XIVE KVM device maintains a list of interrupt sources for the VM
which are allocated in the pool of generic interrupts (IPIs) of the
main XIVE IC controller. These are used for the CPU IPIs as well as
for virtual device interrupts. The IRQ number space is defined by
QEMU.

The XIVE device reuses the source structures of the XICS-on-XIVE
device for the source blocks (2-level tree) and for the source
interrupts. Under XIVE native, the source interrupt caches mostly
configuration information and is less used than under the XICS-on-XIVE
device in which hcalls are still necessary at run-time.

When a source is initialized in KVM, an IPI interrupt source is simply
allocated at the OPAL level and then MASKED. KVM only needs to know
about its type: LSI or MSI.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Documentation/virtual/kvm/devices/xive.txt
arch/powerpc/include/uapi/asm/kvm.h
arch/powerpc/kvm/book3s_xive.c
arch/powerpc/kvm/book3s_xive.h
arch/powerpc/kvm/book3s_xive_native.c

index fdbd2ff92a88c05e72f71994ad06ff3e1569c7e8..cd8bfc37b72ec4d13225d1e1873b7d1193041966 100644 (file)
@@ -17,3 +17,18 @@ the legacy interrupt mode, referred as XICS (POWER7/8).
 
   1. KVM_DEV_XIVE_GRP_CTRL
   Provides global controls on the device
+
+  2. KVM_DEV_XIVE_GRP_SOURCE (write only)
+  Initializes a new source in the XIVE device and mask it.
+  Attributes:
+    Interrupt source number  (64-bit)
+  The kvm_device_attr.addr points to a __u64 value:
+  bits:     | 63   ....  2 |   1   |   0
+  values:   |    unused    | level | type
+  - type:  0:MSI 1:LSI
+  - level: assertion level in case of an LSI.
+  Errors:
+    -E2BIG:  Interrupt source number is out of range
+    -ENOMEM: Could not create a new source block
+    -EFAULT: Invalid user pointer for attr->addr.
+    -ENXIO:  Could not allocate underlying HW interrupt
index be0ce1f1762531d4c455c4d16bc438f4225d2f7f..d468294c2a6722d98ea123ecc4479cc1f929abb4 100644 (file)
@@ -679,5 +679,10 @@ struct kvm_ppc_cpu_char {
 
 /* POWER9 XIVE Native Interrupt Controller */
 #define KVM_DEV_XIVE_GRP_CTRL          1
+#define KVM_DEV_XIVE_GRP_SOURCE                2       /* 64-bit source identifier */
+
+/* Layout of 64-bit XIVE source attribute values */
+#define KVM_XIVE_LEVEL_SENSITIVE       (1ULL << 0)
+#define KVM_XIVE_LEVEL_ASSERTED                (1ULL << 1)
 
 #endif /* __LINUX_KVM_POWERPC_H */
index e7f1ada1c3decab1fdd928d18b2e466ab8e28d26..6c9f9fd0855fdd52ab602375f885ea9f4401301f 100644 (file)
@@ -1480,8 +1480,8 @@ static int xive_get_source(struct kvmppc_xive *xive, long irq, u64 addr)
        return 0;
 }
 
-static struct kvmppc_xive_src_block *xive_create_src_block(struct kvmppc_xive *xive,
-                                                          int irq)
+struct kvmppc_xive_src_block *kvmppc_xive_create_src_block(
+       struct kvmppc_xive *xive, int irq)
 {
        struct kvm *kvm = xive->kvm;
        struct kvmppc_xive_src_block *sb;
@@ -1560,7 +1560,7 @@ static int xive_set_source(struct kvmppc_xive *xive, long irq, u64 addr)
        sb = kvmppc_xive_find_source(xive, irq, &idx);
        if (!sb) {
                pr_devel("No source, creating source block...\n");
-               sb = xive_create_src_block(xive, irq);
+               sb = kvmppc_xive_create_src_block(xive, irq);
                if (!sb) {
                        pr_devel("Failed to create block...\n");
                        return -ENOMEM;
@@ -1784,7 +1784,7 @@ static void kvmppc_xive_cleanup_irq(u32 hw_num, struct xive_irq_data *xd)
        xive_cleanup_irq_data(xd);
 }
 
-static void kvmppc_xive_free_sources(struct kvmppc_xive_src_block *sb)
+void kvmppc_xive_free_sources(struct kvmppc_xive_src_block *sb)
 {
        int i;
 
index d366df69b9cbacd4eab27ec066399c921f3fc9e8..1be921cb5dcb2041c42dbae2c8acb89b78f830a6 100644 (file)
 #ifdef CONFIG_KVM_XICS
 #include "book3s_xics.h"
 
+/*
+ * The XIVE Interrupt source numbers are within the range 0 to
+ * KVMPPC_XICS_NR_IRQS.
+ */
+#define KVMPPC_XIVE_FIRST_IRQ  0
+#define KVMPPC_XIVE_NR_IRQS    KVMPPC_XICS_NR_IRQS
+
 /*
  * State for one guest irq source.
  *
@@ -258,6 +265,9 @@ extern int (*__xive_vm_h_eoi)(struct kvm_vcpu *vcpu, unsigned long xirr);
  */
 void kvmppc_xive_disable_vcpu_interrupts(struct kvm_vcpu *vcpu);
 int kvmppc_xive_debug_show_queues(struct seq_file *m, struct kvm_vcpu *vcpu);
+struct kvmppc_xive_src_block *kvmppc_xive_create_src_block(
+       struct kvmppc_xive *xive, int irq);
+void kvmppc_xive_free_sources(struct kvmppc_xive_src_block *sb);
 
 #endif /* CONFIG_KVM_XICS */
 #endif /* _KVM_PPC_BOOK3S_XICS_H */
index 6fa73cfd9d9c7b87a849cfaf75a04956c0121c3c..5f2bd6c137b71802e33bdaf0f98193586d4827bf 100644 (file)
 
 #include "book3s_xive.h"
 
+static u8 xive_vm_esb_load(struct xive_irq_data *xd, u32 offset)
+{
+       u64 val;
+
+       if (xd->flags & XIVE_IRQ_FLAG_SHIFT_BUG)
+               offset |= offset << 4;
+
+       val = in_be64(xd->eoi_mmio + offset);
+       return (u8)val;
+}
+
 static void kvmppc_xive_native_cleanup_queue(struct kvm_vcpu *vcpu, int prio)
 {
        struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
@@ -154,12 +165,94 @@ bail:
        return rc;
 }
 
+static int kvmppc_xive_native_set_source(struct kvmppc_xive *xive, long irq,
+                                        u64 addr)
+{
+       struct kvmppc_xive_src_block *sb;
+       struct kvmppc_xive_irq_state *state;
+       u64 __user *ubufp = (u64 __user *) addr;
+       u64 val;
+       u16 idx;
+       int rc;
+
+       pr_devel("%s irq=0x%lx\n", __func__, irq);
+
+       if (irq < KVMPPC_XIVE_FIRST_IRQ || irq >= KVMPPC_XIVE_NR_IRQS)
+               return -E2BIG;
+
+       sb = kvmppc_xive_find_source(xive, irq, &idx);
+       if (!sb) {
+               pr_debug("No source, creating source block...\n");
+               sb = kvmppc_xive_create_src_block(xive, irq);
+               if (!sb) {
+                       pr_err("Failed to create block...\n");
+                       return -ENOMEM;
+               }
+       }
+       state = &sb->irq_state[idx];
+
+       if (get_user(val, ubufp)) {
+               pr_err("fault getting user info !\n");
+               return -EFAULT;
+       }
+
+       arch_spin_lock(&sb->lock);
+
+       /*
+        * If the source doesn't already have an IPI, allocate
+        * one and get the corresponding data
+        */
+       if (!state->ipi_number) {
+               state->ipi_number = xive_native_alloc_irq();
+               if (state->ipi_number == 0) {
+                       pr_err("Failed to allocate IRQ !\n");
+                       rc = -ENXIO;
+                       goto unlock;
+               }
+               xive_native_populate_irq_data(state->ipi_number,
+                                             &state->ipi_data);
+               pr_debug("%s allocated hw_irq=0x%x for irq=0x%lx\n", __func__,
+                        state->ipi_number, irq);
+       }
+
+       /* Restore LSI state */
+       if (val & KVM_XIVE_LEVEL_SENSITIVE) {
+               state->lsi = true;
+               if (val & KVM_XIVE_LEVEL_ASSERTED)
+                       state->asserted = true;
+               pr_devel("  LSI ! Asserted=%d\n", state->asserted);
+       }
+
+       /* Mask IRQ to start with */
+       state->act_server = 0;
+       state->act_priority = MASKED;
+       xive_vm_esb_load(&state->ipi_data, XIVE_ESB_SET_PQ_01);
+       xive_native_configure_irq(state->ipi_number, 0, MASKED, 0);
+
+       /* Increment the number of valid sources and mark this one valid */
+       if (!state->valid)
+               xive->src_count++;
+       state->valid = true;
+
+       rc = 0;
+
+unlock:
+       arch_spin_unlock(&sb->lock);
+
+       return rc;
+}
+
 static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
                                       struct kvm_device_attr *attr)
 {
+       struct kvmppc_xive *xive = dev->private;
+
        switch (attr->group) {
        case KVM_DEV_XIVE_GRP_CTRL:
                break;
+       case KVM_DEV_XIVE_GRP_SOURCE:
+               return kvmppc_xive_native_set_source(xive, attr->attr,
+                                                    attr->addr);
        }
        return -ENXIO;
 }
@@ -176,6 +269,11 @@ static int kvmppc_xive_native_has_attr(struct kvm_device *dev,
        switch (attr->group) {
        case KVM_DEV_XIVE_GRP_CTRL:
                break;
+       case KVM_DEV_XIVE_GRP_SOURCE:
+               if (attr->attr >= KVMPPC_XIVE_FIRST_IRQ &&
+                   attr->attr < KVMPPC_XIVE_NR_IRQS)
+                       return 0;
+               break;
        }
        return -ENXIO;
 }
@@ -184,6 +282,7 @@ static void kvmppc_xive_native_free(struct kvm_device *dev)
 {
        struct kvmppc_xive *xive = dev->private;
        struct kvm *kvm = xive->kvm;
+       int i;
 
        debugfs_remove(xive->dentry);
 
@@ -192,6 +291,13 @@ static void kvmppc_xive_native_free(struct kvm_device *dev)
        if (kvm)
                kvm->arch.xive = NULL;
 
+       for (i = 0; i <= xive->max_sbid; i++) {
+               if (xive->src_blocks[i])
+                       kvmppc_xive_free_sources(xive->src_blocks[i]);
+               kfree(xive->src_blocks[i]);
+               xive->src_blocks[i] = NULL;
+       }
+
        if (xive->vp_base != XIVE_INVALID_VP)
                xive_native_free_vp_block(xive->vp_base);