Merge tag 'platform-drivers-x86-v5.10-1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / arch / arm64 / kvm / pmu-emul.c
index f0d0312..81916e3 100644 (file)
@@ -269,6 +269,7 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)
 
        for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++)
                kvm_pmu_release_perf_event(&pmu->pmc[i]);
+       irq_work_sync(&vcpu->arch.pmu.overflow_work);
 }
 
 u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
@@ -433,6 +434,22 @@ void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu)
        kvm_pmu_update_state(vcpu);
 }
 
+/**
+ * When perf interrupt is an NMI, we cannot safely notify the vcpu corresponding
+ * to the event.
+ * This is why we need a callback to do it once outside of the NMI context.
+ */
+static void kvm_pmu_perf_overflow_notify_vcpu(struct irq_work *work)
+{
+       struct kvm_vcpu *vcpu;
+       struct kvm_pmu *pmu;
+
+       pmu = container_of(work, struct kvm_pmu, overflow_work);
+       vcpu = kvm_pmc_to_vcpu(pmu->pmc);
+
+       kvm_vcpu_kick(vcpu);
+}
+
 /**
  * When the perf event overflows, set the overflow status and inform the vcpu.
  */
@@ -465,7 +482,11 @@ static void kvm_pmu_perf_overflow(struct perf_event *perf_event,
 
        if (kvm_pmu_overflow_status(vcpu)) {
                kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
-               kvm_vcpu_kick(vcpu);
+
+               if (!in_nmi())
+                       kvm_vcpu_kick(vcpu);
+               else
+                       irq_work_queue(&vcpu->arch.pmu.overflow_work);
        }
 
        cpu_pmu->pmu.start(perf_event, PERF_EF_RELOAD);
@@ -764,6 +785,9 @@ static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
                        return ret;
        }
 
+       init_irq_work(&vcpu->arch.pmu.overflow_work,
+                     kvm_pmu_perf_overflow_notify_vcpu);
+
        vcpu->arch.pmu.created = true;
        return 0;
 }