]> www.infradead.org Git - users/willy/xarray.git/commitdiff
KVM: x86/pmu: Add pmc->intr to refactor kvm_perf_overflow{_intr}()
authorLike Xu <likexu@tencent.com>
Tue, 30 Nov 2021 07:42:19 +0000 (15:42 +0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 7 Jan 2022 15:44:42 +0000 (10:44 -0500)
Depending on whether intr should be triggered or not, KVM registers
two different event overflow callbacks in the perf_event context.

The code skeleton of these two functions is very similar, so
the pmc->intr can be stored into pmc from pmc_reprogram_counter()
which provides smaller instructions footprint against the
u-architecture branch predictor.

The __kvm_perf_overflow() can be called in non-nmi contexts
and a flag is needed to distinguish the caller context and thus
avoid a check on kvm_is_in_guest(), otherwise we might get
warnings from suspicious RCU or check_preemption_disabled().

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Like Xu <likexu@tencent.com>
Message-Id: <20211130074221.93635-5-likexu@tencent.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/pmu.c

index 344ced0b6b684d650bdf30a6b7dd62847127933e..ce622b89c5d80ab0ee213d4042a2087a6dd3c364 100644 (file)
@@ -495,6 +495,7 @@ struct kvm_pmc {
         */
        u64 current_config;
        bool is_paused;
+       bool intr;
 };
 
 struct kvm_pmu {
index b7a1ae28ab872b93f723939dd8bbf8d9695a6104..a20207ee4014c2d592ec88ac033bfc947e44c810 100644 (file)
@@ -55,43 +55,41 @@ static void kvm_pmi_trigger_fn(struct irq_work *irq_work)
        kvm_pmu_deliver_pmi(vcpu);
 }
 
-static void kvm_perf_overflow(struct perf_event *perf_event,
-                             struct perf_sample_data *data,
-                             struct pt_regs *regs)
+static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi)
 {
-       struct kvm_pmc *pmc = perf_event->overflow_handler_context;
        struct kvm_pmu *pmu = pmc_to_pmu(pmc);
 
-       if (!test_and_set_bit(pmc->idx, pmu->reprogram_pmi)) {
-               __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
-               kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
-       }
+       /* Ignore counters that have been reprogrammed already. */
+       if (test_and_set_bit(pmc->idx, pmu->reprogram_pmi))
+               return;
+
+       __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
+       kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
+
+       if (!pmc->intr)
+               return;
+
+       /*
+        * Inject PMI. If vcpu was in a guest mode during NMI PMI
+        * can be ejected on a guest mode re-entry. Otherwise we can't
+        * be sure that vcpu wasn't executing hlt instruction at the
+        * time of vmexit and is not going to re-enter guest mode until
+        * woken up. So we should wake it, but this is impossible from
+        * NMI context. Do it from irq work instead.
+        */
+       if (in_pmi && !kvm_is_in_guest())
+               irq_work_queue(&pmc_to_pmu(pmc)->irq_work);
+       else
+               kvm_make_request(KVM_REQ_PMI, pmc->vcpu);
 }
 
-static void kvm_perf_overflow_intr(struct perf_event *perf_event,
-                                  struct perf_sample_data *data,
-                                  struct pt_regs *regs)
+static void kvm_perf_overflow(struct perf_event *perf_event,
+                             struct perf_sample_data *data,
+                             struct pt_regs *regs)
 {
        struct kvm_pmc *pmc = perf_event->overflow_handler_context;
-       struct kvm_pmu *pmu = pmc_to_pmu(pmc);
-
-       if (!test_and_set_bit(pmc->idx, pmu->reprogram_pmi)) {
-               __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
-               kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
 
-               /*
-                * Inject PMI. If vcpu was in a guest mode during NMI PMI
-                * can be ejected on a guest mode re-entry. Otherwise we can't
-                * be sure that vcpu wasn't executing hlt instruction at the
-                * time of vmexit and is not going to re-enter guest mode until
-                * woken up. So we should wake it, but this is impossible from
-                * NMI context. Do it from irq work instead.
-                */
-               if (!kvm_is_in_guest())
-                       irq_work_queue(&pmc_to_pmu(pmc)->irq_work);
-               else
-                       kvm_make_request(KVM_REQ_PMI, pmc->vcpu);
-       }
+       __kvm_perf_overflow(pmc, true);
 }
 
 static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type,
@@ -126,7 +124,6 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type,
        }
 
        event = perf_event_create_kernel_counter(&attr, -1, current,
-                                                intr ? kvm_perf_overflow_intr :
                                                 kvm_perf_overflow, pmc);
        if (IS_ERR(event)) {
                pr_debug_ratelimited("kvm_pmu: event creation failed %ld for pmc->idx = %d\n",
@@ -138,6 +135,7 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type,
        pmc_to_pmu(pmc)->event_count++;
        clear_bit(pmc->idx, pmc_to_pmu(pmc)->reprogram_pmi);
        pmc->is_paused = false;
+       pmc->intr = intr;
 }
 
 static void pmc_pause_counter(struct kvm_pmc *pmc)