Commit 40ccb96d authored by Like Xu's avatar Like Xu Committed by Paolo Bonzini

KVM: x86/pmu: Add pmc->intr to refactor kvm_perf_overflow{_intr}()

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: default avatarPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarLike Xu <likexu@tencent.com>
Message-Id: <20211130074221.93635-5-likexu@tencent.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 6ed1298e
...@@ -495,6 +495,7 @@ struct kvm_pmc { ...@@ -495,6 +495,7 @@ struct kvm_pmc {
*/ */
u64 current_config; u64 current_config;
bool is_paused; bool is_paused;
bool intr;
}; };
struct kvm_pmu { struct kvm_pmu {
......
...@@ -55,43 +55,41 @@ static void kvm_pmi_trigger_fn(struct irq_work *irq_work) ...@@ -55,43 +55,41 @@ static void kvm_pmi_trigger_fn(struct irq_work *irq_work)
kvm_pmu_deliver_pmi(vcpu); kvm_pmu_deliver_pmi(vcpu);
} }
static void kvm_perf_overflow(struct perf_event *perf_event, static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi)
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); struct kvm_pmu *pmu = pmc_to_pmu(pmc);
if (!test_and_set_bit(pmc->idx, pmu->reprogram_pmi)) { /* Ignore counters that have been reprogrammed already. */
__set_bit(pmc->idx, (unsigned long *)&pmu->global_status); if (test_and_set_bit(pmc->idx, pmu->reprogram_pmi))
kvm_make_request(KVM_REQ_PMU, pmc->vcpu); 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, static void kvm_perf_overflow(struct perf_event *perf_event,
struct perf_sample_data *data, struct perf_sample_data *data,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct kvm_pmc *pmc = perf_event->overflow_handler_context; 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);
/* __kvm_perf_overflow(pmc, true);
* 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);
}
} }
static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type, 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, ...@@ -126,7 +124,6 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type,
} }
event = perf_event_create_kernel_counter(&attr, -1, current, event = perf_event_create_kernel_counter(&attr, -1, current,
intr ? kvm_perf_overflow_intr :
kvm_perf_overflow, pmc); kvm_perf_overflow, pmc);
if (IS_ERR(event)) { if (IS_ERR(event)) {
pr_debug_ratelimited("kvm_pmu: event creation failed %ld for pmc->idx = %d\n", 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, ...@@ -138,6 +135,7 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type,
pmc_to_pmu(pmc)->event_count++; pmc_to_pmu(pmc)->event_count++;
clear_bit(pmc->idx, pmc_to_pmu(pmc)->reprogram_pmi); clear_bit(pmc->idx, pmc_to_pmu(pmc)->reprogram_pmi);
pmc->is_paused = false; pmc->is_paused = false;
pmc->intr = intr;
} }
static void pmc_pause_counter(struct kvm_pmc *pmc) static void pmc_pause_counter(struct kvm_pmc *pmc)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment