* [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes
@ 2026-09-24 9:49 Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 1/2] KVM: arm64: ptdump: Check the page tables aren't freed when accessing Wei-Lin Chang
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Wei-Lin Chang @ 2026-09-24 9:49 UTC (permalink / raw)
To: linux-arm-kernel, kvmarm, linux-kernel
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly,
Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Mark Rutland, Sebastian Ene, Vincent Donnefort,
Itaru Kitayama, Wei-Lin Chang
Hi,
This is v4 of fixing the sleep-in-atomic problem of the shadow ptdump.
This version relaxes a few write_lock to read_lock, and collected tags.
Please see the details below.
* Changes from v3 [1]:
- Take read_lock instead of write_lock when getting pgt metadata.
(Vincent)
- Collected Tested-bys and Reviewed-by from Vincent and Itaru.
(Thanks!)
Below is mostly the same content from the v3 cover letter:
The approach taken now is simply creating a file for each nested mmu.
The ptdump files are created alongside the nested mmus in
kvm_vcpu_init_nested(), in a sleepable context, and removed when the VM
is destroyed (kvm_destroy_vm_debugfs(), before the nested mmus are freed
in kvm_destroy_nested()). This allows us to store the kvm_s2_mmu pointers
in the ptdump code, after Marc's nested mmu lifecycle fixes [2].
For the shadow ptdump, for valid nested mmus the first line will show
the nested context's VTTBR and VTCR values, and whether s2 is enabled or
not. The ptdump then follows.
The first patch fixes mmu->pgt accesses after the VM MMU teardown in
ptdump.c, the second patch switches from per nested context ptdumps to
per nested mmu ptdumps.
Tested with CONFIG_PROVE_LOCKING, CONFIG_DEBUG_ATOMIC_SLEEP, and
CONFIG_KASAN.
Series based on kvmarm/fixes.
Thanks!
[1]: https://lore.kernel.org/kvmarm/20260916230337.4162485-1-weilin.chang@arm.com/
[2]: https://lore.kernel.org/kvmarm/20260911162203.1919330-1-maz@kernel.org/
Wei-Lin Chang (2):
KVM: arm64: ptdump: Check the page tables aren't freed when accessing
KVM: arm64: ptdump: Fix shadow ptdump sleep-in-atomic-context problem
arch/arm64/include/asm/kvm_host.h | 4 --
arch/arm64/include/asm/kvm_mmu.h | 6 +--
arch/arm64/kvm/nested.c | 9 ++--
arch/arm64/kvm/ptdump.c | 75 ++++++++++++++++++++-----------
4 files changed, 54 insertions(+), 40 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v4 1/2] KVM: arm64: ptdump: Check the page tables aren't freed when accessing
2026-09-24 9:49 [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Wei-Lin Chang
@ 2026-09-24 9:49 ` Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 2/2] KVM: arm64: ptdump: Fix shadow ptdump sleep-in-atomic-context problem Wei-Lin Chang
2026-09-24 20:41 ` [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Marc Zyngier
2 siblings, 0 replies; 4+ messages in thread
From: Wei-Lin Chang @ 2026-09-24 9:49 UTC (permalink / raw)
To: linux-arm-kernel, kvmarm, linux-kernel
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly,
Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Mark Rutland, Sebastian Ene, Vincent Donnefort,
Itaru Kitayama, Wei-Lin Chang, Sashiko AI, stable
An open debugfs file keeps the KVM structure alive, but does not prevent
mmu notifier release from freeing the stage-2 page tables when the VMM’s
address space is torn down. Therefore page tables belonging to the mmus
could have been freed when a thread opens or reads the ptdump files.
Take the mmu_lock and check mmu->pgt is still alive before accessing the
page tables.
Also for the ipa_range file and stage2_levels file, switch from keeping
the pointer to the page tables to keeping the pointer to the mmu.
Fixes: 7c4f73548ed1 ("KVM: arm64: Register ptdump with debugfs on guest creation")
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/kvmarm/20260623144054.B91D21F000E9@smtp.kernel.org/
Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
Tested-by: Vincent Donnefort <vdonnefort@google.com>
Tested-by: Itaru Kitayama <itaru.kitayama@fujitsu.com>
Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
Cc: stable@vger.kernel.org
---
arch/arm64/kvm/ptdump.c | 43 +++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
index 69899797dbad..b5a0584260e8 100644
--- a/arch/arm64/kvm/ptdump.c
+++ b/arch/arm64/kvm/ptdump.c
@@ -115,15 +115,28 @@ static int kvm_ptdump_build_levels(struct ptdump_pg_level *level, u32 start_lvl)
static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu *mmu)
{
+ struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
struct kvm_ptdump_guest_state *st;
- struct kvm_pgtable *pgtable = mmu->pgt;
+ s8 start_level;
int ret;
+ /*
+ * We only need the pgt start level to initialize the ptdump, get it
+ * while holding the mmu_lock. It's fine if the pgt gets freed
+ * afterwards, we'll check again when doing the actual dump.
+ */
+ scoped_guard(read_lock, &kvm->mmu_lock) {
+ if (mmu->pgt)
+ start_level = mmu->pgt->start_level;
+ else
+ return ERR_PTR(-ENOENT);
+ }
+
st = kzalloc_obj(struct kvm_ptdump_guest_state, GFP_KERNEL_ACCOUNT);
if (!st)
return ERR_PTR(-ENOMEM);
- ret = kvm_ptdump_build_levels(&st->level[0], pgtable->start_level);
+ ret = kvm_ptdump_build_levels(&st->level[0], start_level);
if (ret) {
kfree(st);
return ERR_PTR(ret);
@@ -149,6 +162,9 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
};
guard(write_lock)(&kvm->mmu_lock);
+ if (!mmu->pgt)
+ return 0;
+
st->parser_state = (struct ptdump_pg_state) {
.marker = &st->ipa_marker[0],
.end_address = BIT(mmu->pgt->ia_bits),
@@ -211,17 +227,27 @@ static const struct file_operations kvm_ptdump_guest_fops = {
static int kvm_pgtable_range_show(struct seq_file *m, void *unused)
{
- struct kvm_pgtable *pgtable = m->private;
+ struct kvm_s2_mmu *mmu = m->private;
+ struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+
+ guard(read_lock)(&kvm->mmu_lock);
+
+ if (mmu->pgt)
+ seq_printf(m, "%2u\n", mmu->pgt->ia_bits);
- seq_printf(m, "%2u\n", pgtable->ia_bits);
return 0;
}
static int kvm_pgtable_levels_show(struct seq_file *m, void *unused)
{
- struct kvm_pgtable *pgtable = m->private;
+ struct kvm_s2_mmu *mmu = m->private;
+ struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+
+ guard(read_lock)(&kvm->mmu_lock);
+
+ if (mmu->pgt)
+ seq_printf(m, "%1d\n", KVM_PGTABLE_MAX_LEVELS - mmu->pgt->start_level);
- seq_printf(m, "%1d\n", KVM_PGTABLE_MAX_LEVELS - pgtable->start_level);
return 0;
}
@@ -230,15 +256,12 @@ static int kvm_pgtable_debugfs_open(struct inode *m, struct file *file,
{
struct kvm_s2_mmu *mmu = m->i_private;
struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
- struct kvm_pgtable *pgtable;
int ret;
if (!kvm_get_kvm_safe(kvm))
return -ENOENT;
- pgtable = mmu->pgt;
-
- ret = single_open(file, show, pgtable);
+ ret = single_open(file, show, mmu);
if (ret < 0)
kvm_put_kvm(kvm);
return ret;
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v4 2/2] KVM: arm64: ptdump: Fix shadow ptdump sleep-in-atomic-context problem
2026-09-24 9:49 [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 1/2] KVM: arm64: ptdump: Check the page tables aren't freed when accessing Wei-Lin Chang
@ 2026-09-24 9:49 ` Wei-Lin Chang
2026-09-24 20:41 ` [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Marc Zyngier
2 siblings, 0 replies; 4+ messages in thread
From: Wei-Lin Chang @ 2026-09-24 9:49 UTC (permalink / raw)
To: linux-arm-kernel, kvmarm, linux-kernel
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly,
Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Mark Rutland, Sebastian Ene, Vincent Donnefort,
Itaru Kitayama, Wei-Lin Chang, stable
Previously we exposed shadow page tables by creating a debugfs ptdump
file whenever a nested mmu instance gets bound to a new context, and
deleting the debugfs file whose context was getting unbound.
This turned out to be buggy, as the instance<->context binding process
is done with the mmu_lock held, and debugfs creation/deletion can sleep.
Change the approach and create a shadow ptdump file for each nested mmu
instead during kvm_vcpu_init_nested(), in a sleepable context. The files
will be named nested_mmu<index>, and reading it will return the nested
context's VTTBR, VTCR, and s2 enabled or not before dumping the shadow
page tables, given the nested mmu is valid.
Fixes: 19e15dc73f0f ("KVM: arm64: nv: Expose shadow page tables in debugfs")
Reported-by: Itaru Kitayama <itaru.kitayama@fujitsu.com>
Closes: https://lore.kernel.org/kvmarm/aiuF0KSvvv-ZozI1@sm-arm-grace07/
Tested-by: Itaru Kitayama <itaru.kitayama@fujitsu.com>
Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
Cc: stable@vger.kernel.org
---
arch/arm64/include/asm/kvm_host.h | 4 ----
arch/arm64/include/asm/kvm_mmu.h | 6 ++----
arch/arm64/kvm/nested.c | 9 ++++-----
arch/arm64/kvm/ptdump.c | 32 +++++++++++++++----------------
4 files changed, 21 insertions(+), 30 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index cd9b9d2462f9..db177af9149f 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -217,10 +217,6 @@ struct kvm_s2_mmu {
*/
bool nested_stage2_enabled;
-#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
- struct dentry *shadow_pt_debugfs_dentry;
-#endif
-
/*
* true when this MMU needs to be unmapped before being used for a new
* purpose.
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 6eae7e7e2a68..ac2b0637692a 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -392,12 +392,10 @@ static inline bool kvm_supports_cacheable_pfnmap(void)
#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
void kvm_s2_ptdump_create_debugfs(struct kvm *kvm);
-void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu);
-void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu);
+void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu, int idx);
#else
static inline void kvm_s2_ptdump_create_debugfs(struct kvm *kvm) {}
-static inline void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu) {}
-static inline void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu) {}
+static inline void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu, int idx) {}
#endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */
#endif /* __ASSEMBLER__ */
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index b191365d97cc..41253fe45941 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -124,6 +124,9 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
return ret;
}
+ for (i = 0; i < S2_MMU_PER_VCPU; i++)
+ kvm_nested_s2_ptdump_create_debugfs(&tmp[i], i + kvm->arch.nested_mmus_size);
+
guard(write_lock)(&kvm->mmu_lock);
for (i = 0; i < S2_MMU_PER_VCPU; i++)
@@ -837,10 +840,8 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
kvm->arch.nested_mmus_next = (i + 1) % kvm->arch.nested_mmus_size;
/* Make sure we don't forget to do the laundry */
- if (kvm_s2_mmu_valid(s2_mmu)) {
- kvm_nested_s2_ptdump_remove_debugfs(s2_mmu);
+ if (kvm_s2_mmu_valid(s2_mmu))
s2_mmu->pending_unmap = true;
- }
/*
* The virtual VMID (modulo CnP) will be used as a key when matching
@@ -854,8 +855,6 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
s2_mmu->tlb_vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2);
s2_mmu->nested_stage2_enabled = vcpu_read_sys_reg(vcpu, HCR_EL2) & HCR_VM;
- kvm_nested_s2_ptdump_create_debugfs(s2_mmu);
-
out:
atomic_inc(&s2_mmu->refcnt);
diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
index b5a0584260e8..7829fe376385 100644
--- a/arch/arm64/kvm/ptdump.c
+++ b/arch/arm64/kvm/ptdump.c
@@ -17,7 +17,7 @@
#define MARKERS_LEN 2
#define KVM_PGTABLE_MAX_LEVELS (KVM_PGTABLE_LAST_LEVEL + 1)
-#define S2FNAMESZ sizeof("0x0123456789abcdef-0x0123456789abcdef-s2-disabled")
+#define S2FNAMESZ sizeof("nested_mmu0000")
struct kvm_ptdump_guest_state {
struct kvm_s2_mmu *mmu;
@@ -173,6 +173,15 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
.seq = m,
};
+ if (kvm_is_nested_s2_mmu(kvm, mmu)) {
+ if (kvm_s2_mmu_valid(mmu))
+ seq_printf(m, "VTCR: 0x%016llx VTTBR: 0x%016llx s2: %s\n",
+ mmu->tlb_vtcr, mmu->tlb_vttbr,
+ mmu->nested_stage2_enabled ? "enabled" : "disabled");
+ else
+ return 0;
+ }
+
ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
if (ret)
return ret;
@@ -299,26 +308,15 @@ static const struct file_operations kvm_pgtable_levels_fops = {
.release = kvm_pgtable_debugfs_close,
};
-void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu)
+void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu, int idx)
{
- struct dentry *dent;
char file_name[S2FNAMESZ];
- snprintf(file_name, sizeof(file_name), "0x%016llx-0x%016llx-s2-%sabled",
- mmu->tlb_vttbr,
- mmu->tlb_vtcr,
- mmu->nested_stage2_enabled ? "en" : "dis");
-
- dent = debugfs_create_file(file_name, 0400,
- mmu->arch->debugfs_nv_dentry, mmu,
- &kvm_ptdump_guest_fops);
+ snprintf(file_name, sizeof(file_name), "nested_mmu%d", idx);
- mmu->shadow_pt_debugfs_dentry = dent;
-}
-
-void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu)
-{
- debugfs_remove(mmu->shadow_pt_debugfs_dentry);
+ debugfs_create_file(file_name, 0400,
+ mmu->arch->debugfs_nv_dentry, mmu,
+ &kvm_ptdump_guest_fops);
}
void kvm_s2_ptdump_create_debugfs(struct kvm *kvm)
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes
2026-09-24 9:49 [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 1/2] KVM: arm64: ptdump: Check the page tables aren't freed when accessing Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 2/2] KVM: arm64: ptdump: Fix shadow ptdump sleep-in-atomic-context problem Wei-Lin Chang
@ 2026-09-24 20:41 ` Marc Zyngier
2 siblings, 0 replies; 4+ messages in thread
From: Marc Zyngier @ 2026-09-24 20:41 UTC (permalink / raw)
To: linux-arm-kernel, kvmarm, linux-kernel, Wei-Lin Chang
Cc: Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
Mark Rutland, Sebastian Ene, Vincent Donnefort, Itaru Kitayama
On Thu, 24 Sep 2026 10:49:49 +0100, Wei-Lin Chang wrote:
> This is v4 of fixing the sleep-in-atomic problem of the shadow ptdump.
> This version relaxes a few write_lock to read_lock, and collected tags.
> Please see the details below.
>
> * Changes from v3 [1]:
>
> - Take read_lock instead of write_lock when getting pgt metadata.
> (Vincent)
>
> [...]
Applied to next, thanks!
[1/2] KVM: arm64: ptdump: Check the page tables aren't freed when accessing
commit: 7d7199bdf77a3a6ed74d0037133f435a7febe97c
[2/2] KVM: arm64: ptdump: Fix shadow ptdump sleep-in-atomic-context problem
commit: b1cc219eb21ab5f31fe4caa32be8057159e42db5
Cheers,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-24 20:41 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 9:49 [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 1/2] KVM: arm64: ptdump: Check the page tables aren't freed when accessing Wei-Lin Chang
2026-09-24 9:49 ` [PATCH v4 2/2] KVM: arm64: ptdump: Fix shadow ptdump sleep-in-atomic-context problem Wei-Lin Chang
2026-09-24 20:41 ` [PATCH v4 0/2] KVM: arm64: ptdump: Shadow ptdump fixes Marc Zyngier
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®