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

Tomasz Nowicki tn at semihalf.com
Wed Jan 31 00:32:26 PST 2018


Hi Christoffer,

On 30.01.2018 13:49, Christoffer Dall wrote:
> Hi Tomasz,
> 
> On Mon, Jan 22, 2018 at 01:32:57PM +0100, Tomasz Nowicki wrote:
>> 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?
>>
> 
> This patch should fix the issue, thanks for pointing it out.
> https://lists.cs.columbia.edu/pipermail/kvmarm/2018-January/029582.html
> 
> I'd be happy to hear if you can confirm this or not.

Yes, the issue is fixed. Thanks!

> 
> I forgot that my git auto-cc doesn't pick up reported-by tags (I should
> fix that), so apologies for not cc'ing you on the series.
> 

No problem at all.

Tomasz



More information about the linux-arm-kernel mailing list