KVM: PPC: Book3S HV: XIVE: Add a global reset control
authorCédric Le Goater <clg@kaod.org>
Thu, 18 Apr 2019 10:39:32 +0000 (12:39 +0200)
committerPaul Mackerras <paulus@ozlabs.org>
Tue, 30 Apr 2019 09:35:16 +0000 (19:35 +1000)
This control is to be used by the H_INT_RESET hcall from QEMU. Its
purpose is to clear all configuration of the sources and EQs. This is
necessary in case of a kexec (for a kdump kernel for instance) to make
sure that no remaining configuration is left from the previous boot
setup so that the new kernel can start safely from a clean state.

The queue 7 is ignored when the XIVE device is configured to run in
single escalation mode. Prio 7 is used by escalations.

The XIVE VP is kept enabled as the vCPU is still active and connected
to the XIVE device.

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_native.c

index cc13bfd..429cbc4 100644 (file)
@@ -17,6 +17,11 @@ the legacy interrupt mode, referred as XICS (POWER7/8).
 
   1. KVM_DEV_XIVE_GRP_CTRL
   Provides global controls on the device
+  Attributes:
+    1.1 KVM_DEV_XIVE_RESET (write only)
+    Resets the interrupt controller configuration for sources and event
+    queues. To be used by kexec and kdump.
+    Errors: none
 
   2. KVM_DEV_XIVE_GRP_SOURCE (write only)
   Initializes a new source in the XIVE device and mask it.
index 8500540..f045f9d 100644 (file)
@@ -679,6 +679,7 @@ struct kvm_ppc_cpu_char {
 
 /* POWER9 XIVE Native Interrupt Controller */
 #define KVM_DEV_XIVE_GRP_CTRL          1
+#define   KVM_DEV_XIVE_RESET           1
 #define KVM_DEV_XIVE_GRP_SOURCE                2       /* 64-bit source identifier */
 #define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3       /* 64-bit source identifier */
 #define KVM_DEV_XIVE_GRP_EQ_CONFIG     4       /* 64-bit EQ identifier */
index 3e7cdca..b9597d8 100644 (file)
@@ -572,6 +572,83 @@ static int kvmppc_xive_native_get_queue_config(struct kvmppc_xive *xive,
        return 0;
 }
 
+static void kvmppc_xive_reset_sources(struct kvmppc_xive_src_block *sb)
+{
+       int i;
+
+       for (i = 0; i < KVMPPC_XICS_IRQ_PER_ICS; i++) {
+               struct kvmppc_xive_irq_state *state = &sb->irq_state[i];
+
+               if (!state->valid)
+                       continue;
+
+               if (state->act_priority == MASKED)
+                       continue;
+
+               state->eisn = 0;
+               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);
+               if (state->pt_number) {
+                       xive_vm_esb_load(state->pt_data, XIVE_ESB_SET_PQ_01);
+                       xive_native_configure_irq(state->pt_number,
+                                                 0, MASKED, 0);
+               }
+       }
+}
+
+static int kvmppc_xive_reset(struct kvmppc_xive *xive)
+{
+       struct kvm *kvm = xive->kvm;
+       struct kvm_vcpu *vcpu;
+       unsigned int i;
+
+       pr_devel("%s\n", __func__);
+
+       mutex_lock(&kvm->lock);
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
+               unsigned int prio;
+
+               if (!xc)
+                       continue;
+
+               kvmppc_xive_disable_vcpu_interrupts(vcpu);
+
+               for (prio = 0; prio < KVMPPC_XIVE_Q_COUNT; prio++) {
+
+                       /* Single escalation, no queue 7 */
+                       if (prio == 7 && xive->single_escalation)
+                               break;
+
+                       if (xc->esc_virq[prio]) {
+                               free_irq(xc->esc_virq[prio], vcpu);
+                               irq_dispose_mapping(xc->esc_virq[prio]);
+                               kfree(xc->esc_virq_names[prio]);
+                               xc->esc_virq[prio] = 0;
+                       }
+
+                       kvmppc_xive_native_cleanup_queue(vcpu, prio);
+               }
+       }
+
+       for (i = 0; i <= xive->max_sbid; i++) {
+               struct kvmppc_xive_src_block *sb = xive->src_blocks[i];
+
+               if (sb) {
+                       arch_spin_lock(&sb->lock);
+                       kvmppc_xive_reset_sources(sb);
+                       arch_spin_unlock(&sb->lock);
+               }
+       }
+
+       mutex_unlock(&kvm->lock);
+
+       return 0;
+}
+
 static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
                                       struct kvm_device_attr *attr)
 {
@@ -579,6 +656,10 @@ static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
 
        switch (attr->group) {
        case KVM_DEV_XIVE_GRP_CTRL:
+               switch (attr->attr) {
+               case KVM_DEV_XIVE_RESET:
+                       return kvmppc_xive_reset(xive);
+               }
                break;
        case KVM_DEV_XIVE_GRP_SOURCE:
                return kvmppc_xive_native_set_source(xive, attr->attr,
@@ -611,6 +692,10 @@ static int kvmppc_xive_native_has_attr(struct kvm_device *dev,
 {
        switch (attr->group) {
        case KVM_DEV_XIVE_GRP_CTRL:
+               switch (attr->attr) {
+               case KVM_DEV_XIVE_RESET:
+                       return 0;
+               }
                break;
        case KVM_DEV_XIVE_GRP_SOURCE:
        case KVM_DEV_XIVE_GRP_SOURCE_CONFIG: