From: Marc Zyngier <maz@kernel.org>
To: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Cc: Oliver Upton <oupton@kernel.org>, Joey Gouly <joey.gouly@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>, Kees Cook <kees@kernel.org>,
"Gustavo A. R. Silva" <gustavoars@kernel.org>,
Paolo Bonzini <pbonzini@redhat.com>,
Jonathan Corbet <corbet@lwn.net>, Shuah Khan <shuah@kernel.org>,
linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org,
devel@daynix.com, kvm@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kselftest@vger.kernel.org
Subject: Re: [PATCH v7 2/4] KVM: arm64: PMU: Protect the list of PMUs with RCU
Date: Mon, 20 Apr 2026 08:01:51 +0100 [thread overview]
Message-ID: <86se8q15eo.wl-maz@kernel.org> (raw)
In-Reply-To: <483e5cf2-a54c-4781-ac6d-49f5bc7128ba@rsg.ci.i.u-tokyo.ac.jp>
On Mon, 20 Apr 2026 07:21:45 +0100,
Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> wrote:
>
> On 2026/04/19 23:34, Marc Zyngier wrote:
> > On Sat, 18 Apr 2026 09:14:24 +0100,
> > Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> wrote:
> >>
> >> Convert the list of PMUs to a RCU-protected list that has primitives to
> >> avoid read-side contention.
> >>
> >> Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
> >> ---
> >> arch/arm64/kvm/pmu-emul.c | 14 ++++++--------
> >> 1 file changed, 6 insertions(+), 8 deletions(-)
> >>
> >> diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
> >> index 59ec96e09321..ef5140bbfe28 100644
> >> --- a/arch/arm64/kvm/pmu-emul.c
> >> +++ b/arch/arm64/kvm/pmu-emul.c
> >> @@ -7,9 +7,9 @@
> >> #include <linux/cpu.h>
> >> #include <linux/kvm.h>
> >> #include <linux/kvm_host.h>
> >> -#include <linux/list.h>
> >> #include <linux/perf_event.h>
> >> #include <linux/perf/arm_pmu.h>
> >> +#include <linux/rculist.h>
> >> #include <linux/uaccess.h>
> >> #include <asm/kvm_emulate.h>
> >> #include <kvm/arm_pmu.h>
> >> @@ -26,7 +26,6 @@ static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc);
> >> bool kvm_supports_guest_pmuv3(void)
> >> {
> >> - guard(mutex)(&arm_pmus_lock);
> >> return !list_empty(&arm_pmus);
> >
> > Please read include/linux/rculist.h and the discussion about the
> > interaction of list_empty() with RCU-protected lists. How about using
> > list_first_or_null_rcu() for peace of mind?
>
> list_first_or_null_rcu() is useful to replace a sequence of
> list_empty() and list_first_entry() that is protected by a lock, but
> this function instead requires the invariant that nobody deletes an
> element from the list, and list_first_or_null_rcu() does not allow
> removing the requirement.
>
> The header file says:
> > Where are list_empty_rcu() and list_first_entry_rcu()?
> >
> > They do not exist because they would lead to subtle race conditions:
> >
> > if (!list_empty_rcu(mylist)) {
> > struct foo *bar = list_first_entry_rcu(mylist, struct foo,
> > list_member);
> > do_something(bar);
> > }
> >
> > The list might be non-empty when list_empty_rcu() checks it, but it
> > might have become empty by the time that list_first_entry_rcu()
> > rereads the ->next pointer, which would result in a SEGV.
> >
> > When not using RCU, it is OK for list_first_entry() to re-read that
> > pointer because both functions should be protected by some lock that
> > blocks writers.
> >
> > When using RCU, list_empty() uses READ_ONCE() to fetch the
> > RCU-protected ->next pointer and then compares it to the address of
> > the list head. However, it neither dereferences this pointer nor
> > provides this pointer to its caller. Thus, READ_ONCE() suffices
> > (that is, rcu_dereference() is not needed), which means that
> > list_empty() can be used anywhere you would want to use
> > list_empty_rcu(). Just don't expect anything useful to happen if you
> > do a subsequent lockless call to list_first_entry_rcu()!!!
> >
> > See list_first_or_null_rcu for an alternative.
>
> However, kvm_supports_guest_pmuv3() locked a mutex when calling
> list_empty() and unlocked it immediately after that, instead of
> re-reading list_first_entry(). This construct inherently had a race
> condition with code that deletes an element; when the caller of
> kvm_supports_guest_pmuv3() decides to enable guest PMUv3, the host PMU
> may have been gone. But it was still safe because no one deletes an
> element.
>
> The same logic also applies when using RCU. As the comment says, we
> can use list_empty() instead of the hypothetical list_empty_rcu()
> macro because we don't expect it to magically enable something like
> list_first_entry_rcu(). This function instead keep relying on the fact
> that no one deletes an element of the list.
And that's exactly the sort of thing I am trying to plan for. *Should*
we introduce a way to remove PMUs from the list, this predicate
becomes unsafe.
So I want at least a comment explaining this to the unsuspecting
reader, as this is rather subtle.
M.
--
Without deviation from the norm, progress is not possible.
next prev parent reply other threads:[~2026-04-20 7:01 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-18 8:14 [PATCH v7 0/4] KVM: arm64: PMU: Use multiple host PMUs Akihiko Odaki
2026-04-18 8:14 ` [PATCH v7 1/4] KVM: arm64: PMU: Add kvm_pmu_enabled_counter_mask() Akihiko Odaki
2026-04-19 14:13 ` Marc Zyngier
2026-04-20 5:27 ` Akihiko Odaki
2026-04-18 8:14 ` [PATCH v7 2/4] KVM: arm64: PMU: Protect the list of PMUs with RCU Akihiko Odaki
2026-04-19 14:34 ` Marc Zyngier
2026-04-20 6:21 ` Akihiko Odaki
2026-04-20 7:01 ` Marc Zyngier [this message]
2026-04-20 7:17 ` Akihiko Odaki
2026-04-18 8:14 ` [PATCH v7 3/4] KVM: arm64: PMU: Introduce FIXED_COUNTERS_ONLY Akihiko Odaki
2026-04-19 17:19 ` Marc Zyngier
2026-04-20 8:36 ` Akihiko Odaki
2026-04-20 9:51 ` Marc Zyngier
2026-04-20 12:07 ` Akihiko Odaki
2026-04-20 13:53 ` Marc Zyngier
2026-04-22 7:02 ` Akihiko Odaki
2026-04-18 8:14 ` [PATCH v7 4/4] KVM: arm64: selftests: Test PMU_V3_FIXED_COUNTERS_ONLY Akihiko Odaki
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=86se8q15eo.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=corbet@lwn.net \
--cc=devel@daynix.com \
--cc=gustavoars@kernel.org \
--cc=joey.gouly@arm.com \
--cc=kees@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=odaki@rsg.ci.i.u-tokyo.ac.jp \
--cc=oupton@kernel.org \
--cc=pbonzini@redhat.com \
--cc=shuah@kernel.org \
--cc=suzuki.poulose@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.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.