[PATCH] arm/arm64: KVM: Perform local TLB invalidation when multiplexing vcpus on a single CPU

Marc Zyngier marc.zyngier at arm.com
Tue Oct 25 03:20:13 PDT 2016


Hi Mark,

On 24/10/16 17:16, Mark Rutland wrote:
> Hi Marc,
> 
> On Mon, Oct 24, 2016 at 04:31:28PM +0100, Marc Zyngier wrote:
>> Architecturally, TLBs are private to the (physical) CPU they're
>> associated with. But when multiple vcpus from the same VM are
>> being multiplexed on the same CPU, the TLBs are not private
>> to the vcpus (and are actually shared across the VMID).
>>
>> Let's consider the following scenario:
>>
>> - vcpu-0 maps PA to VA
>> - vcpu-1 maps PA' to VA
>>
>> If run on the same physical CPU, vcpu-1 can hit TLB entries generated
>> by vcpu-0 accesses, and access the wrong physical page.
> 
> It might be worth noting that this could also lead to TLB conflicts and
> other such fun usually associated with missing TLB maintenance,
> depending on the two mappings (or the intermediate stages thereof).
> 
>> The solution to this is to keep a per-VM map of which vcpu ran last
>> on each given physical CPU, and invalidate local TLBs when switching
>> to a different vcpu from the same VM.
>>
>> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> 
> Modulo my comment on preemptiblity of kvm_arch_sched_in, everything
> below is a nit. Assuming that's not preemptible, this looks right to me.
> 
> FWIW, with or without the other comments considered:
> 
> Reviewed-by: Mark Rutland <mark.rutland at arm.com>
> 
>> ---
>>  arch/arm/include/asm/kvm_host.h   |  5 +++++
>>  arch/arm/include/asm/kvm_hyp.h    |  1 +
>>  arch/arm/kvm/arm.c                | 35 ++++++++++++++++++++++++++++++++++-
>>  arch/arm/kvm/hyp/switch.c         |  9 +++++++++
>>  arch/arm64/include/asm/kvm_host.h |  6 +++++-
>>  arch/arm64/kvm/hyp/switch.c       |  8 ++++++++
>>  6 files changed, 62 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
>> index 2d19e02..035e744 100644
>> --- a/arch/arm/include/asm/kvm_host.h
>> +++ b/arch/arm/include/asm/kvm_host.h
>> @@ -57,6 +57,8 @@ struct kvm_arch {
>>  	/* VTTBR value associated with below pgd and vmid */
>>  	u64    vttbr;
>>  
>> +	int __percpu *last_vcpu_ran;
>> +
>>  	/* Timer */
>>  	struct arch_timer_kvm	timer;
>>  
>> @@ -174,6 +176,9 @@ struct kvm_vcpu_arch {
>>  	/* vcpu power-off state */
>>  	bool power_off;
>>  
>> +	/* TLBI required */
>> +	bool requires_tlbi;
> 
> A bit of a nit, but it's not clear which class of TLBI is required, or
> why. It's probably worth a comment (and perhaps a bikeshed), like:
> 
> 	/*
> 	 * Local TLBs potentially contain conflicting entries from
> 	 * another vCPU within this VMID. All entries for this VMID must
> 	 * be invalidated from (local) TLBs before we run this vCPU.
> 	 */
> 	bool tlb_vmid_stale;

Yup, looks good.

> 
> [...]
> 
>> +void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu)
>> +{
>> +	int *last_ran;
>> +
>> +	last_ran = per_cpu_ptr(vcpu->kvm->arch.last_vcpu_ran, cpu);
>> +
>> +	/*
>> +	 * If we're very unlucky and get preempted before having ran
>> +	 * this vcpu for real, we'll end-up in a situation where any
>> +	 * vcpu that gets scheduled will perform an invalidation (this
>> +	 * vcpu explicitely requires it, and all the others will have
>> +	 * a different vcpu_id).
>> +	 */
> 
> Nit: s/explicitely/explicitly/
> 
> To bikeshed a little further, perhaps:
> 
> 	/*
> 	 * We might get preempted before the vCPU actually runs, but
> 	 * this is fine. Our TLBI stays pending until we actually make
> 	 * it to __activate_vm, so we won't miss a TLBI. If another vCPU
> 	 * gets scheduled, it will see our vcpu_id in last_ran, and pend
> 	 * a TLBI for itself.
> 	 */

Looks good too. I'll incorporate this into v2.

> 
>> +	if (*last_ran != vcpu->vcpu_id) {
>> +		if (*last_ran != -1)
>> +			vcpu->arch.requires_tlbi = true;
>> +
>> +		*last_ran = vcpu->vcpu_id;
>> +	}
>> +}
> 
> I take it this function is run in some non-preemptible context; if so,
> this looks correct to me.
> 
> If this is preemptible, then this looks racy.

This function is called from a preempt notifier, which itself isn't
preemptible.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...



More information about the linux-arm-kernel mailing list