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

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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 {
+28 −30
Original line number Original line Diff line number Diff line
@@ -55,30 +55,20 @@ 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;
	}
}

static void kvm_perf_overflow_intr(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);
	__set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
	kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
	kvm_make_request(KVM_REQ_PMU, pmc->vcpu);


	if (!pmc->intr)
		return;

	/*
	/*
	 * Inject PMI. If vcpu was in a guest mode during NMI PMI
	 * 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
	 * can be ejected on a guest mode re-entry. Otherwise we can't
@@ -87,11 +77,19 @@ static void kvm_perf_overflow_intr(struct perf_event *perf_event,
	 * woken up. So we should wake it, but this is impossible from
	 * woken up. So we should wake it, but this is impossible from
	 * NMI context. Do it from irq work instead.
	 * NMI context. Do it from irq work instead.
	 */
	 */
		if (!kvm_is_in_guest())
	if (in_pmi && !kvm_is_in_guest())
		irq_work_queue(&pmc_to_pmu(pmc)->irq_work);
		irq_work_queue(&pmc_to_pmu(pmc)->irq_work);
	else
	else
		kvm_make_request(KVM_REQ_PMI, pmc->vcpu);
		kvm_make_request(KVM_REQ_PMI, pmc->vcpu);
}
}

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;

	__kvm_perf_overflow(pmc, true);
}
}


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,
	}
	}


	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,
	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)