* Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread
From: Aditya Sharma @ 2026-07-20 18:38 UTC (permalink / raw)
To: Liam R . Howlett
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
David Rientjes, Shakeel Butt, Jonathan Corbet, Shuah Khan,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers, Kees Cook,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, linux-mm, linux-doc, linux-trace-kernel,
linux-kernel, imbrenda
In-Reply-To: <ljr2jkxomq4ccgzq2ip3c66mjdryomhl2gxkswv4wzry5ye2ax@xaehxzjkkk3m>
Sent using Zoho Mail
From: Liam R . Howlett <liam@infradead.org>
To: "Aditya Sharma"<adi.sharma@zohomail.in>
Cc: "Andrew Morton"<akpm@linux-foundation.org>, "David Hildenbrand"<david@kernel.org>, "Lorenzo Stoakes"<ljs@kernel.org>, "Vlastimil Babka"<vbabka@kernel.org>, "Mike Rapoport"<rppt@kernel.org>, "Suren Baghdasaryan"<surenb@google.com>, "Michal Hocko"<mhocko@suse.com>, "David Rientjes"<rientjes@google.com>, "Shakeel Butt"<shakeel.butt@linux.dev>, "Jonathan Corbet"<corbet@lwn.net>, "Shuah Khan"<skhan@linuxfoundation.org>, "Steven Rostedt"<rostedt@goodmis.org>, "Masami Hiramatsu"<mhiramat@kernel.org>, "Mathieu Desnoyers"<mathieu.desnoyers@efficios.com>, "Kees Cook"<kees@kernel.org>, "Ingo Molnar"<mingo@redhat.com>, "Peter Zijlstra"<peterz@infradead.org>, "Juri Lelli"<juri.lelli@redhat.com>, "Vincent Guittot"<vincent.guittot@linaro.org>, "Dietmar Eggemann"<dietmar.eggemann@arm.com>, "Ben Segall"<bsegall@google.com>, "Mel Gorman"<mgorman@suse.de>, "Valentin Schneider"<vschneid@redhat.com>, "K Prateek Nayak"<kprateek.nayak@amd.com>, <linux-mm@kvack.org>, <linux-doc@vger.kernel.org>, <linux-trace-kernel@vger.kernel.org>, <linux-kernel@vger.kernel.org>, <imbrenda@linux.ibm.com>
Date: Mon, 20 Jul 2026 03:58:47 +0530
Subject: Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread
> This reads like it was written by an AI, but there is no assisted-by
> tags. Which AI did you use to generate these patches and/or cover
> letter?
I used claude opus 4.8 to refine commit messages (the design is mine,
the model was used to convey the intentions concretely). Further, I used
claude fable 5 for drafting the cover letter and reviewing the series, where
it flagged issues (I fixed some of those by hand and some were fixed by fable itself,
which I then reviewed). It also assisted in writing the benchmark harness
behind the numbers and did the litmus test the cover letter mentions. Every numbers
was measured by me, on my hardware. I take full responsibily of the entire
series.
My bad that I did not add an assisted-by tag. Should have added it to v1
v2 will add it to the patches
> I do not see a benefit to running the task cleanup 'off the side'. In
> modern computers, that may mean switching core types that run at
> different speeds.
That's why the reaper's affinity is a preference, not a bind (patch 2).
It uses kthread_affine_preferred() with housekeeping mask, so it can be
re-affined. kthread_bind_mask() would have taken that away. On a hybrid cpu,
it can be affined(by the (super)user) to whichever cores are appropriate.
I do not have numbers for P core and E core teardown costs. Will measure and
send them.
> Regardless of speed, the CPU that will be doing the work is remote to
> the workload by design, (or may be, depending on the scheduling?). That
> is, you are tasking another CPU to do cleanup of local CPU-aware
> information.. The ideal CPU to do a task exit is the one that's doing it
> already.
Yes, it does cost something. Five consecutive 4GB inline teardowns in my testing
costed 372ms in exit path, while 440 ms in the case of kthread drain for the same
(five teardowns on another cpu). This cost became 1.18x. For multi gb mms,
the memory to be reaped would be much larger than the cpu cache can hold.
For small mms (< 64 MB, configurable by sysctl), the teardowns happen inline.
The oom_reaper already reaps the victim's address space from a remote context,
as returning memory matters more than locality of teardown.
> There are hidden costs to your approach and I don't really understand
> how this could possibly speed things up - even parallelized tasks will
> take more CPU time in total, even if locking issues are avoided.
This is not meant to speed up teardown. Rather the latency for someone waiting
on death is reduced. Killing a 16 GB redis cost 292.97ms before returning to
waitpid(). It costs 0.24 with async teardown. The memory itself was returned
in about 300ms.
"parallelized" - This series does not parallelize the teardown. The design is serial.
N procesess exiting together today would tear down concurrently today. The reaper here,
uses one thread (nice 19, affined to housekeeping mask).
The numbers in cover letter support that this work is memory bound rather than CPU bound.
In sync case: teardown is around 56 GB/s on my machine (1GB in 18.01 ms and 16GB in 282.16ms).
Thirty two concurrent 0.5GB exits reap in 157.42ms (around 101 GB/s). So, a concurrency
of 32 gets about 1.8x. The single reaper thread takes 650ms to clear the same (about 25 GB/s).
Thus, this serialization took 4x the drain time, but takes peak concurrency down to
one cpu. Memory actully comes back later. Whether that's a good tradeoff, depends on
what else we wish to run on the box (open questions #1 in cover letter)
> Sure, the task waiting for cleanup to complete may think it's done and
> continue to do the Next Thing - but if it really needs the cleanup to be
> completed
It is off by default and opt in (sysctl), so anything that depends on the
current semantics, continues to work exactly the same by doing nothing.
Also, mmput_async() already defers the teardown to a workqueue. So, in the current
kernel, there already exists a way where continuing to the next thing
does not necessarily mean that the memory is reclaimed.
> completed then you've just made it impossible to know when it has
> happened.
There are three vmstat counters and two tracepoint (patch 6).
Someone who opts in to the mechanism, and does need to make sure that the
cleanup is completed, can use the counters and tracepoints.
> Also, you've just made this extremely hard to debug if something is
> missed.
There are reap tracepoints that carry the pid, so we can get per pid completion status.
However, there is no number indicating the current depth of the queue. The series addds queued,
sync and rejected counters. Will add a reaped counter in v2 so that current queue depth
can be estimated. Please mention what else tracability can be added to this,
and what senairo do you have in mind that these counters and tracepoints might not
cover. I can add those to v2 as well.
> The work that is done in teardown is necessary. Your time (and tokens?)
> would be better spent trying to improve what we have to do instead of
> shifting the work around.
This does not compete with with current teardown path. it goes through the
same exit_mm(). Any future improvements there, also improve the reaper's drain
times.
Thanks
Aditya
^ permalink raw reply
* Re: [PATCH v1 01/11] tracing: Include linux/types.h in trace_remote_event.h
From: Steven Rostedt @ 2026-07-20 18:32 UTC (permalink / raw)
To: Fuad Tabba
Cc: maz, oupton, linux-arm-kernel, kvmarm, catalin.marinas, will,
mhiramat, alexandru.elisei, vdonnefort, joey.gouly, seiden,
suzuki.poulose, yuzenghui, qperret, ardb, linux-kernel,
linux-trace-kernel, tabba
In-Reply-To: <20260720161343.1367007-2-fuad.tabba@linux.dev>
On Mon, 20 Jul 2026 17:13:33 +0100
Fuad Tabba <fuad.tabba@linux.dev> wrote:
> diff --git a/include/linux/trace_remote_event.h b/include/linux/trace_remote_event.h
> index c8ae1e1f5e721..e4cc2d4497bcf 100644
> --- a/include/linux/trace_remote_event.h
> +++ b/include/linux/trace_remote_event.h
> @@ -3,6 +3,8 @@
> #ifndef _LINUX_TRACE_REMOTE_EVENTS_H
> #define _LINUX_TRACE_REMOTE_EVENTS_H
>
> +#include <linux/types.h>
> +
> struct trace_remote;
> struct trace_event_fields;
> struct trace_seq;
I don't know if this is correct or not, but here's my ack so others can
decide and take it through their trees.
Acked-by: Steven Rostedt <rostedt@goodmis.org.
-- Steve
^ permalink raw reply
* Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread
From: Aditya Sharma @ 2026-07-20 18:30 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, David Rientjes,
Shakeel Butt, Jonathan Corbet, Shuah Khan, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Kees Cook, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
linux-mm, linux-doc, linux-trace-kernel, linux-kernel, imbrenda
In-Reply-To: <d1180a70-f4b4-4c22-9ce5-881cb0e2370d@kernel.org>
Sent using Zoho Mail
From: David Hildenbrand (Arm) <david@kernel.org>
To: "Aditya Sharma"<adi.sharma@zohomail.in>, "Andrew Morton"<akpm@linux-foundation.org>, "Lorenzo Stoakes"<ljs@kernel.org>, "Liam R . Howlett"<liam@infradead.org>, "Vlastimil Babka"<vbabka@kernel.org>, "Mike Rapoport"<rppt@kernel.org>, "Suren Baghdasaryan"<surenb@google.com>, "Michal Hocko"<mhocko@suse.com>
Cc: "David Rientjes"<rientjes@google.com>, "Shakeel Butt"<shakeel.butt@linux.dev>, "Jonathan Corbet"<corbet@lwn.net>, "Shuah Khan"<skhan@linuxfoundation.org>, "Steven Rostedt"<rostedt@goodmis.org>, "Masami Hiramatsu"<mhiramat@kernel.org>, "Mathieu Desnoyers"<mathieu.desnoyers@efficios.com>, "Kees Cook"<kees@kernel.org>, "Ingo Molnar"<mingo@redhat.com>, "Peter Zijlstra"<peterz@infradead.org>, "Juri Lelli"<juri.lelli@redhat.com>, "Vincent Guittot"<vincent.guittot@linaro.org>, "Dietmar Eggemann"<dietmar.eggemann@arm.com>, "Ben Segall"<bsegall@google.com>, "Mel Gorman"<mgorman@suse.de>, "Valentin Schneider"<vschneid@redhat.com>, "K Prateek Nayak"<kprateek.nayak@amd.com>, <linux-mm@kvack.org>, <linux-doc@vger.kernel.org>, <linux-trace-kernel@vger.kernel.org>, <linux-kernel@vger.kernel.org>, <imbrenda@linux.ibm.com>
Date: Mon, 20 Jul 2026 13:43:34 +0530
Subject: Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread
> QEMU has an rather short implementation for async teardown using
> clone(CLONE_VM), which is IIRC essentially the result of Claudios previous
> kernel work you note below.
>
> So nothing got merged because the problem was solvable in userspace.
Thanks for this. My related works paragraph is wrong on this then. It
says that the s390 case was addressed with KVM specific async destruction,
but what you point to is QEMU's shadow process. I will correct that in v2.
Yeah, that CLONE_VM trick works, so I would measure and compare them
and get back.
I plan to measure:
- Memory return latency
- N concurrent shadow teardowns on N cpus vs the reaper.
One difference between CLONE_VM approach and CONFIG_ASYNC_MM_TEARDOWN
is that the shadow process has to be created by the process that would
be torn down itself. If that same behaviour is wanted on a workload
where we dont own the source, CONFIG_ASYNC_MM_TEARDOWN enables it here
as well. Also, in the QEMU case, it also means that the deferred teardown
decision was made without knowing how large the process might become.
CONFIG_ASYNC_MM_TEARDOWN adds a gate where for the exiting process,
RSS is evaluated againsts the current backlog before queuing it for
async teardown.
I will drop a follow up with numbers
Aditya
^ permalink raw reply
* Re: [PATCH v1 00/11] RCU: Enable callbacks to benefit from expedited grace periods
From: Paul E. McKenney @ 2026-07-20 18:02 UTC (permalink / raw)
To: Puranjay Mohan
Cc: rcu, linux-kernel, linux-trace-kernel, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Masami Hiramatsu, Davidlohr Bueso,
Breno Leitao
In-Reply-To: <20260624132356.516959-1-puranjay@kernel.org>
On Wed, Jun 24, 2026 at 06:23:42AM -0700, Puranjay Mohan wrote:
> This series lets call_rcu() callbacks be reclaimed as soon as either a
> normal or an expedited grace period that covers them has elapsed, rather
> than always waiting for a normal grace period.
>
> Motivation
> ==========
> Today there is an asymmetry: synchronize_rcu_expedited() callers get fast
> reclaim, but call_rcu() callers never benefit from those same expedited
> grace periods, even though an expedited GP proves exactly the same thing
> as a normal one -- all pre-existing readers are done. When expedited GPs
> are running on the system (driven by other subsystems), call_rcu()
> callbacks that could already be freed instead sit in RCU_WAIT_TAIL until
> the next normal GP. This series treats a grace period as a grace period
> regardless of how it was driven, so memory is reclaimed sooner.
>
> Design
> ======
> Callback segments now record both the normal and expedited grace-period
> sequence in struct rcu_gp_seq, and rcu_segcblist_advance() releases a
> segment as soon as poll_state_synchronize_rcu_full() reports that either
> has completed. Three notification paths are taught about expedited
> completion so the advance actually happens: the NOCB rcuog kthreads,
> the rcu_pending() tick gate, and rcu_core().
>
> Changelog:
> RFC: https://lore.kernel.org/all/20260417231203.785172-1-puranjay@kernel.org/
> Changes in v1:
> - New prep patch 1 renames struct rcu_gp_oldstate to struct rcu_gp_seq
> and its fields rgos_norm/rgos_exp to norm/exp tree-wide (Frederic).
> - The rcu_segcblist segment field stays named gp_seq; only its type
> changes (Frederic).
> - Patch 8 (NOCB wake) is reworked. v1 woke the wrong waitqueue
> (rdp_gp->nocb_gp_wq via wake_nocb_gp() rather than the leaf
> rnp->nocb_gp_wq[] that an rcuog kthread waiting for a GP sleeps on),
> and the wait condition only checked the normal ->gp_seq. The rcuog
> grace-period wait now tracks a struct rcu_gp_seq and is released via
> poll_state_synchronize_rcu_full(); rcu_exp_wait_wake() wakes the leaf
> node through the new rcu_nocb_exp_cleanup() (Frederic).
> - rcu_pending() uses a new memory-ordering-free
> poll_state_synchronize_rcu_full_unordered() to avoid memory barriers
> on every tick, leaving the ordering duty to rcu_core() (Frederic).
>
> Still open: Frederic asked whether the first smp_mb() in
> poll_state_synchronize_rcu_full() is needed on the callback-advance path
> (patch 6). That path still uses the fully ordered helper; only
> rcu_pending() was switched to the unordered variant. Happy to revisit.
And these all have reviews, other than 9/11, which is mine to review.
So I have pulled them in for further review and testing, thank you
very much!!!
If everything goes well, there is some chance that these will make the
next merge window.
Thanx, Paul
> Puranjay Mohan (11):
> rcu: Rename struct rcu_gp_oldstate to rcu_gp_seq
> rcu/segcblist: Add SRCU and Tasks RCU wrapper functions
> rcu/segcblist: Factor out rcu_segcblist_advance_compact() helper
> rcu/segcblist: Track segment grace periods with struct rcu_gp_seq
> rcu: Add RCU_GET_STATE_NOT_TRACKED for subsystems without expedited
> GPs
> rcu: Enable RCU callbacks to benefit from expedited grace periods
> rcu: Update comments for gp_seq and expedited GP tracking
> rcu: Wake NOCB rcuog kthreads on expedited grace period completion
> rcu: Detect expedited grace period completion in rcu_pending()
> rcu: Advance callbacks for expedited GP completion in rcu_core()
> rcuscale: Add concurrent expedited GP threads for callback scaling
> tests
>
> include/linux/rcu_segcblist.h | 16 ++--
> include/linux/rcupdate.h | 13 ++-
> include/linux/rcupdate_wait.h | 2 +-
> include/linux/rcutiny.h | 36 ++++-----
> include/linux/rcutree.h | 29 +++----
> include/trace/events/rcu.h | 5 +-
> kernel/rcu/rcu.h | 13 ++-
> kernel/rcu/rcu_segcblist.c | 139 ++++++++++++++++++++++----------
> kernel/rcu/rcu_segcblist.h | 8 +-
> kernel/rcu/rcuscale.c | 84 ++++++++++++++++++-
> kernel/rcu/rcutorture.c | 30 +++----
> kernel/rcu/srcutree.c | 14 ++--
> kernel/rcu/tasks.h | 8 +-
> kernel/rcu/tiny.c | 4 +-
> kernel/rcu/tree.c | 147 ++++++++++++++++++++++------------
> kernel/rcu/tree.h | 3 +-
> kernel/rcu/tree_exp.h | 20 ++---
> kernel/rcu/tree_nocb.h | 131 ++++++++++++++++++++++++------
> mm/slab_common.c | 6 +-
> 19 files changed, 496 insertions(+), 212 deletions(-)
>
>
> base-commit: 709d17a22bfac78765f6cbaec42e15bcd4aa4f08
> --
> 2.53.0-Meta
>
^ permalink raw reply
* Re: [PATCH v1 06/11] rcu: Enable RCU callbacks to benefit from expedited grace periods
From: Paul E. McKenney @ 2026-07-20 16:55 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: Puranjay Mohan, rcu, linux-kernel, linux-trace-kernel,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Masami Hiramatsu, Davidlohr Bueso,
Breno Leitao
In-Reply-To: <al4vKQ02tBYyYANk@localhost.localdomain>
On Mon, Jul 20, 2026 at 04:22:33PM +0200, Frederic Weisbecker wrote:
> Le Tue, Jul 14, 2026 at 11:48:08AM -0700, Paul E. McKenney a écrit :
> > > I have similar concerns about the three smp_mb() in
> > > get_state_synchronize_rcu_full(). It could be just two (rcu_seq_snap()
> > > has a barrier that could be just one). Not sure if that matters but,
> > > just wanted to point that.
> >
> > We need the one at the beginning of get_state_synchronize_rcu_full(),
> > but from what I can see, not the ones in the calls to rcu_seq_snap().
> > I blame laziness. We could make an rcu_seq_snap_no_ordering() that
> > didn't have the smp_mb(), but I didn't believe that the overhead would
> > be visible at the system level.
>
> It isn't so much about performance than being clear about ordering
> expectations. Though we could argue that grace period polling can be
> about performance.
>
> But in general rcu_seq_snap() advertizes:
>
> READ seq
> smp_mb() /* Above access must not bleed into critical section. */
>
> This doesn't tell much. Which critical section? That's not used on
> read side.
Fair question!
And the answer is "any critical section that might later be executed by
the current task."
Would it help if I made that comment read as follows, separately from
Puranjay's series?
// The above access must not bleed into any later RCU read-side
// critical section executed by the current task.
Thanx, Paul
^ permalink raw reply
* [PATCH v1 10/11] KVM: arm64: nVHE: Check hypercall handlers against the declared ABI
From: Fuad Tabba @ 2026-07-20 16:24 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
Each hypercall handler unmarshals its arguments from the host context
with hand-written DECLARE_REG() casts that nothing ties to what the
caller passed: a handler can disagree with its caller in argument type,
count or register index without a diagnostic.
Generate the unmarshalling instead. DEFINE_KVM_HOST_HCALL() expands to
the handle_<name>() glue, modelled on the syscall wrappers, and checks
the handler's parameter list against the signature declared in
kvm_hcall.h, so both ends of every hypercall are now compiled against
the same declaration. Handler bodies keep their logic and lose the
DECLARE_REG() and return-register boilerplate. The compiled handlers
are instruction-for-instruction identical, apart from flush_hyp_vcpu()
and sync_hyp_vcpu() now being inlined into their only caller.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 19 +-
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 420 ++++++++++++++---------------
2 files changed, 213 insertions(+), 226 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
index 51bfd14748464..6aa2df90a14a4 100644
--- a/arch/arm64/include/asm/kvm_hcall.h
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -41,6 +41,8 @@ struct vgic_v5_cpu_if;
#define __KVM_HCALL_MAP(n, ...) __KVM_HCALL_MAP##n(__VA_ARGS__)
#define __KVM_HCALL_DECL(t, a) t a
+#define __KVM_HCALL_LONG(t, a) unsigned long a
+#define __KVM_HCALL_CAST(t, a) (__force t) a
#define __KVM_HCALL_ARGS(t, a) a
#ifndef __KVM_NVHE_HYPERVISOR__
@@ -124,10 +126,19 @@ struct vgic_v5_cpu_if;
#define kvm_call_hyp_ret(f, ...) f(__VA_ARGS__)
#define kvm_call_hyp_nvhe(f, ...) f(__VA_ARGS__)
-#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...)
-#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...)
-#define DECLARE_KVM_HOST_HCALL0(ret, name)
-#define DECLARE_KVM_HOST_HCALL0_VOID(name)
+/*
+ * At EL2 each declaration emits the canonical signature of the hypercall,
+ * which DEFINE_KVM_HOST_HCALL() in hyp-main.c checks the handler
+ * definition against.
+ */
+#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...) \
+ typedef ret kvm_host_hcall_sig_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__));
+#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...) \
+ typedef void kvm_host_hcall_sig_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__));
+#define DECLARE_KVM_HOST_HCALL0(ret, name) \
+ typedef ret kvm_host_hcall_sig_##name(void);
+#define DECLARE_KVM_HOST_HCALL0_VOID(name) \
+ typedef void kvm_host_hcall_sig_##name(void);
#endif /* __KVM_NVHE_HYPERVISOR__ */
/* Hypercalls that are unavailable once pKVM has finalised. */
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 7537d422deab3..f2501249f4391 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -24,6 +24,62 @@
DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
+/*
+ * Define a hypercall handler: handle_<name> unmarshals the arguments from
+ * the host context and hands them, correctly typed, to the body that
+ * follows the macro. The parameter list is type-checked against the
+ * signature declared in <asm/kvm_hcall.h>, so the handler cannot drift
+ * from what the typed caller stubs marshal in. Modelled on the syscall
+ * wrappers.
+ */
+#define KVM_HOST_HCALL_REGS(x) \
+ __KVM_HCALL_MAP(x, __KVM_HCALL_ARGS \
+ ,, cpu_reg(host_ctxt, 1),, cpu_reg(host_ctxt, 2) \
+ ,, cpu_reg(host_ctxt, 3),, cpu_reg(host_ctxt, 4) \
+ ,, cpu_reg(host_ctxt, 5),, cpu_reg(host_ctxt, 6))
+
+#define DEFINE_KVM_HOST_HCALL(ret, name, x, ...) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static __always_inline \
+ ret __se_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_LONG, __VA_ARGS__)) \
+ { \
+ return __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_CAST, __VA_ARGS__)); \
+ } \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ cpu_reg(host_ctxt, 1) = __se_##name(KVM_HOST_HCALL_REGS(x)); \
+ } \
+ static ret __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__))
+
+#define DEFINE_KVM_HOST_HCALL_VOID(name, x, ...) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static __always_inline \
+ void __se_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_LONG, __VA_ARGS__)) \
+ { \
+ __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_CAST, __VA_ARGS__)); \
+ } \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ __se_##name(KVM_HOST_HCALL_REGS(x)); \
+ } \
+ static void __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__))
+
+#define DEFINE_KVM_HOST_HCALL0(ret, name) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ cpu_reg(host_ctxt, 1) = __do_##name(); \
+ } \
+ static ret __do_##name(void)
+
+#define DEFINE_KVM_HOST_HCALL0_VOID(name) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ __do_##name(); \
+ } \
+ static void __do_##name(void)
+
/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */
unsigned int hyp_gicv3_nr_lr;
@@ -185,11 +241,9 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
}
-static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__pkvm_vcpu_load, 3,
+ pkvm_handle_t, handle, unsigned int, vcpu_idx, u64, hcr_el2)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(unsigned int, vcpu_idx, host_ctxt, 2);
- DECLARE_REG(u64, hcr_el2, host_ctxt, 3);
struct pkvm_hyp_vcpu *hyp_vcpu;
hyp_vcpu = pkvm_load_hyp_vcpu(handle, vcpu_idx);
@@ -206,7 +260,7 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
}
}
-static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__pkvm_vcpu_put)
{
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
@@ -214,9 +268,9 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
pkvm_put_hyp_vcpu(hyp_vcpu);
}
-static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
+ struct kvm_vcpu *, host_vcpu)
{
- DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 1);
int ret;
if (unlikely(is_protected_kvm_enabled())) {
@@ -228,15 +282,11 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
* loading a vcpu. Therefore, if SME features enabled the host
* is misbehaving.
*/
- if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR))) {
- ret = -EINVAL;
- goto out;
- }
+ if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR)))
+ return -EINVAL;
- if (!hyp_vcpu) {
- ret = -EINVAL;
- goto out;
- }
+ if (!hyp_vcpu)
+ return -EINVAL;
flush_hyp_vcpu(hyp_vcpu);
@@ -251,8 +301,8 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
ret = __kvm_vcpu_run(vcpu);
fpsimd_lazy_switch_to_host(vcpu);
}
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
static int pkvm_refill_memcache(struct pkvm_hyp_vcpu *hyp_vcpu)
@@ -264,184 +314,150 @@ static int pkvm_refill_memcache(struct pkvm_hyp_vcpu *hyp_vcpu)
&host_vcpu->arch.pkvm_memcache);
}
-static void handle___pkvm_host_donate_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_donate_guest, 2,
+ u64, pfn, u64, gfn)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
+ int ret;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || !pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
ret = pkvm_refill_memcache(hyp_vcpu);
if (ret)
- goto out;
+ return ret;
- ret = __pkvm_host_donate_guest(pfn, gfn, hyp_vcpu);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_donate_guest(pfn, gfn, hyp_vcpu);
}
-static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_share_guest, 4,
+ u64, pfn, u64, gfn, u64, nr_pages, u64, prot)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
- DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 4);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
+ int ret;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
ret = pkvm_refill_memcache(hyp_vcpu);
if (ret)
- goto out;
+ return ret;
- ret = __pkvm_host_share_guest(pfn, gfn, nr_pages, hyp_vcpu, prot);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_share_guest(pfn, gfn, nr_pages, hyp_vcpu, prot);
}
-static void handle___pkvm_host_unshare_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_unshare_guest, 3,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
struct pkvm_hyp_vm *hyp_vm;
- int ret = -EINVAL;
+ int ret;
hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
- goto out;
+ return -EINVAL;
ret = __pkvm_host_unshare_guest(gfn, nr_pages, hyp_vm);
put_pkvm_hyp_vm(hyp_vm);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
-static void handle___pkvm_host_relax_perms_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_relax_perms_guest, 2,
+ u64, gfn, u64, prot)
{
- DECLARE_REG(u64, gfn, host_ctxt, 1);
- DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 2);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
- ret = __pkvm_host_relax_perms_guest(gfn, hyp_vcpu, prot);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_relax_perms_guest(gfn, hyp_vcpu, prot);
}
-static void handle___pkvm_host_wrprotect_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_wrprotect_guest, 3,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
struct pkvm_hyp_vm *hyp_vm;
- int ret = -EINVAL;
+ int ret;
hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
- goto out;
+ return -EINVAL;
ret = __pkvm_host_wrprotect_guest(gfn, nr_pages, hyp_vm);
put_pkvm_hyp_vm(hyp_vm);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
-static void handle___pkvm_host_test_clear_young_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_test_clear_young_guest, 4,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages, bool, mkold)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
- DECLARE_REG(bool, mkold, host_ctxt, 4);
struct pkvm_hyp_vm *hyp_vm;
- int ret = -EINVAL;
+ int ret;
hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
- goto out;
+ return -EINVAL;
ret = __pkvm_host_test_clear_young_guest(gfn, nr_pages, mkold, hyp_vm);
put_pkvm_hyp_vm(hyp_vm);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
-static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest, 1,
+ u64, gfn)
{
- DECLARE_REG(u64, gfn, host_ctxt, 1);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
- ret = __pkvm_host_mkyoung_guest(gfn, hyp_vcpu);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_mkyoung_guest(gfn, hyp_vcpu);
}
-static void handle___kvm_adjust_pc(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
+ struct kvm_vcpu *, vcpu)
{
- DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
-
__kvm_adjust_pc(kern_hyp_va(vcpu));
}
-static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
{
__kvm_flush_vm_context();
}
-static void handle___kvm_tlb_flush_vmid_ipa(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
- DECLARE_REG(phys_addr_t, ipa, host_ctxt, 2);
- DECLARE_REG(int, level, host_ctxt, 3);
-
__kvm_tlb_flush_vmid_ipa(kern_hyp_va(mmu), ipa, level);
}
-static void handle___kvm_tlb_flush_vmid_ipa_nsh(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
- DECLARE_REG(phys_addr_t, ipa, host_ctxt, 2);
- DECLARE_REG(int, level, host_ctxt, 3);
-
__kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va(mmu), ipa, level);
}
-static void
-handle___kvm_tlb_flush_vmid_range(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
- DECLARE_REG(phys_addr_t, start, host_ctxt, 2);
- DECLARE_REG(unsigned long, pages, host_ctxt, 3);
-
__kvm_tlb_flush_vmid_range(kern_hyp_va(mmu), start, pages);
}
-static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
+ struct kvm_s2_mmu *, mmu)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
-
__kvm_tlb_flush_vmid(kern_hyp_va(mmu));
}
-static void handle___pkvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
struct pkvm_hyp_vm *hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
@@ -451,19 +467,19 @@ static void handle___pkvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
put_pkvm_hyp_vm(hyp_vm);
}
-static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
+ struct kvm_s2_mmu *, mmu)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
-
__kvm_flush_cpu_context(kern_hyp_va(mmu));
}
-static void handle___kvm_timer_set_cntvoff(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
+ u64, cntvoff)
{
- __kvm_timer_set_cntvoff(cpu_reg(host_ctxt, 1));
+ __kvm_timer_set_cntvoff(cntvoff);
}
-static void handle___kvm_enable_ssbs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__kvm_enable_ssbs)
{
u64 tmp;
@@ -472,72 +488,61 @@ static void handle___kvm_enable_ssbs(struct kvm_cpu_context *host_ctxt)
write_sysreg_el2(tmp, SYS_SCTLR);
}
-static void handle___vgic_v3_get_gic_config(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(u64, __vgic_v3_get_gic_config)
{
- cpu_reg(host_ctxt, 1) = __vgic_v3_get_gic_config();
+ return __vgic_v3_get_gic_config();
}
-static void handle___vgic_v3_init_lrs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__vgic_v3_init_lrs)
{
__vgic_v3_init_lrs();
}
-static void handle___vgic_v3_save_aprs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
+ struct vgic_v3_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v3_save_aprs(kern_hyp_va(cpu_if));
}
-static void handle___vgic_v3_restore_vmcr_aprs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
+ struct vgic_v3_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v3_restore_vmcr_aprs(kern_hyp_va(cpu_if));
}
-static void handle___pkvm_init(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_init, 4,
+ phys_addr_t, phys, unsigned long, size,
+ unsigned long *, per_cpu_base, u32, hyp_va_bits)
{
- DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
- DECLARE_REG(unsigned long, size, host_ctxt, 2);
- DECLARE_REG(unsigned long *, per_cpu_base, host_ctxt, 3);
- DECLARE_REG(u32, hyp_va_bits, host_ctxt, 4);
-
/*
* __pkvm_init() will return only if an error occurred, otherwise it
* will tail-call in __pkvm_init_finalise() which will have to deal
* with the host context directly.
*/
- cpu_reg(host_ctxt, 1) = __pkvm_init(phys, size, per_cpu_base, hyp_va_bits);
+ return __pkvm_init(phys, size, per_cpu_base, hyp_va_bits);
}
-static void handle___pkvm_cpu_set_vector(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_cpu_set_vector, 1,
+ enum arm64_hyp_spectre_vector, slot)
{
- DECLARE_REG(enum arm64_hyp_spectre_vector, slot, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = pkvm_cpu_set_vector(slot);
+ return pkvm_cpu_set_vector(slot);
}
-static void handle___pkvm_host_share_hyp(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_share_hyp, 1,
+ u64, pfn)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_host_share_hyp(pfn);
+ return __pkvm_host_share_hyp(pfn);
}
-static void handle___pkvm_host_unshare_hyp(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_unshare_hyp, 1,
+ u64, pfn)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_host_unshare_hyp(pfn);
+ return __pkvm_host_unshare_hyp(pfn);
}
-static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(unsigned long, __pkvm_create_private_mapping, 3,
+ phys_addr_t, phys, size_t, size, u64, prot)
{
- DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
- DECLARE_REG(size_t, size, host_ctxt, 2);
- DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 3);
-
/*
* __pkvm_create_private_mapping() populates a pointer with the
* hypervisor start address of the allocation.
@@ -554,154 +559,125 @@ static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ct
if (err)
haddr = (unsigned long)ERR_PTR(err);
- cpu_reg(host_ctxt, 1) = haddr;
+ return haddr;
}
-static void handle___pkvm_prot_finalize(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(int, __pkvm_prot_finalize)
{
- cpu_reg(host_ctxt, 1) = __pkvm_prot_finalize();
+ return __pkvm_prot_finalize();
}
-static void handle___pkvm_reserve_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(int, __pkvm_reserve_vm)
{
- cpu_reg(host_ctxt, 1) = __pkvm_reserve_vm();
+ return __pkvm_reserve_vm();
}
-static void handle___pkvm_unreserve_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
-
__pkvm_unreserve_vm(handle);
}
-static void handle___pkvm_init_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
+ struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
{
- DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1);
- DECLARE_REG(void *, vm_hva, host_ctxt, 2);
- DECLARE_REG(void *, pgd_hva, host_ctxt, 3);
-
- host_kvm = kern_hyp_va(host_kvm);
- cpu_reg(host_ctxt, 1) = __pkvm_init_vm(host_kvm, vm_hva, pgd_hva);
+ return __pkvm_init_vm(kern_hyp_va(host_kvm), vm_hva, pgd_hva);
}
-static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
+ pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
+ void *, vcpu_hva)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 2);
- DECLARE_REG(void *, vcpu_hva, host_ctxt, 3);
-
- host_vcpu = kern_hyp_va(host_vcpu);
- cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva);
+ return __pkvm_init_vcpu(handle, kern_hyp_va(host_vcpu), vcpu_hva);
}
-static void handle___pkvm_vcpu_in_poison_fault(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
{
- int ret;
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
- ret = hyp_vcpu ? __pkvm_vcpu_in_poison_fault(hyp_vcpu) : -EINVAL;
- cpu_reg(host_ctxt, 1) = ret;
+ return hyp_vcpu ? __pkvm_vcpu_in_poison_fault(hyp_vcpu) : -EINVAL;
}
-static void handle___pkvm_force_reclaim_guest_page(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_force_reclaim_guest_page, 1,
+ phys_addr_t, phys)
{
- DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_host_force_reclaim_page_guest(phys);
+ return __pkvm_host_force_reclaim_page_guest(phys);
}
-static void handle___pkvm_reclaim_dying_guest_page(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_reclaim_dying_guest_page, 2,
+ pkvm_handle_t, handle, u64, gfn)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
-
- cpu_reg(host_ctxt, 1) = __pkvm_reclaim_dying_guest_page(handle, gfn);
+ return __pkvm_reclaim_dying_guest_page(handle, gfn);
}
-static void handle___pkvm_start_teardown_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_start_teardown_vm, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_start_teardown_vm(handle);
+ return __pkvm_start_teardown_vm(handle);
}
-static void handle___pkvm_finalize_teardown_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_finalize_teardown_vm, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_finalize_teardown_vm(handle);
+ return __pkvm_finalize_teardown_vm(handle);
}
-static void handle___tracing_load(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_load, 2,
+ void *, desc_hva, size_t, desc_size)
{
- DECLARE_REG(void *, desc_hva, host_ctxt, 1);
- DECLARE_REG(size_t, desc_size, host_ctxt, 2);
-
- cpu_reg(host_ctxt, 1) = __tracing_load(desc_hva, desc_size);
+ return __tracing_load(desc_hva, desc_size);
}
-static void handle___tracing_unload(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__tracing_unload)
{
__tracing_unload();
}
-static void handle___tracing_enable(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_enable, 1,
+ bool, enable)
{
- DECLARE_REG(bool, enable, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __tracing_enable(enable);
+ return __tracing_enable(enable);
}
-static void handle___tracing_swap_reader(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_swap_reader, 1,
+ unsigned int, cpu)
{
- DECLARE_REG(unsigned int, cpu, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __tracing_swap_reader(cpu);
+ return __tracing_swap_reader(cpu);
}
-static void handle___tracing_update_clock(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__tracing_update_clock, 4,
+ u32, mult, u32, shift, u64, epoch_ns, u64, epoch_cyc)
{
- DECLARE_REG(u32, mult, host_ctxt, 1);
- DECLARE_REG(u32, shift, host_ctxt, 2);
- DECLARE_REG(u64, epoch_ns, host_ctxt, 3);
- DECLARE_REG(u64, epoch_cyc, host_ctxt, 4);
-
__tracing_update_clock(mult, shift, epoch_ns, epoch_cyc);
}
-static void handle___tracing_reset(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_reset, 1,
+ unsigned int, cpu)
{
- DECLARE_REG(unsigned int, cpu, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __tracing_reset(cpu);
+ return __tracing_reset(cpu);
}
-static void handle___tracing_enable_event(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_enable_event, 2,
+ unsigned short, id, bool, enable)
{
- DECLARE_REG(unsigned short, id, host_ctxt, 1);
- DECLARE_REG(bool, enable, host_ctxt, 2);
-
- cpu_reg(host_ctxt, 1) = __tracing_enable_event(id, enable);
+ return __tracing_enable_event(id, enable);
}
-static void handle___tracing_write_event(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
+ u64, id)
{
- DECLARE_REG(u64, id, host_ctxt, 1);
-
trace_selftest(id);
}
-static void handle___vgic_v5_save_apr(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
+ struct vgic_v5_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v5_save_apr(kern_hyp_va(cpu_if));
}
-static void handle___vgic_v5_restore_vmcr_apr(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
+ struct vgic_v5_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
}
--
2.39.5
^ permalink raw reply related
* [PATCH v1 11/11] KVM: arm64: Tag host-VA hypercall parameters __hostva
From: Fuad Tabba @ 2026-07-20 16:24 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720162412.1401272-1-fuad.tabba@linux.dev>
The nVHE hypervisor takes host virtual addresses as hypercall arguments
and translates each with kern_hyp_va() before use. Nothing marks them as
host-owned, so dereferencing one untranslated at EL2 - a recurring bug
class - is invisible to the compiler.
Add a __hostva sparse address space, active only for EL2 code, and tag
the host-VA parameters in the hypercall declarations. kern_hyp_va_host()
is the only sanctioned unwrap: it translates the address, preserves the
pointee type (stripped of qualifiers, as with the percpu accessors) and
drops the tag with a __force cast, so an untranslated host VA fails
sparse. The tag flows from the shared declaration into the generated
handler and on into the donated-memory and tracing-descriptor helpers,
so a handler cannot extract a host VA without it. Host code sees plain
pointers, and the tag is checker-only: no code is generated.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 43 ++++++++++++-------
arch/arm64/include/asm/kvm_mmu.h | 10 +++++
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 6 ++-
arch/arm64/kvm/hyp/include/nvhe/trace.h | 5 ++-
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 57 +++++++++++++------------
arch/arm64/kvm/hyp/nvhe/pkvm.c | 11 ++---
arch/arm64/kvm/hyp/nvhe/trace.c | 4 +-
7 files changed, 82 insertions(+), 54 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
index 6aa2df90a14a4..3033a6ba1462a 100644
--- a/arch/arm64/include/asm/kvm_hcall.h
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -27,6 +27,18 @@ struct kvm_vcpu;
struct vgic_v3_cpu_if;
struct vgic_v5_cpu_if;
+/*
+ * A host VA carried by a hypercall argument. At EL2 such a pointer must not
+ * be dereferenced until it is translated with kern_hyp_va_host(); sparse
+ * flags any use that skips the translation. The tag describes the EL2 view
+ * only: the host dereferences its own VAs freely.
+ */
+#if defined(__KVM_NVHE_HYPERVISOR__) && defined(__CHECKER__)
+#define __hostva __attribute__((noderef, address_space(__hostva)))
+#else
+#define __hostva
+#endif
+
/*
* Hypercall signatures are declared as (type, name) argument pairs.
* __KVM_HCALL_MAP() applies a macro to each pair, in the mold of __MAP()
@@ -157,24 +169,24 @@ DECLARE_KVM_HOST_HCALL0(int, __pkvm_prot_finalize)
/* Hypercalls that are always available and common to [nh]VHE/pKVM. */
DECLARE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
- struct kvm_vcpu *, vcpu)
+ struct kvm_vcpu __hostva *, vcpu)
DECLARE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
- struct kvm_vcpu *, vcpu)
+ struct kvm_vcpu __hostva *, vcpu)
DECLARE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, start, unsigned long, pages)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
u64, cntvoff)
DECLARE_KVM_HOST_HCALL(int, __tracing_load, 2,
- void *, desc_hva, size_t, desc_size)
+ void __hostva *, desc_hva, size_t, desc_size)
DECLARE_KVM_HOST_HCALL0_VOID(__tracing_unload)
DECLARE_KVM_HOST_HCALL(int, __tracing_enable, 1,
bool, enable)
@@ -189,13 +201,13 @@ DECLARE_KVM_HOST_HCALL(int, __tracing_enable_event, 2,
DECLARE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
u64, id)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
/* Hypercalls that are available only when pKVM has finalised. */
DECLARE_KVM_HOST_HCALL(int, __pkvm_host_share_hyp, 1,
@@ -220,10 +232,11 @@ DECLARE_KVM_HOST_HCALL0(int, __pkvm_reserve_vm)
DECLARE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
pkvm_handle_t, handle)
DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
- struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
+ struct kvm __hostva *, host_kvm, void __hostva *, vm_hva,
+ void __hostva *, pgd_hva)
DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
- pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
- void *, vcpu_hva)
+ pkvm_handle_t, handle, struct kvm_vcpu __hostva *, host_vcpu,
+ void __hostva *, vcpu_hva)
DECLARE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
DECLARE_KVM_HOST_HCALL(int, __pkvm_force_reclaim_guest_page, 1,
phys_addr_t, phys)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 6eae7e7e2a684..55fac40377c03 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -7,6 +7,8 @@
#ifndef __ARM64_KVM_MMU_H__
#define __ARM64_KVM_MMU_H__
+#include <linux/compiler.h>
+
#include <asm/page.h>
#include <asm/memory.h>
#include <asm/mmu.h>
@@ -140,6 +142,14 @@ static __always_inline unsigned long __kern_hyp_va(unsigned long v)
#define kern_hyp_va(v) ((typeof(v))(__kern_hyp_va((unsigned long)(v))))
+/*
+ * Translate a __hostva-tagged host VA, dropping the tag: the only sanctioned
+ * unwrap. Translation only, no ownership or bounds validation; the result
+ * carries the pointee type stripped of the tag and of any cv-qualifiers.
+ */
+#define kern_hyp_va_host(v) \
+ ((TYPEOF_UNQUAL(*(v)) *)__kern_hyp_va((unsigned long)(__force void *)(v)))
+
extern u32 __hyp_va_bits;
/*
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index 2643a1a819668..dcbe8b8913270 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -7,6 +7,7 @@
#ifndef __ARM64_KVM_NVHE_PKVM_H__
#define __ARM64_KVM_NVHE_PKVM_H__
+#include <asm/kvm_hcall.h>
#include <asm/kvm_pkvm.h>
#include <nvhe/gfp.h>
@@ -69,9 +70,10 @@ void pkvm_hyp_vm_table_init(void *tbl);
int __pkvm_reserve_vm(void);
void __pkvm_unreserve_vm(pkvm_handle_t handle);
-int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva);
+int __pkvm_init_vm(struct kvm *host_kvm, void __hostva *vm_hva,
+ void __hostva *pgd_hva);
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- void *vcpu_hva);
+ void __hostva *vcpu_hva);
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn);
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
index 4aa36fd76b9e2..58e0ad3329034 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
@@ -4,6 +4,7 @@
#include <linux/trace_remote_event.h>
+#include <asm/kvm_hcall.h>
#include <asm/kvm_hyptrace.h>
static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
@@ -46,7 +47,7 @@ static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
void *tracing_reserve_entry(unsigned long length);
void tracing_commit_entry(void);
-int __tracing_load(void *desc_va, size_t desc_size);
+int __tracing_load(void __hostva *desc_va, size_t desc_size);
void __tracing_unload(void);
int __tracing_enable(bool enable);
int __tracing_swap_reader(unsigned int cpu);
@@ -59,7 +60,7 @@ static inline void tracing_commit_entry(void) { }
#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \
static inline void trace_##__name(__proto) {}
-static inline int __tracing_load(void *desc_va, size_t desc_size) { return -ENODEV; }
+static inline int __tracing_load(void __hostva *desc_va, size_t desc_size) { return -ENODEV; }
static inline void __tracing_unload(void) { }
static inline int __tracing_enable(bool enable) { return -ENODEV; }
static inline int __tracing_swap_reader(unsigned int cpu) { return -ENODEV; }
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index f2501249f4391..679b6df9c998d 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -269,7 +269,7 @@ DEFINE_KVM_HOST_HCALL0_VOID(__pkvm_vcpu_put)
}
DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
- struct kvm_vcpu *, host_vcpu)
+ struct kvm_vcpu __hostva *, host_vcpu)
{
int ret;
@@ -294,7 +294,7 @@ DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
sync_hyp_vcpu(hyp_vcpu);
} else {
- struct kvm_vcpu *vcpu = kern_hyp_va(host_vcpu);
+ struct kvm_vcpu *vcpu = kern_hyp_va_host(host_vcpu);
/* The host is fully trusted, run its vCPU directly. */
fpsimd_lazy_switch_to_guest(vcpu);
@@ -421,9 +421,9 @@ DEFINE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest, 1,
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
- struct kvm_vcpu *, vcpu)
+ struct kvm_vcpu __hostva *, vcpu)
{
- __kvm_adjust_pc(kern_hyp_va(vcpu));
+ __kvm_adjust_pc(kern_hyp_va_host(vcpu));
}
DEFINE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
@@ -432,27 +432,27 @@ DEFINE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
{
- __kvm_tlb_flush_vmid_ipa(kern_hyp_va(mmu), ipa, level);
+ __kvm_tlb_flush_vmid_ipa(kern_hyp_va_host(mmu), ipa, level);
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
{
- __kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va(mmu), ipa, level);
+ __kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va_host(mmu), ipa, level);
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, start, unsigned long, pages)
{
- __kvm_tlb_flush_vmid_range(kern_hyp_va(mmu), start, pages);
+ __kvm_tlb_flush_vmid_range(kern_hyp_va_host(mmu), start, pages);
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
{
- __kvm_tlb_flush_vmid(kern_hyp_va(mmu));
+ __kvm_tlb_flush_vmid(kern_hyp_va_host(mmu));
}
DEFINE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
@@ -468,9 +468,9 @@ DEFINE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
{
- __kvm_flush_cpu_context(kern_hyp_va(mmu));
+ __kvm_flush_cpu_context(kern_hyp_va_host(mmu));
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
@@ -499,15 +499,15 @@ DEFINE_KVM_HOST_HCALL0_VOID(__vgic_v3_init_lrs)
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
{
- __vgic_v3_save_aprs(kern_hyp_va(cpu_if));
+ __vgic_v3_save_aprs(kern_hyp_va_host(cpu_if));
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
{
- __vgic_v3_restore_vmcr_aprs(kern_hyp_va(cpu_if));
+ __vgic_v3_restore_vmcr_aprs(kern_hyp_va_host(cpu_if));
}
DEFINE_KVM_HOST_HCALL(int, __pkvm_init, 4,
@@ -579,16 +579,17 @@ DEFINE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
}
DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
- struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
+ struct kvm __hostva *, host_kvm, void __hostva *, vm_hva,
+ void __hostva *, pgd_hva)
{
- return __pkvm_init_vm(kern_hyp_va(host_kvm), vm_hva, pgd_hva);
+ return __pkvm_init_vm(kern_hyp_va_host(host_kvm), vm_hva, pgd_hva);
}
DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
- pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
- void *, vcpu_hva)
+ pkvm_handle_t, handle, struct kvm_vcpu __hostva *, host_vcpu,
+ void __hostva *, vcpu_hva)
{
- return __pkvm_init_vcpu(handle, kern_hyp_va(host_vcpu), vcpu_hva);
+ return __pkvm_init_vcpu(handle, kern_hyp_va_host(host_vcpu), vcpu_hva);
}
DEFINE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
@@ -623,7 +624,7 @@ DEFINE_KVM_HOST_HCALL(int, __pkvm_finalize_teardown_vm, 1,
}
DEFINE_KVM_HOST_HCALL(int, __tracing_load, 2,
- void *, desc_hva, size_t, desc_size)
+ void __hostva *, desc_hva, size_t, desc_size)
{
return __tracing_load(desc_hva, desc_size);
}
@@ -670,15 +671,15 @@ DEFINE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
{
- __vgic_v5_save_apr(kern_hyp_va(cpu_if));
+ __vgic_v5_save_apr(kern_hyp_va_host(cpu_if));
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
{
- __vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
+ __vgic_v5_restore_vmcr_apr(kern_hyp_va_host(cpu_if));
}
typedef void (*hcall_t)(struct kvm_cpu_context *);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 205c52535c887..bfc9f07f336a1 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -644,9 +644,9 @@ static size_t pkvm_get_hyp_vm_size(unsigned int nr_vcpus)
size_mul(sizeof(struct pkvm_hyp_vcpu *), nr_vcpus));
}
-static void *map_donated_memory_noclear(void *host_va, size_t size)
+static void *map_donated_memory_noclear(void __hostva *host_va, size_t size)
{
- void *va = kern_hyp_va(host_va);
+ void *va = kern_hyp_va_host(host_va);
if (!PAGE_ALIGNED(va))
return NULL;
@@ -658,7 +658,7 @@ static void *map_donated_memory_noclear(void *host_va, size_t size)
return va;
}
-static void *map_donated_memory(void *host_va, size_t size)
+static void *map_donated_memory(void __hostva *host_va, size_t size)
{
void *va = map_donated_memory_noclear(host_va, size);
@@ -805,7 +805,8 @@ void teardown_selftest_vm(void)
*
* Return 0 success, negative error code on failure.
*/
-int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva)
+int __pkvm_init_vm(struct kvm *host_kvm, void __hostva *vm_hva,
+ void __hostva *pgd_hva)
{
struct pkvm_hyp_vm *hyp_vm = NULL;
size_t vm_size, pgd_size;
@@ -896,7 +897,7 @@ static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
}
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- void *vcpu_hva)
+ void __hostva *vcpu_hva)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
struct pkvm_hyp_vm *hyp_vm;
diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
index 97203ddd3cf45..5d0fcdce9507d 100644
--- a/arch/arm64/kvm/hyp/nvhe/trace.c
+++ b/arch/arm64/kvm/hyp/nvhe/trace.c
@@ -206,9 +206,9 @@ static bool hyp_trace_desc_is_valid(struct hyp_trace_desc *desc, size_t desc_siz
return true;
}
-int __tracing_load(void *desc_hva, size_t desc_size)
+int __tracing_load(void __hostva *desc_hva, size_t desc_size)
{
- struct hyp_trace_desc *desc = kern_hyp_va(desc_hva);
+ struct hyp_trace_desc *desc = kern_hyp_va_host(desc_hva);
int ret;
ret = __admit_host_mem(desc, desc_size);
--
2.39.5
^ permalink raw reply related
* [PATCH v1 09/11] KVM: arm64: Type-check hypercall arguments at the caller
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
kvm_call_hyp_nvhe() reduces its target to an SMCCC function number, so
the compiler never sees a callable: arguments that are wrong in number,
type or order are silently marshalled into registers. The kvm_call_hyp()
wrappers only catch this on the VHE branch, and the pKVM-only hypercalls
have no such branch.
Declare each hypercall's signature once in kvm_hcall.h and generate a
typed stub from it, in the mold of the syscall wrappers. Make
kvm_call_hyp_nvhe() resolve to the stub so every caller is checked
against the declared signature; a stale or mistyped call now fails to
compile. The stubs inline to the same SMCCC call the untyped macro used
to make: the compiled callers are unchanged, apart from hypercall
returns now being tested at their declared width.
The stage-2 protection arguments are declared u64 rather than
enum kvm_pgtable_prot, as kvm_pgtable.h includes linux/kvm_host.h and
the enum cannot be completed here.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 165 ++++++++++++++++++++++++++++-
arch/arm64/kvm/hyp_trace.c | 2 +-
2 files changed, 165 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
index d925b2c28a3d8..51bfd14748464 100644
--- a/arch/arm64/include/asm/kvm_hcall.h
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -16,12 +16,35 @@
#include <asm/barrier.h>
#include <asm/kvm_asm.h>
+#include <asm/spectre.h>
#include <asm/virt.h>
typedef u16 pkvm_handle_t;
+struct kvm;
+struct kvm_s2_mmu;
+struct kvm_vcpu;
+struct vgic_v3_cpu_if;
+struct vgic_v5_cpu_if;
+
+/*
+ * Hypercall signatures are declared as (type, name) argument pairs.
+ * __KVM_HCALL_MAP() applies a macro to each pair, in the mold of __MAP()
+ * in <linux/syscalls.h>.
+ */
+#define __KVM_HCALL_MAP1(m, t, a, ...) m(t, a)
+#define __KVM_HCALL_MAP2(m, t, a, ...) m(t, a), __KVM_HCALL_MAP1(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP3(m, t, a, ...) m(t, a), __KVM_HCALL_MAP2(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP4(m, t, a, ...) m(t, a), __KVM_HCALL_MAP3(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP5(m, t, a, ...) m(t, a), __KVM_HCALL_MAP4(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP6(m, t, a, ...) m(t, a), __KVM_HCALL_MAP5(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP(n, ...) __KVM_HCALL_MAP##n(__VA_ARGS__)
+
+#define __KVM_HCALL_DECL(t, a) t a
+#define __KVM_HCALL_ARGS(t, a) a
+
#ifndef __KVM_NVHE_HYPERVISOR__
-#define kvm_call_hyp_nvhe(f, ...) \
+#define __kvm_call_hyp_nvhe(f, ...) \
({ \
struct arm_smccc_res res; \
\
@@ -33,6 +56,43 @@ typedef u16 pkvm_handle_t;
res.a1; \
})
+/*
+ * Generate a typed stub for each declared hypercall. kvm_call_hyp_nvhe()
+ * resolves to the stub, so a call with the wrong argument count or types
+ * fails to compile instead of being silently truncated to an SMCCC function
+ * number and a pile of registers. The stub inlines to the same SMCCC call
+ * the untyped macro used to make.
+ */
+#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...) \
+ static __always_inline \
+ ret nvhe_hvc_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__)) \
+ { \
+ return (ret)__kvm_call_hyp_nvhe(name, \
+ __KVM_HCALL_MAP(x, __KVM_HCALL_ARGS, __VA_ARGS__)); \
+ }
+
+#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...) \
+ static __always_inline \
+ void nvhe_hvc_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__)) \
+ { \
+ __kvm_call_hyp_nvhe(name, \
+ __KVM_HCALL_MAP(x, __KVM_HCALL_ARGS, __VA_ARGS__)); \
+ }
+
+#define DECLARE_KVM_HOST_HCALL0(ret, name) \
+ static __always_inline ret nvhe_hvc_##name(void) \
+ { \
+ return (ret)__kvm_call_hyp_nvhe(name); \
+ }
+
+#define DECLARE_KVM_HOST_HCALL0_VOID(name) \
+ static __always_inline void nvhe_hvc_##name(void) \
+ { \
+ __kvm_call_hyp_nvhe(name); \
+ }
+
+#define kvm_call_hyp_nvhe(f, ...) nvhe_hvc_##f(__VA_ARGS__)
+
/*
* The isb() below is there to guarantee the same behaviour on VHE as on !VHE,
* where the eret to EL1 acts as a context synchronization event.
@@ -63,6 +123,109 @@ typedef u16 pkvm_handle_t;
#define kvm_call_hyp(f, ...) f(__VA_ARGS__)
#define kvm_call_hyp_ret(f, ...) f(__VA_ARGS__)
#define kvm_call_hyp_nvhe(f, ...) f(__VA_ARGS__)
+
+#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...)
+#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...)
+#define DECLARE_KVM_HOST_HCALL0(ret, name)
+#define DECLARE_KVM_HOST_HCALL0_VOID(name)
#endif /* __KVM_NVHE_HYPERVISOR__ */
+/* Hypercalls that are unavailable once pKVM has finalised. */
+DECLARE_KVM_HOST_HCALL(int, __pkvm_init, 4,
+ phys_addr_t, phys, unsigned long, size,
+ unsigned long *, per_cpu_base, u32, hyp_va_bits)
+DECLARE_KVM_HOST_HCALL(unsigned long, __pkvm_create_private_mapping, 3,
+ phys_addr_t, phys, size_t, size, u64, prot)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_cpu_set_vector, 1,
+ enum arm64_hyp_spectre_vector, slot)
+DECLARE_KVM_HOST_HCALL0_VOID(__kvm_enable_ssbs)
+DECLARE_KVM_HOST_HCALL0_VOID(__vgic_v3_init_lrs)
+DECLARE_KVM_HOST_HCALL0(u64, __vgic_v3_get_gic_config)
+
+DECLARE_KVM_HOST_HCALL0(int, __pkvm_prot_finalize)
+
+/* Hypercalls that are always available and common to [nh]VHE/pKVM. */
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
+ struct kvm_vcpu *, vcpu)
+DECLARE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
+ struct kvm_vcpu *, vcpu)
+DECLARE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
+ struct kvm_s2_mmu *, mmu)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
+ struct kvm_s2_mmu *, mmu)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
+ u64, cntvoff)
+DECLARE_KVM_HOST_HCALL(int, __tracing_load, 2,
+ void *, desc_hva, size_t, desc_size)
+DECLARE_KVM_HOST_HCALL0_VOID(__tracing_unload)
+DECLARE_KVM_HOST_HCALL(int, __tracing_enable, 1,
+ bool, enable)
+DECLARE_KVM_HOST_HCALL(int, __tracing_swap_reader, 1,
+ unsigned int, cpu)
+DECLARE_KVM_HOST_HCALL_VOID(__tracing_update_clock, 4,
+ u32, mult, u32, shift, u64, epoch_ns, u64, epoch_cyc)
+DECLARE_KVM_HOST_HCALL(int, __tracing_reset, 1,
+ unsigned int, cpu)
+DECLARE_KVM_HOST_HCALL(int, __tracing_enable_event, 2,
+ unsigned short, id, bool, enable)
+DECLARE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
+ u64, id)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
+ struct vgic_v3_cpu_if *, cpu_if)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
+ struct vgic_v3_cpu_if *, cpu_if)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
+ struct vgic_v5_cpu_if *, cpu_if)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
+ struct vgic_v5_cpu_if *, cpu_if)
+
+/* Hypercalls that are available only when pKVM has finalised. */
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_share_hyp, 1,
+ u64, pfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_unshare_hyp, 1,
+ u64, pfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_donate_guest, 2,
+ u64, pfn, u64, gfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_share_guest, 4,
+ u64, pfn, u64, gfn, u64, nr_pages, u64, prot)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_unshare_guest, 3,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_relax_perms_guest, 2,
+ u64, gfn, u64, prot)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_wrprotect_guest, 3,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_test_clear_young_guest, 4,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages, bool, mkold)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest, 1,
+ u64, gfn)
+DECLARE_KVM_HOST_HCALL0(int, __pkvm_reserve_vm)
+DECLARE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
+ pkvm_handle_t, handle)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
+ struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
+ pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
+ void *, vcpu_hva)
+DECLARE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_force_reclaim_guest_page, 1,
+ phys_addr_t, phys)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_reclaim_dying_guest_page, 2,
+ pkvm_handle_t, handle, u64, gfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_start_teardown_vm, 1,
+ pkvm_handle_t, handle)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_finalize_teardown_vm, 1,
+ pkvm_handle_t, handle)
+DECLARE_KVM_HOST_HCALL_VOID(__pkvm_vcpu_load, 3,
+ pkvm_handle_t, handle, unsigned int, vcpu_idx, u64, hcr_el2)
+DECLARE_KVM_HOST_HCALL0_VOID(__pkvm_vcpu_put)
+DECLARE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
+ pkvm_handle_t, handle)
+
#endif /* __ARM64_KVM_HCALL_H__ */
diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
index 2411b4c32932c..7fe0e8feb7d7c 100644
--- a/arch/arm64/kvm/hyp_trace.c
+++ b/arch/arm64/kvm/hyp_trace.c
@@ -264,7 +264,7 @@ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv)
if (ret)
goto err_free_buffer;
- ret = kvm_call_hyp_nvhe(__tracing_load, (unsigned long)desc, desc_size);
+ ret = kvm_call_hyp_nvhe(__tracing_load, desc, desc_size);
if (ret)
goto err_unload_pages;
--
2.39.5
^ permalink raw reply related
* [PATCH v1 08/11] KVM: arm64: Move the host hypercall interface to its own header
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
Move the kvm_call_hyp() dispatch macros and pkvm_handle_t out of
kvm_host.h into a new kvm_hcall.h, giving the host<->hyp hypercall
interface a single home that subsequent patches build on to restore
type-checking across the boundary. The only adjustment to the moved
code is the checkpatch-mandated space in "while (0)".
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 68 ++++++++++++++++++++++++++++++
arch/arm64/include/asm/kvm_host.h | 48 +--------------------
2 files changed, 69 insertions(+), 47 deletions(-)
create mode 100644 arch/arm64/include/asm/kvm_hcall.h
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
new file mode 100644
index 0000000000000..d925b2c28a3d8
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * The host<->hyp hypercall interface.
+ *
+ * Copyright (C) 2026 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ */
+
+#ifndef __ARM64_KVM_HCALL_H__
+#define __ARM64_KVM_HCALL_H__
+
+#include <linux/arm-smccc.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+#include <asm/barrier.h>
+#include <asm/kvm_asm.h>
+#include <asm/virt.h>
+
+typedef u16 pkvm_handle_t;
+
+#ifndef __KVM_NVHE_HYPERVISOR__
+#define kvm_call_hyp_nvhe(f, ...) \
+ ({ \
+ struct arm_smccc_res res; \
+ \
+ arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \
+ ##__VA_ARGS__, &res); \
+ if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS)) \
+ res.a1 = -EOPNOTSUPP; \
+ \
+ res.a1; \
+ })
+
+/*
+ * The isb() below is there to guarantee the same behaviour on VHE as on !VHE,
+ * where the eret to EL1 acts as a context synchronization event.
+ */
+#define kvm_call_hyp(f, ...) \
+ do { \
+ if (has_vhe()) { \
+ f(__VA_ARGS__); \
+ isb(); \
+ } else { \
+ kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define kvm_call_hyp_ret(f, ...) \
+ ({ \
+ typeof(f(__VA_ARGS__)) ret; \
+ \
+ if (has_vhe()) { \
+ ret = f(__VA_ARGS__); \
+ } else { \
+ ret = kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
+ } \
+ \
+ ret; \
+ })
+#else /* __KVM_NVHE_HYPERVISOR__ */
+#define kvm_call_hyp(f, ...) f(__VA_ARGS__)
+#define kvm_call_hyp_ret(f, ...) f(__VA_ARGS__)
+#define kvm_call_hyp_nvhe(f, ...) f(__VA_ARGS__)
+#endif /* __KVM_NVHE_HYPERVISOR__ */
+
+#endif /* __ARM64_KVM_HCALL_H__ */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5c..81d359ac7af14 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -27,6 +27,7 @@
#include <asm/fpsimd.h>
#include <asm/kvm.h>
#include <asm/kvm_asm.h>
+#include <asm/kvm_hcall.h>
#include <asm/vncr_mapping.h>
#define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -251,8 +252,6 @@ struct kvm_smccc_features {
unsigned long vendor_hyp_bmap_2; /* Function numbers 64-127 */
};
-typedef u16 pkvm_handle_t;
-
struct kvm_protected_vm {
pkvm_handle_t handle;
struct kvm_hyp_memcache teardown_mc;
@@ -1252,51 +1251,6 @@ void kvm_arm_resume_guest(struct kvm *kvm);
#define vcpu_has_run_once(vcpu) (!!READ_ONCE((vcpu)->pid))
-#ifndef __KVM_NVHE_HYPERVISOR__
-#define kvm_call_hyp_nvhe(f, ...) \
- ({ \
- struct arm_smccc_res res; \
- \
- arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \
- ##__VA_ARGS__, &res); \
- if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS)) \
- res.a1 = -EOPNOTSUPP; \
- \
- res.a1; \
- })
-
-/*
- * The isb() below is there to guarantee the same behaviour on VHE as on !VHE,
- * where the eret to EL1 acts as a context synchronization event.
- */
-#define kvm_call_hyp(f, ...) \
- do { \
- if (has_vhe()) { \
- f(__VA_ARGS__); \
- isb(); \
- } else { \
- kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
- } \
- } while(0)
-
-#define kvm_call_hyp_ret(f, ...) \
- ({ \
- typeof(f(__VA_ARGS__)) ret; \
- \
- if (has_vhe()) { \
- ret = f(__VA_ARGS__); \
- } else { \
- ret = kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
- } \
- \
- ret; \
- })
-#else /* __KVM_NVHE_HYPERVISOR__ */
-#define kvm_call_hyp(f, ...) f(__VA_ARGS__)
-#define kvm_call_hyp_ret(f, ...) f(__VA_ARGS__)
-#define kvm_call_hyp_nvhe(f, ...) f(__VA_ARGS__)
-#endif /* __KVM_NVHE_HYPERVISOR__ */
-
int handle_exit(struct kvm_vcpu *vcpu, int exception_index);
void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index);
--
2.39.5
^ permalink raw reply related
* [PATCH v1 05/11] KVM: arm64: nVHE: Run the source checker under C=2
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
The custom %.nvhe.o rule reuses rule_cc_o_c, which hooks the source
checker only for C=1, and that only when the object is rebuilt. The
C=2 hook, cmd_force_checksrc, hangs off the standard %.o rule that
nVHE objects do not use, so "make C=2" silently skips every nVHE
source file. Call cmd_force_checksrc after the compile rule, as the
standard rule does.
Fixes: 7621712918ad4 ("KVM: arm64: Add build rules for separate VHE/nVHE object files")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
index f57450ebcb498..ccc1fe8394094 100644
--- a/arch/arm64/kvm/hyp/nvhe/Makefile
+++ b/arch/arm64/kvm/hyp/nvhe/Makefile
@@ -49,6 +49,7 @@ targets += $(hyp-obj) kvm_nvhe.tmp.o kvm_nvhe.rel.o hyp.lds hyp-reloc.S hyp-relo
# avoids file name clashes for files shared with VHE.
$(obj)/%.nvhe.o: $(src)/%.c FORCE
$(call if_changed_rule,cc_o_c)
+ $(call cmd,force_checksrc)
$(obj)/%.nvhe.o: $(src)/%.S FORCE
$(call if_changed_rule,as_o_S)
--
2.39.5
^ permalink raw reply related
* [PATCH v1 07/11] KVM: arm64: nVHE: Pass host VA arguments as pointers
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
Four hypercalls take host VAs as unsigned long: the donated vm, pgd
and vcpu regions, and the tracing descriptor. Retype the arguments
and their EL2 consumers as void *, so that the typed hypercall
declarations introduced later in the series can attach a sparse
address space to them; an address space attaches only to pointers.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 5 ++---
arch/arm64/kvm/hyp/include/nvhe/trace.h | 4 ++--
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++++----
arch/arm64/kvm/hyp/nvhe/pkvm.c | 11 +++++------
arch/arm64/kvm/hyp/nvhe/trace.c | 4 ++--
5 files changed, 15 insertions(+), 17 deletions(-)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index c904647d2f760..2643a1a819668 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -69,10 +69,9 @@ void pkvm_hyp_vm_table_init(void *tbl);
int __pkvm_reserve_vm(void);
void __pkvm_unreserve_vm(pkvm_handle_t handle);
-int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
- unsigned long pgd_hva);
+int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva);
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- unsigned long vcpu_hva);
+ void *vcpu_hva);
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn);
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
index 8813ff250f8e0..4aa36fd76b9e2 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
@@ -46,7 +46,7 @@ static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
void *tracing_reserve_entry(unsigned long length);
void tracing_commit_entry(void);
-int __tracing_load(unsigned long desc_va, size_t desc_size);
+int __tracing_load(void *desc_va, size_t desc_size);
void __tracing_unload(void);
int __tracing_enable(bool enable);
int __tracing_swap_reader(unsigned int cpu);
@@ -59,7 +59,7 @@ static inline void tracing_commit_entry(void) { }
#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \
static inline void trace_##__name(__proto) {}
-static inline int __tracing_load(unsigned long desc_va, size_t desc_size) { return -ENODEV; }
+static inline int __tracing_load(void *desc_va, size_t desc_size) { return -ENODEV; }
static inline void __tracing_unload(void) { }
static inline int __tracing_enable(bool enable) { return -ENODEV; }
static inline int __tracing_swap_reader(unsigned int cpu) { return -ENODEV; }
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index d3c69de698f48..7537d422deab3 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -577,8 +577,8 @@ static void handle___pkvm_unreserve_vm(struct kvm_cpu_context *host_ctxt)
static void handle___pkvm_init_vm(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1);
- DECLARE_REG(unsigned long, vm_hva, host_ctxt, 2);
- DECLARE_REG(unsigned long, pgd_hva, host_ctxt, 3);
+ DECLARE_REG(void *, vm_hva, host_ctxt, 2);
+ DECLARE_REG(void *, pgd_hva, host_ctxt, 3);
host_kvm = kern_hyp_va(host_kvm);
cpu_reg(host_ctxt, 1) = __pkvm_init_vm(host_kvm, vm_hva, pgd_hva);
@@ -588,7 +588,7 @@ static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 2);
- DECLARE_REG(unsigned long, vcpu_hva, host_ctxt, 3);
+ DECLARE_REG(void *, vcpu_hva, host_ctxt, 3);
host_vcpu = kern_hyp_va(host_vcpu);
cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva);
@@ -634,7 +634,7 @@ static void handle___pkvm_finalize_teardown_vm(struct kvm_cpu_context *host_ctxt
static void handle___tracing_load(struct kvm_cpu_context *host_ctxt)
{
- DECLARE_REG(unsigned long, desc_hva, host_ctxt, 1);
+ DECLARE_REG(void *, desc_hva, host_ctxt, 1);
DECLARE_REG(size_t, desc_size, host_ctxt, 2);
cpu_reg(host_ctxt, 1) = __tracing_load(desc_hva, desc_size);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 24d6f164129ac..205c52535c887 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -644,9 +644,9 @@ static size_t pkvm_get_hyp_vm_size(unsigned int nr_vcpus)
size_mul(sizeof(struct pkvm_hyp_vcpu *), nr_vcpus));
}
-static void *map_donated_memory_noclear(unsigned long host_va, size_t size)
+static void *map_donated_memory_noclear(void *host_va, size_t size)
{
- void *va = (void *)kern_hyp_va(host_va);
+ void *va = kern_hyp_va(host_va);
if (!PAGE_ALIGNED(va))
return NULL;
@@ -658,7 +658,7 @@ static void *map_donated_memory_noclear(unsigned long host_va, size_t size)
return va;
}
-static void *map_donated_memory(unsigned long host_va, size_t size)
+static void *map_donated_memory(void *host_va, size_t size)
{
void *va = map_donated_memory_noclear(host_va, size);
@@ -805,8 +805,7 @@ void teardown_selftest_vm(void)
*
* Return 0 success, negative error code on failure.
*/
-int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
- unsigned long pgd_hva)
+int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva)
{
struct pkvm_hyp_vm *hyp_vm = NULL;
size_t vm_size, pgd_size;
@@ -897,7 +896,7 @@ static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
}
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- unsigned long vcpu_hva)
+ void *vcpu_hva)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
struct pkvm_hyp_vm *hyp_vm;
diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
index 177fe3d8fbb13..97203ddd3cf45 100644
--- a/arch/arm64/kvm/hyp/nvhe/trace.c
+++ b/arch/arm64/kvm/hyp/nvhe/trace.c
@@ -206,9 +206,9 @@ static bool hyp_trace_desc_is_valid(struct hyp_trace_desc *desc, size_t desc_siz
return true;
}
-int __tracing_load(unsigned long desc_hva, size_t desc_size)
+int __tracing_load(void *desc_hva, size_t desc_size)
{
- struct hyp_trace_desc *desc = (struct hyp_trace_desc *)kern_hyp_va(desc_hva);
+ struct hyp_trace_desc *desc = kern_hyp_va(desc_hva);
int ret;
ret = __admit_host_mem(desc, desc_size);
--
2.39.5
^ permalink raw reply related
* [PATCH v1 06/11] arm64: pi: Run the source checker on the libfdt objects under C=2
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
The custom lib-%.o rule reuses rule_cc_o_c, which hooks the source
checker only for C=1, and that only when the object is rebuilt. The
C=2 hook, cmd_force_checksrc, hangs off the standard %.o rule, so
"make C=2" silently skips the two libfdt objects. Call
cmd_force_checksrc after the compile rule, as the standard rule does.
Fixes: aacd149b6238 ("arm64: head: avoid relocating the kernel twice for KASLR")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kernel/pi/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kernel/pi/Makefile b/arch/arm64/kernel/pi/Makefile
index be92d73c25b21..96243c291e39d 100644
--- a/arch/arm64/kernel/pi/Makefile
+++ b/arch/arm64/kernel/pi/Makefile
@@ -34,6 +34,7 @@ $(obj)/lib-%.pi.o: OBJCOPYFLAGS += --prefix-alloc-sections=.init
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
$(call if_changed_rule,cc_o_c)
+ $(call cmd,force_checksrc)
obj-y := idreg-override.pi.o \
map_kernel.pi.o map_range.pi.o \
--
2.39.5
^ permalink raw reply related
* [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
Hi folks,
For a change, I thought I'd send a series that's neither a bunch of
Sashiko bug fixes nor pKVM-specific.
The SMCCC conversion of the host/hyp interface reduced every hypercall
to a function number and a pile of registers: kvm_call_hyp_nvhe() never
shows the compiler a callable, so the caller's arguments and the
handler's DECLARE_REG() casts can disagree in type, count or order
without a diagnostic. Reviewing a fix for exactly that class of bug,
Marc wrote [1]:
"we lost all form of type-checking when everything was hastily
converted to SMCCC to avoid function pointers. Somehow, I feel that
the cure was worse than the disease.
I wish we'd reintroduce some form of compile-time checks, maybe by
having generated stubs?"
and sketched the shape he preferred [2]: a generated nvhe_hvc_##f()
stub that does the type-checking, driven by "some form of declarative
IDL", with "both the caller and callee stubs ... totally generated".
This series implements that, in plain preprocessor macros rather
than an external generator, in the mold of the syscall wrappers.
Each hypercall's signature is declared once, in kvm_hcall.h:
kvm_call_hyp_nvhe() resolves to a typed nvhe_hvc_##f() stub
generated from the declaration, and the hyp-main.c handlers
unmarshal their arguments through glue that is type-checked
against the same declaration. A mistyped or stale call, or a
handler that drifts from its caller, now fails to compile. On top
of that shared declaration, host-VA parameters gain a __hostva
sparse address space, so dereferencing one at EL2 without
kern_hyp_va_host() translation is flagged by sparse. The address
space is KVM-private and lives with the interface it annotates
rather than in compiler_types.h, like x86's __seg_gs in asm/percpu.h.
The deeper instances of that bug class, host VAs reached through
struct fields after the boundary, are follow-up work.
The declarations do not also generate the function-number enum or the
dispatch table. The enum carries the availability-band markers that
decide when each hypercall becomes reachable, and its base entry is
consumed from assembly, so generating it would hide the banding. The
remaining duplication cannot drift silently: a name mismatch between
declaration, caller and handler fails to compile, and a missing
dispatch-table entry is rejected at run time as
SMCCC_RET_NOT_SUPPORTED.
Along the way it turned out "make C=2" has never checked the nVHE
objects at all: the custom %.nvhe.o rule reuses rule_cc_o_c, which
only hooks the checker for C=1, and that only on a rebuild. Patches
2-5 fix the hook and the handful of pre-existing sparse warnings it
flushes out, so the checker lands clean. The libfdt objects under
arch/arm64/kernel/pi/ have the same gap through the same rule, so
patch 6 gives them the same one-line fix; it touches arm64 core
rather than KVM, so it can go through either tree. Patch 1 is
adjacent: trace_remote_event.h is not self-contained (it uses bool
without including linux/types.h), which the nVHE include order had
been silently working around; it can go through the tracing tree
instead if preferred.
The first six patches do not depend on the type-checking work (the
tracing-header fix, the sparse cleanups, and the two C=2 hook fixes),
so they could be taken on their own, ahead of the rest of the series
if that is easier.
The series is structured as follows:
01: Make trace_remote_event.h self-contained.
02-04: Fix the pre-existing sparse warnings in the nVHE code.
05: Run the source checker on nVHE objects under C=2.
06: Same for the libfdt objects under arch/arm64/kernel/pi/.
07: Pass the host-VA hypercall arguments as pointers at EL2.
08: Move the dispatch macros to a new kvm_hcall.h.
09: Declare the hypercall signatures, type-check the callers.
10: Generate the handler unmarshalling, type-check the handlers.
11: Tag host-VA parameters __hostva for sparse.
The generated code is unchanged, checked by comparing the disassembly
of every KVM object, function by function, against the base: the
callers are identical apart from hypercall returns now being tested at
their declared width and the stage-2 TLB-flush level argument now
being sign-extended as it is loaded (an s8 passed as an int), and the
handlers are instruction-for-instruction identical apart from
flush_hyp_vcpu()/sync_hyp_vcpu() being inlined into their only
caller. The extra argument that prompted the thread, a
mistyped or reordered argument, and a handler that disagrees with its
declaration all now fail to compile. checkpatch complains about the
__KVM_HCALL_MAP() machinery, the (type, name) pair syntax and the
generated signature typedefs, as it does about __MAP() in the syscall
wrappers; the idiom cannot be parenthesized, and the typedef is what
checks the handler against the declaration. Enforcing the
__hostva tag needs sparse v0.6.5-rc1 or later for __typeof_unqual__,
as all sparse checking on current kernels does; no stable sparse
release has it yet, so build from sparse.git (checker-valid.sh skips
the check, with a warning, on anything older).
Based on Linux 7.2-rc4 (1590cf0329716).
Cheers,
/fuad
[1] https://lore.kernel.org/all/86zf7po2n3.wl-maz@kernel.org/
[2] https://lore.kernel.org/all/86wm2tnsd8.wl-maz@kernel.org/
Fuad Tabba (11):
tracing: Include linux/types.h in trace_remote_event.h
KVM: arm64: nVHE: Share the stacktrace per-CPU declarations with EL2
KVM: arm64: nVHE: Declare the hyp event IDs before defining them
KVM: arm64: nVHE: Use NULL to reset the trace buffer backing pointer
KVM: arm64: nVHE: Run the source checker under C=2
arm64: pi: Run the source checker on the libfdt objects under C=2
KVM: arm64: nVHE: Pass host VA arguments as pointers
KVM: arm64: Move the host hypercall interface to its own header
KVM: arm64: Type-check hypercall arguments at the caller
KVM: arm64: nVHE: Check hypercall handlers against the declared ABI
KVM: arm64: Tag host-VA hypercall parameters __hostva
arch/arm64/include/asm/kvm_hcall.h | 255 +++++++++++++
arch/arm64/include/asm/kvm_host.h | 48 +--
arch/arm64/include/asm/kvm_mmu.h | 10 +
arch/arm64/include/asm/stacktrace/nvhe.h | 10 +-
arch/arm64/kernel/pi/Makefile | 1 +
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 7 +-
arch/arm64/kvm/hyp/include/nvhe/trace.h | 5 +-
arch/arm64/kvm/hyp/nvhe/Makefile | 1 +
arch/arm64/kvm/hyp/nvhe/events.c | 2 +
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 443 +++++++++++------------
arch/arm64/kvm/hyp/nvhe/pkvm.c | 12 +-
arch/arm64/kvm/hyp/nvhe/stacktrace.c | 3 +-
arch/arm64/kvm/hyp/nvhe/trace.c | 6 +-
arch/arm64/kvm/hyp_trace.c | 2 +-
arch/arm64/kvm/stacktrace.c | 3 -
include/linux/trace_remote_event.h | 2 +
16 files changed, 508 insertions(+), 302 deletions(-)
create mode 100644 arch/arm64/include/asm/kvm_hcall.h
base-commit: 1590cf0329716306e948a8fc29f1d3ee87d3989f
--
2.39.5
^ permalink raw reply
* [PATCH v1 04/11] KVM: arm64: nVHE: Use NULL to reset the trace buffer backing pointer
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
bpages_backing_start is a pointer; resetting it to plain 0 triggers a
sparse warning.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/trace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
index e7e150ab265ff..177fe3d8fbb13 100644
--- a/arch/arm64/kvm/hyp/nvhe/trace.c
+++ b/arch/arm64/kvm/hyp/nvhe/trace.c
@@ -93,7 +93,7 @@ static void hyp_trace_buffer_unload_bpage_backing(struct hyp_trace_buffer *trace
__release_host_mem(start, size);
- trace_buffer->bpages_backing_start = 0;
+ trace_buffer->bpages_backing_start = NULL;
trace_buffer->bpages_backing_size = 0;
}
--
2.39.5
^ permalink raw reply related
* [PATCH v1 02/11] KVM: arm64: nVHE: Share the stacktrace per-CPU declarations with EL2
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
The declarations for overflow_stack, kvm_stacktrace_info and
pkvm_stacktrace are only visible to the host (the first two sit in the
host-only section of stacktrace/nvhe.h, the last is private to
kvm/stacktrace.c), so the definitions in nvhe/stacktrace.c compile
with no declaration in sight and sparse suggests making them static.
DECLARE_KVM_NVHE_PER_CPU() resolves to the right symbol name on both
sides of the build: move the declarations where both can see them and
include the header from the EL2 side unconditionally.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/stacktrace/nvhe.h | 10 ++++++++--
arch/arm64/kvm/hyp/nvhe/stacktrace.c | 3 +--
arch/arm64/kvm/stacktrace.c | 3 ---
3 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/stacktrace/nvhe.h b/arch/arm64/include/asm/stacktrace/nvhe.h
index 171f9edef49fc..a631a577cbe5d 100644
--- a/arch/arm64/include/asm/stacktrace/nvhe.h
+++ b/arch/arm64/include/asm/stacktrace/nvhe.h
@@ -37,6 +37,14 @@ static inline void kvm_nvhe_unwind_init(struct unwind_state *state,
state->pc = pc;
}
+DECLARE_KVM_NVHE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack);
+DECLARE_KVM_NVHE_PER_CPU(struct kvm_nvhe_stacktrace_info, kvm_stacktrace_info);
+
+#ifdef CONFIG_PKVM_STACKTRACE
+DECLARE_KVM_NVHE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)],
+ pkvm_stacktrace);
+#endif
+
#ifndef __KVM_NVHE_HYPERVISOR__
/*
* Conventional (non-protected) nVHE HYP stack unwinder
@@ -45,8 +53,6 @@ static inline void kvm_nvhe_unwind_init(struct unwind_state *state,
* (by the host in EL1).
*/
-DECLARE_KVM_NVHE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack);
-DECLARE_KVM_NVHE_PER_CPU(struct kvm_nvhe_stacktrace_info, kvm_stacktrace_info);
DECLARE_PER_CPU(unsigned long, kvm_arm_hyp_stack_base);
void kvm_nvhe_dump_backtrace(unsigned long hyp_offset);
diff --git a/arch/arm64/kvm/hyp/nvhe/stacktrace.c b/arch/arm64/kvm/hyp/nvhe/stacktrace.c
index 7c832d60d22bb..11fadbebbf1d6 100644
--- a/arch/arm64/kvm/hyp/nvhe/stacktrace.c
+++ b/arch/arm64/kvm/hyp/nvhe/stacktrace.c
@@ -8,6 +8,7 @@
#include <asm/kvm_hyp.h>
#include <asm/memory.h>
#include <asm/percpu.h>
+#include <asm/stacktrace/nvhe.h>
DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
__aligned(16);
@@ -35,8 +36,6 @@ static void hyp_prepare_backtrace(unsigned long fp, unsigned long pc)
}
#ifdef CONFIG_PKVM_STACKTRACE
-#include <asm/stacktrace/nvhe.h>
-
DEFINE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], pkvm_stacktrace);
static struct stack_info stackinfo_get_overflow(void)
diff --git a/arch/arm64/kvm/stacktrace.c b/arch/arm64/kvm/stacktrace.c
index 9724c320126b7..69377195e18b7 100644
--- a/arch/arm64/kvm/stacktrace.c
+++ b/arch/arm64/kvm/stacktrace.c
@@ -198,9 +198,6 @@ static void hyp_dump_backtrace(unsigned long hyp_offset)
}
#ifdef CONFIG_PKVM_STACKTRACE
-DECLARE_KVM_NVHE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)],
- pkvm_stacktrace);
-
/*
* pkvm_dump_backtrace - Dump the protected nVHE HYP backtrace.
*
--
2.39.5
^ permalink raw reply related
* [PATCH v1 01/11] tracing: Include linux/types.h in trace_remote_event.h
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>
trace_remote_event.h uses bool without including linux/types.h, so a
translation unit that includes it ahead of anything else that pulls
types.h in fails to build, as with nvhe/trace.h at EL2.
Fixes: 072529158e60 ("tracing: Add events to trace remotes")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
include/linux/trace_remote_event.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/trace_remote_event.h b/include/linux/trace_remote_event.h
index c8ae1e1f5e721..e4cc2d4497bcf 100644
--- a/include/linux/trace_remote_event.h
+++ b/include/linux/trace_remote_event.h
@@ -3,6 +3,8 @@
#ifndef _LINUX_TRACE_REMOTE_EVENTS_H
#define _LINUX_TRACE_REMOTE_EVENTS_H
+#include <linux/types.h>
+
struct trace_remote;
struct trace_event_fields;
struct trace_seq;
--
2.39.5
^ permalink raw reply related
* Re: [PATCH v1 08/11] rcu: Wake NOCB rcuog kthreads on expedited grace period completion
From: Frederic Weisbecker @ 2026-07-20 15:56 UTC (permalink / raw)
To: Puranjay Mohan
Cc: rcu, linux-kernel, linux-trace-kernel, Paul E. McKenney,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Masami Hiramatsu, Davidlohr Bueso,
Breno Leitao
In-Reply-To: <20260624132356.516959-9-puranjay@kernel.org>
Le Wed, Jun 24, 2026 at 06:23:50AM -0700, Puranjay Mohan a écrit :
> When an expedited grace period completes, rcu_exp_wait_wake() wakes
> waiters on rnp->exp_wq[] but does not notify the NOCB rcuog kthreads. An
> rcuog kthread that is waiting for a grace period sleeps on the leaf
> rcu_node's ->nocb_gp_wq[] with a wait condition based on the grace-period
> state, so without a wakeup, callbacks on offloaded CPUs that could
> benefit from the expedited GP wait until the rcuog kthread wakes for some
> other reason (e.g. the next normal GP or a timer).
>
> Make the rcuog grace-period wait honour expedited GPs and wake it when
> one completes:
>
> - nocb_gp_wait() now records the grace period to wait for as a struct
> rcu_gp_seq (both normal and expedited), tracks the earliest pending
> normal and expedited sequence across the group, and releases the wait
> via poll_state_synchronize_rcu_full() so it wakes for whichever
> completes first. ->nocb_gp_seq is widened to struct rcu_gp_seq
> accordingly.
>
> - rcu_exp_wait_wake() calls the new rcu_nocb_exp_cleanup() on leaf
> nodes, which wakes both ->nocb_gp_wq[0] and ->nocb_gp_wq[1] (the
> expedited sequence does not share parity with the normal ->gp_seq the
> waiter indexed with). Both this path and rcu_nocb_gp_cleanup() use
> the shared rcu_nocb_cleanup_wake() helper, which checks swait_active()
> first; the smp_mb() in rcu_gp_cleanup()/rcu_exp_wait_wake() orders the
> grace-period state update before that check.
>
> A stub rcu_nocb_exp_cleanup() is provided for CONFIG_RCU_NOCB_CPU=n.
>
> Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply
* Re: [RFC PATCH v2 00/13] mm/kwatch: dynamic hardware watchpoints for hunting memory corruption
From: Marco Elver @ 2026-07-20 15:26 UTC (permalink / raw)
To: Jinchao Wang
Cc: Andrew Morton, Peter Zijlstra, Thomas Gleixner, Steven Rostedt,
Masami Hiramatsu, Ingo Molnar, Borislav Petkov, Dave Hansen,
H . Peter Anvin, x86, Arnaldo Carvalho de Melo, Namhyung Kim,
Mark Rutland, Mathieu Desnoyers, David Hildenbrand,
Jonathan Corbet, Matthew Wilcox, Alan Stern, Randy Dunlap,
Alexander Potapenko, Mike Rapoport, linux-kernel, linux-mm,
linux-trace-kernel, linux-perf-users, linux-doc
In-Reply-To: <20260717125023.1895892-1-wangjinchao600@gmail.com>
On Fri, 17 Jul 2026 at 14:50, Jinchao Wang <wangjinchao600@gmail.com> wrote:
>
> Motivation
> ==========
>
> The hardest memory corruption bugs are the silent ones: a rogue writer
> scribbles over a live object through a stale pointer or a race, and
The stale/dangling pointer (use-after-free) case is the main one you're after?
> the victim crashes in a code path far away from the culprit. Any
> single developer hits such a bug rarely, but across the kernel's code
> base and install base they keep arriving, and each one is
> disproportionately expensive to localize. The question to answer is
> "who wrote to this object, and from where?", and it is hard to get at
> with the existing tools:
>
> - The kernel's own reports - an oops on a clobbered pointer, a
> BUG_ON, a list-corruption warning - fire at the victim's access,
> not at the corrupting write.
> - KASAN/KFENCE catch memory-safety violations: out-of-bounds
> accesses and use-after-free. But they have a blind spot: a
> corrupting write can be fully memory-safe - a *valid* pointer, in
Language-semantically speaking, a use-after-free write to recycled
memory (be it in heap or stack) is NOT memory-safe. The detectors may
not always catch these (KASAN relies on quarantine for that to
increase the chances, but yeah, not guaranteed..).
> bounds, to a live object, written just at the wrong time or to the
> wrong place - and then they stay silent by design. And even for
> the bugs they can catch, KASAN's rebuild, overhead and redzones
> change timing and layout enough that racy corruption often no
> longer reproduces.
There's also tag-based KASAN (KASAN_SW_TAGS or KASAN_HW_TAGS), of
which KASAN_SW_TAGS has an x86 version based on LAM (not yet merged
though? see https://lwn.net/ml/all/cover.1773164688.git.m.wieczorretman@pm.me/).
One property of tag-based schemes (and any other lock+key detection
scheme) is that upon recycling some memory, the tag (or key) to that
location changes, and any previous pointer that still has the old tag
will fault. The caveat with current pointer-tagging schemes is that
entropy is relatively low (4 bit tags).
^ permalink raw reply
* Re: [PATCH v4 2/8] rv: add generic uprobe infrastructure for RV monitors
From: Gabriele Monaco @ 2026-07-20 15:22 UTC (permalink / raw)
To: wen.yang; +Cc: Nam Cao, linux-trace-kernel, linux-kernel
In-Reply-To: <31d0438f98e80503370a6fb3932d0ec7df0673c3.1783524627.git.wen.yang@linux.dev>
On Wed, 2026-07-08 at 23:38 +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> +++ b/kernel/trace/rv/Kconfig
> @@ -59,6 +59,13 @@ config RV_PER_TASK_MONITORS
> This option configures the maximum number of per-task RV monitors
> that can run
> simultaneously.
>
> +config RV_UPROBE
> + bool
> + depends on RV && UPROBES
> + help
> + Generic uprobe infrastructure for RV monitors. Provides path
> + resolution, registration, and safe synchronous teardown.
This isn't exposed, it's selected automatically when required, I don't
even think the help text is visible (menuconfig doesn't show it), do we
really need it?
> +
> source "kernel/trace/rv/monitors/wip/Kconfig"
> source "kernel/trace/rv/monitors/wwnr/Kconfig"
>
...
> +++ b/kernel/trace/rv/rv_uprobe.c
> @@ -0,0 +1,104 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Generic uprobe infrastructure for RV monitors.
> + *
> + * struct rv_uprobe embeds struct uprobe_consumer directly. This is safe
> + * because rv_uprobe_sync() calls uprobe_unregister_sync(), which calls
> + * synchronize_rcu_tasks_trace(). handler_chain() runs under
> + * rcu_read_lock_trace(), so after synchronize_rcu_tasks_trace() returns,
> + * all in-flight handler_chain() iterations, including any pending
> + * uc->cons_node.next reads, have completed on all CPUs. The caller may
> + * then free the struct containing rv_uprobe immediately.
> + */
> +#include <linux/dcache.h>
> +#include <linux/fs.h>
> +#include <linux/namei.h>
> +#include <linux/uprobes.h>
> +#include <rv/rv_uprobe.h>
> +
> +/**
> + * rv_uprobe_register - initialise and register an uprobe
> + */
> +int rv_uprobe_register(const char *binpath, loff_t offset, struct rv_uprobe
> *p)
> +{
> + struct inode *inode;
> + struct path path;
> + int ret;
> +
> + if (!p->uc.handler && !p->uc.ret_handler)
> + return -EINVAL;
uprobe_register() does this already, do we need it here too?
> +
> + ret = kern_path(binpath, LOOKUP_FOLLOW, &path);
> + if (ret)
> + return ret;
> +
> + if (!d_is_reg(path.dentry)) {
> + path_put(&path);
> + return -EINVAL;
> + }
> +
> + inode = d_real_inode(path.dentry);
> + p->inode = inode;
> +
> + /*
> + * uprobe_register() requires the inode (and mount) to remain
> + * referenced across the call. Keep the path alive until after
> + * uprobe_register() has stored its own reference, then release it.
> + */
> + p->uprobe = uprobe_register(inode, offset, 0, &p->uc);
> + path_put(&path);
I believe I was mistaken here, as sashiko pointed out, uprobe_register()
doesn't keep a reference to the inode, (explicitly stated in it's docs:
"Caller of uprobe_register() is required to keep @inode (and the
containing mount) referenced.").
We should probably revert back to holding path instead of inode and
putting it after synchronous cleanup. That's also what BPF does.
Thanks,
Gabriele
> + if (IS_ERR(p->uprobe)) {
> + ret = PTR_ERR(p->uprobe);
> + p->uprobe = NULL;
> + p->inode = NULL;
> + return ret;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rv_uprobe_register);
> +
> +/**
> + * rv_uprobe_is_registered - test whether an uprobe is currently active
> + */
> +bool rv_uprobe_is_registered(const struct rv_uprobe *p)
> +{
> + return p && p->uprobe;
> +}
> +EXPORT_SYMBOL_GPL(rv_uprobe_is_registered);
> +
> +/**
> + * rv_uprobe_unregister - synchronously unregister a uprobe
> + */
> +void rv_uprobe_unregister(struct rv_uprobe *p)
> +{
> + if (!p || !p->uprobe)
> + return;
> +
> + rv_uprobe_unregister_nosync(p);
> + rv_uprobe_sync();
> +}
> +EXPORT_SYMBOL_GPL(rv_uprobe_unregister);
> +
> +/**
> + * rv_uprobe_unregister_nosync - dequeue an uprobe without waiting
> + */
> +void rv_uprobe_unregister_nosync(struct rv_uprobe *p)
> +{
> + if (!p || !p->uprobe)
> + return;
> +
> + uprobe_unregister_nosync(p->uprobe, &p->uc);
> + p->uprobe = NULL;
> + p->inode = NULL;
> +}
> +EXPORT_SYMBOL_GPL(rv_uprobe_unregister_nosync);
> +
> +/**
> + * rv_uprobe_sync - wait for all in-flight uprobe handlers to complete
> + */
> +void rv_uprobe_sync(void)
> +{
> + uprobe_unregister_sync();
> +}
> +EXPORT_SYMBOL_GPL(rv_uprobe_sync);
^ permalink raw reply
* Re: [PATCH v1 07/11] rcu: Update comments for gp_seq and expedited GP tracking
From: Frederic Weisbecker @ 2026-07-20 14:56 UTC (permalink / raw)
To: Puranjay Mohan
Cc: rcu, linux-kernel, linux-trace-kernel, Paul E. McKenney,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Masami Hiramatsu, Davidlohr Bueso,
Breno Leitao
In-Reply-To: <20260624132356.516959-8-puranjay@kernel.org>
Le Wed, Jun 24, 2026 at 06:23:49AM -0700, Puranjay Mohan a écrit :
> Update documentation comments throughout the RCU callback infrastructure
> to reflect the transition from a single grace-period sequence number to
> the full struct rcu_gp_seq that tracks both normal and expedited grace
> periods.
>
> The ->gp_seq[] array documentation in rcu_segcblist.h is updated to
> describe dual (normal and expedited) GP tracking. The
> rcu_segcblist_advance(), rcu_segcblist_accelerate(), and
> rcu_advance_cbs() comments are updated to refer to the struct rcu_gp_seq
> state (gsp) instead of the old bare grace-period sequence number (seq).
>
> Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
> Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply
* Re: [PATCH v4 6/8] rv/tlob: add tlob hybrid automaton monitor
From: Gabriele Monaco @ 2026-07-20 14:49 UTC (permalink / raw)
To: wen.yang; +Cc: Nam Cao, linux-trace-kernel, linux-kernel
In-Reply-To: <09d656759685edcb0fbeead775300094e7ca5002.1783524627.git.wen.yang@linux.dev>
On Wed, 2026-07-08 at 23:38 +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> +/* Uprobe binding list; protected by tlob_uprobe_mutex. */
> +static LIST_HEAD(tlob_uprobe_list);
> +static DEFINE_MUTEX(tlob_uprobe_mutex);
> +
> +/*
> + * Serialises duplicate-check + da_handle_start_run_event() per pid.
> + * spinlock_t not raw_spinlock_t: uprobe handlers run under Tasks Trace
> + * SRCU (rcu_read_lock_trace()), which permits sleeping on PREEMPT_RT.
> + */
I don't think these comments add much value. spinlock_t is allowed in
RCU critical sections anyway (it's some sort of preemption and RCU is
preemptible under PREEMPT_RT).
Of course if it isn't really required we don't use a raw spinlock, you
don't need a justification here.
> +static DEFINE_SPINLOCK(tlob_start_lock);
> +
> +/* Per-uprobe-binding state: a start + stop probe pair for one binary region.
> */
> +struct tlob_uprobe_binding {
> + struct list_head list;
> + u64 threshold_ns;
> + char binpath[TLOB_MAX_PATH];
> + loff_t offset_start;
> + loff_t offset_stop;
> + DECLARE_RV_UPROBE(start_probe);
> + DECLARE_RV_UPROBE(stop_probe);
> +};
> +
> +/*
> + * Per-task teardown invoked by da_monitor_destroy() for each hash entry.
> + * CAS on stopping (0->1) claims exclusive cleanup ownership.
I find reading acronyms extremely annoying, since non-locking algorithms
are already complex on their own, why don't you just say cmpxchg (which
is searchable) instead of CAS. Sure CAS isn't an obscure acronym but I
could find at least another 4 different definitions in the kernel tree.
> + *
> + * No per-entry ha_cancel_timer_sync(): da_monitor_destroy() calls
> + * da_monitor_reset_all() + synchronize_rcu() before this hook, and
> + * ha_mon_destroying prevents new timer callbacks from running.
> + */
> +static inline void tlob_extra_cleanup(struct da_monitor *da_mon)
> +{
> + struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
> + struct tlob_task_state *ws = ha_get_target(ha_mon);
> +
> + if (!ws)
> + return;
> +
> + if (atomic_cmpxchg_release(&ws->stopping, 0, 1) != 0)
> + return;
> +
> + put_task_struct(ws->task);
> + /*
> + * da_monitor_destroy() has already called synchronize_rcu(); no
> + * reader holds ws. Return the slot directly without call_rcu.
> + */
> + llist_add(&ws->free_node, &tlob_ws_free_list);
> +}
> +
> +static inline bool __tlob_acc(struct task_struct *task, ktime_t now,
> + enum tlob_acc_idx idx)
> +{
> + struct tlob_task_state *ws;
> + unsigned long flags;
> +
> + guard(rcu)();
> + ws = da_get_target_by_id(task->pid);
> + if (!ws)
> + return false;
> + raw_spin_lock_irqsave(&ws->entry_lock, flags);
> + ws->accs_ns[idx] += ktime_to_ns(ktime_sub(now, ws->last_ts));
> + ws->last_ts = now;
> + raw_spin_unlock_irqrestore(&ws->entry_lock, flags);
> + return true;
> +}
> +
> +/* Accumulate running_ns for prev; returns true if prev is monitored. */
> +static inline bool tlob_acc_running(struct task_struct *task, ktime_t now)
> +{
> + return __tlob_acc(task, now, TLOB_ACC_RUNNING);
> +}
> +
> +/* Accumulate waiting_ns for next; returns true if next is monitored. */
There's no next and prev here, plus you're describing __tlob_acc()'s
behaviour 3 times, I'd say just document that (even if it isn't the
primary facing function) and that's all.
> +static inline bool tlob_acc_waiting(struct task_struct *task, ktime_t now)
> +{
> + return __tlob_acc(task, now, TLOB_ACC_WAITING);
> +}
> +
> +/*
> + * handle_sched_switch - advance the DA on every context switch.
> + *
> + * Generates three DA events:
> + * prev, prev_state != 0 -> sleep_tlob (running -> sleeping)
> + * prev, prev_state == 0 -> preempt_tlob (running -> waiting)
> + * next -> switch_in_tlob (waiting -> running)
> + *
> + * A single ktime_get() at handler entry is shared by both acc calls so that
> + * prev's running_ns and next's waiting_ns share the same context-switch
> + * timestamp; neither absorbs handler overhead into its accumulator.
> + *
> + * No waiting->sleeping edge exists: a task can only block voluntarily
> + * (call schedule()) while it is executing on CPU, which corresponds to
> + * the running DA state. A task in the waiting state is TASK_RUNNING in
> + * kernel terms (on the runqueue) and cannot block itself.
> + *
> + * da_handle_event() is called unconditionally: it skips tasks that have no
> + * monitor entry in the hash table.
> + */
> +static void handle_sched_switch(void *data, bool preempt_unused,
> + struct task_struct *prev,
> + struct task_struct *next,
> + unsigned int prev_state)
> +{
> + ktime_t now = ktime_get();
> + bool prev_preempted = (prev_state == 0);
> +
> + if (tlob_acc_running(prev, now))
> + da_handle_event(prev->pid, NULL,
> + prev_preempted ? preempt_tlob : sleep_tlob);
> + if (tlob_acc_waiting(next, now))
> + da_handle_event(next->pid, NULL, switch_in_tlob);
> +}
> +
> +/* Accumulate sleeping_ns on wakeup; returns true if task is monitored. */
> +static inline bool tlob_acc_sleeping(struct task_struct *task, ktime_t now)
> +{
> + return __tlob_acc(task, now, TLOB_ACC_SLEEPING);
> +}
> +
> +/*
> + * handle_sched_wakeup - sleeping -> waiting transition.
> + *
> + * try_to_wake_up() skips TASK_RUNNING tasks, so this never fires for a
> + * task already in running or waiting state.
> + */
> +static void handle_sched_wakeup(void *data, struct task_struct *p)
> +{
> + ktime_t now = ktime_get();
> +
> + if (tlob_acc_sleeping(p, now))
> + da_handle_event(p->pid, NULL, wakeup_tlob);
> +}
> +
> +/*
> + * handle_sched_process_exit - clean up if a task exits without TRACE_STOP.
> + *
> + * Called in do_exit() context; the task still has a valid pid here.
> + * tlob_stop_task() returns -ESRCH if the task is not monitored, which is
> fine.
> + */
> +static void handle_sched_process_exit(void *data, struct task_struct *p,
> + bool group_dead)
> +{
> + tlob_stop_task(p);
> +}
> +
> +/**
> + * tlob_start_task - begin monitoring @task with budget @threshold_ns ns.
> + * @task: Task to monitor; may be current or another task.
> + * @threshold_ns: Latency budget in nanoseconds (wall-clock; running +
> + * waiting + sleeping).
> + * Must be in [1000, TLOB_MAX_THRESHOLD_NS].
> + *
> + * Returns 0, -ENODEV, -ERANGE, -EALREADY, or -ENOSPC (pool at capacity).
> + */
> +int tlob_start_task(struct task_struct *task, u64 threshold_ns)
> +{
> + struct tlob_task_state *ws;
> +
> + if (!da_monitor_enabled())
> + return -ENODEV;
> +
> + if (threshold_ns < TLOB_MIN_THRESHOLD_NS ||
> + threshold_ns > TLOB_MAX_THRESHOLD_NS)
> + return -ERANGE;
> +
> + /* Serialise duplicate-check + pool-slot claim; see tlob_start_lock.
> */
> + guard(spinlock)(&tlob_start_lock);
> +
> + /*
> + * __da_get_mon_storage() uses hash_for_each_possible_rcu(), which
> + * requires an RCU read-side critical section. On PREEMPT_RT,
> + * spinlock_t is an rt_mutex and does not satisfy this requirement.
> + */
Also this is probably misleading, getting the monitor requires RCU, then
the fact some in some configuration something other than read_lock_rcu()
would work too (e.g. disabling preemption) is irrelevant.
> + scoped_guard(rcu) {
> + if (da_get_target_by_id(task->pid))
> + return -EALREADY;
> + }
> +
> + /*
> + * Both tlob_ws_alloc() and da_handle_start_run_event() pop from
> + * pre-allocated pools of size TLOB_MAX_MONITORED; NULL return means
> + * the pool is at capacity.
> + */
> + ws = tlob_ws_alloc();
> + if (!ws)
> + return -ENOSPC;
> +
> + ws->task = task;
> + get_task_struct(task);
> + ws->threshold_ns = threshold_ns;
> + ws->last_ts = ktime_get();
> + raw_spin_lock_init(&ws->entry_lock);
> +
> + /*
> + * da_handle_start_run_event() claims a pool slot via
> da_prepare_storage(),
> + * initialises the monitor, and delivers start_tlob in one step: the
> + * generated ha_setup_invariants() resets clk_elapsed and arms the
> timer.
> + * Returns 0 if the da_monitor_storage pool is exhausted.
> + */
And try not to be too specific about the internal implementation of the
library, that can change without notice, we don't want to have to update
all comments.
Here it is indeed non-trivial to assume no space if
da_handle_start_run_event() fails, but that's only consequence of the
fact that this ws is certainly new (by construction) and the monitor is
enabled: da_handle_start_run_event() can only fail if allocation failed.
da_handle_start_* functions return 1 if an event was handled, this is
by the way not documented..
You could simply say something like:
da_handle_start_run_event() returns false if no event was handled, in
this case it can happen only if memory allocation failed
> + if (!da_handle_start_run_event(task->pid, ws, start_tlob)) {
> + put_task_struct(task);
> + tlob_ws_direct_return(ws);
> + return -ENOSPC;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(tlob_start_task);
> +
> +/**
> + * tlob_stop_task - stop monitoring @task.
> + * @task: Task to stop.
> + *
> + * CAS on ws->stopping (0->1) under RCU claims cleanup ownership;
> + * the winner cancels the timer synchronously and frees all resources.
> + *
> + * Returns 0, -EOVERFLOW (budget exceeded), -ESRCH (not monitored),
> + * or -EAGAIN (concurrent caller claimed cleanup).
> + */
> +int tlob_stop_task(struct task_struct *task)
> +{
> + struct da_monitor *da_mon;
> + struct ha_monitor *ha_mon;
> + struct tlob_task_state *ws;
> + bool budget_exceeded;
> +
> + scoped_guard(rcu) {
> + ws = da_get_target_by_id(task->pid);
> + if (!ws)
> + return -ESRCH;
> +
> + da_mon = da_get_monitor(task->pid, NULL);
> + if (unlikely(WARN_ON_ONCE(!da_mon)))
> + return -ESRCH;
> +
> + ha_mon = to_ha_monitor(da_mon);
> +
> + /*
> + * CAS (0->1) claims cleanup ownership under RCU (ws
> guaranteed valid).
> + * _release pairs with atomic_read_acquire in
> ha_setup_invariants.
> + */
> + if (atomic_cmpxchg_release(&ws->stopping, 0, 1) != 0)
> + return -EAGAIN;
> + }
> + /*
> + * ws and ha_mon are used below outside the RCU guard. This is safe:
> + * the winning CAS (stopping: 0->1) is the only path that frees ws,
> + * and da_destroy_storage() below is the only call that returns the
> + * pool slot. No concurrent path can free either object.
> + */
> +
> + /* Wait for in-flight timer callback before reading da_monitoring. */
> + ha_cancel_timer_sync(ha_mon);
> +
> + /* Timer fired first -> budget exceeded; otherwise reset normally. */
> + scoped_guard(rcu) {
> + budget_exceeded = !da_monitoring(da_mon);
> + if (!budget_exceeded)
> + da_monitor_reset(da_mon);
> + }
> + da_destroy_storage(task->pid);
Here you're playing with the state machine, that looks fragile from a
monitor code.
You could probably have budget_exceeded as part of your target and set
it from tlob_reset_notify() when you know there was an expiration.
Perhaps "stop" could be yet another event in the state machine, bringing
to the "stopped" state, which would automatically call reset(), you only
need to grab ws->stopping before handling the event for
tlob_reset_notify() to work as expected.
Though you'd probably still need ha_cancel_timer_sync() for the rare but
not impossible in-flight timer not yet in RCU critical section..
I'd rather avoid as much as possible bringing in ha_mon/da_mon, but you
probably cannot do better here, just you could avoid relying on
da_monitoring().
What do you think?
Thanks,
Gabriele
^ permalink raw reply
* Re: [linus:master] [tracing] 206b25c090: kselftests-bpf.ftrace.ftracetest-ktap.Generic_dynamic_event_-_add/remove_eprobe_events.fail
From: Masami Hiramatsu @ 2026-07-20 14:31 UTC (permalink / raw)
To: Martin Kaiser
Cc: kernel test robot, oe-lkp, lkp, linux-kernel, Masami Hiramatsu,
linux-trace-kernel
In-Reply-To: <alyhJP3w_cZCmLLu@akranes.kaiser.cx>
On Sun, 19 Jul 2026 12:04:20 +0200
Martin Kaiser <martin@kaiser.cx> wrote:
> Hi,
>
> Thus wrote kernel test robot (oliver.sang@intel.com):
>
> > kernel test robot noticed "kselftests-bpf.ftrace.ftracetest-ktap.Generic_dynamic_event_-_add/remove_eprobe_events.fail" on:
>
> > commit: 206b25c09080cc20fd4c2bea12d59df4b7ba2121 ("tracing: eprobe: read the complete FILTER_PTR_STRING pointer")
> > https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git master
>
> > [test failed on linus/master 3b029c035b34bbc693405ddf759f0e9b920c27f1]
> > [test failed on linux-next/master 49362394dad7df66c274c867a271394c10ca2bb8]
>
> > in testcase: kselftests-bpf
> > version:
> > with following parameters:
>
> > group: ftrace
>
> > config: x86_64-rhel-9.4-bpf
> > compiler: gcc-14
> > test machine: 16 threads Intel(R) Core(TM) i7-13620H (Raptor Lake) with 32G memory
>
> > (please refer to attached dmesg/kmsg for entire log/backtrace)
>
> [...]
>
> > # # ++ echo 0
> > # # +++ grep '^ *ls-' trace
> > # # +++ grep file=
> > # # ++ content=' ls-5520 [006] ...1. 335.361454: eprobe_open: (syscalls.sys_enter_openat) file="/usr/libexec/coreutils/libstdbuf.so"
> > # # ls-5520 [006] ...1. 335.361620: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v3/libselinux.so.1"
> > # # ls-5520 [006] ...1. 335.361649: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v2/libselinux.so.1"
> [...]
> > # # ls-5520 [006] ...1. 335.364212: eprobe_open: (syscalls.sys_enter_openat) file="/usr/lib/locale/C.UTF-8/LC_NUMERIC"
> > # # ls-5520 [006] ...1. 335.364239: eprobe_open: (syscalls.sys_enter_openat) file="/usr/lib/locale/C.utf8/LC_NUMERIC"
> > # # ls-5520 [006] ...1. 335.364265: eprobe_open: (syscalls.sys_enter_openat) file="/usr/lib/locale/C.UTF-8/LC_CTYPE"
> > # # ls-5520 [006] ...1. 335.364292: eprobe_open: (syscalls.sys_enter_openat) file="/usr/lib/locale/C.utf8/LC_CTYPE"
> > # # ls-5520 [006] ...1. 335.364341: eprobe_open: (syscalls.sys_enter_openat) file="."'
> > # # +++ grep '^ *ls-' trace
> > # # +++ grep file=
> > # # +++ grep -v -e '"/' -e '"."' -e '(fault)'
> > # # ++ nocontent=' ls-5520 [006] ...1. 335.361620: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v3/libselinux.so.1"
> > # # ls-5520 [006] ...1. 335.361649: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v2/libselinux.so.1"
> > # # ls-5520 [006] ...1. 335.361676: eprobe_open: (syscalls.sys_enter_openat) file="libselinux.so.1"
> > # # ls-5520 [006] ...1. 335.362051: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v3/libcap.so.2"
> > # # ls-5520 [006] ...1. 335.362080: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v2/libcap.so.2"
> > # # ls-5520 [006] ...1. 335.362107: eprobe_open: (syscalls.sys_enter_openat) file="libcap.so.2"
> > # # ls-5520 [006] ...1. 335.362288: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v3/libc.so.6"
> > # # ls-5520 [006] ...1. 335.362314: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v2/libc.so.6"
> > # # ls-5520 [006] ...1. 335.362340: eprobe_open: (syscalls.sys_enter_openat) file="libc.so.6"
> > # # ls-5520 [006] ...1. 335.362552: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v3/libpcre2-8.so.0"
> > # # ls-5520 [006] ...1. 335.362581: eprobe_open: (syscalls.sys_enter_openat) file="glibc-hwcaps/x86-64-v2/libpcre2-8.so.0"
> > # # ls-5520 [006] ...1. 335.362607: eprobe_open: (syscalls.sys_enter_openat) file="libpcre2-8.so.0"'
>
> This list must be empty for the test to pass. Files with absolute paths and
> pointers that can't be dereferenced "(fault)" are filtered.
Yeah, it seems like a test bug.
>
> My patch fixed the dereferencing, some of the faults are now readable
> filenames. And for some of those, openat used a relative path.
Hm, in this case, we need to fix the test too.
>
> IMHO, the test is not correct, relative paths should be allowed. (In fact, any
> name of an existing file should be ok.)
>
> I'll think about this a bit more and submit a patch to fix the test.
Thanks for you help!
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [RFC PATCH 0/1] psi: Introduce in-kernel PSI auto monitor feature
From: Pintu Kumar Agarwal @ 2026-07-20 14:29 UTC (permalink / raw)
To: Johannes Weiner
Cc: linux-kernel, linux-trace-kernel, surenb, rostedt, mhiramat,
peterz, mathieu.desnoyers, mingo, juri.lelli, vincent.guittot,
dietmar.eggemann, bsegall, mgorman, vschneid, kprateek.nayak,
pintu.ping, nathan, ojeda, nsc, gary, tglx, thomas.weissschuh,
aliceryhl, dianders, linux.amoon, rdunlap, akpm, shuah,
linux-embedded, tim.bird@sony.com
In-Reply-To: <20260713115936.GM276793@cmpxchg.org>
Dear Johannes,
On Mon, Jul 13, 2026 at 5:29 PM Johannes Weiner <hannes@cmpxchg.org> wrote:
>
> On Thu, Jul 02, 2026 at 10:46:05PM +0530, Pintu Kumar Agarwal wrote:
> > Hi all,
> >
> > This RFC introduces an in-kernel PSI auto monitor aimed at improving
> > root-cause visibility for resource pressure events in Linux systems.
> >
> > Motivation:
> >
> > PSI already provides an excellent mechanism to detect CPU, memory and
> > I/O pressure and includes trigger-based notifications via pollable
> > interfaces. However, it deliberately avoids attributing pressure to
> > individual tasks.
>
> Why is this necessary?
>
> > In real-world systems, this creates a gap: when a PSI trigger fires,
> > users still need to determine *which tasks caused the stall* by combining
> > multiple tools (top, meminfo, vmstat, perf, tracing, etc.), often after
> > the event has already passed.
>
> I've never found myself needing to identify which specific task was
> stalled. Tasks competing over a shared resource are interdependent.
>
> Consider this scenario: Task A, B, C are allocating memory and
> creating pressure together; B randomly becomes the sucker to hit
> direct reclaim and making room for the other too as well. Why is it
> meaningful to know that B stalled? It caused the resource contention
> no more than the other two.
>
> Then A and C refault: A is fast, but C happens to hit when the flash
> drive is running garbage collection. Why is it meaningful to know C?
> Again, C is no more the culprit in that situation than the others.
>
> The meaningful thing you can say is that the domain as a whole is
> under pressure. Who exactly becomes the lightning rod is noise.
>
> That said, if you do need it, why not use delayacct? It already tracks
> the reclaim, swap, thrashing time and events that also go into psi on
> a per-task basis. It's not sampled, either.
Thank you so much for your detailed feedback. This is very helpful.
Your point about resource pressure being a domain-level phenomenon
rather is well taken.
I agree that pressure is often created collectively by multiple
workloads, and that the task experiencing the stall is not necessarily
the root cause.
The goal of the RFC was not to identify a single culprit, but to
provide additional attribution context and surrounding tasks around
PSI events
by capturing the major resource consumers and participants exactly at
the time pressure becomes significant.
Your suggestion regarding delayacct is very insightful.
I think there is a utility under tools/accounting/delaytop.c which
already provides task-level delay information in user space when run
manually.
I am currently studying delayacct, taskstats and the existing delaytop
utility to understand how I can correlate PSI auto-monitor attribution
using delayacct.
The idea could be:
PSI threshold breach -> PSI Auto Monitor trigger -> Delayacct provides
delayed task list -> Auto monitor dump / create trace events
PSI threshold breach -> PSI Auto Monitor is triggered -> Collect
per-task delayacct/taskstats data
-> Identify tasks with significant reclaim / swap / I/O / CPU
scheduling delay -> Emit trace events / logs / structured report
One additional motivation for the in-kernel prototype was early-boot
observability.
Unlike a userspace monitor, it can capture pressure events and
attribution context before userspace services are available,
which proved useful in several embedded boot-time investigations.
One more point is that the psi auto monitor can run and trigger on its
own during early boot.
It can quickly help to identify bottlenecks during boot and aid in
boot time optimization at both kernel and user space level.
As a side note, please see one of the snapshots below captured during boot.
# dmesg | grep psi
[ 30.948653][ T420] psi_monitor: pressure high: cpu=50% mem=1%
io=11% (thresh cpu=50 mem=60 io=40)
[ 30.958342][ T420] psi_monitor: logging top 8 tasks under pressure:
[ 30.964959][ T420] psi_monitor: pid=2637 comm=pcid-loc-api
psi_flag=12 oncpu=0 cputime(ms)=443 rss(kB)=21976 io(kB)=0 score=13203
[ 30.977392][ T420] psi_monitor: pid=1974 comm=syslogd psi_flag=0
oncpu=3 cputime(ms)=643 rss(kB)=2156 io(kB)=272 score=4565
[ 30.989137][ T420] psi_monitor: pid=2597 comm=qwesd psi_flag=4
oncpu=3 cputime(ms)=104 rss(kB)=7964 io(kB)=0 score=4502
[ 31.000540][ T420] psi_monitor: pid=1493 comm=dlt-system
psi_flag=0 oncpu=3 cputime(ms)=370 rss(kB)=1984 io(kB)=136 score=2978
[ 31.014588][ T420] psi_monitor: pid=15 comm=rcu_preempt psi_flag=0
oncpu=2 cputime(ms)=443 rss(kB)=0 io(kB)=0 score=2215
[ 31.033593][ T420] psi_monitor: pid=420 comm=kworker/2:9+events
psi_flag=12 oncpu=2 cputime(ms)=351 rss(kB)=0 io(kB)=0 score=1755
[ 31.051276][ T420] psi_monitor: pid=2713 comm=sdir psi_flag=0
oncpu=1 cputime(ms)=124 rss(kB)=0 io(kB)=20 score=640
[ 31.065800][ T420] psi_monitor: pid=41 comm=kworker/u8:1-memlat_wq
psi_flag=0 oncpu=3 cputime(ms)=52 rss(kB)=0 io(kB)=0 score=260
Thank you once again for your review and valuable feedback.
I will explore more and improve the design for v2.
Thanks,
Pintu
^ permalink raw reply
* Re: [linus:master] [selftests/tracing] c5c413534d: kselftests-bpf.ftrace.ftracetest-ktap._instance_event_trigger_-_test_poll_wait_on_histogram.fail
From: Masami Hiramatsu @ 2026-07-20 14:29 UTC (permalink / raw)
To: kernel test robot
Cc: Steven Rostedt, oe-lkp, lkp, linux-kernel, Shuah Khan,
Masami Hiramatsu, linux-trace-kernel
In-Reply-To: <202607161651.ab0a5bf6-lkp@intel.com>
On Thu, 16 Jul 2026 22:30:11 +0800
kernel test robot <oliver.sang@intel.com> wrote:
>
>
>
> Hello,
>
> in fact, we found below two test cases randomly failed upon c5c413534d, while
> keep succeed on parent:
>
> =========================================================================================
> tbox_group/testcase/rootfs/kconfig/compiler/group:
> igk-rpl-d04/kselftests-bpf/debian-13-x86_64-20250902.cgz/x86_64-rhel-9.4-bpf/gcc-14/ftrace
>
> 22a78be4123dce81 c5c413534d40eb4c982f1794e62
> ---------------- ---------------------------
> fail:runs %reproduction fail:runs
> | | |
> :31 52% 16:32 kselftests-bpf.ftrace.ftracetest-ktap._instance_event_trigger_-_test_poll_wait_on_histogram.fail
> :31 29% 9:32 kselftests-bpf.ftrace.ftracetest-ktap.event_trigger_-_test_poll_wait_on_histogram.fail
>
>
>
> kernel test robot noticed "kselftests-bpf.ftrace.ftracetest-ktap._instance_event_trigger_-_test_poll_wait_on_histogram.fail" on:
>
> commit: c5c413534d40eb4c982f1794e62813f926aba747 ("selftests/tracing: Have trigger-hist-poll.tc use sched_process_exit")
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git master
>
> [test failed on linux-next/master 49362394dad7df66c274c867a271394c10ca2bb8]
Hmm, this seems like a scheduler (event) behavior is different from what
the test expected.
Steve, I think we should use another deterministic events.
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [RFC PATCH v2 00/13] mm/kwatch: dynamic hardware watchpoints for hunting memory corruption
From: Masami Hiramatsu @ 2026-07-20 14:24 UTC (permalink / raw)
To: Borislav Petkov, Jinchao Wang
Cc: Dave Hansen, Jinchao Wang, Andrew Morton, Peter Zijlstra,
Thomas Gleixner, Steven Rostedt, Masami Hiramatsu, Ingo Molnar,
Dave Hansen, H . Peter Anvin, x86, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Mathieu Desnoyers, David Hildenbrand,
Jonathan Corbet, Matthew Wilcox, Alan Stern, Randy Dunlap,
Alexander Potapenko, Marco Elver, Mike Rapoport, linux-kernel,
linux-mm, linux-trace-kernel, linux-perf-users, linux-doc
In-Reply-To: <20260717181004.GBalpv_MOaAflgFxgh@fat_crate.local>
On Fri, 17 Jul 2026 11:10:04 -0700
Borislav Petkov <bp@alien8.de> wrote:
> On Fri, Jul 17, 2026 at 06:41:49AM -0700, Dave Hansen wrote:
> > On 7/17/26 05:50, Jinchao Wang wrote:
> > > 24 files changed, 2115 insertions(+), 63 deletions(-)
> > Reading this, I wonder how many kernel debugging features we need. I
> > don't even think we have a centralized list of them. They all just live
> > in their own silos.
> >
> > This one really seems like a super specialized tool. It has to be
> > enabled at compile time and specifically aimed at a specific function.
> >
> > Maybe this should live off on the side for a while. If folks end up
> > actually needing it, they can point their friendly LLM over to its tree.
>
> Right, and in my mbox right under this mail thread there's a
>
> https://lore.kernel.org/all/178429796992.157981.3393977217853767915.stgit@devnote2/
>
> which Masami has been blasting almost every day this week which contains two
> of the patches from this set here...
Sorry about that. I tried to fix the reviewed comment from Sashiko,
but I should wait for someone's comment.
>
> Looks to me like folks need to sit down and agree on strategy first.
Yeah, I need to talk with Jinchao.
Like kprobe, wprobe itself integrates watchpoint functionality into
the tracing subsystem. Jinchao, I would like to ask you to redesign
the kwatch tool based on fprobe and wprobe events in user space?
I think that could reduce redundant efforts on the similar feature.
Using fprobe entry and exit event trigger, we can make a Function-
scoped watch window. Also, since fprobe and wprobe are having BTF
typecast support, you can access to the members of data structures
more naturally.
If you are interested, I would be open to having you handle the
development of the necessary features for wprobe.
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox