From: Sean Christopherson <seanjc@google.com>
To: James Houghton <jthoughton@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
David Matlack <dmatlack@google.com>,
David Rientjes <rientjes@google.com>,
Marc Zyngier <maz@kernel.org>,
Oliver Upton <oliver.upton@linux.dev>,
Wei Xu <weixugc@google.com>, Yu Zhao <yuzhao@google.com>,
Axel Rasmussen <axelrasmussen@google.com>,
kvm@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v8 08/11] KVM: x86/mmu: Add infrastructure to allow walking rmaps outside of mmu_lock
Date: Fri, 10 Jan 2025 15:18:59 -0800 [thread overview]
Message-ID: <Z4Gq443gcop9mL4X@google.com> (raw)
In-Reply-To: <20241105184333.2305744-9-jthoughton@google.com>
On Tue, Nov 05, 2024, James Houghton wrote:
> +static unsigned long kvm_rmap_lock(struct kvm_rmap_head *rmap_head)
> +{
> + unsigned long old_val, new_val;
> +
> + /*
> + * Elide the lock if the rmap is empty, as lockless walkers (read-only
> + * mode) don't need to (and can't) walk an empty rmap, nor can they add
> + * entries to the rmap. I.e. the only paths that process empty rmaps
> + * do so while holding mmu_lock for write, and are mutually exclusive.
> + */
> + old_val = atomic_long_read(&rmap_head->val);
> + if (!old_val)
> + return 0;
> +
> + do {
> + /*
> + * If the rmap is locked, wait for it to be unlocked before
> + * trying acquire the lock, e.g. to bounce the cache line.
> + */
> + while (old_val & KVM_RMAP_LOCKED) {
> + old_val = atomic_long_read(&rmap_head->val);
> + cpu_relax();
> + }
As Lai Jiangshan pointed out[1][2], this should PAUSE first, then re-read the SPTE,
and KVM needs to disable preemption while holding the lock, because this is nothing
more than a rudimentary spinlock.
[1] https://lore.kernel.org/all/ZrooozABEWSnwzxh@google.com
[2] https://lore.kernel.org/all/Zrt5eNArfQA7x1qj@google.com
I think this?
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 1a0950b77126..9dac1bbb77d4 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -873,6 +873,8 @@ static unsigned long __kvm_rmap_lock(struct kvm_rmap_head *rmap_head)
{
unsigned long old_val, new_val;
+ lockdep_assert_preemption_disabled();
+
/*
* Elide the lock if the rmap is empty, as lockless walkers (read-only
* mode) don't need to (and can't) walk an empty rmap, nor can they add
@@ -889,8 +891,8 @@ static unsigned long __kvm_rmap_lock(struct kvm_rmap_head *rmap_head)
* trying acquire the lock, e.g. to bounce the cache line.
*/
while (old_val & KVM_RMAP_LOCKED) {
- old_val = atomic_long_read(&rmap_head->val);
cpu_relax();
+ old_val = atomic_long_read(&rmap_head->val);
}
/*
@@ -931,6 +933,8 @@ static unsigned long kvm_rmap_lock(struct kvm *kvm,
static void kvm_rmap_unlock(struct kvm_rmap_head *rmap_head,
unsigned long new_val)
{
+ lockdep_assert_held_write(&kvm->mmu_lock);
+
KVM_MMU_WARN_ON(new_val & KVM_RMAP_LOCKED);
/*
* Ensure that all accesses to the rmap have completed
@@ -948,12 +952,21 @@ static unsigned long kvm_rmap_get(struct kvm_rmap_head *rmap_head)
/*
* If mmu_lock isn't held, rmaps can only locked in read-only mode. The actual
- * locking is the same, but the caller is disallowed from modifying the rmap,
- * and so the unlock flow is a nop if the rmap is/was empty.
+ * locking is the same, but preemption needs to be manually disabled (because
+ * a spinlock isn't already held) and the caller is disallowed from modifying
+ * the rmap, and so the unlock flow is a nop if the rmap is/was empty. Note,
+ * preemption must be disable *before* acquiring the bitlock. If the rmap is
+ * empty, i.e. isn't truly locked, immediately re-enable preemption.
*/
static unsigned long kvm_rmap_lock_readonly(struct kvm_rmap_head *rmap_head)
{
- return __kvm_rmap_lock(rmap_head);
+ unsigned rmap_val;
+ preempt_disable();
+
+ rmap_val = __kvm_rmap_lock(rmap_head);
+ if (!rmap_val)
+ preempt_enable();
+ return rmap_val;
}
static void kvm_rmap_unlock_readonly(struct kvm_rmap_head *rmap_head,
@@ -964,6 +977,7 @@ static void kvm_rmap_unlock_readonly(struct kvm_rmap_head *rmap_head,
KVM_MMU_WARN_ON(old_val != kvm_rmap_get(rmap_head));
atomic_long_set(&rmap_head->val, old_val);
+ preempt_enable();
}
/*
next prev parent reply other threads:[~2025-01-10 23:19 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-05 18:43 [PATCH v8 00/11] KVM: x86/mmu: Age sptes locklessly James Houghton
2024-11-05 18:43 ` [PATCH v8 01/11] KVM: Remove kvm_handle_hva_range helper functions James Houghton
2025-01-10 22:15 ` Sean Christopherson
2025-01-27 19:50 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 02/11] KVM: Add lockless memslot walk to KVM James Houghton
2025-01-10 22:26 ` Sean Christopherson
2025-01-27 19:51 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 03/11] KVM: x86/mmu: Factor out spte atomic bit clearing routine James Houghton
2024-11-05 18:45 ` Yu Zhao
2025-01-10 22:34 ` Sean Christopherson
2025-01-27 19:51 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 04/11] KVM: x86/mmu: Relax locking for kvm_test_age_gfn and kvm_age_gfn James Houghton
2024-11-06 8:22 ` kernel test robot
2024-11-08 3:00 ` James Houghton
2024-11-08 22:45 ` Sean Christopherson
2024-11-11 14:45 ` James Houghton
2025-01-10 22:47 ` Sean Christopherson
2025-01-27 19:52 ` James Houghton
2025-01-27 19:57 ` James Houghton
2025-01-27 20:09 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 05/11] KVM: x86/mmu: Rearrange kvm_{test_,}age_gfn James Houghton
2024-11-05 18:46 ` Yu Zhao
2025-01-10 22:59 ` Sean Christopherson
2025-01-27 19:58 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 06/11] KVM: x86/mmu: Only check gfn age in shadow MMU if indirect_shadow_pages > 0 James Houghton
2024-11-05 18:49 ` Yu Zhao
2025-01-10 23:05 ` Sean Christopherson
2025-01-27 19:58 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 07/11] KVM: x86/mmu: Refactor low level rmap helpers to prep for walking w/o mmu_lock James Houghton
2024-11-05 18:43 ` [PATCH v8 08/11] KVM: x86/mmu: Add infrastructure to allow walking rmaps outside of mmu_lock James Houghton
2025-01-10 23:18 ` Sean Christopherson [this message]
2025-01-27 21:42 ` James Houghton
2025-01-27 21:52 ` James Houghton
2024-11-05 18:43 ` [PATCH v8 09/11] KVM: x86/mmu: Add support for lockless walks of rmap SPTEs James Houghton
2024-11-05 18:43 ` [PATCH v8 10/11] KVM: x86/mmu: Support rmap walks without holding mmu_lock when aging gfns James Houghton
2024-11-05 18:43 ` [PATCH v8 11/11] KVM: selftests: Add multi-gen LRU aging to access_tracking_perf_test James Houghton
2025-01-11 0:12 ` Sean Christopherson
2025-02-03 22:46 ` James Houghton
2025-01-11 0:21 ` Yosry Ahmed
2024-11-05 19:21 ` [PATCH v8 00/11] KVM: x86/mmu: Age sptes locklessly Yu Zhao
2024-11-05 19:28 ` Yu Zhao
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=Z4Gq443gcop9mL4X@google.com \
--to=seanjc@google.com \
--cc=axelrasmussen@google.com \
--cc=dmatlack@google.com \
--cc=jthoughton@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maz@kernel.org \
--cc=oliver.upton@linux.dev \
--cc=pbonzini@redhat.com \
--cc=rientjes@google.com \
--cc=weixugc@google.com \
--cc=yuzhao@google.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.