kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Maxim Levitsky <mlevitsk@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>, kvm@vger.kernel.org
Cc: Thomas Gleixner <tglx@linutronix.de>,
	Sean Christopherson <seanjc@google.com>,
	Wanpeng Li <wanpengli@tencent.com>,
	Vitaly Kuznetsov <vkuznets@redhat.com>,
	Joerg Roedel <joro@8bytes.org>, Borislav Petkov <bp@alien8.de>,
	"H. Peter Anvin" <hpa@zytor.com>, Ingo Molnar <mingo@redhat.com>,
	"open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)" 
	<linux-kernel@vger.kernel.org>,
	"maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT)"
	<x86@kernel.org>, Jim Mattson <jmattson@google.com>
Subject: Re: [PATCH 02/10] KVM: x86: APICv: fix race in kvm_request_apicv_update on SVM
Date: Thu, 24 Jun 2021 11:07:02 +0300	[thread overview]
Message-ID: <43ef1a1ea488977db11d40ec9672b524ec816112.camel@redhat.com> (raw)
In-Reply-To: <6c4a69ce-595e-d5a1-7b4e-e6ce1afe1252@redhat.com>

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
> 



  reply	other threads:[~2021-06-24  8:07 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-23 11:29 [PATCH 00/10] My AVIC patch queue Maxim Levitsky
2021-06-23 11:29 ` [PATCH 01/10] KVM: x86: extract block/allow guest enteries Maxim Levitsky
2021-06-23 11:29 ` [PATCH 02/10] KVM: x86: APICv: fix race in kvm_request_apicv_update on SVM Maxim Levitsky
2021-06-23 21:50   ` Paolo Bonzini
2021-06-24  8:07     ` Maxim Levitsky [this message]
2021-07-07 12:57       ` Maxim Levitsky
2021-07-07 13:58         ` Paolo Bonzini
2021-06-23 11:29 ` [PATCH 03/10] KVM: x86: rename apic_access_page_done to apic_access_memslot_enabled Maxim Levitsky
2021-06-23 21:50   ` Paolo Bonzini
2021-06-23 11:29 ` [PATCH 04/10] KVM: SVM: add warning for mistmatch between AVIC state and AVIC access page state Maxim Levitsky
2021-06-23 21:53   ` Paolo Bonzini
2021-06-24  8:13     ` Maxim Levitsky
2021-06-23 11:29 ` [PATCH 05/10] KVM: SVM: svm_set_vintr don't warn if AVIC is active but is about to be deactivated Maxim Levitsky
2021-06-23 11:29 ` [PATCH 06/10] KVM: SVM: tweak warning about enabled AVIC on nested entry Maxim Levitsky
2021-06-23 21:52   ` Paolo Bonzini
2021-06-23 11:29 ` [PATCH 07/10] KVM: SVM: use vmcb01 in svm_refresh_apicv_exec_ctrl Maxim Levitsky
2021-06-23 21:54   ` Paolo Bonzini
2021-06-24  8:16     ` Maxim Levitsky
2021-06-23 11:30 ` [PATCH 08/10] KVM: x86: APICv: drop immediate APICv disablement on current vCPU Maxim Levitsky
2021-06-23 11:30 ` [PATCH 09/10] KVM: SVM: call avic_vcpu_load/avic_vcpu_put when enabling/disabling AVIC Maxim Levitsky
2021-06-23 11:30 ` [PATCH 10/10] KVM: x86: hyper-v: Deactivate APICv only when AutoEOI feature is in use Maxim Levitsky

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=43ef1a1ea488977db11d40ec9672b524ec816112.camel@redhat.com \
    --to=mlevitsk@redhat.com \
    --cc=bp@alien8.de \
    --cc=hpa@zytor.com \
    --cc=jmattson@google.com \
    --cc=joro@8bytes.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=tglx@linutronix.de \
    --cc=vkuznets@redhat.com \
    --cc=wanpengli@tencent.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).