Re: [PATCH 02/10] KVM: x86: APICv: fix race in kvm_request_apicv_update on SVM

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2021-06-24 at 11:07 +0300, Maxim Levitsky wrote:
> On Wed, 2021-06-23 at 23:50 +0200, Paolo Bonzini wrote:
> > On 23/06/21 13:29, Maxim Levitsky wrote:
> > > +	kvm_block_guest_entries(kvm);
> > > +
> > >   	trace_kvm_apicv_update_request(activate, bit);
> > >   	if (kvm_x86_ops.pre_update_apicv_exec_ctrl)
> > >   		static_call(kvm_x86_pre_update_apicv_exec_ctrl)(kvm, activate);
> > > @@ -9243,6 +9245,8 @@ void kvm_request_apicv_update(struct kvm *kvm, bool activate, ulong bit)
> > >   	except = kvm_get_running_vcpu();
> > >   	kvm_make_all_cpus_request_except(kvm, KVM_REQ_APICV_UPDATE,
> > >   					 except);
> > > +
> > > +	kvm_allow_guest_entries(kvm);
> > 
> > Doesn't this cause a busy loop during synchronize_rcu?
> 
> Hi,
> 
> If you mean busy loop on other vcpus, then the answer is sadly yes.
> Other option is to use a mutex, which is what I did in a former
> version of this patch, but at last minute I decided that this
> way it was done in this patch would be simplier. 
> AVIC updates don't happen often.
> Also with a request, the KVM_REQ_APICV_UPDATE can be handled in parallel,
> while mutex enforces unneeded mutual execution of it.
> 
> 
> >   It should be 
> > possible to request the vmexit of other CPUs from 
> > avic_update_access_page, and do a lock/unlock of kvm->slots_lock to wait 
> > for the memslot to be updated.
> 
> This would still keep the race. The other vCPUs must not enter the guest mode
> from the moment the memslot update was started and until the KVM_REQ_APICV_UPDATE
> is raised.
>  
> If I were to do any kind of synchronization in avic_update_access_page, then I will
> have to drop the lock/request there, and from this point and till the common code
> raises the KVM_REQ_APICV_UPDATE there is a possibility of a vCPU reentering the
> guest mode without updating its AVIC.
>  
>  
> Here is an older version of this patch that does use mutex instead. 
> Please let me know if you prefer it.
> 
> I copy pasted it here, thus its likely not to apply as my email client
> probably destroys whitespace.
>   
> Thanks for the review,
> 	Best regards,
> 		Maxim Levitsky
> 
> 
> --
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index fdc6b8a4348f..b7dc7fd7b63d 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -9183,11 +9183,8 @@ void kvm_make_scan_ioapic_request(struct kvm *kvm)
>  	kvm_make_all_cpus_request(kvm, KVM_REQ_SCAN_IOAPIC);
>  }
>  
> -void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
> +void __kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
>  {
> -	if (!lapic_in_kernel(vcpu))
> -		return;
> -
>  	vcpu->arch.apicv_active = kvm_apicv_activated(vcpu->kvm);
>  	kvm_apic_update_apicv(vcpu);
>  	static_call(kvm_x86_refresh_apicv_exec_ctrl)(vcpu);
> @@ -9201,6 +9198,16 @@ void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
>  	if (!vcpu->arch.apicv_active)
>  		kvm_make_request(KVM_REQ_EVENT, vcpu);
>  }
> +
> +void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
> +{
> +	if (!lapic_in_kernel(vcpu))
> +		return;
> +
> +	mutex_lock(&vcpu->kvm->apicv_update_lock);
> +	__kvm_vcpu_update_apicv(vcpu);
> +	mutex_unlock(&vcpu->kvm->apicv_update_lock);
> +}
>  EXPORT_SYMBOL_GPL(kvm_vcpu_update_apicv);
>  
>  /*
> @@ -9213,30 +9220,26 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_update_apicv);
>  void kvm_request_apicv_update(struct kvm *kvm, bool activate, ulong bit)
>  {
>  	struct kvm_vcpu *except;
> -	unsigned long old, new, expected;
> +	unsigned long old, new;
>  
>  	if (!kvm_x86_ops.check_apicv_inhibit_reasons ||
>  	    !static_call(kvm_x86_check_apicv_inhibit_reasons)(bit))
>  		return;
>  
> -	old = READ_ONCE(kvm->arch.apicv_inhibit_reasons);
> -	do {
> -		expected = new = old;
> -		if (activate)
> -			__clear_bit(bit, &new);
> -		else
> -			__set_bit(bit, &new);
> -		if (new == old)
> -			break;
> -		old = cmpxchg(&kvm->arch.apicv_inhibit_reasons, expected, new);
> -	} while (old != expected);
> +	mutex_lock(&kvm->apicv_update_lock);
> +
> +	old = new = kvm->arch.apicv_inhibit_reasons;
> +	if (activate)
> +		__clear_bit(bit, &new);
> +	else
> +		__set_bit(bit, &new);
> +
> +	WRITE_ONCE(kvm->arch.apicv_inhibit_reasons, new);
>  
>  	if (!!old == !!new)
> -		return;
> +		goto out;
>  
>  	trace_kvm_apicv_update_request(activate, bit);
> -	if (kvm_x86_ops.pre_update_apicv_exec_ctrl)
> -		static_call(kvm_x86_pre_update_apicv_exec_ctrl)(kvm, activate);
>  
>  	/*
>  	 * Sending request to update APICV for all other vcpus,
> @@ -9244,10 +9247,24 @@ void kvm_request_apicv_update(struct kvm *kvm, bool activate, ulong bit)
>  	 * waiting for another #VMEXIT to handle the request.
>  	 */
>  	except = kvm_get_running_vcpu();
> +
> +	/*
> +	 * on SVM, raising the KVM_REQ_APICV_UPDATE request while holding the
> +	 *  apicv_update_lock ensures that we kick all vCPUs out of the
> +	 *  guest mode and let them wait until the AVIC memslot update
> +	 *  has completed.
> +	 */
> +
>  	kvm_make_all_cpus_request_except(kvm, KVM_REQ_APICV_UPDATE,
>  					 except);
> +
> +	if (kvm_x86_ops.pre_update_apicv_exec_ctrl)
> +		static_call(kvm_x86_pre_update_apicv_exec_ctrl)(kvm, activate);
> +
>  	if (except)
> -		kvm_vcpu_update_apicv(except);
> +		__kvm_vcpu_update_apicv(except);
> +out:
> +	mutex_unlock(&kvm->apicv_update_lock);
>  }
>  EXPORT_SYMBOL_GPL(kvm_request_apicv_update);
>  
> @@ -9454,8 +9471,11 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
>  		 */
>  		if (kvm_check_request(KVM_REQ_HV_STIMER, vcpu))
>  			kvm_hv_process_stimers(vcpu);
> -		if (kvm_check_request(KVM_REQ_APICV_UPDATE, vcpu))
> +		if (kvm_check_request(KVM_REQ_APICV_UPDATE, vcpu)) {
> +			srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
>  			kvm_vcpu_update_apicv(vcpu);
> +			vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
> +		}
>  		if (kvm_check_request(KVM_REQ_APF_READY, vcpu))
>  			kvm_check_async_pf_completion(vcpu);
>  		if (kvm_check_request(KVM_REQ_MSR_FILTER_CHANGED, vcpu))
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index 37cbb56ccd09..0364d35d43dc 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -524,6 +524,7 @@ struct kvm {
>  #endif /* KVM_HAVE_MMU_RWLOCK */
>  
>  	struct mutex slots_lock;
> +	struct mutex apicv_update_lock;
>  
>  	/*
>  	 * Protects the arch-specific fields of struct kvm_memory_slots in
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index ed4d1581d502..ba5d5d9ebc64 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -943,6 +943,7 @@ static struct kvm *kvm_create_vm(unsigned long type)
>  	mutex_init(&kvm->irq_lock);
>  	mutex_init(&kvm->slots_lock);
>  	mutex_init(&kvm->slots_arch_lock);
> +	mutex_init(&kvm->apicv_update_lock);
>  	INIT_LIST_HEAD(&kvm->devices);
>  
>  	BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
> 
> 
> 
> > (As an aside, I'd like to get rid of KVM_REQ_MCLOCK_IN_PROGRESS in 5.15...).
> > 
> > Paolo
> > 


Hi!
Any update? should I use a lock for this?


Best regards,
	Maxim Levitsky





[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux