[PATCH v9 7/9] KVM: arm/arm64: Provide a get_input_level for the arch timer

Tomasz Nowicki tn at semihalf.com
Mon Jan 22 04:32:57 PST 2018


Hello Christoffer,

Please see my observations/comments below.

On 20.12.2017 12:36, Christoffer Dall wrote:
> The VGIC can now support the life-cycle of mapped level-triggered
> interrupts, and we no longer have to read back the timer state on every
> exit from the VM if we had an asserted timer interrupt signal, because
> the VGIC already knows if we hit the unlikely case where the guest
> disables the timer without ACKing the virtual timer interrupt.
> 
> This means we rework a bit of the code to factor out the functionality
> to snapshot the timer state from vtimer_save_state(), and we can reuse
> this functionality in the sync path when we have an irqchip in
> userspace, and also to support our implementation of the
> get_input_level() function for the timer.
> 
> This change also means that we can no longer rely on the timer's view of
> the interrupt line to set the active state, because we no longer
> maintain this state for mapped interrupts when exiting from the guest.
> Instead, we only set the active state if the virtual interrupt is
> active, and otherwise we simply let the timer fire again and raise the
> virtual interrupt from the ISR.
> 
> Reviewed-by: Eric Auger <eric.auger at redhat.com>
> Reviewed-by: Marc Zyngier <marc.zyngier at arm.com>
> Signed-off-by: Christoffer Dall <christoffer.dall at linaro.org>
> ---
>   include/kvm/arm_arch_timer.h |  2 ++
>   virt/kvm/arm/arch_timer.c    | 84 ++++++++++++++++++++------------------------
>   2 files changed, 40 insertions(+), 46 deletions(-)
> 
> diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
> index 6e45608b2399..b1dcfde0a3ef 100644
> --- a/include/kvm/arm_arch_timer.h
> +++ b/include/kvm/arm_arch_timer.h
> @@ -90,6 +90,8 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu);
>   
>   void kvm_timer_init_vhe(void);
>   
> +bool kvm_arch_timer_get_input_level(int vintid);
> +
>   #define vcpu_vtimer(v)	(&(v)->arch.timer_cpu.vtimer)
>   #define vcpu_ptimer(v)	(&(v)->arch.timer_cpu.ptimer)
>   
> diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
> index fb0533ed903d..d845d67b7062 100644
> --- a/virt/kvm/arm/arch_timer.c
> +++ b/virt/kvm/arm/arch_timer.c
> @@ -343,6 +343,12 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu)
>   	phys_timer_emulate(vcpu);
>   }
>   
> +static void __timer_snapshot_state(struct arch_timer_context *timer)
> +{
> +	timer->cnt_ctl = read_sysreg_el0(cntv_ctl);
> +	timer->cnt_cval = read_sysreg_el0(cntv_cval);
> +}
> +
>   static void vtimer_save_state(struct kvm_vcpu *vcpu)
>   {
>   	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
> @@ -354,10 +360,8 @@ static void vtimer_save_state(struct kvm_vcpu *vcpu)
>   	if (!vtimer->loaded)
>   		goto out;
>   
> -	if (timer->enabled) {
> -		vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
> -		vtimer->cnt_cval = read_sysreg_el0(cntv_cval);
> -	}
> +	if (timer->enabled)
> +		__timer_snapshot_state(vtimer);
>   
>   	/* Disable the virtual timer */
>   	write_sysreg_el0(0, cntv_ctl);
> @@ -454,8 +458,7 @@ static void kvm_timer_vcpu_load_vgic(struct kvm_vcpu *vcpu)
>   	bool phys_active;
>   	int ret;
>   
> -	phys_active = vtimer->irq.level ||
> -		      kvm_vgic_map_is_active(vcpu, vtimer->irq.irq);
> +	phys_active = kvm_vgic_map_is_active(vcpu, vtimer->irq.irq);
>   
>   	ret = irq_set_irqchip_state(host_vtimer_irq,
>   				    IRQCHIP_STATE_ACTIVE,
> @@ -535,54 +538,27 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
>   	set_cntvoff(0);
>   }
>   
> -static void unmask_vtimer_irq(struct kvm_vcpu *vcpu)
> +/*
> + * With a userspace irqchip we have to check if the guest de-asserted the
> + * timer and if so, unmask the timer irq signal on the host interrupt
> + * controller to ensure that we see future timer signals.
> + */
> +static void unmask_vtimer_irq_user(struct kvm_vcpu *vcpu)
>   {
>   	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
>   
>   	if (unlikely(!irqchip_in_kernel(vcpu->kvm))) {
> -		kvm_vtimer_update_mask_user(vcpu);
> -		return;
> -	}
> -
> -	/*
> -	 * If the guest disabled the timer without acking the interrupt, then
> -	 * we must make sure the physical and virtual active states are in
> -	 * sync by deactivating the physical interrupt, because otherwise we
> -	 * wouldn't see the next timer interrupt in the host.
> -	 */
> -	if (!kvm_vgic_map_is_active(vcpu, vtimer->irq.irq)) {
> -		int ret;
> -		ret = irq_set_irqchip_state(host_vtimer_irq,
> -					    IRQCHIP_STATE_ACTIVE,
> -					    false);
> -		WARN_ON(ret);
> +		__timer_snapshot_state(vtimer);
> +		if (!kvm_timer_should_fire(vtimer)) {
> +			kvm_timer_update_irq(vcpu, false, vtimer);
> +			kvm_vtimer_update_mask_user(vcpu);
> +		}
>   	}
>   }
>   
> -/**
> - * kvm_timer_sync_hwstate - sync timer state from cpu
> - * @vcpu: The vcpu pointer
> - *
> - * Check if any of the timers have expired while we were running in the guest,
> - * and inject an interrupt if that was the case.
> - */
>   void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
>   {
> -	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
> -
> -	/*
> -	 * If we entered the guest with the vtimer output asserted we have to
> -	 * check if the guest has modified the timer so that we should lower
> -	 * the line at this point.
> -	 */
> -	if (vtimer->irq.level) {
> -		vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
> -		vtimer->cnt_cval = read_sysreg_el0(cntv_cval);
> -		if (!kvm_timer_should_fire(vtimer)) {
> -			kvm_timer_update_irq(vcpu, false, vtimer);
> -			unmask_vtimer_irq(vcpu);
> -		}
> -	}
> +	unmask_vtimer_irq_user(vcpu);
>   }

While testing your VHE optimization series [1] I noticed massive WFI 
exits on ThunderX2 so I bisected 'vhe-optimize-v3-with-fixes' branch of 
[2] down to this commit.

The host traps on WFI so that VCPU thread should be scheduled out for 
some time. However, this is not happening because kvm_vcpu_block() -> 
kvm_vcpu_check_block() gives negative value (vtimer->irq.level is 
asserted) and VCPU is woken up immediately. Nobody takes care about 
lowering the vtimer line in this case.

I reverted shape of above kvm_timer_sync_hwstate() and things are good 
now. Before I start digging I wanted to check with you. Can you please 
check on your side?

[1] https://www.spinics.net/lists/kvm/msg161941.html
[2] git://git.kernel.org/pub/scm/linux/kernel/git/cdall/linux.git

Thanks,
Tomasz



More information about the linux-arm-kernel mailing list