Unverified Commit f44547cb authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!4082 【OLK-6.6】KVM: arm64: vtimer irq bypass support

Merge Pull Request from: @sundongx 
 
KVM: arm64: vtimer irq bypass support
Based on the Hisilicon chip and MBIGEN, it supports direct injection of vtimer interrupts into the virtual machine, reducing vtimer interrupt processing latency(such as cyclictest performance improvement).
On the physical machine, enable vtimer irq bypass by configuring "kvm-arm.vgic_v4_enable=1 kvm-arm.vtimer_irqbypass=1" in the cmdline.
https://gitee.com/openeuler/kernel/issues/I8URKX 
 
Link:https://gitee.com/openeuler/kernel/pulls/4082

 

Reviewed-by: default avatarZhang Jianhua <chris.zjh@huawei.com>
Reviewed-by: default avatarZenghui Yu <yuzenghui@huawei.com>
Reviewed-by: default avatarLiu Chao <liuchao173@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parents b3d782eb 7e3e12fd
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6682,6 +6682,7 @@ CONFIG_ARM_GIC_PHYTIUM_2500=y
# CONFIG_AL_FIC is not set
CONFIG_HISILICON_IRQ_MBIGEN=y
CONFIG_IRQ_MBIGEN_ENABLE_SPI=y
CONFIG_VIRT_VTIMER_IRQ_BYPASS=y
# CONFIG_XILINX_INTC is not set
CONFIG_PARTITION_PERCPU=y
CONFIG_QCOM_IRQ_COMBINER=y
+3 −0
Original line number Diff line number Diff line
@@ -112,6 +112,9 @@ static inline void vcpu_clear_wfx_traps(struct kvm_vcpu *vcpu)
{
	vcpu->arch.hcr_el2 &= ~HCR_TWE;
	if (atomic_read(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vlpi_count) ||
#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	    vcpu->kvm->arch.vgic.vtimer_irqbypass ||
#endif
	    vcpu->kvm->arch.vgic.nassgireq)
		vcpu->arch.hcr_el2 &= ~HCR_TWI;
	else
+5 −0
Original line number Diff line number Diff line
@@ -81,4 +81,9 @@ config ARCH_VCPU_STAT

	  If unsure, say N.

config VIRT_VTIMER_IRQ_BYPASS
	bool "MBIGEN enable vtimer irq bypass"
	depends on KVM && ARM64 && ARCH_HISI && HISILICON_IRQ_MBIGEN
	default n

endif # VIRTUALIZATION
+246 −1
Original line number Diff line number Diff line
@@ -29,6 +29,21 @@ static unsigned int host_ptimer_irq;
static u32 host_vtimer_irq_flags;
static u32 host_ptimer_irq_flags;

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
bool vtimer_irqbypass;

static int __init early_vtimer_irqbypass(char *buf)
{
	return strtobool(buf, &vtimer_irqbypass);
}
early_param("kvm-arm.vtimer_irqbypass", early_vtimer_irqbypass);

static inline bool vtimer_is_irqbypass(void)
{
	return !!vtimer_irqbypass && kvm_vgic_vtimer_irqbypass_support();
}
#endif

static DEFINE_STATIC_KEY_FALSE(has_gic_active_state);

static const u8 default_ppi[] = {
@@ -653,6 +668,45 @@ static inline void set_timer_irq_phys_active(struct arch_timer_context *ctx, boo
	WARN_ON(r);
}

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
static void kvm_vtimer_mbigen_auto_clr_set(struct kvm_vcpu *vcpu, bool set)
{
	BUG_ON(!vtimer_is_irqbypass());

	vtimer_mbigen_set_auto_clr(vcpu->cpu, set);
}

static void kvm_vtimer_gic_auto_clr_set(struct kvm_vcpu *vcpu, bool set)
{
	BUG_ON(!vtimer_is_irqbypass());

	vtimer_gic_set_auto_clr(vcpu->cpu, set);
}

static void kvm_vtimer_mbigen_restore_stat(struct kvm_vcpu *vcpu)
{
	struct vtimer_mbigen_context *mbigen_ctx = vcpu_vtimer_mbigen(vcpu);
	u16 vpeid = kvm_vgic_get_vcpu_vpeid(vcpu);
	unsigned long flags;

	WARN_ON(!vtimer_is_irqbypass());

	local_irq_save(flags);

	if (mbigen_ctx->loaded)
		goto out;

	vtimer_mbigen_set_vector(vcpu->cpu, vpeid);

	if (mbigen_ctx->active)
		vtimer_mbigen_set_active(vcpu->cpu, true);

	mbigen_ctx->loaded = true;
out:
	local_irq_restore(flags);
}
#endif

static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx)
{
	struct kvm_vcpu *vcpu = ctx->vcpu;
@@ -834,20 +888,42 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)

	get_timer_map(vcpu, &map);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass()) {
		kvm_vtimer_mbigen_auto_clr_set(vcpu, false);
		kvm_vtimer_mbigen_restore_stat(vcpu);

		goto skip_load_vtimer;
	}
#endif
	if (static_branch_likely(&has_gic_active_state)) {
		if (vcpu_has_nv(vcpu))
			kvm_timer_vcpu_load_nested_switch(vcpu, &map);

		kvm_timer_vcpu_load_gic(map.direct_vtimer);
#ifndef CONFIG_VIRT_VTIMER_IRQ_BYPASS
		if (map.direct_ptimer)
			kvm_timer_vcpu_load_gic(map.direct_ptimer);
#endif
	} else {
		kvm_timer_vcpu_load_nogic(vcpu);
	}

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
skip_load_vtimer:
	if (static_branch_likely(&has_gic_active_state) && map.direct_ptimer)
		kvm_timer_vcpu_load_gic(map.direct_ptimer);
#endif
	kvm_timer_unblocking(vcpu);

	timer_restore_state(map.direct_vtimer);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass()) {
		kvm_vtimer_mbigen_auto_clr_set(vcpu, true);
		kvm_vtimer_gic_auto_clr_set(vcpu, true);
	}
#endif
	if (map.direct_ptimer)
		timer_restore_state(map.direct_ptimer);
	if (map.emul_vtimer)
@@ -875,6 +951,31 @@ bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu)
	       kvm_timer_should_fire(ptimer) != plevel;
}

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
static void kvm_vtimer_mbigen_save_stat(struct kvm_vcpu *vcpu)
{
	struct vtimer_mbigen_context *mbigen_ctx = vcpu_vtimer_mbigen(vcpu);
	unsigned long flags;

	WARN_ON(!vtimer_is_irqbypass());

	local_irq_save(flags);

	if (!mbigen_ctx->loaded)
		goto out;

	mbigen_ctx->active = vtimer_mbigen_get_active(vcpu->cpu);

	/* Clear active state in MBIGEN now that we've saved everything. */
	if (mbigen_ctx->active)
		vtimer_mbigen_set_active(vcpu->cpu, false);

	mbigen_ctx->loaded = false;
out:
	local_irq_restore(flags);
}
#endif

void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
{
	struct arch_timer_cpu *timer = vcpu_timer(vcpu);
@@ -885,7 +986,21 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)

	get_timer_map(vcpu, &map);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass()) {
		kvm_vtimer_mbigen_auto_clr_set(vcpu, false);
		kvm_vtimer_gic_auto_clr_set(vcpu, false);
	}
#endif

	timer_save_state(map.direct_vtimer);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass()) {
		kvm_vtimer_mbigen_save_stat(vcpu);
		kvm_vtimer_mbigen_auto_clr_set(vcpu, true);
	}
#endif
	if (map.direct_ptimer)
		timer_save_state(map.direct_ptimer);

@@ -965,6 +1080,16 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
	}

	if (timer->enabled) {
#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
		if (vtimer_is_irqbypass()) {
			kvm_timer_update_irq(vcpu, false, vcpu_ptimer(vcpu));

			if (irqchip_in_kernel(vcpu->kvm) && map.direct_ptimer)
				kvm_vgic_reset_mapped_irq(vcpu, timer_irq(map.direct_ptimer));

			goto skip_reset_vtimer;
		}
#endif
		for (int i = 0; i < nr_timers(vcpu); i++)
			kvm_timer_update_irq(vcpu, false,
					     vcpu_get_timer(vcpu, i));
@@ -978,6 +1103,10 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)

	if (map.emul_vtimer)
		soft_timer_cancel(&map.emul_vtimer->hrtimer);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
skip_reset_vtimer:
#endif
	if (map.emul_ptimer)
		soft_timer_cancel(&map.emul_ptimer->hrtimer);

@@ -1036,6 +1165,12 @@ void kvm_timer_init_vm(struct kvm *kvm)

void kvm_timer_cpu_up(void)
{
#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass()) {
		enable_percpu_irq(host_ptimer_irq, host_ptimer_irq_flags);
		return;
	}
#endif
	enable_percpu_irq(host_vtimer_irq, host_vtimer_irq_flags);
	if (host_ptimer_irq)
		enable_percpu_irq(host_ptimer_irq, host_ptimer_irq_flags);
@@ -1043,6 +1178,10 @@ void kvm_timer_cpu_up(void)

void kvm_timer_cpu_down(void)
{
#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass())
		return;
#endif
	disable_percpu_irq(host_vtimer_irq);
	if (host_ptimer_irq)
		disable_percpu_irq(host_ptimer_irq);
@@ -1380,6 +1519,32 @@ int __init kvm_timer_hyp_init(bool has_gic)
		return -ENODEV;
	}

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	/*
	 * vtimer-irqbypass depends on:
	 *
	 * - HW support at mbigen level (vtimer_irqbypass_hw_support)
	 * - HW support at GIC level (kvm_vgic_vtimer_irqbypass_support)
	 * - in_kernel irqchip support
	 * - "kvm-arm.vtimer_irqbypass=1"
	 */
	vtimer_irqbypass &= vtimer_irqbypass_hw_support(info);
	vtimer_irqbypass &= has_gic;
	if (vtimer_is_irqbypass()) {
		kvm_info("vtimer-irqbypass enabled\n");

		/*
		 * If vtimer irqbypass is enabled, there's no need to use the
		 * vtimer forwarded irq inject.
		 */

		if (info->physical_irq > 0) {
			host_ptimer_irq = info->physical_irq;
			kvm_irq_fixup_flags(host_ptimer_irq, &host_ptimer_irq_flags);
		}
		goto ptimer_irq_init;
	}
#endif
	err = kvm_irq_init(info);
	if (err)
		return err;
@@ -1407,6 +1572,9 @@ int __init kvm_timer_hyp_init(bool has_gic)

	kvm_debug("virtual timer IRQ%d\n", host_vtimer_irq);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
ptimer_irq_init:
#endif
	/* Now let's do the physical EL1 timer irq */

	if (info->physical_irq > 0) {
@@ -1506,15 +1674,85 @@ static bool kvm_arch_timer_get_input_level(int vintid)
	return false;
}

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
static void vtimer_set_active_stat(struct kvm_vcpu *vcpu, int vintid, bool set)
{
	struct vtimer_mbigen_context *mbigen_ctx = vcpu_vtimer_mbigen(vcpu);
	int hwirq = timer_irq(vcpu_vtimer(vcpu));

	WARN_ON(!vtimer_is_irqbypass() || hwirq != vintid);

	if (!mbigen_ctx->loaded)
		mbigen_ctx->active = set;
	else
		vtimer_mbigen_set_active(vcpu->cpu, set);
}

static bool vtimer_get_active_stat(struct kvm_vcpu *vcpu, int vintid)
{
	struct vtimer_mbigen_context *mbigen_ctx = vcpu_vtimer_mbigen(vcpu);
	int hwirq = timer_irq(vcpu_vtimer(vcpu));

	WARN_ON(!vtimer_is_irqbypass() || hwirq != vintid);

	if (!mbigen_ctx->loaded)
		return mbigen_ctx->active;
	else
		return vtimer_mbigen_get_active(vcpu->cpu);
}

int kvm_vtimer_config(struct kvm *kvm)
{
	struct vgic_dist *dist = &kvm->arch.vgic;
	struct kvm_vcpu *vcpu;
	int ret = 0;
	unsigned long c;

	if (!vtimer_is_irqbypass())
		return 0;

	if (!irqchip_in_kernel(kvm))
		return -EINVAL;
	mutex_lock(&kvm->lock);
	if (dist->vtimer_irqbypass)
		goto out;

	kvm_for_each_vcpu(c, vcpu, kvm) {
		struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
		int intid;

		WARN_ON(timer->enabled);

		intid = timer_irq(vcpu_vtimer(vcpu));
		ret = kvm_vgic_config_vtimer_irqbypass(vcpu, intid,
							vtimer_get_active_stat,
							vtimer_set_active_stat);
		if (ret)
			goto out;
	}

	dist->vtimer_irqbypass = true;

out:
	mutex_unlock(&kvm->lock);
	return ret;
}
#endif

int kvm_timer_enable(struct kvm_vcpu *vcpu)
{
	struct arch_timer_cpu *timer = vcpu_timer(vcpu);
	struct timer_map map;
	int ret;
	int ret = 0;

	if (timer->enabled)
		return 0;

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (!irqchip_in_kernel(vcpu->kvm) && vtimer_is_irqbypass())
		return -EINVAL;
#endif

	/* Without a VGIC we do not map virtual IRQs to physical IRQs */
	if (!irqchip_in_kernel(vcpu->kvm))
		goto no_vgic;
@@ -1530,6 +1768,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)

	get_timer_map(vcpu, &map);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	if (vtimer_is_irqbypass())
		goto skip_map_vtimer;
#endif
	ret = kvm_vgic_map_phys_irq(vcpu,
				    map.direct_vtimer->host_timer_irq,
				    timer_irq(map.direct_vtimer),
@@ -1537,6 +1779,9 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
	if (ret)
		return ret;

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
skip_map_vtimer:
#endif
	if (map.direct_ptimer) {
		ret = kvm_vgic_map_phys_irq(vcpu,
					    map.direct_ptimer->host_timer_irq,
+6 −0
Original line number Diff line number Diff line
@@ -667,6 +667,12 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)

	kvm_arm_vcpu_init_debug(vcpu);

#ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS
	ret = kvm_vtimer_config(kvm);
	if (ret)
		return ret;
#endif

	if (likely(irqchip_in_kernel(kvm))) {
		/*
		 * Map the VGIC hardware resources before running a vcpu the
Loading