KVM: PPC: Book3S HV: XIVE: Add a control to configure a source
authorCédric Le Goater <clg@kaod.org>
Thu, 18 Apr 2019 10:39:30 +0000 (12:39 +0200)
committerPaul Mackerras <paulus@ozlabs.org>
Tue, 30 Apr 2019 09:35:16 +0000 (19:35 +1000)
This control will be used by the H_INT_SET_SOURCE_CONFIG hcall from
QEMU to configure the target of a source and also to restore the
configuration of a source when migrating the VM.

The XIVE source interrupt structure is extended with the value of the
Effective Interrupt Source Number. The EISN is the interrupt number
pushed in the event queue that the guest OS will use to dispatch
events internally. Caching the EISN value in KVM eases the test when
checking if a reconfiguration is indeed needed.

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 cd8bfc37b72ec4d13225d1e1873b7d1193041966..33c64b2cdbe8534761f10aeff22149e9022838b5 100644 (file)
@@ -32,3 +32,24 @@ the legacy interrupt mode, referred as XICS (POWER7/8).
     -ENOMEM: Could not create a new source block
     -EFAULT: Invalid user pointer for attr->addr.
     -ENXIO:  Could not allocate underlying HW interrupt
+
+  3. KVM_DEV_XIVE_GRP_SOURCE_CONFIG (write only)
+  Configures source targeting
+  Attributes:
+    Interrupt source number  (64-bit)
+  The kvm_device_attr.addr points to a __u64 value:
+  bits:     | 63   ....  33 |  32  | 31 .. 3 |  2 .. 0
+  values:   |    eisn       | mask |  server | priority
+  - priority: 0-7 interrupt priority level
+  - server: CPU number chosen to handle the interrupt
+  - mask: mask flag (unused)
+  - eisn: Effective Interrupt Source Number
+  Errors:
+    -ENOENT: Unknown source number
+    -EINVAL: Not initialized source number
+    -EINVAL: Invalid priority
+    -EINVAL: Invalid CPU number.
+    -EFAULT: Invalid user pointer for attr->addr.
+    -ENXIO:  CPU event queues not configured or configuration of the
+             underlying HW interrupt failed
+    -EBUSY:  No CPU available to serve interrupt
index d468294c2a6722d98ea123ecc4479cc1f929abb4..e8161e21629b1cd7e428e767ccef765ffe11a396 100644 (file)
@@ -680,9 +680,20 @@ 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 */
+#define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3       /* 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)
 
+/* Layout of 64-bit XIVE source configuration attribute values */
+#define KVM_XIVE_SOURCE_PRIORITY_SHIFT 0
+#define KVM_XIVE_SOURCE_PRIORITY_MASK  0x7
+#define KVM_XIVE_SOURCE_SERVER_SHIFT   3
+#define KVM_XIVE_SOURCE_SERVER_MASK    0xfffffff8ULL
+#define KVM_XIVE_SOURCE_MASKED_SHIFT   32
+#define KVM_XIVE_SOURCE_MASKED_MASK    0x100000000ULL
+#define KVM_XIVE_SOURCE_EISN_SHIFT     33
+#define KVM_XIVE_SOURCE_EISN_MASK      0xfffffffe00000000ULL
+
 #endif /* __LINUX_KVM_POWERPC_H */
index 6c9f9fd0855fdd52ab602375f885ea9f4401301f..e09f3addffe5aa6f4b21a32952a57ecb29eeeea8 100644 (file)
@@ -342,7 +342,7 @@ static int xive_try_pick_queue(struct kvm_vcpu *vcpu, u8 prio)
        return atomic_add_unless(&q->count, 1, max) ? 0 : -EBUSY;
 }
 
-static int xive_select_target(struct kvm *kvm, u32 *server, u8 prio)
+int kvmppc_xive_select_target(struct kvm *kvm, u32 *server, u8 prio)
 {
        struct kvm_vcpu *vcpu;
        int i, rc;
@@ -530,7 +530,7 @@ static int xive_target_interrupt(struct kvm *kvm,
         * priority. The count for that new target will have
         * already been incremented.
         */
-       rc = xive_select_target(kvm, &server, prio);
+       rc = kvmppc_xive_select_target(kvm, &server, prio);
 
        /*
         * We failed to find a target ? Not much we can do
@@ -1504,6 +1504,7 @@ struct kvmppc_xive_src_block *kvmppc_xive_create_src_block(
 
        for (i = 0; i < KVMPPC_XICS_IRQ_PER_ICS; i++) {
                sb->irq_state[i].number = (bid << KVMPPC_XICS_ICS_SHIFT) | i;
+               sb->irq_state[i].eisn = 0;
                sb->irq_state[i].guest_priority = MASKED;
                sb->irq_state[i].saved_priority = MASKED;
                sb->irq_state[i].act_priority = MASKED;
index 1be921cb5dcb2041c42dbae2c8acb89b78f830a6..ae26fe653d98952b229bc413028e4c913027ea31 100644 (file)
@@ -61,6 +61,9 @@ struct kvmppc_xive_irq_state {
        bool saved_p;
        bool saved_q;
        u8 saved_scan_prio;
+
+       /* Xive native */
+       u32 eisn;                       /* Guest Effective IRQ number */
 };
 
 /* Select the "right" interrupt (IPI vs. passthrough) */
@@ -268,6 +271,7 @@ 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);
+int kvmppc_xive_select_target(struct kvm *kvm, u32 *server, u8 prio);
 
 #endif /* CONFIG_KVM_XICS */
 #endif /* _KVM_PPC_BOOK3S_XICS_H */
index 5f2bd6c137b71802e33bdaf0f98193586d4827bf..492825a3595803daaae0662297efcf6bf01605cd 100644 (file)
@@ -242,6 +242,99 @@ unlock:
        return rc;
 }
 
+static int kvmppc_xive_native_update_source_config(struct kvmppc_xive *xive,
+                                       struct kvmppc_xive_src_block *sb,
+                                       struct kvmppc_xive_irq_state *state,
+                                       u32 server, u8 priority, bool masked,
+                                       u32 eisn)
+{
+       struct kvm *kvm = xive->kvm;
+       u32 hw_num;
+       int rc = 0;
+
+       arch_spin_lock(&sb->lock);
+
+       if (state->act_server == server && state->act_priority == priority &&
+           state->eisn == eisn)
+               goto unlock;
+
+       pr_devel("new_act_prio=%d new_act_server=%d mask=%d act_server=%d act_prio=%d\n",
+                priority, server, masked, state->act_server,
+                state->act_priority);
+
+       kvmppc_xive_select_irq(state, &hw_num, NULL);
+
+       if (priority != MASKED && !masked) {
+               rc = kvmppc_xive_select_target(kvm, &server, priority);
+               if (rc)
+                       goto unlock;
+
+               state->act_priority = priority;
+               state->act_server = server;
+               state->eisn = eisn;
+
+               rc = xive_native_configure_irq(hw_num,
+                                              kvmppc_xive_vp(xive, server),
+                                              priority, eisn);
+       } else {
+               state->act_priority = MASKED;
+               state->act_server = 0;
+               state->eisn = 0;
+
+               rc = xive_native_configure_irq(hw_num, 0, MASKED, 0);
+       }
+
+unlock:
+       arch_spin_unlock(&sb->lock);
+       return rc;
+}
+
+static int kvmppc_xive_native_set_source_config(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;
+       u16 src;
+       u64 kvm_cfg;
+       u32 server;
+       u8 priority;
+       bool masked;
+       u32 eisn;
+
+       sb = kvmppc_xive_find_source(xive, irq, &src);
+       if (!sb)
+               return -ENOENT;
+
+       state = &sb->irq_state[src];
+
+       if (!state->valid)
+               return -EINVAL;
+
+       if (get_user(kvm_cfg, ubufp))
+               return -EFAULT;
+
+       pr_devel("%s irq=0x%lx cfg=%016llx\n", __func__, irq, kvm_cfg);
+
+       priority = (kvm_cfg & KVM_XIVE_SOURCE_PRIORITY_MASK) >>
+               KVM_XIVE_SOURCE_PRIORITY_SHIFT;
+       server = (kvm_cfg & KVM_XIVE_SOURCE_SERVER_MASK) >>
+               KVM_XIVE_SOURCE_SERVER_SHIFT;
+       masked = (kvm_cfg & KVM_XIVE_SOURCE_MASKED_MASK) >>
+               KVM_XIVE_SOURCE_MASKED_SHIFT;
+       eisn = (kvm_cfg & KVM_XIVE_SOURCE_EISN_MASK) >>
+               KVM_XIVE_SOURCE_EISN_SHIFT;
+
+       if (priority != xive_prio_from_guest(priority)) {
+               pr_err("invalid priority for queue %d for VCPU %d\n",
+                      priority, server);
+               return -EINVAL;
+       }
+
+       return kvmppc_xive_native_update_source_config(xive, sb, state, server,
+                                                      priority, masked, eisn);
+}
+
 static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
                                       struct kvm_device_attr *attr)
 {
@@ -253,6 +346,9 @@ static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
        case KVM_DEV_XIVE_GRP_SOURCE:
                return kvmppc_xive_native_set_source(xive, attr->attr,
                                                     attr->addr);
+       case KVM_DEV_XIVE_GRP_SOURCE_CONFIG:
+               return kvmppc_xive_native_set_source_config(xive, attr->attr,
+                                                           attr->addr);
        }
        return -ENXIO;
 }
@@ -270,6 +366,7 @@ static int kvmppc_xive_native_has_attr(struct kvm_device *dev,
        case KVM_DEV_XIVE_GRP_CTRL:
                break;
        case KVM_DEV_XIVE_GRP_SOURCE:
+       case KVM_DEV_XIVE_GRP_SOURCE_CONFIG:
                if (attr->attr >= KVMPPC_XIVE_FIRST_IRQ &&
                    attr->attr < KVMPPC_XIVE_NR_IRQS)
                        return 0;