* [PATCH v4 1/6] KVM: arm64: Enable eager hugepage splitting if HDBSS is available
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
@ 2026-07-09 10:40 ` Tian Zheng
2026-07-09 10:40 ` [PATCH v4 2/6] KVM: arm64: Add support for FEAT_HDBSS Tian Zheng
` (5 subsequent siblings)
6 siblings, 0 replies; 32+ messages in thread
From: Tian Zheng @ 2026-07-09 10:40 UTC (permalink / raw)
To: maz, oupton, catalin.marinas, will, zhengtian10
Cc: yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose, leo.bras
From: Leonardo Bras <leo.bras@arm.com>
FEAT_HDBSS speeds up guest memory dirty tracking by avoiding a page fault
and saving the entry in a tracking structure.
That may be a problem when we have guest memory backed by hugepages or
transparent huge pages, as it's not possible to do on-demand hugepage
splitting, relying only on eager hugepage splitting.
So, at stage2 initialization, enable eager hugepage splitting with
chunk = 256K * PAGE_SIZE if the system supports HDBSS.
Signed-off-by: Leonardo Bras <leo.bras@arm.com>
---
arch/arm64/kvm/mmu.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 6c941aaa10c6..e086c01a9325 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1027,8 +1027,12 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
for_each_possible_cpu(cpu)
*per_cpu_ptr(mmu->last_vcpu_ran, cpu) = -1;
- /* The eager page splitting is disabled by default */
- mmu->split_page_chunk_size = KVM_ARM_EAGER_SPLIT_CHUNK_SIZE_DEFAULT;
+ /* The eager page splitting is disabled by default if system has no HDBSS */
+ if (system_supports_hdbss())
+ mmu->split_page_chunk_size = 256 * 1024 * PAGE_SIZE;
+ else
+ mmu->split_page_chunk_size = KVM_ARM_EAGER_SPLIT_CHUNK_SIZE_DEFAULT;
+
mmu->split_page_cache.gfp_zero = __GFP_ZERO;
mmu->pgd_phys = __pa(pgt->pgd);
--
2.33.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v4 2/6] KVM: arm64: Add support for FEAT_HDBSS
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
2026-07-09 10:40 ` [PATCH v4 1/6] KVM: arm64: Enable eager hugepage splitting if HDBSS is available Tian Zheng
@ 2026-07-09 10:40 ` Tian Zheng
2026-07-09 10:40 ` [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking Tian Zheng
` (4 subsequent siblings)
6 siblings, 0 replies; 32+ messages in thread
From: Tian Zheng @ 2026-07-09 10:40 UTC (permalink / raw)
To: maz, oupton, catalin.marinas, will, zhengtian10
Cc: yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose, leo.bras
Armv9.5 introduces the Hardware Dirty Bit State Structure (HDBSS)
feature, indicated by ID_AA64MMFR1_EL1.HAFDBS == 0b0100.
Add CPU capability detection for HDBSS. The feature requires VHE mode
and is detected via ID_AA64MMFR1_EL1.HAFDBS.HDBSS field. A helper
function system_supports_hdbss() is provided to check hardware support.
Suggested-by: Zhou Wang <wangzhou1@hisilicon.com>
Co-developed-by: Eillon <yezhenyu2@huawei.com>
Signed-off-by: Eillon <yezhenyu2@huawei.com>
Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
---
arch/arm64/include/asm/cpufeature.h | 5 +++++
arch/arm64/kernel/cpufeature.c | 12 ++++++++++++
arch/arm64/tools/cpucaps | 1 +
3 files changed, 18 insertions(+)
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index a57870fa96db..bdfab086fd94 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -863,6 +863,11 @@ static inline bool system_supports_haft(void)
return cpus_have_final_cap(ARM64_HAFT);
}
+static inline bool system_supports_hdbss(void)
+{
+ return cpus_have_final_cap(ARM64_HAS_HDBSS);
+}
+
static __always_inline bool system_supports_mpam(void)
{
return alternative_has_cap_unlikely(ARM64_MPAM);
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9a22df0c5120..aa327eebaf1c 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2131,6 +2131,11 @@ static bool hvhe_possible(const struct arm64_cpu_capabilities *entry,
return arm64_test_sw_feature_override(ARM64_SW_FEATURE_OVERRIDE_HVHE);
}
+static bool has_vhe_hdbss(const struct arm64_cpu_capabilities *entry, int cope)
+{
+ return is_kernel_in_hyp_mode() && has_cpuid_feature(entry, cope);
+}
+
bool cpu_supports_bbml2_noabort(void)
{
/*
@@ -2781,6 +2786,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
ARM64_CPUID_FIELDS(ID_AA64MMFR1_EL1, HAFDBS, HAFT)
},
#endif
+ {
+ .desc = "Hardware Dirty state tracking structure (HDBSS)",
+ .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .capability = ARM64_HAS_HDBSS,
+ .matches = has_vhe_hdbss,
+ ARM64_CPUID_FIELDS(ID_AA64MMFR1_EL1, HAFDBS, HDBSS)
+ },
{
.desc = "CRC32 instructions",
.capability = ARM64_HAS_CRC32,
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 9b85a84f6fd4..a87706c9d160 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -69,6 +69,7 @@ HAS_VA52
HAS_VIRT_HOST_EXTN
HAS_WFXT
HAS_XNX
+HAS_HDBSS
HAFT
HW_DBM
KVM_HVHE
--
2.33.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
2026-07-09 10:40 ` [PATCH v4 1/6] KVM: arm64: Enable eager hugepage splitting if HDBSS is available Tian Zheng
2026-07-09 10:40 ` [PATCH v4 2/6] KVM: arm64: Add support for FEAT_HDBSS Tian Zheng
@ 2026-07-09 10:40 ` Tian Zheng
2026-07-13 11:17 ` Leonardo Bras
2026-07-09 10:40 ` [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management Tian Zheng
` (3 subsequent siblings)
6 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-09 10:40 UTC (permalink / raw)
To: maz, oupton, catalin.marinas, will, zhengtian10
Cc: yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose, leo.bras
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 5278 bytes --]
The DBM (Dirty Bit Modifier) attribute, introduced in ARMv8.1, enables
hardware to automatically promote write-clean pages to write-dirty. This
prevents the guest from being trapped in EL2 due to missing write
permissions.
In this design, DBM is controlled by the page-table level flag
KVM_PGTABLE_S2_DBM rather than per-PTE software flags. DBM is
automatically set for writable non-device pages when the page-table has
KVM_PGTABLE_S2_DBM flag, which is determined at MMU init time based on
hardware capability.
The DBM bit is set in stage2_set_prot_attr() for initial mappings and
hugepage splitting, and directly manipulated in
kvm_pgtable_stage2_relax_perms() when removing write-protection. On
W->RO downgrade, DBM is cleared to prevent hardware from silently
upgrading RO+DBM back to W+dirty, which would bypass KVM's write
tracking.
kvm_pgtable_stage2_pte_prot() does not extract the DBM bit back into
enum kvm_pgtable_prot because DBM is a page-table policy determined by
pgt->flags, not a per-PTE property. Callers should check
pgt->flags & KVM_PGTABLE_S2_DBM instead.
This ensures DBM is consistently applied across all PTEs, including
during hugepage splitting where child PTEs inherit DBM from the parent
block entry via the pgt->flags mechanism.
Safety: DBM bit is only interpreted by hardware when VTCR_EL2.HD=1.
When HDBSS is not enabled (HD=0), ARM architecture guarantees hardware
completely ignores DBM bit in PTEs.
Co-developed-by: Eillon <yezhenyu2@huawei.com>
Signed-off-by: Eillon <yezhenyu2@huawei.com>
Co-developed-by: Leonardo Bras <leo.bras@arm.com>
Signed-off-by: Leonardo Bras <leo.bras@arm.com>
Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
---
arch/arm64/include/asm/kvm_pgtable.h | 4 ++++
arch/arm64/kvm/hyp/pgtable.c | 35 ++++++++++++++++++++++++++--
arch/arm64/kvm/mmu.c | 3 +++
3 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
index 41a8687938eb..5e0fac4bfa53 100644
--- a/arch/arm64/include/asm/kvm_pgtable.h
+++ b/arch/arm64/include/asm/kvm_pgtable.h
@@ -93,6 +93,8 @@ typedef u64 kvm_pte_t;
#define KVM_PTE_LEAF_ATTR_HI_S2_XN GENMASK(54, 53)
+#define KVM_PTE_LEAF_ATTR_HI_S2_DBM BIT(51)
+
#define KVM_PTE_LEAF_ATTR_HI_S1_GP BIT(50)
#define KVM_PTE_LEAF_ATTR_S2_PERMS (KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | \
@@ -249,10 +251,12 @@ struct kvm_pgtable_mm_ops {
* enum kvm_pgtable_stage2_flags - Stage-2 page-table flags.
* @KVM_PGTABLE_S2_IDMAP: Only use identity mappings.
* @KVM_PGTABLE_S2_AS_S1: Final memory attributes are that of Stage-1.
+ * @KVM_PGTABLE_S2_DBM: Hardware-managed DBM for writable pages.
*/
enum kvm_pgtable_stage2_flags {
KVM_PGTABLE_S2_IDMAP = BIT(0),
KVM_PGTABLE_S2_AS_S1 = BIT(1),
+ KVM_PGTABLE_S2_DBM = BIT(2),
};
/**
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index 91a7dfad6686..21ec456ecc41 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -731,9 +731,23 @@ static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot p
if (prot & KVM_PGTABLE_PROT_R)
attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
- if (prot & KVM_PGTABLE_PROT_W)
+ if (prot & KVM_PGTABLE_PROT_W) {
attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
+ /*
+ * Set DBM bit for writable non-device pages if the page-table
+ * has KVM_PGTABLE_S2_DBM flag (system supports HDBSS).
+ *
+ * For stage 2 translations using Indirect permissions, if the
+ * Effective value of VTCR_EL2.HD is 0, then dirty state is
+ * managed by software. Hardware only updates the dirty state
+ * when VTCR_EL2.HD=1 (HDBSS enabled).
+ */
+ if ((pgt->flags & KVM_PGTABLE_S2_DBM) &&
+ !(prot & KVM_PGTABLE_PROT_DEVICE))
+ attr |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
+ }
+
if (!kvm_lpa2_is_enabled())
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S2_SH, sh);
@@ -1367,9 +1381,26 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
if (prot & KVM_PGTABLE_PROT_R)
set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
- if (prot & KVM_PGTABLE_PROT_W)
+ if (prot & KVM_PGTABLE_PROT_W) {
set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
+ /*
+ * No DEVICE filter needed here: relax_perms is only called
+ * on FSC_PERM faults. Device pages always get full RW from
+ * initial mapping and are never write-protected during
+ * migration, so they never trigger a permission fault.
+ */
+ if (pgt->flags & KVM_PGTABLE_S2_DBM)
+ set |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
+ } else {
+ /*
+ * Clear DBM on W→RO downgrade to prevent hardware from
+ * silently upgrading RO+DBM back to W+dirty, which would
+ * bypass KVM's write tracking and cause data corruption.
+ */
+ clr |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
+ }
+
ret = stage2_set_xn_attr(prot, &xn);
if (ret)
return ret;
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index e086c01a9325..346efed6e605 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1014,6 +1014,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
if (err)
goto out_free_pgtable;
+ if (system_supports_hdbss())
+ pgt->flags |= KVM_PGTABLE_S2_DBM;
+
mmu->pgt = pgt;
if (is_protected_kvm_enabled())
return 0;
--
2.33.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking
2026-07-09 10:40 ` [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking Tian Zheng
@ 2026-07-13 11:17 ` Leonardo Bras
2026-07-14 1:14 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-13 11:17 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Thu, Jul 09, 2026 at 06:40:23PM +0800, Tian Zheng wrote:
> The DBM (Dirty Bit Modifier) attribute, introduced in ARMv8.1, enables
> hardware to automatically promote write-clean pages to write-dirty. This
> prevents the guest from being trapped in EL2 due to missing write
> permissions.
>
> In this design, DBM is controlled by the page-table level flag
> KVM_PGTABLE_S2_DBM rather than per-PTE software flags. DBM is
> automatically set for writable non-device pages when the page-table has
> KVM_PGTABLE_S2_DBM flag, which is determined at MMU init time based on
> hardware capability.
>
> The DBM bit is set in stage2_set_prot_attr() for initial mappings and
> hugepage splitting, and directly manipulated in
> kvm_pgtable_stage2_relax_perms() when removing write-protection. On
> W->RO downgrade, DBM is cleared to prevent hardware from silently
> upgrading RO+DBM back to W+dirty, which would bypass KVM's write
> tracking.
>
> kvm_pgtable_stage2_pte_prot() does not extract the DBM bit back into
> enum kvm_pgtable_prot because DBM is a page-table policy determined by
> pgt->flags, not a per-PTE property. Callers should check
> pgt->flags & KVM_PGTABLE_S2_DBM instead.
>
> This ensures DBM is consistently applied across all PTEs, including
> during hugepage splitting where child PTEs inherit DBM from the parent
> block entry via the pgt->flags mechanism.
>
> Safety: DBM bit is only interpreted by hardware when VTCR_EL2.HD=1.
> When HDBSS is not enabled (HD=0), ARM architecture guarantees hardware
> completely ignores DBM bit in PTEs.
>
> Co-developed-by: Eillon <yezhenyu2@huawei.com>
> Signed-off-by: Eillon <yezhenyu2@huawei.com>
> Co-developed-by: Leonardo Bras <leo.bras@arm.com>
> Signed-off-by: Leonardo Bras <leo.bras@arm.com>
Hello Tian,
Have you added the above tags due to this patch being based on the below?
https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
Thanks!
Leo
> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> ---
> arch/arm64/include/asm/kvm_pgtable.h | 4 ++++
> arch/arm64/kvm/hyp/pgtable.c | 35 ++++++++++++++++++++++++++--
> arch/arm64/kvm/mmu.c | 3 +++
> 3 files changed, 40 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
> index 41a8687938eb..5e0fac4bfa53 100644
> --- a/arch/arm64/include/asm/kvm_pgtable.h
> +++ b/arch/arm64/include/asm/kvm_pgtable.h
> @@ -93,6 +93,8 @@ typedef u64 kvm_pte_t;
>
> #define KVM_PTE_LEAF_ATTR_HI_S2_XN GENMASK(54, 53)
>
> +#define KVM_PTE_LEAF_ATTR_HI_S2_DBM BIT(51)
> +
> #define KVM_PTE_LEAF_ATTR_HI_S1_GP BIT(50)
>
> #define KVM_PTE_LEAF_ATTR_S2_PERMS (KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | \
> @@ -249,10 +251,12 @@ struct kvm_pgtable_mm_ops {
> * enum kvm_pgtable_stage2_flags - Stage-2 page-table flags.
> * @KVM_PGTABLE_S2_IDMAP: Only use identity mappings.
> * @KVM_PGTABLE_S2_AS_S1: Final memory attributes are that of Stage-1.
> + * @KVM_PGTABLE_S2_DBM: Hardware-managed DBM for writable pages.
> */
> enum kvm_pgtable_stage2_flags {
> KVM_PGTABLE_S2_IDMAP = BIT(0),
> KVM_PGTABLE_S2_AS_S1 = BIT(1),
> + KVM_PGTABLE_S2_DBM = BIT(2),
> };
>
> /**
> diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
> index 91a7dfad6686..21ec456ecc41 100644
> --- a/arch/arm64/kvm/hyp/pgtable.c
> +++ b/arch/arm64/kvm/hyp/pgtable.c
> @@ -731,9 +731,23 @@ static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot p
> if (prot & KVM_PGTABLE_PROT_R)
> attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
>
> - if (prot & KVM_PGTABLE_PROT_W)
> + if (prot & KVM_PGTABLE_PROT_W) {
> attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
>
> + /*
> + * Set DBM bit for writable non-device pages if the page-table
> + * has KVM_PGTABLE_S2_DBM flag (system supports HDBSS).
> + *
> + * For stage 2 translations using Indirect permissions, if the
> + * Effective value of VTCR_EL2.HD is 0, then dirty state is
> + * managed by software. Hardware only updates the dirty state
> + * when VTCR_EL2.HD=1 (HDBSS enabled).
> + */
> + if ((pgt->flags & KVM_PGTABLE_S2_DBM) &&
> + !(prot & KVM_PGTABLE_PROT_DEVICE))
> + attr |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
> + }
> +
> if (!kvm_lpa2_is_enabled())
> attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S2_SH, sh);
>
> @@ -1367,9 +1381,26 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
> if (prot & KVM_PGTABLE_PROT_R)
> set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
>
> - if (prot & KVM_PGTABLE_PROT_W)
> + if (prot & KVM_PGTABLE_PROT_W) {
> set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
>
> + /*
> + * No DEVICE filter needed here: relax_perms is only called
> + * on FSC_PERM faults. Device pages always get full RW from
> + * initial mapping and are never write-protected during
> + * migration, so they never trigger a permission fault.
> + */
> + if (pgt->flags & KVM_PGTABLE_S2_DBM)
> + set |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
> + } else {
> + /*
> + * Clear DBM on W→RO downgrade to prevent hardware from
> + * silently upgrading RO+DBM back to W+dirty, which would
> + * bypass KVM's write tracking and cause data corruption.
> + */
> + clr |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
> + }
> +
> ret = stage2_set_xn_attr(prot, &xn);
> if (ret)
> return ret;
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index e086c01a9325..346efed6e605 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1014,6 +1014,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
> if (err)
> goto out_free_pgtable;
>
> + if (system_supports_hdbss())
> + pgt->flags |= KVM_PGTABLE_S2_DBM;
> +
> mmu->pgt = pgt;
> if (is_protected_kvm_enabled())
> return 0;
> --
> 2.33.0
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking
2026-07-13 11:17 ` Leonardo Bras
@ 2026-07-14 1:14 ` Tian Zheng
2026-07-14 7:23 ` Marc Zyngier
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 1:14 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/13/2026 7:17 PM, Leonardo Bras wrote:
> On Thu, Jul 09, 2026 at 06:40:23PM +0800, Tian Zheng wrote:
>> The DBM (Dirty Bit Modifier) attribute, introduced in ARMv8.1, enables
>> hardware to automatically promote write-clean pages to write-dirty. This
>> prevents the guest from being trapped in EL2 due to missing write
>> permissions.
>>
>> In this design, DBM is controlled by the page-table level flag
>> KVM_PGTABLE_S2_DBM rather than per-PTE software flags. DBM is
>> automatically set for writable non-device pages when the page-table has
>> KVM_PGTABLE_S2_DBM flag, which is determined at MMU init time based on
>> hardware capability.
>>
>> The DBM bit is set in stage2_set_prot_attr() for initial mappings and
>> hugepage splitting, and directly manipulated in
>> kvm_pgtable_stage2_relax_perms() when removing write-protection. On
>> W->RO downgrade, DBM is cleared to prevent hardware from silently
>> upgrading RO+DBM back to W+dirty, which would bypass KVM's write
>> tracking.
>>
>> kvm_pgtable_stage2_pte_prot() does not extract the DBM bit back into
>> enum kvm_pgtable_prot because DBM is a page-table policy determined by
>> pgt->flags, not a per-PTE property. Callers should check
>> pgt->flags & KVM_PGTABLE_S2_DBM instead.
>>
>> This ensures DBM is consistently applied across all PTEs, including
>> during hugepage splitting where child PTEs inherit DBM from the parent
>> block entry via the pgt->flags mechanism.
>>
>> Safety: DBM bit is only interpreted by hardware when VTCR_EL2.HD=1.
>> When HDBSS is not enabled (HD=0), ARM architecture guarantees hardware
>> completely ignores DBM bit in PTEs.
>>
>> Co-developed-by: Eillon <yezhenyu2@huawei.com>
>> Signed-off-by: Eillon <yezhenyu2@huawei.com>
>> Co-developed-by: Leonardo Bras <leo.bras@arm.com>
>> Signed-off-by: Leonardo Bras <leo.bras@arm.com>
> Hello Tian,
>
> Have you added the above tags due to this patch being based on the below?
> https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
>
> Thanks!
> Leo
Hi Leo,
Yes, I added your Signed-off-by because the DBM-related code in this patch
is based on your implementation in:
https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
Thanks!
Tian
>
>> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
>> ---
>> arch/arm64/include/asm/kvm_pgtable.h | 4 ++++
>> arch/arm64/kvm/hyp/pgtable.c | 35 ++++++++++++++++++++++++++--
>> arch/arm64/kvm/mmu.c | 3 +++
>> 3 files changed, 40 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
>> index 41a8687938eb..5e0fac4bfa53 100644
>> --- a/arch/arm64/include/asm/kvm_pgtable.h
>> +++ b/arch/arm64/include/asm/kvm_pgtable.h
>> @@ -93,6 +93,8 @@ typedef u64 kvm_pte_t;
>>
>> #define KVM_PTE_LEAF_ATTR_HI_S2_XN GENMASK(54, 53)
>>
>> +#define KVM_PTE_LEAF_ATTR_HI_S2_DBM BIT(51)
>> +
>> #define KVM_PTE_LEAF_ATTR_HI_S1_GP BIT(50)
>>
>> #define KVM_PTE_LEAF_ATTR_S2_PERMS (KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | \
>> @@ -249,10 +251,12 @@ struct kvm_pgtable_mm_ops {
>> * enum kvm_pgtable_stage2_flags - Stage-2 page-table flags.
>> * @KVM_PGTABLE_S2_IDMAP: Only use identity mappings.
>> * @KVM_PGTABLE_S2_AS_S1: Final memory attributes are that of Stage-1.
>> + * @KVM_PGTABLE_S2_DBM: Hardware-managed DBM for writable pages.
>> */
>> enum kvm_pgtable_stage2_flags {
>> KVM_PGTABLE_S2_IDMAP = BIT(0),
>> KVM_PGTABLE_S2_AS_S1 = BIT(1),
>> + KVM_PGTABLE_S2_DBM = BIT(2),
>> };
>>
>> /**
>> diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
>> index 91a7dfad6686..21ec456ecc41 100644
>> --- a/arch/arm64/kvm/hyp/pgtable.c
>> +++ b/arch/arm64/kvm/hyp/pgtable.c
>> @@ -731,9 +731,23 @@ static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot p
>> if (prot & KVM_PGTABLE_PROT_R)
>> attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
>>
>> - if (prot & KVM_PGTABLE_PROT_W)
>> + if (prot & KVM_PGTABLE_PROT_W) {
>> attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
>>
>> + /*
>> + * Set DBM bit for writable non-device pages if the page-table
>> + * has KVM_PGTABLE_S2_DBM flag (system supports HDBSS).
>> + *
>> + * For stage 2 translations using Indirect permissions, if the
>> + * Effective value of VTCR_EL2.HD is 0, then dirty state is
>> + * managed by software. Hardware only updates the dirty state
>> + * when VTCR_EL2.HD=1 (HDBSS enabled).
>> + */
>> + if ((pgt->flags & KVM_PGTABLE_S2_DBM) &&
>> + !(prot & KVM_PGTABLE_PROT_DEVICE))
>> + attr |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
>> + }
>> +
>> if (!kvm_lpa2_is_enabled())
>> attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S2_SH, sh);
>>
>> @@ -1367,9 +1381,26 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
>> if (prot & KVM_PGTABLE_PROT_R)
>> set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
>>
>> - if (prot & KVM_PGTABLE_PROT_W)
>> + if (prot & KVM_PGTABLE_PROT_W) {
>> set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
>>
>> + /*
>> + * No DEVICE filter needed here: relax_perms is only called
>> + * on FSC_PERM faults. Device pages always get full RW from
>> + * initial mapping and are never write-protected during
>> + * migration, so they never trigger a permission fault.
>> + */
>> + if (pgt->flags & KVM_PGTABLE_S2_DBM)
>> + set |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
>> + } else {
>> + /*
>> + * Clear DBM on W→RO downgrade to prevent hardware from
>> + * silently upgrading RO+DBM back to W+dirty, which would
>> + * bypass KVM's write tracking and cause data corruption.
>> + */
>> + clr |= KVM_PTE_LEAF_ATTR_HI_S2_DBM;
>> + }
>> +
>> ret = stage2_set_xn_attr(prot, &xn);
>> if (ret)
>> return ret;
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index e086c01a9325..346efed6e605 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -1014,6 +1014,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
>> if (err)
>> goto out_free_pgtable;
>>
>> + if (system_supports_hdbss())
>> + pgt->flags |= KVM_PGTABLE_S2_DBM;
>> +
>> mmu->pgt = pgt;
>> if (is_protected_kvm_enabled())
>> return 0;
>> --
>> 2.33.0
>>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking
2026-07-14 1:14 ` Tian Zheng
@ 2026-07-14 7:23 ` Marc Zyngier
2026-07-14 7:44 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Marc Zyngier @ 2026-07-14 7:23 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, 14 Jul 2026 02:14:45 +0100,
Tian Zheng <zhengtian10@huawei.com> wrote:
>
>
> On 7/13/2026 7:17 PM, Leonardo Bras wrote:
> > On Thu, Jul 09, 2026 at 06:40:23PM +0800, Tian Zheng wrote:
> >> The DBM (Dirty Bit Modifier) attribute, introduced in ARMv8.1, enables
> >> hardware to automatically promote write-clean pages to write-dirty. This
> >> prevents the guest from being trapped in EL2 due to missing write
> >> permissions.
> >>
> >> In this design, DBM is controlled by the page-table level flag
> >> KVM_PGTABLE_S2_DBM rather than per-PTE software flags. DBM is
> >> automatically set for writable non-device pages when the page-table has
> >> KVM_PGTABLE_S2_DBM flag, which is determined at MMU init time based on
> >> hardware capability.
> >>
> >> The DBM bit is set in stage2_set_prot_attr() for initial mappings and
> >> hugepage splitting, and directly manipulated in
> >> kvm_pgtable_stage2_relax_perms() when removing write-protection. On
> >> W->RO downgrade, DBM is cleared to prevent hardware from silently
> >> upgrading RO+DBM back to W+dirty, which would bypass KVM's write
> >> tracking.
> >>
> >> kvm_pgtable_stage2_pte_prot() does not extract the DBM bit back into
> >> enum kvm_pgtable_prot because DBM is a page-table policy determined by
> >> pgt->flags, not a per-PTE property. Callers should check
> >> pgt->flags & KVM_PGTABLE_S2_DBM instead.
> >>
> >> This ensures DBM is consistently applied across all PTEs, including
> >> during hugepage splitting where child PTEs inherit DBM from the parent
> >> block entry via the pgt->flags mechanism.
> >>
> >> Safety: DBM bit is only interpreted by hardware when VTCR_EL2.HD=1.
> >> When HDBSS is not enabled (HD=0), ARM architecture guarantees hardware
> >> completely ignores DBM bit in PTEs.
> >>
> >> Co-developed-by: Eillon <yezhenyu2@huawei.com>
> >> Signed-off-by: Eillon <yezhenyu2@huawei.com>
> >> Co-developed-by: Leonardo Bras <leo.bras@arm.com>
> >> Signed-off-by: Leonardo Bras <leo.bras@arm.com>
> > Hello Tian,
> >
> > Have you added the above tags due to this patch being based on the below?
> > https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
> >
> > Thanks!
> > Leo
>
> Hi Leo,
>
>
> Yes, I added your Signed-off-by because the DBM-related code in this patch
You really can't do that. Only Leo can give his SoB, you can't forge
it yourself.
>
> is based on your implementation in:
>
> https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
Then take the patch as is, and add to it as a separate patch. Or work
out in private with Leo whether he's happy with a Co-dev. But never do
that unilaterally.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking
2026-07-14 7:23 ` Marc Zyngier
@ 2026-07-14 7:44 ` Tian Zheng
2026-07-14 10:20 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 7:44 UTC (permalink / raw)
To: Marc Zyngier
Cc: Leonardo Bras, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On 7/14/2026 3:23 PM, Marc Zyngier wrote:
> On Tue, 14 Jul 2026 02:14:45 +0100,
> Tian Zheng <zhengtian10@huawei.com> wrote:
>>
>> On 7/13/2026 7:17 PM, Leonardo Bras wrote:
>>> On Thu, Jul 09, 2026 at 06:40:23PM +0800, Tian Zheng wrote:
>>>> The DBM (Dirty Bit Modifier) attribute, introduced in ARMv8.1, enables
>>>> hardware to automatically promote write-clean pages to write-dirty. This
>>>> prevents the guest from being trapped in EL2 due to missing write
>>>> permissions.
>>>>
>>>> In this design, DBM is controlled by the page-table level flag
>>>> KVM_PGTABLE_S2_DBM rather than per-PTE software flags. DBM is
>>>> automatically set for writable non-device pages when the page-table has
>>>> KVM_PGTABLE_S2_DBM flag, which is determined at MMU init time based on
>>>> hardware capability.
>>>>
>>>> The DBM bit is set in stage2_set_prot_attr() for initial mappings and
>>>> hugepage splitting, and directly manipulated in
>>>> kvm_pgtable_stage2_relax_perms() when removing write-protection. On
>>>> W->RO downgrade, DBM is cleared to prevent hardware from silently
>>>> upgrading RO+DBM back to W+dirty, which would bypass KVM's write
>>>> tracking.
>>>>
>>>> kvm_pgtable_stage2_pte_prot() does not extract the DBM bit back into
>>>> enum kvm_pgtable_prot because DBM is a page-table policy determined by
>>>> pgt->flags, not a per-PTE property. Callers should check
>>>> pgt->flags & KVM_PGTABLE_S2_DBM instead.
>>>>
>>>> This ensures DBM is consistently applied across all PTEs, including
>>>> during hugepage splitting where child PTEs inherit DBM from the parent
>>>> block entry via the pgt->flags mechanism.
>>>>
>>>> Safety: DBM bit is only interpreted by hardware when VTCR_EL2.HD=1.
>>>> When HDBSS is not enabled (HD=0), ARM architecture guarantees hardware
>>>> completely ignores DBM bit in PTEs.
>>>>
>>>> Co-developed-by: Eillon <yezhenyu2@huawei.com>
>>>> Signed-off-by: Eillon <yezhenyu2@huawei.com>
>>>> Co-developed-by: Leonardo Bras <leo.bras@arm.com>
>>>> Signed-off-by: Leonardo Bras <leo.bras@arm.com>
>>> Hello Tian,
>>>
>>> Have you added the above tags due to this patch being based on the below?
>>> https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
>>>
>>> Thanks!
>>> Leo
>> Hi Leo,
>>
>>
>> Yes, I added your Signed-off-by because the DBM-related code in this patch
> You really can't do that. Only Leo can give his SoB, you can't forge
> it yourself.
>
>> is based on your implementation in:
>>
>> https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
> Then take the patch as is, and add to it as a separate patch. Or work
> out in private with Leo whether he's happy with a Co-dev. But never do
> that unilaterally.
>
> Thanks,
>
> M.
Got it, thanks for the clarification, Marc.
I'm sorry about that — I'll remove them in the next version.
Instead, I'll mention the reference in the commit message with a link to
Leo's original patch:
Based on Leonardo Bras's patch:
https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking
2026-07-14 7:44 ` Tian Zheng
@ 2026-07-14 10:20 ` Leonardo Bras
0 siblings, 0 replies; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 10:20 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, Marc Zyngier, oupton, catalin.marinas, will,
yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 03:44:37PM +0800, Tian Zheng wrote:
>
> On 7/14/2026 3:23 PM, Marc Zyngier wrote:
> > On Tue, 14 Jul 2026 02:14:45 +0100,
> > Tian Zheng <zhengtian10@huawei.com> wrote:
> > >
> > > On 7/13/2026 7:17 PM, Leonardo Bras wrote:
> > > > On Thu, Jul 09, 2026 at 06:40:23PM +0800, Tian Zheng wrote:
> > > > > The DBM (Dirty Bit Modifier) attribute, introduced in ARMv8.1, enables
> > > > > hardware to automatically promote write-clean pages to write-dirty. This
> > > > > prevents the guest from being trapped in EL2 due to missing write
> > > > > permissions.
> > > > >
> > > > > In this design, DBM is controlled by the page-table level flag
> > > > > KVM_PGTABLE_S2_DBM rather than per-PTE software flags. DBM is
> > > > > automatically set for writable non-device pages when the page-table has
> > > > > KVM_PGTABLE_S2_DBM flag, which is determined at MMU init time based on
> > > > > hardware capability.
> > > > >
> > > > > The DBM bit is set in stage2_set_prot_attr() for initial mappings and
> > > > > hugepage splitting, and directly manipulated in
> > > > > kvm_pgtable_stage2_relax_perms() when removing write-protection. On
> > > > > W->RO downgrade, DBM is cleared to prevent hardware from silently
> > > > > upgrading RO+DBM back to W+dirty, which would bypass KVM's write
> > > > > tracking.
> > > > >
> > > > > kvm_pgtable_stage2_pte_prot() does not extract the DBM bit back into
> > > > > enum kvm_pgtable_prot because DBM is a page-table policy determined by
> > > > > pgt->flags, not a per-PTE property. Callers should check
> > > > > pgt->flags & KVM_PGTABLE_S2_DBM instead.
> > > > >
> > > > > This ensures DBM is consistently applied across all PTEs, including
> > > > > during hugepage splitting where child PTEs inherit DBM from the parent
> > > > > block entry via the pgt->flags mechanism.
> > > > >
> > > > > Safety: DBM bit is only interpreted by hardware when VTCR_EL2.HD=1.
> > > > > When HDBSS is not enabled (HD=0), ARM architecture guarantees hardware
> > > > > completely ignores DBM bit in PTEs.
> > > > >
> > > > > Co-developed-by: Eillon <yezhenyu2@huawei.com>
> > > > > Signed-off-by: Eillon <yezhenyu2@huawei.com>
> > > > > Co-developed-by: Leonardo Bras <leo.bras@arm.com>
> > > > > Signed-off-by: Leonardo Bras <leo.bras@arm.com>
> > > > Hello Tian,
> > > >
> > > > Have you added the above tags due to this patch being based on the below?
> > > > https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
> > > >
> > > > Thanks!
> > > > Leo
> > > Hi Leo,
> > >
> > >
> > > Yes, I added your Signed-off-by because the DBM-related code in this patch
> > You really can't do that. Only Leo can give his SoB, you can't forge
> > it yourself.
> >
> > > is based on your implementation in:
> > >
> > > https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
> > Then take the patch as is, and add to it as a separate patch. Or work
> > out in private with Leo whether he's happy with a Co-dev. But never do
> > that unilaterally.
> >
> > Thanks,
> >
> > M.
> Got it, thanks for the clarification, Marc.
>
> I'm sorry about that — I'll remove them in the next version.
> Instead, I'll mention the reference in the commit message with a link to
> Leo's original patch:
>
> Based on Leonardo Bras's patch:
> https://lore.kernel.org/all/20260629111820.1873540-2-leo.bras@arm.com/
That works out for me!
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
` (2 preceding siblings ...)
2026-07-09 10:40 ` [PATCH v4 3/6] KVM: arm64: Add auto DBM support for hardware dirty tracking Tian Zheng
@ 2026-07-09 10:40 ` Tian Zheng
2026-07-13 13:39 ` Leonardo Bras
2026-07-09 10:40 ` [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush Tian Zheng
` (2 subsequent siblings)
6 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-09 10:40 UTC (permalink / raw)
To: maz, oupton, catalin.marinas, will, zhengtian10
Cc: yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose, leo.bras
From: eillon <yezhenyu2@huawei.com>
Add HDBSS (Hardware Dirty Bit State Structure) per-vCPU buffer
management including allocation, freeing, and loading of HDBSS
registers during vCPU load.
This patch creates the foundational infrastructure:
- struct vcpu_hdbss_state and enable_hdbss/hdbss_order in kvm_arch
- kvm_dirty_bit.h header with alloc/free declarations
- dirty_bit.c with alloc/free helpers
- __load_hdbss() in VHE switch for register loading
- vCPU create/destroy hooks for buffer lifecycle
- sysreg definitions for HDBSS register manipulation
- Makefile update for dirty_bit.o
Signed-off-by: Eillon <yezhenyu2@huawei.com>
Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
---
arch/arm64/include/asm/kvm_dirty_bit.h | 16 ++++++++
arch/arm64/include/asm/kvm_host.h | 13 +++++++
arch/arm64/include/asm/sysreg.h | 11 ++++++
arch/arm64/kvm/Makefile | 1 +
arch/arm64/kvm/arm.c | 7 ++++
arch/arm64/kvm/dirty_bit.c | 52 ++++++++++++++++++++++++++
arch/arm64/kvm/hyp/vhe/switch.c | 15 ++++++++
arch/arm64/kvm/mmu.c | 1 +
arch/arm64/kvm/reset.c | 4 ++
9 files changed, 120 insertions(+)
create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
create mode 100644 arch/arm64/kvm/dirty_bit.c
diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
new file mode 100644
index 000000000000..84b12f0a10af
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_dirty_bit.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ * Author: Leonardo Bras <leo.bras@arm.com>
+ */
+
+#ifndef __ARM64_KVM_DIRTY_BIT_H__
+#define __ARM64_KVM_DIRTY_BIT_H__
+
+#include <asm/kvm_pgtable.h>
+#include <asm/sysreg.h>
+
+int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
+void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
+
+#endif /* __ARM64_KVM_DIRTY_BIT_H__ */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5..c41ec6d9c45a 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -420,6 +420,10 @@ struct kvm_arch {
*/
struct kvm_protected_vm pkvm;
+ /* HDBSS: per-VM dirty tracking state */
+ bool enable_hdbss;
+ unsigned int hdbss_order;
+
#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
/* Nested virtualization info */
struct dentry *debugfs_nv_dentry;
@@ -838,6 +842,12 @@ struct vcpu_reset_state {
bool reset;
};
+struct vcpu_hdbss_state {
+ phys_addr_t base_phys; /* for memory free */
+ u64 hdbssbr_el2; /* load directly */
+ u64 hdbssprod_el2; /* save directly */
+};
+
struct vncr_tlb;
struct kvm_vcpu_arch {
@@ -945,6 +955,9 @@ struct kvm_vcpu_arch {
/* Hyp-readable copy of kvm_vcpu::pid */
pid_t pid;
+
+ /* HDBSS registers info */
+ struct vcpu_hdbss_state hdbss;
};
/*
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 7aa08d59d494..1354a58c3316 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -1039,6 +1039,17 @@
#define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | \
GCS_CAP_VALID_TOKEN)
+
+/*
+ * Definitions for the HDBSS feature
+ */
+#define HDBSS_MAX_ORDER HDBSSBR_EL2_SZ_2MB
+
+#define HDBSSBR_EL2(baddr, sz) (((baddr) & HDBSSBR_EL2_BADDR_MASK) | \
+ FIELD_PREP(HDBSSBR_EL2_SZ_MASK, sz))
+
+#define HDBSSPROD_IDX(prod) FIELD_GET(HDBSSPROD_EL2_INDEX_MASK, prod)
+
/*
* Definitions for GICv5 instructions
*/
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 59612d2f277c..ec2749af64fa 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -18,6 +18,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
guest.o debug.o reset.o sys_regs.o stacktrace.o \
vgic-sys-reg-v3.o fpsimd.o pkvm.o \
arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
+ dirty_bit.o \
vgic/vgic.o vgic/vgic-init.o \
vgic/vgic-irqfd.o vgic/vgic-v2.o \
vgic/vgic-v3.o vgic/vgic-v4.o \
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 50adfff75be8..bf6688245d83 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -38,6 +38,7 @@
#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_hyp.h>
+#include <asm/kvm_dirty_bit.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_nested.h>
#include <asm/kvm_pkvm.h>
@@ -565,6 +566,12 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
if (err)
kvm_vgic_vcpu_destroy(vcpu);
+ if (vcpu->kvm->arch.enable_hdbss) {
+ err = kvm_arm_vcpu_alloc_hdbss(vcpu, vcpu->kvm->arch.hdbss_order);
+ if (err)
+ kvm_vgic_vcpu_destroy(vcpu);
+ }
+
return err;
}
diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
new file mode 100644
index 000000000000..6c7a6ef66b5a
--- /dev/null
+++ b/arch/arm64/kvm/dirty_bit.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ * Author: Leonardo Bras <leo.bras@arm.com>
+ */
+
+#include <asm/kvm_dirty_bit.h>
+#include <asm/kvm_mmu.h>
+#include <asm/sysreg.h>
+#include <linux/gfp.h>
+#include <linux/kconfig.h>
+#include <linux/mm.h>
+
+int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order)
+{
+ struct page *hdbss_pg = NULL;
+
+ if (vcpu->arch.hdbss.hdbssbr_el2 || !system_supports_hdbss())
+ return 0;
+
+ if (order > HDBSS_MAX_ORDER)
+ return -EINVAL;
+
+ hdbss_pg = alloc_pages(GFP_KERNEL_ACCOUNT, order);
+ if (!hdbss_pg)
+ return -ENOMEM;
+
+ vcpu->arch.hdbss = (struct vcpu_hdbss_state) {
+ .base_phys = page_to_phys(hdbss_pg),
+ .hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg), order),
+ .hdbssprod_el2 = 0,
+ };
+
+ return 0;
+}
+
+void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
+{
+ struct page *hdbss_pg;
+
+ if (!vcpu->arch.hdbss.hdbssbr_el2) {
+ return;
+ }
+
+ hdbss_pg = phys_to_page(vcpu->arch.hdbss.base_phys);
+ if (hdbss_pg)
+ __free_pages(hdbss_pg,
+ FIELD_GET(HDBSSBR_EL2_SZ_MASK,
+ vcpu->arch.hdbss.hdbssbr_el2));
+
+ vcpu->arch.hdbss.hdbssbr_el2 = 0;
+}
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index bbe9cebd3d9d..fe72944bfd3d 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -22,6 +22,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_hyp.h>
#include <asm/kvm_mmu.h>
+#include <asm/kvm_dirty_bit.h>
#include <asm/fpsimd.h>
#include <asm/debug-monitors.h>
#include <asm/processor.h>
@@ -213,6 +214,19 @@ static void __vcpu_put_deactivate_traps(struct kvm_vcpu *vcpu)
local_irq_restore(flags);
}
+static void __load_hdbss(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = vcpu->kvm;
+
+ if (!kvm->arch.enable_hdbss)
+ return;
+
+ write_sysreg_s(vcpu->arch.hdbss.hdbssbr_el2, SYS_HDBSSBR_EL2);
+ write_sysreg_s(vcpu->arch.hdbss.hdbssprod_el2, SYS_HDBSSPROD_EL2);
+
+ isb();
+}
+
void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
{
host_data_ptr(host_ctxt)->__hyp_running_vcpu = vcpu;
@@ -220,6 +234,7 @@ void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
__vcpu_load_switch_sysregs(vcpu);
__vcpu_load_activate_traps(vcpu);
__load_stage2(vcpu->arch.hw_mmu);
+ __load_hdbss(vcpu);
}
void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 346efed6e605..83251d95bf3f 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -16,6 +16,7 @@
#include <asm/cacheflush.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_mmu.h>
+#include <asm/kvm_dirty_bit.h>
#include <asm/kvm_pgtable.h>
#include <asm/kvm_pkvm.h>
#include <asm/kvm_asm.h>
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index b963fd975aac..d8104bcbd9ff 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -27,6 +27,7 @@
#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
+#include <asm/kvm_dirty_bit.h>
#include <asm/kvm_nested.h>
#include <asm/virt.h>
@@ -161,6 +162,9 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
kfree(vcpu->arch.vncr_tlb);
kfree(vcpu->arch.ccsidr);
+
+ if (vcpu->kvm->arch.enable_hdbss)
+ kvm_arm_vcpu_free_hdbss(vcpu);
}
static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
--
2.33.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management
2026-07-09 10:40 ` [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management Tian Zheng
@ 2026-07-13 13:39 ` Leonardo Bras
2026-07-14 7:15 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-13 13:39 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Thu, Jul 09, 2026 at 06:40:24PM +0800, Tian Zheng wrote:
> From: eillon <yezhenyu2@huawei.com>
>
> Add HDBSS (Hardware Dirty Bit State Structure) per-vCPU buffer
> management including allocation, freeing, and loading of HDBSS
> registers during vCPU load.
>
> This patch creates the foundational infrastructure:
> - struct vcpu_hdbss_state and enable_hdbss/hdbss_order in kvm_arch
> - kvm_dirty_bit.h header with alloc/free declarations
> - dirty_bit.c with alloc/free helpers
> - __load_hdbss() in VHE switch for register loading
> - vCPU create/destroy hooks for buffer lifecycle
> - sysreg definitions for HDBSS register manipulation
> - Makefile update for dirty_bit.o
>
> Signed-off-by: Eillon <yezhenyu2@huawei.com>
> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> ---
> arch/arm64/include/asm/kvm_dirty_bit.h | 16 ++++++++
> arch/arm64/include/asm/kvm_host.h | 13 +++++++
> arch/arm64/include/asm/sysreg.h | 11 ++++++
> arch/arm64/kvm/Makefile | 1 +
> arch/arm64/kvm/arm.c | 7 ++++
> arch/arm64/kvm/dirty_bit.c | 52 ++++++++++++++++++++++++++
> arch/arm64/kvm/hyp/vhe/switch.c | 15 ++++++++
> arch/arm64/kvm/mmu.c | 1 +
> arch/arm64/kvm/reset.c | 4 ++
> 9 files changed, 120 insertions(+)
> create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
> create mode 100644 arch/arm64/kvm/dirty_bit.c
>
> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> new file mode 100644
> index 000000000000..84b12f0a10af
> --- /dev/null
> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2026 ARM Ltd.
> + * Author: Leonardo Bras <leo.bras@arm.com>
You are adding the file, so the copyright note should be yours, then?
> + */
> +
> +#ifndef __ARM64_KVM_DIRTY_BIT_H__
> +#define __ARM64_KVM_DIRTY_BIT_H__
> +
> +#include <asm/kvm_pgtable.h>
> +#include <asm/sysreg.h>
> +
> +int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> +void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> +
> +#endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index bae2c4f92ef5..c41ec6d9c45a 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -420,6 +420,10 @@ struct kvm_arch {
> */
> struct kvm_protected_vm pkvm;
>
> + /* HDBSS: per-VM dirty tracking state */
> + bool enable_hdbss;
Is there not a way of checking hdbss without adding this new member on the
struct? Would not checking the vcpu_hdbss_state from current vcpu should be
enough?
> + unsigned int hdbss_order;
Is that the order for hdbss buffer size?
Haven't finished reading the series, but would that be bad if this was also
in the per-vcpu state struct, instead?
Also, here I suggest that we save a number, instead of the encoding for
HDBSSBR_ELS_SZ, and convert it on hdbss setup. It could be either a
shift, or size.
Reason being that you used this to compare with HDBSS_MAX_ORDER at some
point, and there is no guarantee that the encoding will always be in
crescent form.
Also, it's not clear where you set this value.
> +
> #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
> /* Nested virtualization info */
> struct dentry *debugfs_nv_dentry;
> @@ -838,6 +842,12 @@ struct vcpu_reset_state {
> bool reset;
> };
>
> +struct vcpu_hdbss_state {
> + phys_addr_t base_phys; /* for memory free */
> + u64 hdbssbr_el2; /* load directly */
> + u64 hdbssprod_el2; /* save directly */
> +};
> +
> struct vncr_tlb;
>
> struct kvm_vcpu_arch {
> @@ -945,6 +955,9 @@ struct kvm_vcpu_arch {
>
> /* Hyp-readable copy of kvm_vcpu::pid */
> pid_t pid;
> +
> + /* HDBSS registers info */
> + struct vcpu_hdbss_state hdbss;/
> };
>
> /*
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index 7aa08d59d494..1354a58c3316 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -1039,6 +1039,17 @@
>
> #define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | \
> GCS_CAP_VALID_TOKEN)
> +
> +/*
> + * Definitions for the HDBSS feature
> + */
> +#define HDBSS_MAX_ORDER HDBSSBR_EL2_SZ_2MB
> +
See above comment on using the encoding instead of an actual size/shift
number.
> +#define HDBSSBR_EL2(baddr, sz) (((baddr) & HDBSSBR_EL2_BADDR_MASK) | \
> + FIELD_PREP(HDBSSBR_EL2_SZ_MASK, sz))
> +
> +#define HDBSSPROD_IDX(prod) FIELD_GET(HDBSSPROD_EL2_INDEX_MASK, prod)
> +
> /*
> * Definitions for GICv5 instructions
> */
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 59612d2f277c..ec2749af64fa 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -18,6 +18,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
> guest.o debug.o reset.o sys_regs.o stacktrace.o \
> vgic-sys-reg-v3.o fpsimd.o pkvm.o \
> arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
> + dirty_bit.o \
> vgic/vgic.o vgic/vgic-init.o \
> vgic/vgic-irqfd.o vgic/vgic-v2.o \
> vgic/vgic-v3.o vgic/vgic-v4.o \
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 50adfff75be8..bf6688245d83 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -38,6 +38,7 @@
> #include <asm/kvm_asm.h>
> #include <asm/kvm_emulate.h>
> #include <asm/kvm_hyp.h>
> +#include <asm/kvm_dirty_bit.h>
> #include <asm/kvm_mmu.h>
> #include <asm/kvm_nested.h>
> #include <asm/kvm_pkvm.h>
> @@ -565,6 +566,12 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
> if (err)
> kvm_vgic_vcpu_destroy(vcpu);
>
> + if (vcpu->kvm->arch.enable_hdbss) {
> + err = kvm_arm_vcpu_alloc_hdbss(vcpu, vcpu->kvm->arch.hdbss_order);
> + if (err)
> + kvm_vgic_vcpu_destroy(vcpu);
> + }
> +
> return err;
> }
>
> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> new file mode 100644
> index 000000000000..6c7a6ef66b5a
> --- /dev/null
> +++ b/arch/arm64/kvm/dirty_bit.c
> @@ -0,0 +1,52 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 ARM Ltd.
> + * Author: Leonardo Bras <leo.bras@arm.com>
> + */
> +
> +#include <asm/kvm_dirty_bit.h>
> +#include <asm/kvm_mmu.h>
> +#include <asm/sysreg.h>
> +#include <linux/gfp.h>
> +#include <linux/kconfig.h>
> +#include <linux/mm.h>
> +
> +int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order)
> +{
> + struct page *hdbss_pg = NULL;
> +
> + if (vcpu->arch.hdbss.hdbssbr_el2 || !system_supports_hdbss())
> + return 0;
> +
> + if (order > HDBSS_MAX_ORDER)
> + return -EINVAL;
> +
> + hdbss_pg = alloc_pages(GFP_KERNEL_ACCOUNT, order);
> + if (!hdbss_pg)
> + return -ENOMEM;
> +
> + vcpu->arch.hdbss = (struct vcpu_hdbss_state) {
> + .base_phys = page_to_phys(hdbss_pg),
> + .hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg), order),
> + .hdbssprod_el2 = 0,
> + };
> +
> + return 0;
> +}
> +
> +void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
> +{
> + struct page *hdbss_pg;
> +
> + if (!vcpu->arch.hdbss.hdbssbr_el2) {
> + return;
> + }
> +
> + hdbss_pg = phys_to_page(vcpu->arch.hdbss.base_phys);
> + if (hdbss_pg)
> + __free_pages(hdbss_pg,
> + FIELD_GET(HDBSSBR_EL2_SZ_MASK,
> + vcpu->arch.hdbss.hdbssbr_el2));
> +
> + vcpu->arch.hdbss.hdbssbr_el2 = 0;
> +}
> diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
> index bbe9cebd3d9d..fe72944bfd3d 100644
> --- a/arch/arm64/kvm/hyp/vhe/switch.c
> +++ b/arch/arm64/kvm/hyp/vhe/switch.c
> @@ -22,6 +22,7 @@
> #include <asm/kvm_emulate.h>
> #include <asm/kvm_hyp.h>
> #include <asm/kvm_mmu.h>
> +#include <asm/kvm_dirty_bit.h>
> #include <asm/fpsimd.h>
> #include <asm/debug-monitors.h>
> #include <asm/processor.h>
> @@ -213,6 +214,19 @@ static void __vcpu_put_deactivate_traps(struct kvm_vcpu *vcpu)
> local_irq_restore(flags);
> }
>
> +static void __load_hdbss(struct kvm_vcpu *vcpu)
> +{
> + struct kvm *kvm = vcpu->kvm;
> +
> + if (!kvm->arch.enable_hdbss)
> + return;
> +
> + write_sysreg_s(vcpu->arch.hdbss.hdbssbr_el2, SYS_HDBSSBR_EL2);
> + write_sysreg_s(vcpu->arch.hdbss.hdbssprod_el2, SYS_HDBSSPROD_EL2);
> +
> + isb();
> +}
> +
> void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
> {
> host_data_ptr(host_ctxt)->__hyp_running_vcpu = vcpu;
> @@ -220,6 +234,7 @@ void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
> __vcpu_load_switch_sysregs(vcpu);
> __vcpu_load_activate_traps(vcpu);
> __load_stage2(vcpu->arch.hw_mmu);
> + __load_hdbss(vcpu);
> }
>
> void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 346efed6e605..83251d95bf3f 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -16,6 +16,7 @@
> #include <asm/cacheflush.h>
> #include <asm/kvm_arm.h>
> #include <asm/kvm_mmu.h>
> +#include <asm/kvm_dirty_bit.h>
> #include <asm/kvm_pgtable.h>
> #include <asm/kvm_pkvm.h>
> #include <asm/kvm_asm.h>
> diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
> index b963fd975aac..d8104bcbd9ff 100644
> --- a/arch/arm64/kvm/reset.c
> +++ b/arch/arm64/kvm/reset.c
> @@ -27,6 +27,7 @@
> #include <asm/kvm_asm.h>
> #include <asm/kvm_emulate.h>
> #include <asm/kvm_mmu.h>
> +#include <asm/kvm_dirty_bit.h>
> #include <asm/kvm_nested.h>
> #include <asm/virt.h>
>
> @@ -161,6 +162,9 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
> free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
> kfree(vcpu->arch.vncr_tlb);
> kfree(vcpu->arch.ccsidr);
> +
> + if (vcpu->kvm->arch.enable_hdbss)
> + kvm_arm_vcpu_free_hdbss(vcpu);
> }
>
> static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
> --
> 2.33.0
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management
2026-07-13 13:39 ` Leonardo Bras
@ 2026-07-14 7:15 ` Tian Zheng
2026-07-14 10:47 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 7:15 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/13/2026 9:39 PM, Leonardo Bras wrote:
> On Thu, Jul 09, 2026 at 06:40:24PM +0800, Tian Zheng wrote:
>> From: eillon <yezhenyu2@huawei.com>
>>
>> Add HDBSS (Hardware Dirty Bit State Structure) per-vCPU buffer
>> management including allocation, freeing, and loading of HDBSS
>> registers during vCPU load.
>>
>> This patch creates the foundational infrastructure:
>> - struct vcpu_hdbss_state and enable_hdbss/hdbss_order in kvm_arch
>> - kvm_dirty_bit.h header with alloc/free declarations
>> - dirty_bit.c with alloc/free helpers
>> - __load_hdbss() in VHE switch for register loading
>> - vCPU create/destroy hooks for buffer lifecycle
>> - sysreg definitions for HDBSS register manipulation
>> - Makefile update for dirty_bit.o
>>
>> Signed-off-by: Eillon <yezhenyu2@huawei.com>
>> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
>> ---
>> arch/arm64/include/asm/kvm_dirty_bit.h | 16 ++++++++
>> arch/arm64/include/asm/kvm_host.h | 13 +++++++
>> arch/arm64/include/asm/sysreg.h | 11 ++++++
>> arch/arm64/kvm/Makefile | 1 +
>> arch/arm64/kvm/arm.c | 7 ++++
>> arch/arm64/kvm/dirty_bit.c | 52 ++++++++++++++++++++++++++
>> arch/arm64/kvm/hyp/vhe/switch.c | 15 ++++++++
>> arch/arm64/kvm/mmu.c | 1 +
>> arch/arm64/kvm/reset.c | 4 ++
>> 9 files changed, 120 insertions(+)
>> create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
>> create mode 100644 arch/arm64/kvm/dirty_bit.c
>>
>> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
>> new file mode 100644
>> index 000000000000..84b12f0a10af
>> --- /dev/null
>> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
>> @@ -0,0 +1,16 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2026 ARM Ltd.
>> + * Author: Leonardo Bras <leo.bras@arm.com>
> You are adding the file, so the copyright note should be yours, then?
I originally thought your earlier patchset had already created this
file, so I
kept your copyright and author info. I'll update it to mine in the next
version.
Thanks for pointing that out!
>
>> + */
>> +
>> +#ifndef __ARM64_KVM_DIRTY_BIT_H__
>> +#define __ARM64_KVM_DIRTY_BIT_H__
>> +
>> +#include <asm/kvm_pgtable.h>
>> +#include <asm/sysreg.h>
>> +
>> +int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
>> +void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
>> +
>> +#endif /* __ARM64_KVM_DIRTY_BIT_H__ */
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>> index bae2c4f92ef5..c41ec6d9c45a 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -420,6 +420,10 @@ struct kvm_arch {
>> */
>> struct kvm_protected_vm pkvm;
>>
>> + /* HDBSS: per-VM dirty tracking state */
>> + bool enable_hdbss;
> Is there not a way of checking hdbss without adding this new member on the
> struct? Would not checking the vcpu_hdbss_state from current vcpu should be
> enough?
HDBSS is a VM-wide property, so I currently use a VM-level flag
(kvm->arch.enable_hdbss)
to track whether it's enabled. Paths like
kvm_arch_commit_memory_region() and VM teardown
don't have a vCPU context to query, so per-vCPU state wouldn't work there.
But you're right, maybe we don't need a value to store it — we could
remove this flag and instead
check the VTCR_EL2_HDBSS bit directly from kvm->arch.mmu.vtcr:
```
static inline bool kvm_hdbss_enabled(const struct kvm *kvm)
{
return kvm->arch.mmu.vtcr & VTCR_EL2_HDBSS;
}
if (kvm_hdbss_enabled(kvm))
...
```
>
>> + unsigned int hdbss_order;
> Is that the order for hdbss buffer size?
> Haven't finished reading the series, but would that be bad if this was also
> in the per-vcpu state struct, instead?
>
> Also, here I suggest that we save a number, instead of the encoding for
> HDBSSBR_ELS_SZ, and convert it on hdbss setup. It could be either a
> shift, or size.
>
> Reason being that you used this to compare with HDBSS_MAX_ORDER at some
> point, and there is no guarantee that the encoding will always be in
> crescent form.
>
> Also, it's not clear where you set this value.
Yes, this is the HDBSS buffer order (2^order bytes per vCPU).
I put it in kvm->arch *to make sure all vCPUs in a VM have the same order*.
I agree we should store a plain order number rather than the raw SZ
encoding.
The assignment should happen in kvm_arm_enable_hdbss_global(), which I
missed in v4 and will add in v5.
>
>> +
>> #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
>> /* Nested virtualization info */
>> struct dentry *debugfs_nv_dentry;
>> @@ -838,6 +842,12 @@ struct vcpu_reset_state {
>> bool reset;
>> };
>>
>> +struct vcpu_hdbss_state {
>> + phys_addr_t base_phys; /* for memory free */
>> + u64 hdbssbr_el2; /* load directly */
>> + u64 hdbssprod_el2; /* save directly */
>> +};
>> +
>> struct vncr_tlb;
>>
>> struct kvm_vcpu_arch {
>> @@ -945,6 +955,9 @@ struct kvm_vcpu_arch {
>>
>> /* Hyp-readable copy of kvm_vcpu::pid */
>> pid_t pid;
>> +
>> + /* HDBSS registers info */
>> + struct vcpu_hdbss_state hdbss;/
>> };
>>
>> /*
>> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
>> index 7aa08d59d494..1354a58c3316 100644
>> --- a/arch/arm64/include/asm/sysreg.h
>> +++ b/arch/arm64/include/asm/sysreg.h
>> @@ -1039,6 +1039,17 @@
>>
>> #define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | \
>> GCS_CAP_VALID_TOKEN)
>> +
>> +/*
>> + * Definitions for the HDBSS feature
>> + */
>> +#define HDBSS_MAX_ORDER HDBSSBR_EL2_SZ_2MB
>> +
> See above comment on using the encoding instead of an actual size/shift
> number.
You're right — I will fix this in the next version as follows:
First, HDBSS_MAX_ORDER will be defined as a plain order number. In v4 I
only considered the 4KB page
case and hard-coded it to 9. To make it work for any PAGE_SIZE
configuration (4KB, 16KB, or 64KB), I'll derive
it from HDBSSBR_EL2_SZ_2MB:
```
#define HDBSS_MAX_ORDER (12 + HDBSSBR_EL2_SZ_2MB - PAGE_SHIFT)
```
This evaluates to 9 on 4KB pages, and the correct value on 16KB/64KB
pages as well.
Second, I will add two conversion helpers in sysreg.h to bridge between
kernel semantics (alloc_pages order)
and hardware encoding (HDBSSBR_EL2.SZ):
```
/* Convert alloc_pages order to HDBSSBR_EL2.SZ encoding */
#define hdbss_order_to_sz(order) (PAGE_SHIFT + (order) - 12)
```
These helpers work for any PAGE_SIZE configuration (4KB, 16KB, or 64KB)
because they use PAGE_SHIFT directly.
Third, I will update the HDBSSBR_EL2 register construction in
kvm_arm_vcpu_alloc_hdbss():
```
/* before */
.hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg), order),
/* after */
.hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg),
hdbss_order_to_sz(order)),
```
And I will also update the __free_pages() call to use
vcpu->kvm->arch.hdbss_order directly, since that field
stores the plain order number and avoids decoding the SZ encoding at
free time:
```
/* before */
__free_pages(hdbss_pg,
FIELD_GET(HDBSSBR_EL2_SZ_MASK,
vcpu->arch.hdbss.hdbssbr_el2));
/* after */
__free_pages(hdbss_pg,
vcpu->kvm->arch.hdbss_order);
```
>
>> +#define HDBSSBR_EL2(baddr, sz) (((baddr) & HDBSSBR_EL2_BADDR_MASK) | \
>> + FIELD_PREP(HDBSSBR_EL2_SZ_MASK, sz))
>> +
>> +#define HDBSSPROD_IDX(prod) FIELD_GET(HDBSSPROD_EL2_INDEX_MASK, prod)
>> +
>> /*
>> * Definitions for GICv5 instructions
>> */
>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>> index 59612d2f277c..ec2749af64fa 100644
>> --- a/arch/arm64/kvm/Makefile
>> +++ b/arch/arm64/kvm/Makefile
>> @@ -18,6 +18,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
>> guest.o debug.o reset.o sys_regs.o stacktrace.o \
>> vgic-sys-reg-v3.o fpsimd.o pkvm.o \
>> arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
>> + dirty_bit.o \
>> vgic/vgic.o vgic/vgic-init.o \
>> vgic/vgic-irqfd.o vgic/vgic-v2.o \
>> vgic/vgic-v3.o vgic/vgic-v4.o \
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 50adfff75be8..bf6688245d83 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -38,6 +38,7 @@
>> #include <asm/kvm_asm.h>
>> #include <asm/kvm_emulate.h>
>> #include <asm/kvm_hyp.h>
>> +#include <asm/kvm_dirty_bit.h>
>> #include <asm/kvm_mmu.h>
>> #include <asm/kvm_nested.h>
>> #include <asm/kvm_pkvm.h>
>> @@ -565,6 +566,12 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
>> if (err)
>> kvm_vgic_vcpu_destroy(vcpu);
>>
>> + if (vcpu->kvm->arch.enable_hdbss) {
>> + err = kvm_arm_vcpu_alloc_hdbss(vcpu, vcpu->kvm->arch.hdbss_order);
>> + if (err)
>> + kvm_vgic_vcpu_destroy(vcpu);
>> + }
>> +
>> return err;
>> }
>>
>> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
>> new file mode 100644
>> index 000000000000..6c7a6ef66b5a
>> --- /dev/null
>> +++ b/arch/arm64/kvm/dirty_bit.c
>> @@ -0,0 +1,52 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2026 ARM Ltd.
>> + * Author: Leonardo Bras <leo.bras@arm.com>
>> + */
>> +
>> +#include <asm/kvm_dirty_bit.h>
>> +#include <asm/kvm_mmu.h>
>> +#include <asm/sysreg.h>
>> +#include <linux/gfp.h>
>> +#include <linux/kconfig.h>
>> +#include <linux/mm.h>
>> +
>> +int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order)
>> +{
>> + struct page *hdbss_pg = NULL;
>> +
>> + if (vcpu->arch.hdbss.hdbssbr_el2 || !system_supports_hdbss())
>> + return 0;
>> +
>> + if (order > HDBSS_MAX_ORDER)
>> + return -EINVAL;
>> +
>> + hdbss_pg = alloc_pages(GFP_KERNEL_ACCOUNT, order);
>> + if (!hdbss_pg)
>> + return -ENOMEM;
>> +
>> + vcpu->arch.hdbss = (struct vcpu_hdbss_state) {
>> + .base_phys = page_to_phys(hdbss_pg),
>> + .hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg), order),
>> + .hdbssprod_el2 = 0,
>> + };
>> +
>> + return 0;
>> +}
>> +
>> +void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
>> +{
>> + struct page *hdbss_pg;
>> +
>> + if (!vcpu->arch.hdbss.hdbssbr_el2) {
>> + return;
>> + }
>> +
>> + hdbss_pg = phys_to_page(vcpu->arch.hdbss.base_phys);
>> + if (hdbss_pg)
>> + __free_pages(hdbss_pg,
>> + FIELD_GET(HDBSSBR_EL2_SZ_MASK,
>> + vcpu->arch.hdbss.hdbssbr_el2));
>> +
>> + vcpu->arch.hdbss.hdbssbr_el2 = 0;
>> +}
>> diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
>> index bbe9cebd3d9d..fe72944bfd3d 100644
>> --- a/arch/arm64/kvm/hyp/vhe/switch.c
>> +++ b/arch/arm64/kvm/hyp/vhe/switch.c
>> @@ -22,6 +22,7 @@
>> #include <asm/kvm_emulate.h>
>> #include <asm/kvm_hyp.h>
>> #include <asm/kvm_mmu.h>
>> +#include <asm/kvm_dirty_bit.h>
>> #include <asm/fpsimd.h>
>> #include <asm/debug-monitors.h>
>> #include <asm/processor.h>
>> @@ -213,6 +214,19 @@ static void __vcpu_put_deactivate_traps(struct kvm_vcpu *vcpu)
>> local_irq_restore(flags);
>> }
>>
>> +static void __load_hdbss(struct kvm_vcpu *vcpu)
>> +{
>> + struct kvm *kvm = vcpu->kvm;
>> +
>> + if (!kvm->arch.enable_hdbss)
>> + return;
>> +
>> + write_sysreg_s(vcpu->arch.hdbss.hdbssbr_el2, SYS_HDBSSBR_EL2);
>> + write_sysreg_s(vcpu->arch.hdbss.hdbssprod_el2, SYS_HDBSSPROD_EL2);
>> +
>> + isb();
>> +}
>> +
>> void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
>> {
>> host_data_ptr(host_ctxt)->__hyp_running_vcpu = vcpu;
>> @@ -220,6 +234,7 @@ void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
>> __vcpu_load_switch_sysregs(vcpu);
>> __vcpu_load_activate_traps(vcpu);
>> __load_stage2(vcpu->arch.hw_mmu);
>> + __load_hdbss(vcpu);
>> }
>>
>> void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index 346efed6e605..83251d95bf3f 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -16,6 +16,7 @@
>> #include <asm/cacheflush.h>
>> #include <asm/kvm_arm.h>
>> #include <asm/kvm_mmu.h>
>> +#include <asm/kvm_dirty_bit.h>
>> #include <asm/kvm_pgtable.h>
>> #include <asm/kvm_pkvm.h>
>> #include <asm/kvm_asm.h>
>> diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
>> index b963fd975aac..d8104bcbd9ff 100644
>> --- a/arch/arm64/kvm/reset.c
>> +++ b/arch/arm64/kvm/reset.c
>> @@ -27,6 +27,7 @@
>> #include <asm/kvm_asm.h>
>> #include <asm/kvm_emulate.h>
>> #include <asm/kvm_mmu.h>
>> +#include <asm/kvm_dirty_bit.h>
>> #include <asm/kvm_nested.h>
>> #include <asm/virt.h>
>>
>> @@ -161,6 +162,9 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
>> free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
>> kfree(vcpu->arch.vncr_tlb);
>> kfree(vcpu->arch.ccsidr);
>> +
>> + if (vcpu->kvm->arch.enable_hdbss)
>> + kvm_arm_vcpu_free_hdbss(vcpu);
>> }
>>
>> static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
>> --
>> 2.33.0
>>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management
2026-07-14 7:15 ` Tian Zheng
@ 2026-07-14 10:47 ` Leonardo Bras
0 siblings, 0 replies; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 10:47 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 03:15:03PM +0800, Tian Zheng wrote:
>
> On 7/13/2026 9:39 PM, Leonardo Bras wrote:
> > On Thu, Jul 09, 2026 at 06:40:24PM +0800, Tian Zheng wrote:
> > > From: eillon <yezhenyu2@huawei.com>
> > >
> > > Add HDBSS (Hardware Dirty Bit State Structure) per-vCPU buffer
> > > management including allocation, freeing, and loading of HDBSS
> > > registers during vCPU load.
> > >
> > > This patch creates the foundational infrastructure:
> > > - struct vcpu_hdbss_state and enable_hdbss/hdbss_order in kvm_arch
> > > - kvm_dirty_bit.h header with alloc/free declarations
> > > - dirty_bit.c with alloc/free helpers
> > > - __load_hdbss() in VHE switch for register loading
> > > - vCPU create/destroy hooks for buffer lifecycle
> > > - sysreg definitions for HDBSS register manipulation
> > > - Makefile update for dirty_bit.o
> > >
> > > Signed-off-by: Eillon <yezhenyu2@huawei.com>
> > > Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> > > ---
> > > arch/arm64/include/asm/kvm_dirty_bit.h | 16 ++++++++
> > > arch/arm64/include/asm/kvm_host.h | 13 +++++++
> > > arch/arm64/include/asm/sysreg.h | 11 ++++++
> > > arch/arm64/kvm/Makefile | 1 +
> > > arch/arm64/kvm/arm.c | 7 ++++
> > > arch/arm64/kvm/dirty_bit.c | 52 ++++++++++++++++++++++++++
> > > arch/arm64/kvm/hyp/vhe/switch.c | 15 ++++++++
> > > arch/arm64/kvm/mmu.c | 1 +
> > > arch/arm64/kvm/reset.c | 4 ++
> > > 9 files changed, 120 insertions(+)
> > > create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
> > > create mode 100644 arch/arm64/kvm/dirty_bit.c
> > >
> > > diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > new file mode 100644
> > > index 000000000000..84b12f0a10af
> > > --- /dev/null
> > > +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > @@ -0,0 +1,16 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > +/*
> > > + * Copyright (C) 2026 ARM Ltd.
> > > + * Author: Leonardo Bras <leo.bras@arm.com>
> > You are adding the file, so the copyright note should be yours, then?
>
>
> I originally thought your earlier patchset had already created this file, so
> I
>
> kept your copyright and author info. I'll update it to mine in the next
> version.
>
>
> Thanks for pointing that out!
:)
>
>
> >
> > > + */
> > > +
> > > +#ifndef __ARM64_KVM_DIRTY_BIT_H__
> > > +#define __ARM64_KVM_DIRTY_BIT_H__
> > > +
> > > +#include <asm/kvm_pgtable.h>
> > > +#include <asm/sysreg.h>
> > > +
> > > +int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> > > +void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> > > +
> > > +#endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > index bae2c4f92ef5..c41ec6d9c45a 100644
> > > --- a/arch/arm64/include/asm/kvm_host.h
> > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > @@ -420,6 +420,10 @@ struct kvm_arch {
> > > */
> > > struct kvm_protected_vm pkvm;
> > >
> > > + /* HDBSS: per-VM dirty tracking state */
> > > + bool enable_hdbss;
> > Is there not a way of checking hdbss without adding this new member on the
> > struct? Would not checking the vcpu_hdbss_state from current vcpu should be
> > enough?
>
>
> HDBSS is a VM-wide property, so I currently use a VM-level flag
> (kvm->arch.enable_hdbss)
>
> to track whether it's enabled. Paths like kvm_arch_commit_memory_region()
> and VM teardown
>
> don't have a vCPU context to query, so per-vCPU state wouldn't work there.
>
>
> But you're right, maybe we don't need a value to store it — we could remove
> this flag and instead
>
> check the VTCR_EL2_HDBSS bit directly from kvm->arch.mmu.vtcr:
>
>
> ```
> static inline bool kvm_hdbss_enabled(const struct kvm *kvm)
> {
> return kvm->arch.mmu.vtcr & VTCR_EL2_HDBSS;
> }
>
> if (kvm_hdbss_enabled(kvm))
> ...
> ```
That looks better :)
>
>
> >
> > > + unsigned int hdbss_order;
> > Is that the order for hdbss buffer size?
> > Haven't finished reading the series, but would that be bad if this was also
> > in the per-vcpu state struct, instead?
> >
> > Also, here I suggest that we save a number, instead of the encoding for
> > HDBSSBR_ELS_SZ, and convert it on hdbss setup. It could be either a
> > shift, or size.
> >
> > Reason being that you used this to compare with HDBSS_MAX_ORDER at some
> > point, and there is no guarantee that the encoding will always be in
> > crescent form.
> >
> > Also, it's not clear where you set this value.
>
>
> Yes, this is the HDBSS buffer order (2^order bytes per vCPU).
>
> I put it in kvm->arch *to make sure all vCPUs in a VM have the same order*.
>
Right, but do we need to retrieve this value at any context that is not
per-vcpu later? If all allocations happen at the same time, and the vcpus
can get the size of their arrays (even by the register itself), maybe there
is no need to add this member in the kvm_arch structure.
>
>
> I agree we should store a plain order number rather than the raw SZ
> encoding.
>
I would recommend we always save the value in the way its most convenient
to use. In this case, I only recall it to be used as bounds to read the
HDBSS array, so it would be better if we save it as plain size.
>
> The assignment should happen in kvm_arm_enable_hdbss_global(), which I
> missed in v4 and will add in v5.
>
Okay
>
> > > +
> > > #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
> > > /* Nested virtualization info */
> > > struct dentry *debugfs_nv_dentry;
> > > @@ -838,6 +842,12 @@ struct vcpu_reset_state {
> > > bool reset;
> > > };
> > >
> > > +struct vcpu_hdbss_state {
> > > + phys_addr_t base_phys; /* for memory free */
> > > + u64 hdbssbr_el2; /* load directly */
> > > + u64 hdbssprod_el2; /* save directly */
> > > +};
> > > +
> > > struct vncr_tlb;
> > >
> > > struct kvm_vcpu_arch {
> > > @@ -945,6 +955,9 @@ struct kvm_vcpu_arch {
> > >
> > > /* Hyp-readable copy of kvm_vcpu::pid */
> > > pid_t pid;
> > > +
> > > + /* HDBSS registers info */
> > > + struct vcpu_hdbss_state hdbss;/
> > > };
> > >
> > > /*
> > > diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> > > index 7aa08d59d494..1354a58c3316 100644
> > > --- a/arch/arm64/include/asm/sysreg.h
> > > +++ b/arch/arm64/include/asm/sysreg.h
> > > @@ -1039,6 +1039,17 @@
> > >
> > > #define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | \
> > > GCS_CAP_VALID_TOKEN)
> > > +
> > > +/*
> > > + * Definitions for the HDBSS feature
> > > + */
> > > +#define HDBSS_MAX_ORDER HDBSSBR_EL2_SZ_2MB
> > > +
> > See above comment on using the encoding instead of an actual size/shift
> > number.
>
>
> You're right — I will fix this in the next version as follows:
>
> First, HDBSS_MAX_ORDER will be defined as a plain order number. In v4 I only
> considered the 4KB page
Maybe consider a plain size number, instead.
>
> case and hard-coded it to 9. To make it work for any PAGE_SIZE configuration
> (4KB, 16KB, or 64KB), I'll derive
>
> it from HDBSSBR_EL2_SZ_2MB:
>
> ```
> #define HDBSS_MAX_ORDER (12 + HDBSSBR_EL2_SZ_2MB - PAGE_SHIFT)
> ```
>
> This evaluates to 9 on 4KB pages, and the correct value on 16KB/64KB pages
> as well.
Wait, I don't see what you are trying to achieve here with this.
This order is related to the size of a HDBSS buffer, which is defined
independently of the page size being used. In 2MB example, it does hold
256K HDBSS entries, disregarding on the page size, IIRC.
>
> Second, I will add two conversion helpers in sysreg.h to bridge between
> kernel semantics (alloc_pages order)
>
> and hardware encoding (HDBSSBR_EL2.SZ):
>
> ```
> /* Convert alloc_pages order to HDBSSBR_EL2.SZ encoding */
> #define hdbss_order_to_sz(order) (PAGE_SHIFT + (order) - 12)
> ```
(would be *_to_shift() no?)
In any case, see above.
>
> These helpers work for any PAGE_SIZE configuration (4KB, 16KB, or 64KB)
> because they use PAGE_SHIFT directly.
>
> Third, I will update the HDBSSBR_EL2 register construction in
> kvm_arm_vcpu_alloc_hdbss():
>
> ```
> /* before */
> .hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg), order),
>
> /* after */
> .hdbssbr_el2 = HDBSSBR_EL2(page_to_phys(hdbss_pg),
> hdbss_order_to_sz(order)),
> ```
>
That would be the other way around, right? I mean, here you would want the
order encoded in HDBSSBR_SZ from the page shift, not the other way around.
In any case, see above.
> And I will also update the __free_pages() call to use
> vcpu->kvm->arch.hdbss_order directly, since that field
>
> stores the plain order number and avoids decoding the SZ encoding at free
> time:
>
>
> ```
> /* before */
> __free_pages(hdbss_pg,
> FIELD_GET(HDBSSBR_EL2_SZ_MASK,
> vcpu->arch.hdbss.hdbssbr_el2));
>
> /* after */
> __free_pages(hdbss_pg,
> vcpu->kvm->arch.hdbss_order);
> ```
>
Ah, you use the 'order' to free the buffers as well, ok.
But those are per-vcpu buffers as well, right?
Again, the HDBSSBR_EL2.SZ AFAIK is not related to page size.
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
` (3 preceding siblings ...)
2026-07-09 10:40 ` [PATCH v4 4/6] KVM: arm64: Add HDBSS per-vCPU buffer management Tian Zheng
@ 2026-07-09 10:40 ` Tian Zheng
2026-07-13 14:06 ` Leonardo Bras
2026-07-09 10:40 ` [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change Tian Zheng
2026-07-13 10:31 ` [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Leonardo Bras
6 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-09 10:40 UTC (permalink / raw)
To: maz, oupton, catalin.marinas, will, zhengtian10
Cc: yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose, leo.bras
From: eillon <yezhenyu2@huawei.com>
Add HDBSS fault handling for buffer full, external abort, and general
protection fault (GPF) events. When the HDBSS buffer becomes full,
the hardware traps to EL2 with an HDBSSF event, which is handled by
setting a flush request.
Add kvm_flush_hdbss_buffer() to consume HDBSS buffer entries and
propagate dirty information into the userspace-visible dirty bitmap.
Flush is triggered on vcpu_put, check_vcpu_requests, and
sync_dirty_log.
Add esr_iss2_is_hdbssf() helper for HDBSS fault detection in guest
abort handling.
Signed-off-by: Eillon <yezhenyu2@huawei.com>
Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
---
arch/arm64/include/asm/esr.h | 5 +++
arch/arm64/include/asm/kvm_dirty_bit.h | 11 +++++
arch/arm64/include/asm/kvm_host.h | 1 +
arch/arm64/kvm/arm.c | 14 ++++++
arch/arm64/kvm/dirty_bit.c | 62 ++++++++++++++++++++++++++
arch/arm64/kvm/mmu.c | 4 ++
6 files changed, 97 insertions(+)
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 81c17320a588..2e6b679b5908 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -437,6 +437,11 @@
#ifndef __ASSEMBLER__
#include <asm/types.h>
+static inline bool esr_iss2_is_hdbssf(unsigned long esr)
+{
+ return ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF;
+}
+
static inline unsigned long esr_brk_comment(unsigned long esr)
{
return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
index 84b12f0a10af..4b28000e972f 100644
--- a/arch/arm64/include/asm/kvm_dirty_bit.h
+++ b/arch/arm64/include/asm/kvm_dirty_bit.h
@@ -10,7 +10,18 @@
#include <asm/kvm_pgtable.h>
#include <asm/sysreg.h>
+/* HDBSS entry field definitions */
+#define HDBSS_ENTRY_VALID BIT(0)
+#define HDBSS_ENTRY_TTWL_SHIFT (1)
+#define HDBSS_ENTRY_TTWL_MASK (GENMASK(3, 1))
+#define HDBSS_ENTRY_TTWL(x) \
+ (((x) << HDBSS_ENTRY_TTWL_SHIFT) & HDBSS_ENTRY_TTWL_MASK)
+#define HDBSS_ENTRY_TTWL_RESV HDBSS_ENTRY_TTWL(-4)
+#define HDBSS_ENTRY_IPA GENMASK_ULL(55, 12)
+
int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
+void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
+int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
#endif /* __ARM64_KVM_DIRTY_BIT_H__ */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index c41ec6d9c45a..cecfb884a64f 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -55,6 +55,7 @@
#define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
#define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
#define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11)
+#define KVM_REQ_FLUSH_HDBSS KVM_ARCH_REQ(12)
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
KVM_DIRTY_LOG_INITIALLY_SET)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index bf6688245d83..566953a4e23a 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -755,6 +755,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
kvm_vcpu_put_hw_mmu(vcpu);
kvm_arm_vmid_clear_active();
+ if (vcpu->kvm->arch.enable_hdbss)
+ kvm_flush_hdbss_buffer(vcpu);
+
vcpu_clear_on_unsupported_cpu(vcpu);
vcpu->cpu = -1;
}
@@ -1157,6 +1160,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
if (kvm_dirty_ring_check_request(vcpu))
return 0;
+ if (kvm_check_request(KVM_REQ_FLUSH_HDBSS, vcpu))
+ kvm_flush_hdbss_buffer(vcpu);
+
check_nested_vcpu_requests(vcpu);
}
@@ -1971,7 +1977,15 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
{
+ /*
+ * Flush all CPUs' dirty log buffers to the dirty_bitmap. Called
+ * before reporting dirty_bitmap to userspace. Send a request with
+ * KVM_REQUEST_WAIT to flush buffer synchronously.
+ */
+ if (!kvm->arch.enable_hdbss)
+ return;
+ kvm_make_all_cpus_request(kvm, KVM_REQ_FLUSH_HDBSS | KVM_REQUEST_WAIT);
}
static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
index 6c7a6ef66b5a..002366337637 100644
--- a/arch/arm64/kvm/dirty_bit.c
+++ b/arch/arm64/kvm/dirty_bit.c
@@ -50,3 +50,65 @@ void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
vcpu->arch.hdbss.hdbssbr_el2 = 0;
}
+
+void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu)
+{
+ int idx, curr_idx;
+ u64 *hdbss_buf;
+ struct kvm *kvm = vcpu->kvm;
+
+ if (!kvm->arch.enable_hdbss)
+ return;
+
+ curr_idx = HDBSSPROD_IDX(read_sysreg_s(SYS_HDBSSPROD_EL2));
+
+ /* Do nothing if HDBSS buffer is empty or br_el2 is NULL */
+ if (curr_idx == 0 || vcpu->arch.hdbss.hdbssbr_el2 == 0)
+ return;
+
+ hdbss_buf = page_address(phys_to_page(vcpu->arch.hdbss.base_phys));
+ if (!hdbss_buf)
+ return;
+
+ guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
+ for (idx = 0; idx < curr_idx; idx++) {
+ u64 gpa;
+
+ gpa = hdbss_buf[idx];
+ if (!(gpa & HDBSS_ENTRY_VALID))
+ continue;
+
+ gpa &= HDBSS_ENTRY_IPA;
+ kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
+ }
+
+ /* reset HDBSS index */
+ write_sysreg_s(0, SYS_HDBSSPROD_EL2);
+ vcpu->arch.hdbss.hdbssprod_el2 = 0;
+ isb();
+}
+
+int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
+{
+ u64 prod;
+ u64 fsc;
+
+ prod = read_sysreg_s(SYS_HDBSSPROD_EL2);
+ fsc = FIELD_GET(HDBSSPROD_EL2_FSC_MASK, prod);
+
+ switch (fsc) {
+ case HDBSSPROD_EL2_FSC_OK:
+ /* Buffer full, set request to flush on next vcpu exit */
+ kvm_make_request(KVM_REQ_FLUSH_HDBSS, vcpu);
+ return 1;
+ case HDBSSPROD_EL2_FSC_ExternalAbort:
+ case HDBSSPROD_EL2_FSC_GPF:
+ return -EFAULT;
+ default:
+ /* Unknown fault. */
+ WARN_ONCE(1,
+ "Unexpected HDBSS fault type, FSC: 0x%llx (prod=0x%llx, vcpu=%d)\n",
+ fsc, prod, vcpu->vcpu_id);
+ return -EFAULT;
+ }
+}
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 83251d95bf3f..949fb895add6 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2242,6 +2242,7 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
}
/**
+
* kvm_handle_guest_abort - handles all 2nd stage aborts
* @vcpu: the VCPU pointer
*
@@ -2279,6 +2280,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
+ if (esr_iss2_is_hdbssf(esr))
+ return kvm_handle_hdbss_fault(vcpu);
+
if (esr_fsc_is_translation_fault(esr)) {
/* Beyond sanitised PARange (which is the IPA limit) */
if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit())) {
--
2.33.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush
2026-07-09 10:40 ` [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush Tian Zheng
@ 2026-07-13 14:06 ` Leonardo Bras
2026-07-14 7:38 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-13 14:06 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Thu, Jul 09, 2026 at 06:40:25PM +0800, Tian Zheng wrote:
> From: eillon <yezhenyu2@huawei.com>
>
> Add HDBSS fault handling for buffer full, external abort, and general
> protection fault (GPF) events. When the HDBSS buffer becomes full,
> the hardware traps to EL2 with an HDBSSF event, which is handled by
> setting a flush request.
>
> Add kvm_flush_hdbss_buffer() to consume HDBSS buffer entries and
> propagate dirty information into the userspace-visible dirty bitmap.
> Flush is triggered on vcpu_put, check_vcpu_requests, and
> sync_dirty_log.
>
> Add esr_iss2_is_hdbssf() helper for HDBSS fault detection in guest
> abort handling.
>
> Signed-off-by: Eillon <yezhenyu2@huawei.com>
> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> ---
> arch/arm64/include/asm/esr.h | 5 +++
> arch/arm64/include/asm/kvm_dirty_bit.h | 11 +++++
> arch/arm64/include/asm/kvm_host.h | 1 +
> arch/arm64/kvm/arm.c | 14 ++++++
> arch/arm64/kvm/dirty_bit.c | 62 ++++++++++++++++++++++++++
> arch/arm64/kvm/mmu.c | 4 ++
> 6 files changed, 97 insertions(+)
>
> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> index 81c17320a588..2e6b679b5908 100644
> --- a/arch/arm64/include/asm/esr.h
> +++ b/arch/arm64/include/asm/esr.h
> @@ -437,6 +437,11 @@
> #ifndef __ASSEMBLER__
> #include <asm/types.h>
>
> +static inline bool esr_iss2_is_hdbssf(unsigned long esr)
> +{
> + return ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF;
This will return a long, which will be casted as bool.
In general, what I see in the kernel is something like:
return !!(ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF)
> +}
> +
> static inline unsigned long esr_brk_comment(unsigned long esr)
> {
> return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> index 84b12f0a10af..4b28000e972f 100644
> --- a/arch/arm64/include/asm/kvm_dirty_bit.h
> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> @@ -10,7 +10,18 @@
> #include <asm/kvm_pgtable.h>
> #include <asm/sysreg.h>
>
> +/* HDBSS entry field definitions */
> +#define HDBSS_ENTRY_VALID BIT(0)
> +#define HDBSS_ENTRY_TTWL_SHIFT (1)
> +#define HDBSS_ENTRY_TTWL_MASK (GENMASK(3, 1))
> +#define HDBSS_ENTRY_TTWL(x) \
> + (((x) << HDBSS_ENTRY_TTWL_SHIFT) & HDBSS_ENTRY_TTWL_MASK)
> +#define HDBSS_ENTRY_TTWL_RESV HDBSS_ENTRY_TTWL(-4)
> +#define HDBSS_ENTRY_IPA GENMASK_ULL(55, 12)
> +
> int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
> +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
>
> #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index c41ec6d9c45a..cecfb884a64f 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -55,6 +55,7 @@
> #define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
> #define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
> #define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11)
> +#define KVM_REQ_FLUSH_HDBSS KVM_ARCH_REQ(12)
>
> #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
> KVM_DIRTY_LOG_INITIALLY_SET)
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index bf6688245d83..566953a4e23a 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -755,6 +755,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
> kvm_vcpu_put_hw_mmu(vcpu);
> kvm_arm_vmid_clear_active();
>
> + if (vcpu->kvm->arch.enable_hdbss)
> + kvm_flush_hdbss_buffer(vcpu);
> +
> vcpu_clear_on_unsupported_cpu(vcpu);
> vcpu->cpu = -1;
> }
> @@ -1157,6 +1160,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
> if (kvm_dirty_ring_check_request(vcpu))
> return 0;
>
> + if (kvm_check_request(KVM_REQ_FLUSH_HDBSS, vcpu))
> + kvm_flush_hdbss_buffer(vcpu);
> +
> check_nested_vcpu_requests(vcpu);
> }
>
> @@ -1971,7 +1977,15 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
>
> void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
> {
> + /*
> + * Flush all CPUs' dirty log buffers to the dirty_bitmap. Called
> + * before reporting dirty_bitmap to userspace. Send a request with
> + * KVM_REQUEST_WAIT to flush buffer synchronously.
> + */
> + if (!kvm->arch.enable_hdbss)
> + return;
>
> + kvm_make_all_cpus_request(kvm, KVM_REQ_FLUSH_HDBSS | KVM_REQUEST_WAIT);
> }
>
> static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> index 6c7a6ef66b5a..002366337637 100644
> --- a/arch/arm64/kvm/dirty_bit.c
> +++ b/arch/arm64/kvm/dirty_bit.c
> @@ -50,3 +50,65 @@ void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
>
> vcpu->arch.hdbss.hdbssbr_el2 = 0;
> }
> +
> +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu)
> +{
> + int idx, curr_idx;
> + u64 *hdbss_buf;
> + struct kvm *kvm = vcpu->kvm;
> +
> + if (!kvm->arch.enable_hdbss)
> + return;
> +
> + curr_idx = HDBSSPROD_IDX(read_sysreg_s(SYS_HDBSSPROD_EL2));
> +
> + /* Do nothing if HDBSS buffer is empty or br_el2 is NULL */
> + if (curr_idx == 0 || vcpu->arch.hdbss.hdbssbr_el2 == 0)
> + return;
> +
> + hdbss_buf = page_address(phys_to_page(vcpu->arch.hdbss.base_phys));
> + if (!hdbss_buf)
> + return;
> +
> + guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
> + for (idx = 0; idx < curr_idx; idx++) {
> + u64 gpa;
> +
> + gpa = hdbss_buf[idx];
> + if (!(gpa & HDBSS_ENTRY_VALID))
> + continue;
> +
> + gpa &= HDBSS_ENTRY_IPA;
> + kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
You mention that it does not support dirty-ring, but above function will
mark the page as dirty in the dirty-ring :/
> + }
> +
> + /* reset HDBSS index */
> + write_sysreg_s(0, SYS_HDBSSPROD_EL2);
> + vcpu->arch.hdbss.hdbssprod_el2 = 0;
> + isb();
> +}
> +
> +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
> +{
> + u64 prod;
> + u64 fsc;
> +
> + prod = read_sysreg_s(SYS_HDBSSPROD_EL2);
> + fsc = FIELD_GET(HDBSSPROD_EL2_FSC_MASK, prod);
> +
> + switch (fsc) {
> + case HDBSSPROD_EL2_FSC_OK:
> + /* Buffer full, set request to flush on next vcpu exit */
> + kvm_make_request(KVM_REQ_FLUSH_HDBSS, vcpu);
> + return 1;
> + case HDBSSPROD_EL2_FSC_ExternalAbort:
> + case HDBSSPROD_EL2_FSC_GPF:
> + return -EFAULT;
> + default:
> + /* Unknown fault. */
> + WARN_ONCE(1,
> + "Unexpected HDBSS fault type, FSC: 0x%llx (prod=0x%llx, vcpu=%d)\n",
> + fsc, prod, vcpu->vcpu_id);
> + return -EFAULT;
> + }
> +}
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 83251d95bf3f..949fb895add6 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -2242,6 +2242,7 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
> }
>
> /**
> +
> * kvm_handle_guest_abort - handles all 2nd stage aborts
> * @vcpu: the VCPU pointer
> *
> @@ -2279,6 +2280,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>
> is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
>
> + if (esr_iss2_is_hdbssf(esr))
> + return kvm_handle_hdbss_fault(vcpu);
> +
> if (esr_fsc_is_translation_fault(esr)) {
> /* Beyond sanitised PARange (which is the IPA limit) */
> if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit())) {
> --
> 2.33.0
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush
2026-07-13 14:06 ` Leonardo Bras
@ 2026-07-14 7:38 ` Tian Zheng
2026-07-14 10:50 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 7:38 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/13/2026 10:06 PM, Leonardo Bras wrote:
> On Thu, Jul 09, 2026 at 06:40:25PM +0800, Tian Zheng wrote:
>> From: eillon <yezhenyu2@huawei.com>
>>
>> Add HDBSS fault handling for buffer full, external abort, and general
>> protection fault (GPF) events. When the HDBSS buffer becomes full,
>> the hardware traps to EL2 with an HDBSSF event, which is handled by
>> setting a flush request.
>>
>> Add kvm_flush_hdbss_buffer() to consume HDBSS buffer entries and
>> propagate dirty information into the userspace-visible dirty bitmap.
>> Flush is triggered on vcpu_put, check_vcpu_requests, and
>> sync_dirty_log.
>>
>> Add esr_iss2_is_hdbssf() helper for HDBSS fault detection in guest
>> abort handling.
>>
>> Signed-off-by: Eillon <yezhenyu2@huawei.com>
>> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
>> ---
>> arch/arm64/include/asm/esr.h | 5 +++
>> arch/arm64/include/asm/kvm_dirty_bit.h | 11 +++++
>> arch/arm64/include/asm/kvm_host.h | 1 +
>> arch/arm64/kvm/arm.c | 14 ++++++
>> arch/arm64/kvm/dirty_bit.c | 62 ++++++++++++++++++++++++++
>> arch/arm64/kvm/mmu.c | 4 ++
>> 6 files changed, 97 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
>> index 81c17320a588..2e6b679b5908 100644
>> --- a/arch/arm64/include/asm/esr.h
>> +++ b/arch/arm64/include/asm/esr.h
>> @@ -437,6 +437,11 @@
>> #ifndef __ASSEMBLER__
>> #include <asm/types.h>
>>
>> +static inline bool esr_iss2_is_hdbssf(unsigned long esr)
>> +{
>> + return ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF;
> This will return a long, which will be casted as bool.
> In general, what I see in the kernel is something like:
>
> return !!(ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF)
ok!
>> +}
>> +
>> static inline unsigned long esr_brk_comment(unsigned long esr)
>> {
>> return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
>> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
>> index 84b12f0a10af..4b28000e972f 100644
>> --- a/arch/arm64/include/asm/kvm_dirty_bit.h
>> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
>> @@ -10,7 +10,18 @@
>> #include <asm/kvm_pgtable.h>
>> #include <asm/sysreg.h>
>>
>> +/* HDBSS entry field definitions */
>> +#define HDBSS_ENTRY_VALID BIT(0)
>> +#define HDBSS_ENTRY_TTWL_SHIFT (1)
>> +#define HDBSS_ENTRY_TTWL_MASK (GENMASK(3, 1))
>> +#define HDBSS_ENTRY_TTWL(x) \
>> + (((x) << HDBSS_ENTRY_TTWL_SHIFT) & HDBSS_ENTRY_TTWL_MASK)
>> +#define HDBSS_ENTRY_TTWL_RESV HDBSS_ENTRY_TTWL(-4)
>> +#define HDBSS_ENTRY_IPA GENMASK_ULL(55, 12)
>> +
>> int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
>> void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
>> +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
>> +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
>>
>> #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>> index c41ec6d9c45a..cecfb884a64f 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -55,6 +55,7 @@
>> #define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
>> #define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
>> #define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11)
>> +#define KVM_REQ_FLUSH_HDBSS KVM_ARCH_REQ(12)
>>
>> #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
>> KVM_DIRTY_LOG_INITIALLY_SET)
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index bf6688245d83..566953a4e23a 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -755,6 +755,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
>> kvm_vcpu_put_hw_mmu(vcpu);
>> kvm_arm_vmid_clear_active();
>>
>> + if (vcpu->kvm->arch.enable_hdbss)
>> + kvm_flush_hdbss_buffer(vcpu);
>> +
>> vcpu_clear_on_unsupported_cpu(vcpu);
>> vcpu->cpu = -1;
>> }
>> @@ -1157,6 +1160,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
>> if (kvm_dirty_ring_check_request(vcpu))
>> return 0;
>>
>> + if (kvm_check_request(KVM_REQ_FLUSH_HDBSS, vcpu))
>> + kvm_flush_hdbss_buffer(vcpu);
>> +
>> check_nested_vcpu_requests(vcpu);
>> }
>>
>> @@ -1971,7 +1977,15 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
>>
>> void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
>> {
>> + /*
>> + * Flush all CPUs' dirty log buffers to the dirty_bitmap. Called
>> + * before reporting dirty_bitmap to userspace. Send a request with
>> + * KVM_REQUEST_WAIT to flush buffer synchronously.
>> + */
>> + if (!kvm->arch.enable_hdbss)
>> + return;
>>
>> + kvm_make_all_cpus_request(kvm, KVM_REQ_FLUSH_HDBSS | KVM_REQUEST_WAIT);
>> }
>>
>> static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
>> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
>> index 6c7a6ef66b5a..002366337637 100644
>> --- a/arch/arm64/kvm/dirty_bit.c
>> +++ b/arch/arm64/kvm/dirty_bit.c
>> @@ -50,3 +50,65 @@ void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
>>
>> vcpu->arch.hdbss.hdbssbr_el2 = 0;
>> }
>> +
>> +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu)
>> +{
>> + int idx, curr_idx;
>> + u64 *hdbss_buf;
>> + struct kvm *kvm = vcpu->kvm;
>> +
>> + if (!kvm->arch.enable_hdbss)
>> + return;
>> +
>> + curr_idx = HDBSSPROD_IDX(read_sysreg_s(SYS_HDBSSPROD_EL2));
>> +
>> + /* Do nothing if HDBSS buffer is empty or br_el2 is NULL */
>> + if (curr_idx == 0 || vcpu->arch.hdbss.hdbssbr_el2 == 0)
>> + return;
>> +
>> + hdbss_buf = page_address(phys_to_page(vcpu->arch.hdbss.base_phys));
>> + if (!hdbss_buf)
>> + return;
>> +
>> + guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
>> + for (idx = 0; idx < curr_idx; idx++) {
>> + u64 gpa;
>> +
>> + gpa = hdbss_buf[idx];
>> + if (!(gpa & HDBSS_ENTRY_VALID))
>> + continue;
>> +
>> + gpa &= HDBSS_ENTRY_IPA;
>> + kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
> You mention that it does not support dirty-ring, but above function will
> mark the page as dirty in the dirty-ring :/
>
In kvm_arm_enable_hdbss_global(), we explicitly check and reject HDBSS
enablement if dirty-ring is active:
```
if (kvm->dirty_ring_size)
return 0;
```
So when kvm_flush_hdbss_buffer() runs (which requires enable_hdbss =
true), we know for certain that
kvm->dirty_ring_size == 0. Therefore, kvm_vcpu_mark_page_dirty() will
always take the dirty_bitmap path,
never the dirty-ring path.
That said, I'll add a comment in kvm_flush_hdbss_buffer() before dirty
ring mode is supported, to make this explicit:
```
/*
* HDBSS is mutually exclusive with dirty-ring mode (see
* kvm_arm_enable_hdbss_global()), so kvm_vcpu_mark_page_dirty()
* will update the dirty_bitmap, not the dirty-ring.
*/
```
>> + }
>> +
>> + /* reset HDBSS index */
>> + write_sysreg_s(0, SYS_HDBSSPROD_EL2);
>> + vcpu->arch.hdbss.hdbssprod_el2 = 0;
>> + isb();
>> +}
>> +
>> +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
>> +{
>> + u64 prod;
>> + u64 fsc;
>> +
>> + prod = read_sysreg_s(SYS_HDBSSPROD_EL2);
>> + fsc = FIELD_GET(HDBSSPROD_EL2_FSC_MASK, prod);
>> +
>> + switch (fsc) {
>> + case HDBSSPROD_EL2_FSC_OK:
>> + /* Buffer full, set request to flush on next vcpu exit */
>> + kvm_make_request(KVM_REQ_FLUSH_HDBSS, vcpu);
>> + return 1;
>> + case HDBSSPROD_EL2_FSC_ExternalAbort:
>> + case HDBSSPROD_EL2_FSC_GPF:
>> + return -EFAULT;
>> + default:
>> + /* Unknown fault. */
>> + WARN_ONCE(1,
>> + "Unexpected HDBSS fault type, FSC: 0x%llx (prod=0x%llx, vcpu=%d)\n",
>> + fsc, prod, vcpu->vcpu_id);
>> + return -EFAULT;
>> + }
>> +}
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index 83251d95bf3f..949fb895add6 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -2242,6 +2242,7 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
>> }
>>
>> /**
>> +
>> * kvm_handle_guest_abort - handles all 2nd stage aborts
>> * @vcpu: the VCPU pointer
>> *
>> @@ -2279,6 +2280,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>>
>> is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
>>
>> + if (esr_iss2_is_hdbssf(esr))
>> + return kvm_handle_hdbss_fault(vcpu);
>> +
>> if (esr_fsc_is_translation_fault(esr)) {
>> /* Beyond sanitised PARange (which is the IPA limit) */
>> if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit())) {
>> --
>> 2.33.0
>>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush
2026-07-14 7:38 ` Tian Zheng
@ 2026-07-14 10:50 ` Leonardo Bras
2026-07-14 13:27 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 10:50 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 03:38:39PM +0800, Tian Zheng wrote:
>
> On 7/13/2026 10:06 PM, Leonardo Bras wrote:
> > On Thu, Jul 09, 2026 at 06:40:25PM +0800, Tian Zheng wrote:
> > > From: eillon <yezhenyu2@huawei.com>
> > >
> > > Add HDBSS fault handling for buffer full, external abort, and general
> > > protection fault (GPF) events. When the HDBSS buffer becomes full,
> > > the hardware traps to EL2 with an HDBSSF event, which is handled by
> > > setting a flush request.
> > >
> > > Add kvm_flush_hdbss_buffer() to consume HDBSS buffer entries and
> > > propagate dirty information into the userspace-visible dirty bitmap.
> > > Flush is triggered on vcpu_put, check_vcpu_requests, and
> > > sync_dirty_log.
> > >
> > > Add esr_iss2_is_hdbssf() helper for HDBSS fault detection in guest
> > > abort handling.
> > >
> > > Signed-off-by: Eillon <yezhenyu2@huawei.com>
> > > Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> > > ---
> > > arch/arm64/include/asm/esr.h | 5 +++
> > > arch/arm64/include/asm/kvm_dirty_bit.h | 11 +++++
> > > arch/arm64/include/asm/kvm_host.h | 1 +
> > > arch/arm64/kvm/arm.c | 14 ++++++
> > > arch/arm64/kvm/dirty_bit.c | 62 ++++++++++++++++++++++++++
> > > arch/arm64/kvm/mmu.c | 4 ++
> > > 6 files changed, 97 insertions(+)
> > >
> > > diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> > > index 81c17320a588..2e6b679b5908 100644
> > > --- a/arch/arm64/include/asm/esr.h
> > > +++ b/arch/arm64/include/asm/esr.h
> > > @@ -437,6 +437,11 @@
> > > #ifndef __ASSEMBLER__
> > > #include <asm/types.h>
> > >
> > > +static inline bool esr_iss2_is_hdbssf(unsigned long esr)
> > > +{
> > > + return ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF;
> > This will return a long, which will be casted as bool.
> > In general, what I see in the kernel is something like:
> >
> > return !!(ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF)
>
>
> ok!
>
>
> > > +}
> > > +
> > > static inline unsigned long esr_brk_comment(unsigned long esr)
> > > {
> > > return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
> > > diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > index 84b12f0a10af..4b28000e972f 100644
> > > --- a/arch/arm64/include/asm/kvm_dirty_bit.h
> > > +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > @@ -10,7 +10,18 @@
> > > #include <asm/kvm_pgtable.h>
> > > #include <asm/sysreg.h>
> > >
> > > +/* HDBSS entry field definitions */
> > > +#define HDBSS_ENTRY_VALID BIT(0)
> > > +#define HDBSS_ENTRY_TTWL_SHIFT (1)
> > > +#define HDBSS_ENTRY_TTWL_MASK (GENMASK(3, 1))
> > > +#define HDBSS_ENTRY_TTWL(x) \
> > > + (((x) << HDBSS_ENTRY_TTWL_SHIFT) & HDBSS_ENTRY_TTWL_MASK)
> > > +#define HDBSS_ENTRY_TTWL_RESV HDBSS_ENTRY_TTWL(-4)
> > > +#define HDBSS_ENTRY_IPA GENMASK_ULL(55, 12)
> > > +
> > > int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> > > void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> > > +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
> > > +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
> > >
> > > #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > index c41ec6d9c45a..cecfb884a64f 100644
> > > --- a/arch/arm64/include/asm/kvm_host.h
> > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > @@ -55,6 +55,7 @@
> > > #define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
> > > #define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
> > > #define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11)
> > > +#define KVM_REQ_FLUSH_HDBSS KVM_ARCH_REQ(12)
> > >
> > > #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
> > > KVM_DIRTY_LOG_INITIALLY_SET)
> > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > index bf6688245d83..566953a4e23a 100644
> > > --- a/arch/arm64/kvm/arm.c
> > > +++ b/arch/arm64/kvm/arm.c
> > > @@ -755,6 +755,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
> > > kvm_vcpu_put_hw_mmu(vcpu);
> > > kvm_arm_vmid_clear_active();
> > >
> > > + if (vcpu->kvm->arch.enable_hdbss)
> > > + kvm_flush_hdbss_buffer(vcpu);
> > > +
> > > vcpu_clear_on_unsupported_cpu(vcpu);
> > > vcpu->cpu = -1;
> > > }
> > > @@ -1157,6 +1160,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
> > > if (kvm_dirty_ring_check_request(vcpu))
> > > return 0;
> > >
> > > + if (kvm_check_request(KVM_REQ_FLUSH_HDBSS, vcpu))
> > > + kvm_flush_hdbss_buffer(vcpu);
> > > +
> > > check_nested_vcpu_requests(vcpu);
> > > }
> > >
> > > @@ -1971,7 +1977,15 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
> > >
> > > void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
> > > {
> > > + /*
> > > + * Flush all CPUs' dirty log buffers to the dirty_bitmap. Called
> > > + * before reporting dirty_bitmap to userspace. Send a request with
> > > + * KVM_REQUEST_WAIT to flush buffer synchronously.
> > > + */
> > > + if (!kvm->arch.enable_hdbss)
> > > + return;
> > >
> > > + kvm_make_all_cpus_request(kvm, KVM_REQ_FLUSH_HDBSS | KVM_REQUEST_WAIT);
> > > }
> > >
> > > static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
> > > diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> > > index 6c7a6ef66b5a..002366337637 100644
> > > --- a/arch/arm64/kvm/dirty_bit.c
> > > +++ b/arch/arm64/kvm/dirty_bit.c
> > > @@ -50,3 +50,65 @@ void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
> > >
> > > vcpu->arch.hdbss.hdbssbr_el2 = 0;
> > > }
> > > +
> > > +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu)
> > > +{
> > > + int idx, curr_idx;
> > > + u64 *hdbss_buf;
> > > + struct kvm *kvm = vcpu->kvm;
> > > +
> > > + if (!kvm->arch.enable_hdbss)
> > > + return;
> > > +
> > > + curr_idx = HDBSSPROD_IDX(read_sysreg_s(SYS_HDBSSPROD_EL2));
> > > +
> > > + /* Do nothing if HDBSS buffer is empty or br_el2 is NULL */
> > > + if (curr_idx == 0 || vcpu->arch.hdbss.hdbssbr_el2 == 0)
> > > + return;
> > > +
> > > + hdbss_buf = page_address(phys_to_page(vcpu->arch.hdbss.base_phys));
> > > + if (!hdbss_buf)
> > > + return;
> > > +
> > > + guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
> > > + for (idx = 0; idx < curr_idx; idx++) {
> > > + u64 gpa;
> > > +
> > > + gpa = hdbss_buf[idx];
> > > + if (!(gpa & HDBSS_ENTRY_VALID))
> > > + continue;
> > > +
> > > + gpa &= HDBSS_ENTRY_IPA;
> > > + kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
> > You mention that it does not support dirty-ring, but above function will
> > mark the page as dirty in the dirty-ring :/
> >
>
> In kvm_arm_enable_hdbss_global(), we explicitly check and reject HDBSS
> enablement if dirty-ring is active:
>
> ```
> if (kvm->dirty_ring_size)
> return 0;
> ```
>
> So when kvm_flush_hdbss_buffer() runs (which requires enable_hdbss = true),
> we know for certain that
>
> kvm->dirty_ring_size == 0. Therefore, kvm_vcpu_mark_page_dirty() will always
> take the dirty_bitmap path,
>
> never the dirty-ring path.
>
> That said, I'll add a comment in kvm_flush_hdbss_buffer() before dirty ring
> mode is supported, to make this explicit:
>
> ```
> /*
> * HDBSS is mutually exclusive with dirty-ring mode (see
> * kvm_arm_enable_hdbss_global()), so kvm_vcpu_mark_page_dirty()
> * will update the dirty_bitmap, not the dirty-ring.
> */
> ```
>
Got it :)
Out of curiosity: which issues have you found on supporting dirty-ring at
this point?
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush
2026-07-14 10:50 ` Leonardo Bras
@ 2026-07-14 13:27 ` Tian Zheng
2026-07-14 14:19 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 13:27 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/14/2026 6:50 PM, Leonardo Bras wrote:
> On Tue, Jul 14, 2026 at 03:38:39PM +0800, Tian Zheng wrote:
>> On 7/13/2026 10:06 PM, Leonardo Bras wrote:
>>> On Thu, Jul 09, 2026 at 06:40:25PM +0800, Tian Zheng wrote:
>>>> From: eillon <yezhenyu2@huawei.com>
>>>>
>>>> Add HDBSS fault handling for buffer full, external abort, and general
>>>> protection fault (GPF) events. When the HDBSS buffer becomes full,
>>>> the hardware traps to EL2 with an HDBSSF event, which is handled by
>>>> setting a flush request.
>>>>
>>>> Add kvm_flush_hdbss_buffer() to consume HDBSS buffer entries and
>>>> propagate dirty information into the userspace-visible dirty bitmap.
>>>> Flush is triggered on vcpu_put, check_vcpu_requests, and
>>>> sync_dirty_log.
>>>>
>>>> Add esr_iss2_is_hdbssf() helper for HDBSS fault detection in guest
>>>> abort handling.
>>>>
>>>> Signed-off-by: Eillon <yezhenyu2@huawei.com>
>>>> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
>>>> ---
>>>> arch/arm64/include/asm/esr.h | 5 +++
>>>> arch/arm64/include/asm/kvm_dirty_bit.h | 11 +++++
>>>> arch/arm64/include/asm/kvm_host.h | 1 +
>>>> arch/arm64/kvm/arm.c | 14 ++++++
>>>> arch/arm64/kvm/dirty_bit.c | 62 ++++++++++++++++++++++++++
>>>> arch/arm64/kvm/mmu.c | 4 ++
>>>> 6 files changed, 97 insertions(+)
>>>>
>>>> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
>>>> index 81c17320a588..2e6b679b5908 100644
>>>> --- a/arch/arm64/include/asm/esr.h
>>>> +++ b/arch/arm64/include/asm/esr.h
>>>> @@ -437,6 +437,11 @@
>>>> #ifndef __ASSEMBLER__
>>>> #include <asm/types.h>
>>>>
>>>> +static inline bool esr_iss2_is_hdbssf(unsigned long esr)
>>>> +{
>>>> + return ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF;
>>> This will return a long, which will be casted as bool.
>>> In general, what I see in the kernel is something like:
>>>
>>> return !!(ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF)
>>
>> ok!
>>
>>
>>>> +}
>>>> +
>>>> static inline unsigned long esr_brk_comment(unsigned long esr)
>>>> {
>>>> return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
>>>> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
>>>> index 84b12f0a10af..4b28000e972f 100644
>>>> --- a/arch/arm64/include/asm/kvm_dirty_bit.h
>>>> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
>>>> @@ -10,7 +10,18 @@
>>>> #include <asm/kvm_pgtable.h>
>>>> #include <asm/sysreg.h>
>>>>
>>>> +/* HDBSS entry field definitions */
>>>> +#define HDBSS_ENTRY_VALID BIT(0)
>>>> +#define HDBSS_ENTRY_TTWL_SHIFT (1)
>>>> +#define HDBSS_ENTRY_TTWL_MASK (GENMASK(3, 1))
>>>> +#define HDBSS_ENTRY_TTWL(x) \
>>>> + (((x) << HDBSS_ENTRY_TTWL_SHIFT) & HDBSS_ENTRY_TTWL_MASK)
>>>> +#define HDBSS_ENTRY_TTWL_RESV HDBSS_ENTRY_TTWL(-4)
>>>> +#define HDBSS_ENTRY_IPA GENMASK_ULL(55, 12)
>>>> +
>>>> int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
>>>> void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
>>>> +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
>>>> +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
>>>>
>>>> #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
>>>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>>>> index c41ec6d9c45a..cecfb884a64f 100644
>>>> --- a/arch/arm64/include/asm/kvm_host.h
>>>> +++ b/arch/arm64/include/asm/kvm_host.h
>>>> @@ -55,6 +55,7 @@
>>>> #define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
>>>> #define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
>>>> #define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11)
>>>> +#define KVM_REQ_FLUSH_HDBSS KVM_ARCH_REQ(12)
>>>>
>>>> #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
>>>> KVM_DIRTY_LOG_INITIALLY_SET)
>>>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>>>> index bf6688245d83..566953a4e23a 100644
>>>> --- a/arch/arm64/kvm/arm.c
>>>> +++ b/arch/arm64/kvm/arm.c
>>>> @@ -755,6 +755,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
>>>> kvm_vcpu_put_hw_mmu(vcpu);
>>>> kvm_arm_vmid_clear_active();
>>>>
>>>> + if (vcpu->kvm->arch.enable_hdbss)
>>>> + kvm_flush_hdbss_buffer(vcpu);
>>>> +
>>>> vcpu_clear_on_unsupported_cpu(vcpu);
>>>> vcpu->cpu = -1;
>>>> }
>>>> @@ -1157,6 +1160,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
>>>> if (kvm_dirty_ring_check_request(vcpu))
>>>> return 0;
>>>>
>>>> + if (kvm_check_request(KVM_REQ_FLUSH_HDBSS, vcpu))
>>>> + kvm_flush_hdbss_buffer(vcpu);
>>>> +
>>>> check_nested_vcpu_requests(vcpu);
>>>> }
>>>>
>>>> @@ -1971,7 +1977,15 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
>>>>
>>>> void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
>>>> {
>>>> + /*
>>>> + * Flush all CPUs' dirty log buffers to the dirty_bitmap. Called
>>>> + * before reporting dirty_bitmap to userspace. Send a request with
>>>> + * KVM_REQUEST_WAIT to flush buffer synchronously.
>>>> + */
>>>> + if (!kvm->arch.enable_hdbss)
>>>> + return;
>>>>
>>>> + kvm_make_all_cpus_request(kvm, KVM_REQ_FLUSH_HDBSS | KVM_REQUEST_WAIT);
>>>> }
>>>>
>>>> static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
>>>> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
>>>> index 6c7a6ef66b5a..002366337637 100644
>>>> --- a/arch/arm64/kvm/dirty_bit.c
>>>> +++ b/arch/arm64/kvm/dirty_bit.c
>>>> @@ -50,3 +50,65 @@ void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
>>>>
>>>> vcpu->arch.hdbss.hdbssbr_el2 = 0;
>>>> }
>>>> +
>>>> +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu)
>>>> +{
>>>> + int idx, curr_idx;
>>>> + u64 *hdbss_buf;
>>>> + struct kvm *kvm = vcpu->kvm;
>>>> +
>>>> + if (!kvm->arch.enable_hdbss)
>>>> + return;
>>>> +
>>>> + curr_idx = HDBSSPROD_IDX(read_sysreg_s(SYS_HDBSSPROD_EL2));
>>>> +
>>>> + /* Do nothing if HDBSS buffer is empty or br_el2 is NULL */
>>>> + if (curr_idx == 0 || vcpu->arch.hdbss.hdbssbr_el2 == 0)
>>>> + return;
>>>> +
>>>> + hdbss_buf = page_address(phys_to_page(vcpu->arch.hdbss.base_phys));
>>>> + if (!hdbss_buf)
>>>> + return;
>>>> +
>>>> + guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
>>>> + for (idx = 0; idx < curr_idx; idx++) {
>>>> + u64 gpa;
>>>> +
>>>> + gpa = hdbss_buf[idx];
>>>> + if (!(gpa & HDBSS_ENTRY_VALID))
>>>> + continue;
>>>> +
>>>> + gpa &= HDBSS_ENTRY_IPA;
>>>> + kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
>>> You mention that it does not support dirty-ring, but above function will
>>> mark the page as dirty in the dirty-ring :/
>>>
>> In kvm_arm_enable_hdbss_global(), we explicitly check and reject HDBSS
>> enablement if dirty-ring is active:
>>
>> ```
>> if (kvm->dirty_ring_size)
>> return 0;
>> ```
>>
>> So when kvm_flush_hdbss_buffer() runs (which requires enable_hdbss = true),
>> we know for certain that
>>
>> kvm->dirty_ring_size == 0. Therefore, kvm_vcpu_mark_page_dirty() will always
>> take the dirty_bitmap path,
>>
>> never the dirty-ring path.
>>
>> That said, I'll add a comment in kvm_flush_hdbss_buffer() before dirty ring
>> mode is supported, to make this explicit:
>>
>> ```
>> /*
>> * HDBSS is mutually exclusive with dirty-ring mode (see
>> * kvm_arm_enable_hdbss_global()), so kvm_vcpu_mark_page_dirty()
>> * will update the dirty_bitmap, not the dirty-ring.
>> */
>> ```
>>
> Got it :)
>
> Out of curiosity: which issues have you found on supporting dirty-ring at
> this point?
>
> Thanks!
> Leo
>
I haven't looked deeply into dirty-ring yet — my main concern is that if
both the dirty
ring and HDBSS buffer fill up, the flush path might get blocked or
complicated.
For now, I'm planning to match the HDBSS buffer size to the dirty ring
size in v5 and test it.
Ideally, the two buffers would be the same size, and the entire dirty
tracking path would use
HDBSS exclusively — no fallback to the legacy dirty bitmap path. If that
works, I think this approach should be fine.
Let me know if you have any insights on dirty ring's full-buffer
behavior — that would be helpful.
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush
2026-07-14 13:27 ` Tian Zheng
@ 2026-07-14 14:19 ` Leonardo Bras
0 siblings, 0 replies; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 14:19 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 09:27:15PM +0800, Tian Zheng wrote:
>
> On 7/14/2026 6:50 PM, Leonardo Bras wrote:
> > On Tue, Jul 14, 2026 at 03:38:39PM +0800, Tian Zheng wrote:
> > > On 7/13/2026 10:06 PM, Leonardo Bras wrote:
> > > > On Thu, Jul 09, 2026 at 06:40:25PM +0800, Tian Zheng wrote:
> > > > > From: eillon <yezhenyu2@huawei.com>
> > > > >
> > > > > Add HDBSS fault handling for buffer full, external abort, and general
> > > > > protection fault (GPF) events. When the HDBSS buffer becomes full,
> > > > > the hardware traps to EL2 with an HDBSSF event, which is handled by
> > > > > setting a flush request.
> > > > >
> > > > > Add kvm_flush_hdbss_buffer() to consume HDBSS buffer entries and
> > > > > propagate dirty information into the userspace-visible dirty bitmap.
> > > > > Flush is triggered on vcpu_put, check_vcpu_requests, and
> > > > > sync_dirty_log.
> > > > >
> > > > > Add esr_iss2_is_hdbssf() helper for HDBSS fault detection in guest
> > > > > abort handling.
> > > > >
> > > > > Signed-off-by: Eillon <yezhenyu2@huawei.com>
> > > > > Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> > > > > ---
> > > > > arch/arm64/include/asm/esr.h | 5 +++
> > > > > arch/arm64/include/asm/kvm_dirty_bit.h | 11 +++++
> > > > > arch/arm64/include/asm/kvm_host.h | 1 +
> > > > > arch/arm64/kvm/arm.c | 14 ++++++
> > > > > arch/arm64/kvm/dirty_bit.c | 62 ++++++++++++++++++++++++++
> > > > > arch/arm64/kvm/mmu.c | 4 ++
> > > > > 6 files changed, 97 insertions(+)
> > > > >
> > > > > diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> > > > > index 81c17320a588..2e6b679b5908 100644
> > > > > --- a/arch/arm64/include/asm/esr.h
> > > > > +++ b/arch/arm64/include/asm/esr.h
> > > > > @@ -437,6 +437,11 @@
> > > > > #ifndef __ASSEMBLER__
> > > > > #include <asm/types.h>
> > > > >
> > > > > +static inline bool esr_iss2_is_hdbssf(unsigned long esr)
> > > > > +{
> > > > > + return ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF;
> > > > This will return a long, which will be casted as bool.
> > > > In general, what I see in the kernel is something like:
> > > >
> > > > return !!(ESR_ELx_ISS2(esr) & ESR_ELx_HDBSSF)
> > >
> > > ok!
> > >
> > >
> > > > > +}
> > > > > +
> > > > > static inline unsigned long esr_brk_comment(unsigned long esr)
> > > > > {
> > > > > return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
> > > > > diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > > > index 84b12f0a10af..4b28000e972f 100644
> > > > > --- a/arch/arm64/include/asm/kvm_dirty_bit.h
> > > > > +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > > > @@ -10,7 +10,18 @@
> > > > > #include <asm/kvm_pgtable.h>
> > > > > #include <asm/sysreg.h>
> > > > >
> > > > > +/* HDBSS entry field definitions */
> > > > > +#define HDBSS_ENTRY_VALID BIT(0)
> > > > > +#define HDBSS_ENTRY_TTWL_SHIFT (1)
> > > > > +#define HDBSS_ENTRY_TTWL_MASK (GENMASK(3, 1))
> > > > > +#define HDBSS_ENTRY_TTWL(x) \
> > > > > + (((x) << HDBSS_ENTRY_TTWL_SHIFT) & HDBSS_ENTRY_TTWL_MASK)
> > > > > +#define HDBSS_ENTRY_TTWL_RESV HDBSS_ENTRY_TTWL(-4)
> > > > > +#define HDBSS_ENTRY_IPA GENMASK_ULL(55, 12)
> > > > > +
> > > > > int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> > > > > void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> > > > > +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
> > > > > +int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
> > > > >
> > > > > #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> > > > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > > > index c41ec6d9c45a..cecfb884a64f 100644
> > > > > --- a/arch/arm64/include/asm/kvm_host.h
> > > > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > > > @@ -55,6 +55,7 @@
> > > > > #define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
> > > > > #define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
> > > > > #define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11)
> > > > > +#define KVM_REQ_FLUSH_HDBSS KVM_ARCH_REQ(12)
> > > > >
> > > > > #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
> > > > > KVM_DIRTY_LOG_INITIALLY_SET)
> > > > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > > > index bf6688245d83..566953a4e23a 100644
> > > > > --- a/arch/arm64/kvm/arm.c
> > > > > +++ b/arch/arm64/kvm/arm.c
> > > > > @@ -755,6 +755,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
> > > > > kvm_vcpu_put_hw_mmu(vcpu);
> > > > > kvm_arm_vmid_clear_active();
> > > > >
> > > > > + if (vcpu->kvm->arch.enable_hdbss)
> > > > > + kvm_flush_hdbss_buffer(vcpu);
> > > > > +
> > > > > vcpu_clear_on_unsupported_cpu(vcpu);
> > > > > vcpu->cpu = -1;
> > > > > }
> > > > > @@ -1157,6 +1160,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
> > > > > if (kvm_dirty_ring_check_request(vcpu))
> > > > > return 0;
> > > > >
> > > > > + if (kvm_check_request(KVM_REQ_FLUSH_HDBSS, vcpu))
> > > > > + kvm_flush_hdbss_buffer(vcpu);
> > > > > +
> > > > > check_nested_vcpu_requests(vcpu);
> > > > > }
> > > > >
> > > > > @@ -1971,7 +1977,15 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
> > > > >
> > > > > void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
> > > > > {
> > > > > + /*
> > > > > + * Flush all CPUs' dirty log buffers to the dirty_bitmap. Called
> > > > > + * before reporting dirty_bitmap to userspace. Send a request with
> > > > > + * KVM_REQUEST_WAIT to flush buffer synchronously.
> > > > > + */
> > > > > + if (!kvm->arch.enable_hdbss)
> > > > > + return;
> > > > >
> > > > > + kvm_make_all_cpus_request(kvm, KVM_REQ_FLUSH_HDBSS | KVM_REQUEST_WAIT);
> > > > > }
> > > > >
> > > > > static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
> > > > > diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> > > > > index 6c7a6ef66b5a..002366337637 100644
> > > > > --- a/arch/arm64/kvm/dirty_bit.c
> > > > > +++ b/arch/arm64/kvm/dirty_bit.c
> > > > > @@ -50,3 +50,65 @@ void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu)
> > > > >
> > > > > vcpu->arch.hdbss.hdbssbr_el2 = 0;
> > > > > }
> > > > > +
> > > > > +void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu)
> > > > > +{
> > > > > + int idx, curr_idx;
> > > > > + u64 *hdbss_buf;
> > > > > + struct kvm *kvm = vcpu->kvm;
> > > > > +
> > > > > + if (!kvm->arch.enable_hdbss)
> > > > > + return;
> > > > > +
> > > > > + curr_idx = HDBSSPROD_IDX(read_sysreg_s(SYS_HDBSSPROD_EL2));
> > > > > +
> > > > > + /* Do nothing if HDBSS buffer is empty or br_el2 is NULL */
> > > > > + if (curr_idx == 0 || vcpu->arch.hdbss.hdbssbr_el2 == 0)
> > > > > + return;
> > > > > +
> > > > > + hdbss_buf = page_address(phys_to_page(vcpu->arch.hdbss.base_phys));
> > > > > + if (!hdbss_buf)
> > > > > + return;
> > > > > +
> > > > > + guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
> > > > > + for (idx = 0; idx < curr_idx; idx++) {
> > > > > + u64 gpa;
> > > > > +
> > > > > + gpa = hdbss_buf[idx];
> > > > > + if (!(gpa & HDBSS_ENTRY_VALID))
> > > > > + continue;
> > > > > +
> > > > > + gpa &= HDBSS_ENTRY_IPA;
> > > > > + kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
> > > > You mention that it does not support dirty-ring, but above function will
> > > > mark the page as dirty in the dirty-ring :/
> > > >
> > > In kvm_arm_enable_hdbss_global(), we explicitly check and reject HDBSS
> > > enablement if dirty-ring is active:
> > >
> > > ```
> > > if (kvm->dirty_ring_size)
> > > return 0;
> > > ```
> > >
> > > So when kvm_flush_hdbss_buffer() runs (which requires enable_hdbss = true),
> > > we know for certain that
> > >
> > > kvm->dirty_ring_size == 0. Therefore, kvm_vcpu_mark_page_dirty() will always
> > > take the dirty_bitmap path,
> > >
> > > never the dirty-ring path.
> > >
> > > That said, I'll add a comment in kvm_flush_hdbss_buffer() before dirty ring
> > > mode is supported, to make this explicit:
> > >
> > > ```
> > > /*
> > > * HDBSS is mutually exclusive with dirty-ring mode (see
> > > * kvm_arm_enable_hdbss_global()), so kvm_vcpu_mark_page_dirty()
> > > * will update the dirty_bitmap, not the dirty-ring.
> > > */
> > > ```
> > >
> > Got it :)
> >
> > Out of curiosity: which issues have you found on supporting dirty-ring at
> > this point?
> >
> > Thanks!
> > Leo
>
>
> I haven't looked deeply into dirty-ring yet — my main concern is that if
> both the dirty
>
> ring and HDBSS buffer fill up, the flush path might get blocked or
> complicated.
>
> For now, I'm planning to match the HDBSS buffer size to the dirty ring size
> in v5 and test it.
>
> Ideally, the two buffers would be the same size, and the entire dirty
> tracking path would use
>
Ah, I see the point.
IIRC, when dirty-ring gets full, the kernel returns to userspace with
run->exit_reason == KVM_EXIT_DIRTY_RING_FULL, which will warn the VMM to
drain the dirty-ring, and that makes space for us draining HDBSS to the
dirty-ring again.
The best way to achieve that, as I remember, is to always drain
HDBSS as much as possible at guest_exitting. That will make more space to
newer HDBSS entries, and we can get userspace to drain the dirty-ring
earlier.
I would say to even make HDBSS buffer half (entries) the dirty-ring. Then
we can generally fully drain to the dirty-ring and even report ring full
if the ring is above a given threshold percentage full.
> HDBSS exclusively — no fallback to the legacy dirty bitmap path. If that
> works, I think this approach should be fine.
>
> Let me know if you have any insights on dirty ring's full-buffer behavior —
> that would be helpful.
>
>
Will do!
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
` (4 preceding siblings ...)
2026-07-09 10:40 ` [PATCH v4 5/6] KVM: arm64: Add HDBSS fault handling and buffer flush Tian Zheng
@ 2026-07-09 10:40 ` Tian Zheng
2026-07-13 14:50 ` Leonardo Bras
2026-07-13 10:31 ` [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Leonardo Bras
6 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-09 10:40 UTC (permalink / raw)
To: maz, oupton, catalin.marinas, will, zhengtian10
Cc: yuzenghui, wangzhou1, yangjinqian1, caijian11, liuyonglong,
yezhenyu2, yubihong, linuxarm, joey.gouly, kvmarm, kvm,
linux-arm-kernel, linux-kernel, seiden, suzuki.poulose, leo.bras
From: eillon <yezhenyu2@huawei.com>
HDBSS buffers store per-page dirty state after the stage-2 page tables
have been split down to page granularity (chunk_size == PAGE_SIZE).
When chunk_size == 0 the kernel may lazily skip splitting block mappings,
leaving the page table coarser than what HDBSS expects. Therefore,
enabling HDBSS requires disabling lazy split so that all block mappings
are eagerly broken down before the buffer starts recording.
Add VM-level HDBSS enable/disable support. When dirty logging is
enabled on any memslot, HDBSS is automatically enabled. When dirty
logging is disabled on all memslots, HDBSS is automatically disabled.
This includes:
- kvm_arm_enable_hdbss_global() to enable HDBSS for all vCPUs
- kvm_arm_disable_hdbss_global() to disable and free HDBSS buffers
- kvm_arm_hdbss_on_dirty_logging_change() for auto enable/disable
- kvm_arch_destroy_vm() cleanup path
- kvm_arch_commit_memory_region() integration
Signed-off-by: Eillon <yezhenyu2@huawei.com>
Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
---
arch/arm64/include/asm/kvm_dirty_bit.h | 2 +
arch/arm64/kvm/arm.c | 8 ++
arch/arm64/kvm/dirty_bit.c | 105 +++++++++++++++++++++++++
arch/arm64/kvm/mmu.c | 3 +
4 files changed, 118 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
index 4b28000e972f..a4cda8cdab24 100644
--- a/arch/arm64/include/asm/kvm_dirty_bit.h
+++ b/arch/arm64/include/asm/kvm_dirty_bit.h
@@ -23,5 +23,7 @@ int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
+void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging);
+void kvm_arm_disable_hdbss_global(struct kvm *kvm);
#endif /* __ARM64_KVM_DIRTY_BIT_H__ */
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 566953a4e23a..536d94799ba8 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -317,6 +317,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
if (is_protected_kvm_enabled())
pkvm_destroy_hyp_vm(kvm);
+ /*
+ * Userspace may destroy the VM without disabling dirty logging,
+ * so the auto-disable path is never reached. Force disable HDBSS
+ * here to ensure vCPU buffers are freed and prevent memory leaks.
+ */
+ if (kvm->arch.enable_hdbss)
+ kvm_arm_disable_hdbss_global(kvm);
+
kvm_uninit_stage2_mmu(kvm);
kvm_destroy_mpidr_data(kvm);
diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
index 002366337637..c5bf866c23ef 100644
--- a/arch/arm64/kvm/dirty_bit.c
+++ b/arch/arm64/kvm/dirty_bit.c
@@ -112,3 +112,108 @@ int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
return -EFAULT;
}
}
+
+static unsigned int hdbss_auto_select_order(struct kvm *kvm)
+{
+ unsigned long npages = 0;
+ struct kvm_memory_slot *memslot;
+ int bkt;
+
+ kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm))
+ npages += memslot->npages;
+
+ if (npages <= 16384)
+ return 0;
+ else if (npages <= 262144)
+ return 3;
+ else if (npages <= 4194304)
+ return 6;
+ else
+ return 9;
+}
+
+/*
+ * Enable HDBSS for all vCPUs in the VM.
+ *
+ * Called from kvm_arm_hdbss_on_dirty_logging_change() which is invoked
+ * by kvm_arch_commit_memory_region() under kvm->slots_lock.
+ *
+ * If buffer allocation fails, HDBSS remains disabled and dirty tracking
+ * falls back to the traditional software-based approach (PTE write-protect
+ * + software dirty marking). This does not affect correctness; dirty
+ * logging remains functional without HDBSS.
+ */
+static int kvm_arm_enable_hdbss_global(struct kvm *kvm)
+{
+ int err;
+ unsigned long i;
+ unsigned int order;
+ struct kvm_vcpu *vcpu;
+
+ if (!system_supports_hdbss())
+ return 0;
+
+ if (kvm->dirty_ring_size) /* Don't support HDBSS in dirty ring mode */
+ return 0;
+
+ if (kvm->arch.enable_hdbss) /* Already On */
+ return 0;
+
+ /* Turn it on */
+ order = hdbss_auto_select_order(kvm);
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ err = kvm_arm_vcpu_alloc_hdbss(vcpu, order);
+ if (err)
+ goto error_alloc;
+ }
+
+ kvm->arch.enable_hdbss = true;
+ kvm->arch.mmu.vtcr |= VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA;
+
+ /*
+ * We should kick vcpus out of guest mode here to load new
+ * vtcr value to vtcr_el2 register when re-enter guest mode.
+ */
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_vcpu_kick(vcpu);
+
+ return 0;
+
+error_alloc:
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ if (vcpu->arch.hdbss.base_phys)
+ kvm_arm_vcpu_free_hdbss(vcpu);
+
+ pr_warn_once("kvm: failed to allocate HDBSS buffers (order=%u), "
+ "falling back to software dirty tracking\n", order);
+ return -ENOMEM;
+}
+
+void kvm_arm_disable_hdbss_global(struct kvm *kvm)
+{
+ unsigned long i;
+ struct kvm_vcpu *vcpu;
+
+ if (!kvm->arch.enable_hdbss) /* Already Off */
+ return;
+
+ /* Turn it off */
+ kvm->arch.mmu.vtcr &= ~(VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA);
+
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_arm_vcpu_free_hdbss(vcpu);
+
+ kvm->arch.enable_hdbss = false;
+}
+
+void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging)
+{
+ /*
+ * Called from kvm_arch_commit_memory_region() under kvm->slots_lock.
+ * All state transitions are serialized by slots_lock.
+ */
+ if (nr_memslots_logging > 0 && !kvm->arch.enable_hdbss)
+ kvm_arm_enable_hdbss_global(kvm);
+ else if (nr_memslots_logging == 0 && kvm->arch.enable_hdbss)
+ kvm_arm_disable_hdbss_global(kvm);
+}
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 949fb895add6..484f48dae000 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2588,6 +2588,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
{
bool log_dirty_pages = new && new->flags & KVM_MEM_LOG_DIRTY_PAGES;
+ kvm_arm_hdbss_on_dirty_logging_change(kvm,
+ atomic_read(&kvm->nr_memslots_dirty_logging));
+
/*
* At this point memslot has been committed and there is an
* allocated dirty_bitmap[], dirty pages will be tracked while the
--
2.33.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
2026-07-09 10:40 ` [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change Tian Zheng
@ 2026-07-13 14:50 ` Leonardo Bras
2026-07-14 8:58 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-13 14:50 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Thu, Jul 09, 2026 at 06:40:26PM +0800, Tian Zheng wrote:
> From: eillon <yezhenyu2@huawei.com>
>
> HDBSS buffers store per-page dirty state after the stage-2 page tables
> have been split down to page granularity (chunk_size == PAGE_SIZE).
chunk_size != PAGE_SIZE now, but that should change as well :)
> When chunk_size == 0 the kernel may lazily skip splitting block mappings,
> leaving the page table coarser than what HDBSS expects. Therefore,
> enabling HDBSS requires disabling lazy split so that all block mappings
> are eagerly broken down before the buffer starts recording.
(See cover letter reply)
>
> Add VM-level HDBSS enable/disable support. When dirty logging is
> enabled on any memslot, HDBSS is automatically enabled. When dirty
> logging is disabled on all memslots, HDBSS is automatically disabled.
>
> This includes:
> - kvm_arm_enable_hdbss_global() to enable HDBSS for all vCPUs
> - kvm_arm_disable_hdbss_global() to disable and free HDBSS buffers
> - kvm_arm_hdbss_on_dirty_logging_change() for auto enable/disable
> - kvm_arch_destroy_vm() cleanup path
> - kvm_arch_commit_memory_region() integration
>
> Signed-off-by: Eillon <yezhenyu2@huawei.com>
> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> ---
> arch/arm64/include/asm/kvm_dirty_bit.h | 2 +
> arch/arm64/kvm/arm.c | 8 ++
> arch/arm64/kvm/dirty_bit.c | 105 +++++++++++++++++++++++++
> arch/arm64/kvm/mmu.c | 3 +
> 4 files changed, 118 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> index 4b28000e972f..a4cda8cdab24 100644
> --- a/arch/arm64/include/asm/kvm_dirty_bit.h
> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> @@ -23,5 +23,7 @@ int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
> int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
> +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging);
> +void kvm_arm_disable_hdbss_global(struct kvm *kvm);
>
> #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 566953a4e23a..536d94799ba8 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -317,6 +317,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
> if (is_protected_kvm_enabled())
> pkvm_destroy_hyp_vm(kvm);
>
> + /*
> + * Userspace may destroy the VM without disabling dirty logging,
> + * so the auto-disable path is never reached. Force disable HDBSS
> + * here to ensure vCPU buffers are freed and prevent memory leaks.
> + */
> + if (kvm->arch.enable_hdbss)
> + kvm_arm_disable_hdbss_global(kvm);
> +
> kvm_uninit_stage2_mmu(kvm);
> kvm_destroy_mpidr_data(kvm);
>
> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> index 002366337637..c5bf866c23ef 100644
> --- a/arch/arm64/kvm/dirty_bit.c
> +++ b/arch/arm64/kvm/dirty_bit.c
> @@ -112,3 +112,108 @@ int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
> return -EFAULT;
> }
> }
> +
> +static unsigned int hdbss_auto_select_order(struct kvm *kvm)
> +{
> + unsigned long npages = 0;
> + struct kvm_memory_slot *memslot;
> + int bkt;
> +
> + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm))
> + npages += memslot->npages;
> +
> + if (npages <= 16384)
> + return 0;
> + else if (npages <= 262144)
> + return 3;
> + else if (npages <= 4194304)
> + return 6;
> + else
> + return 9;
> +}
IIUC you are counting the amount of pages the VM has, and based on that
allocating a size for the HDBSS buffer.
A few notes here:
- It's not really nice to use magic numbers around like this. If you
actually want to use it, then use stuff like SZ_16K, SZ_256K, SZ_4M and
so on.
- You are returning magic numbers as well, why is it 0, 3, 6, or 9 here?
It only makes sense if the person is reading HDBSSBR_EL2 documentation,
which should not be necessary at this point. That's one reason I
recommended to using sizes. If that was really the best way to use it,
I would recommend using the defines that we get from sysreg, and you
actually used before to set the maximum order on a previous patch.
- Also, if you can return only valid values here, why do you check against
the maximum value in that previous patch?
- Also, are you using some undisclosed rule here? On 'order 0' the
meanining is 4KB, which translate to 512 HDBSS entries. Why are you using
it for any value under 16K? Same for 3-32KB-4kEntries you use for under
256K pages (and so on). If you are assuming a logical rule such as
'N pages would be ok with N/32 entries' it has to be described here at
least.
- Not sure VM size is the best way of doing that, since it will depend
more on the dirtying rate than the actual size, and most VMs would just
use the biggest size (4M x 4K pages is just 16GB). For instance with
dirty_ring we can use the dirty_ring.size as a better option.
(I know this is a hard one to estimate when using dirty-bitmap, though)
> +
> +/*
> + * Enable HDBSS for all vCPUs in the VM.
> + *
> + * Called from kvm_arm_hdbss_on_dirty_logging_change() which is invoked
> + * by kvm_arch_commit_memory_region() under kvm->slots_lock.
> + *
> + * If buffer allocation fails, HDBSS remains disabled and dirty tracking
> + * falls back to the traditional software-based approach (PTE write-protect
> + * + software dirty marking). This does not affect correctness; dirty
> + * logging remains functional without HDBSS.
> + */
> +static int kvm_arm_enable_hdbss_global(struct kvm *kvm)
> +{
> + int err;
> + unsigned long i;
> + unsigned int order;
> + struct kvm_vcpu *vcpu;
> +
> + if (!system_supports_hdbss())
> + return 0;
> +
> + if (kvm->dirty_ring_size) /* Don't support HDBSS in dirty ring mode */
> + return 0;
> +
> + if (kvm->arch.enable_hdbss) /* Already On */
> + return 0;
> +
> + /* Turn it on */
> + order = hdbss_auto_select_order(kvm);
> + kvm_for_each_vcpu(i, vcpu, kvm) {
> + err = kvm_arm_vcpu_alloc_hdbss(vcpu, order);
> + if (err)
> + goto error_alloc;
> + }
> +
> + kvm->arch.enable_hdbss = true;
> + kvm->arch.mmu.vtcr |= VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA;
> +
> + /*
> + * We should kick vcpus out of guest mode here to load new
> + * vtcr value to vtcr_el2 register when re-enter guest mode.
> + */
> + kvm_for_each_vcpu(i, vcpu, kvm)
> + kvm_vcpu_kick(vcpu);
> +
> + return 0;
> +
> +error_alloc:
> + kvm_for_each_vcpu(i, vcpu, kvm)
> + if (vcpu->arch.hdbss.base_phys)
> + kvm_arm_vcpu_free_hdbss(vcpu);
> +
> + pr_warn_once("kvm: failed to allocate HDBSS buffers (order=%u), "
> + "falling back to software dirty tracking\n", order);
> + return -ENOMEM;
> +}
> +
> +void kvm_arm_disable_hdbss_global(struct kvm *kvm)
> +{
> + unsigned long i;
> + struct kvm_vcpu *vcpu;
> +
> + if (!kvm->arch.enable_hdbss) /* Already Off */
> + return;
> +
> + /* Turn it off */
> + kvm->arch.mmu.vtcr &= ~(VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA);
> +
> + kvm_for_each_vcpu(i, vcpu, kvm)
> + kvm_arm_vcpu_free_hdbss(vcpu);
> +
> + kvm->arch.enable_hdbss = false;
> +}
> +
Okay, say the user requested it to be disabled, you change the global vtcr,
then free the hdbss on every vcpu.
But the vcpus are still running, and since they will only disable this when
they go out of the guest, then in again, HDBSS will still be running,
right?
If some page gets dirty in the between, would not the HDBSS try to write to
the already loaded buffer adress, and write to memory that have already
been freed here?
> +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging)
> +{
> + /*
> + * Called from kvm_arch_commit_memory_region() under kvm->slots_lock.
> + * All state transitions are serialized by slots_lock.
> + */
> + if (nr_memslots_logging > 0 && !kvm->arch.enable_hdbss)
> + kvm_arm_enable_hdbss_global(kvm);
> + else if (nr_memslots_logging == 0 && kvm->arch.enable_hdbss)
> + kvm_arm_disable_hdbss_global(kvm);
> +}
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 949fb895add6..484f48dae000 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -2588,6 +2588,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
> {
> bool log_dirty_pages = new && new->flags & KVM_MEM_LOG_DIRTY_PAGES;
>
> + kvm_arm_hdbss_on_dirty_logging_change(kvm,
> + atomic_read(&kvm->nr_memslots_dirty_logging));
> +
> /*
> * At this point memslot has been committed and there is an
> * allocated dirty_bitmap[], dirty pages will be tracked while the
> --
> 2.33.0
>
Okay, reading the above I remembered something really complicated:
We can't really enable HDBSS partially if we start with DBM set for all
pages. Once we enable HDBSS wit will track changes for all memslots.
The only way to enable it partially would be to set DBM during the
dirty-bit tracking, which I recall being complicated for some reasons.
Well, we have to think about the overall strategy before a next version.
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
2026-07-13 14:50 ` Leonardo Bras
@ 2026-07-14 8:58 ` Tian Zheng
2026-07-14 11:16 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 8:58 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/13/2026 10:50 PM, Leonardo Bras wrote:
> On Thu, Jul 09, 2026 at 06:40:26PM +0800, Tian Zheng wrote:
>> From: eillon <yezhenyu2@huawei.com>
>>
>> HDBSS buffers store per-page dirty state after the stage-2 page tables
>> have been split down to page granularity (chunk_size == PAGE_SIZE).
> chunk_size != PAGE_SIZE now, but that should change as well :)
Thanks, I'll clarify the comment in v5.
>
>> When chunk_size == 0 the kernel may lazily skip splitting block mappings,
>> leaving the page table coarser than what HDBSS expects. Therefore,
>> enabling HDBSS requires disabling lazy split so that all block mappings
>> are eagerly broken down before the buffer starts recording.
> (See cover letter reply)
>
>> Add VM-level HDBSS enable/disable support. When dirty logging is
>> enabled on any memslot, HDBSS is automatically enabled. When dirty
>> logging is disabled on all memslots, HDBSS is automatically disabled.
>>
>> This includes:
>> - kvm_arm_enable_hdbss_global() to enable HDBSS for all vCPUs
>> - kvm_arm_disable_hdbss_global() to disable and free HDBSS buffers
>> - kvm_arm_hdbss_on_dirty_logging_change() for auto enable/disable
>> - kvm_arch_destroy_vm() cleanup path
>> - kvm_arch_commit_memory_region() integration
>>
>> Signed-off-by: Eillon <yezhenyu2@huawei.com>
>> Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
>> ---
>> arch/arm64/include/asm/kvm_dirty_bit.h | 2 +
>> arch/arm64/kvm/arm.c | 8 ++
>> arch/arm64/kvm/dirty_bit.c | 105 +++++++++++++++++++++++++
>> arch/arm64/kvm/mmu.c | 3 +
>> 4 files changed, 118 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
>> index 4b28000e972f..a4cda8cdab24 100644
>> --- a/arch/arm64/include/asm/kvm_dirty_bit.h
>> +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
>> @@ -23,5 +23,7 @@ int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
>> void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
>> void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
>> int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
>> +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging);
>> +void kvm_arm_disable_hdbss_global(struct kvm *kvm);
>>
>> #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 566953a4e23a..536d94799ba8 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -317,6 +317,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
>> if (is_protected_kvm_enabled())
>> pkvm_destroy_hyp_vm(kvm);
>>
>> + /*
>> + * Userspace may destroy the VM without disabling dirty logging,
>> + * so the auto-disable path is never reached. Force disable HDBSS
>> + * here to ensure vCPU buffers are freed and prevent memory leaks.
>> + */
>> + if (kvm->arch.enable_hdbss)
>> + kvm_arm_disable_hdbss_global(kvm);
>> +
>> kvm_uninit_stage2_mmu(kvm);
>> kvm_destroy_mpidr_data(kvm);
>>
>> diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
>> index 002366337637..c5bf866c23ef 100644
>> --- a/arch/arm64/kvm/dirty_bit.c
>> +++ b/arch/arm64/kvm/dirty_bit.c
>> @@ -112,3 +112,108 @@ int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
>> return -EFAULT;
>> }
>> }
>> +
>> +static unsigned int hdbss_auto_select_order(struct kvm *kvm)
>> +{
>> + unsigned long npages = 0;
>> + struct kvm_memory_slot *memslot;
>> + int bkt;
>> +
>> + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm))
>> + npages += memslot->npages;
>> +
>> + if (npages <= 16384)
>> + return 0;
>> + else if (npages <= 262144)
>> + return 3;
>> + else if (npages <= 4194304)
>> + return 6;
>> + else
>> + return 9;
>> +}
> IIUC you are counting the amount of pages the VM has, and based on that
> allocating a size for the HDBSS buffer.
>
> A few notes here:
> - It's not really nice to use magic numbers around like this. If you
> actually want to use it, then use stuff like SZ_16K, SZ_256K, SZ_4M and
> so on.
> - You are returning magic numbers as well, why is it 0, 3, 6, or 9 here?
> It only makes sense if the person is reading HDBSSBR_EL2 documentation,
> which should not be necessary at this point. That's one reason I
> recommended to using sizes. If that was really the best way to use it,
> I would recommend using the defines that we get from sysreg, and you
> actually used before to set the maximum order on a previous patch.
> - Also, if you can return only valid values here, why do you check against
> the maximum value in that previous patch?
> - Also, are you using some undisclosed rule here? On 'order 0' the
> meanining is 4KB, which translate to 512 HDBSS entries. Why are you using
> it for any value under 16K? Same for 3-32KB-4kEntries you use for under
> 256K pages (and so on). If you are assuming a logical rule such as
> 'N pages would be ok with N/32 entries' it has to be described here at
> least.
> - Not sure VM size is the best way of doing that, since it will depend
> more on the dirtying rate than the actual size, and most VMs would just
> use the biggest size (4M x 4K pages is just 16GB). For instance with
> dirty_ring we can use the dirty_ring.size as a better option.
> (I know this is a hard one to estimate when using dirty-bitmap, though)
I'll replace the magic numbers and also add a comment in the next
version explaining
the mapping between the thresholds and the order values.
On auto-choosing the size: VM memory size is a simple starting point,
but I agree it's
not ideal. For dirty-ring mode we could use dirty_ring_size as a
reference; for dirty-bitmap
mode there's no equivalent, so I don't have a good answer yet. I'd
really appreciate any
suggestions from the community on a better idea for the dirty-bitmap case.
Also, since we automatically enable HDBSS in the kernel, the check
against HDBSS_MAX_ORDER is redundant. I'll remove it.
>> +
>> +/*
>> + * Enable HDBSS for all vCPUs in the VM.
>> + *
>> + * Called from kvm_arm_hdbss_on_dirty_logging_change() which is invoked
>> + * by kvm_arch_commit_memory_region() under kvm->slots_lock.
>> + *
>> + * If buffer allocation fails, HDBSS remains disabled and dirty tracking
>> + * falls back to the traditional software-based approach (PTE write-protect
>> + * + software dirty marking). This does not affect correctness; dirty
>> + * logging remains functional without HDBSS.
>> + */
>> +static int kvm_arm_enable_hdbss_global(struct kvm *kvm)
>> +{
>> + int err;
>> + unsigned long i;
>> + unsigned int order;
>> + struct kvm_vcpu *vcpu;
>> +
>> + if (!system_supports_hdbss())
>> + return 0;
>> +
>> + if (kvm->dirty_ring_size) /* Don't support HDBSS in dirty ring mode */
>> + return 0;
>> +
>> + if (kvm->arch.enable_hdbss) /* Already On */
>> + return 0;
>> +
>> + /* Turn it on */
>> + order = hdbss_auto_select_order(kvm);
>> + kvm_for_each_vcpu(i, vcpu, kvm) {
>> + err = kvm_arm_vcpu_alloc_hdbss(vcpu, order);
>> + if (err)
>> + goto error_alloc;
>> + }
>> +
>> + kvm->arch.enable_hdbss = true;
>> + kvm->arch.mmu.vtcr |= VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA;
>> +
>> + /*
>> + * We should kick vcpus out of guest mode here to load new
>> + * vtcr value to vtcr_el2 register when re-enter guest mode.
>> + */
>> + kvm_for_each_vcpu(i, vcpu, kvm)
>> + kvm_vcpu_kick(vcpu);
>> +
>> + return 0;
>> +
>> +error_alloc:
>> + kvm_for_each_vcpu(i, vcpu, kvm)
>> + if (vcpu->arch.hdbss.base_phys)
>> + kvm_arm_vcpu_free_hdbss(vcpu);
>> +
>> + pr_warn_once("kvm: failed to allocate HDBSS buffers (order=%u), "
>> + "falling back to software dirty tracking\n", order);
>> + return -ENOMEM;
>> +}
>> +
>> +void kvm_arm_disable_hdbss_global(struct kvm *kvm)
>> +{
>> + unsigned long i;
>> + struct kvm_vcpu *vcpu;
>> +
>> + if (!kvm->arch.enable_hdbss) /* Already Off */
>> + return;
>> +
>> + /* Turn it off */
>> + kvm->arch.mmu.vtcr &= ~(VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA);
>> +
>> + kvm_for_each_vcpu(i, vcpu, kvm)
>> + kvm_arm_vcpu_free_hdbss(vcpu);
>> +
>> + kvm->arch.enable_hdbss = false;
>> +}
>> +
> Okay, say the user requested it to be disabled, you change the global vtcr,
> then free the hdbss on every vcpu.
>
> But the vcpus are still running, and since they will only disable this when
> they go out of the guest, then in again, HDBSS will still be running,
> right?
>
> If some page gets dirty in the between, would not the HDBSS try to write to
> the already loaded buffer adress, and write to memory that have already
> been freed here?
>
You're right — this is a race condition. I'll fix this in v5 by clearing
VTCR_EL2_HDBSS from
kvm->arch.mmu.vtcr first, then kicking all vCPUs to force them to exit
guest mode and reload the config.
Once all vCPUs are out of guest mode, it will be safe to free the HDBSS
buffers.
>> +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging)
>> +{
>> + /*
>> + * Called from kvm_arch_commit_memory_region() under kvm->slots_lock.
>> + * All state transitions are serialized by slots_lock.
>> + */
>> + if (nr_memslots_logging > 0 && !kvm->arch.enable_hdbss)
>> + kvm_arm_enable_hdbss_global(kvm);
>> + else if (nr_memslots_logging == 0 && kvm->arch.enable_hdbss)
>> + kvm_arm_disable_hdbss_global(kvm);
>> +}
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index 949fb895add6..484f48dae000 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -2588,6 +2588,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
>> {
>> bool log_dirty_pages = new && new->flags & KVM_MEM_LOG_DIRTY_PAGES;
>>
>> + kvm_arm_hdbss_on_dirty_logging_change(kvm,
>> + atomic_read(&kvm->nr_memslots_dirty_logging));
>> +
>> /*
>> * At this point memslot has been committed and there is an
>> * allocated dirty_bitmap[], dirty pages will be tracked while the
>> --
>> 2.33.0
>>
> Okay, reading the above I remembered something really complicated:
> We can't really enable HDBSS partially if we start with DBM set for all
> pages. Once we enable HDBSS wit will track changes for all memslots.
>
> The only way to enable it partially would be to set DBM during the
> dirty-bit tracking, which I recall being complicated for some reasons.
>
> Well, we have to think about the overall strategy before a next version.
>
> Thanks!
> Leo
Yes, I did consider this when I switched to global DBM injection in v4.
There are
indeed some scenarios that are harder to control:
Firstly, for lazy split, if we add the DBM tag lazily during live
migration dirty tracking (like v3 did),
the first write to each page would trap. That trap serves two purposes:
it gives us a chance to split
hugepages on demand (lazy split), and it ensures the DBM addition
happens at a safer, more controlled point.
However, because v4 enables HDBSS and DBM upfront, we lose that initial
trap. That's exactly why we
now rely on your eager hugepage splitting patch as a mandatory dependency.
Secondly, I'm also concerned about whether global DBM injection could
accidentally mark pages
that shouldn't be tracked — for example, pages with special mappings. If
that's possible, then the
lazy approach (only adding DBM on the first write fault) would be safer
because it only touches pages
that are actually written to.
So I'd like to ask: is avoiding the first-trap overhead worth the
potential risks of global DBM injection?
Or do you think the lazy approach is actually safer overall? I'd
appreciate your thoughts on this trade-off.
Thanks!
Tian
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
2026-07-14 8:58 ` Tian Zheng
@ 2026-07-14 11:16 ` Leonardo Bras
2026-07-14 14:33 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 11:16 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 04:58:37PM +0800, Tian Zheng wrote:
>
> On 7/13/2026 10:50 PM, Leonardo Bras wrote:
> > On Thu, Jul 09, 2026 at 06:40:26PM +0800, Tian Zheng wrote:
> > > From: eillon <yezhenyu2@huawei.com>
> > >
> > > HDBSS buffers store per-page dirty state after the stage-2 page tables
> > > have been split down to page granularity (chunk_size == PAGE_SIZE).
> > chunk_size != PAGE_SIZE now, but that should change as well :)
>
>
> Thanks, I'll clarify the comment in v5.
By the discussion we are having in the HACDBS patchset, I think we can't
assume the pages are split in the future. :\
>
>
> >
> > > When chunk_size == 0 the kernel may lazily skip splitting block mappings,
> > > leaving the page table coarser than what HDBSS expects. Therefore,
> > > enabling HDBSS requires disabling lazy split so that all block mappings
> > > are eagerly broken down before the buffer starts recording.
> > (See cover letter reply)
> >
> > > Add VM-level HDBSS enable/disable support. When dirty logging is
> > > enabled on any memslot, HDBSS is automatically enabled. When dirty
> > > logging is disabled on all memslots, HDBSS is automatically disabled.
> > >
> > > This includes:
> > > - kvm_arm_enable_hdbss_global() to enable HDBSS for all vCPUs
> > > - kvm_arm_disable_hdbss_global() to disable and free HDBSS buffers
> > > - kvm_arm_hdbss_on_dirty_logging_change() for auto enable/disable
> > > - kvm_arch_destroy_vm() cleanup path
> > > - kvm_arch_commit_memory_region() integration
> > >
> > > Signed-off-by: Eillon <yezhenyu2@huawei.com>
> > > Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> > > ---
> > > arch/arm64/include/asm/kvm_dirty_bit.h | 2 +
> > > arch/arm64/kvm/arm.c | 8 ++
> > > arch/arm64/kvm/dirty_bit.c | 105 +++++++++++++++++++++++++
> > > arch/arm64/kvm/mmu.c | 3 +
> > > 4 files changed, 118 insertions(+)
> > >
> > > diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > index 4b28000e972f..a4cda8cdab24 100644
> > > --- a/arch/arm64/include/asm/kvm_dirty_bit.h
> > > +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > @@ -23,5 +23,7 @@ int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> > > void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> > > void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
> > > int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
> > > +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging);
> > > +void kvm_arm_disable_hdbss_global(struct kvm *kvm);
> > >
> > > #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > index 566953a4e23a..536d94799ba8 100644
> > > --- a/arch/arm64/kvm/arm.c
> > > +++ b/arch/arm64/kvm/arm.c
> > > @@ -317,6 +317,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
> > > if (is_protected_kvm_enabled())
> > > pkvm_destroy_hyp_vm(kvm);
> > >
> > > + /*
> > > + * Userspace may destroy the VM without disabling dirty logging,
> > > + * so the auto-disable path is never reached. Force disable HDBSS
> > > + * here to ensure vCPU buffers are freed and prevent memory leaks.
> > > + */
> > > + if (kvm->arch.enable_hdbss)
> > > + kvm_arm_disable_hdbss_global(kvm);
> > > +
> > > kvm_uninit_stage2_mmu(kvm);
> > > kvm_destroy_mpidr_data(kvm);
> > >
> > > diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> > > index 002366337637..c5bf866c23ef 100644
> > > --- a/arch/arm64/kvm/dirty_bit.c
> > > +++ b/arch/arm64/kvm/dirty_bit.c
> > > @@ -112,3 +112,108 @@ int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
> > > return -EFAULT;
> > > }
> > > }
> > > +
> > > +static unsigned int hdbss_auto_select_order(struct kvm *kvm)
> > > +{
> > > + unsigned long npages = 0;
> > > + struct kvm_memory_slot *memslot;
> > > + int bkt;
> > > +
> > > + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm))
> > > + npages += memslot->npages;
> > > +
> > > + if (npages <= 16384)
> > > + return 0;
> > > + else if (npages <= 262144)
> > > + return 3;
> > > + else if (npages <= 4194304)
> > > + return 6;
> > > + else
> > > + return 9;
> > > +}
> > IIUC you are counting the amount of pages the VM has, and based on that
> > allocating a size for the HDBSS buffer.
> >
> > A few notes here:
> > - It's not really nice to use magic numbers around like this. If you
> > actually want to use it, then use stuff like SZ_16K, SZ_256K, SZ_4M and
> > so on.
> > - You are returning magic numbers as well, why is it 0, 3, 6, or 9 here?
> > It only makes sense if the person is reading HDBSSBR_EL2 documentation,
> > which should not be necessary at this point. That's one reason I
> > recommended to using sizes. If that was really the best way to use it,
> > I would recommend using the defines that we get from sysreg, and you
> > actually used before to set the maximum order on a previous patch.
> > - Also, if you can return only valid values here, why do you check against
> > the maximum value in that previous patch?
> > - Also, are you using some undisclosed rule here? On 'order 0' the
> > meanining is 4KB, which translate to 512 HDBSS entries. Why are you using
> > it for any value under 16K? Same for 3-32KB-4kEntries you use for under
> > 256K pages (and so on). If you are assuming a logical rule such as
> > 'N pages would be ok with N/32 entries' it has to be described here at
> > least.
> > - Not sure VM size is the best way of doing that, since it will depend
> > more on the dirtying rate than the actual size, and most VMs would just
> > use the biggest size (4M x 4K pages is just 16GB). For instance with
> > dirty_ring we can use the dirty_ring.size as a better option.
> > (I know this is a hard one to estimate when using dirty-bitmap, though)
>
>
> I'll replace the magic numbers and also add a comment in the next version
> explaining
>
> the mapping between the thresholds and the order values.
>
>
> On auto-choosing the size: VM memory size is a simple starting point, but I
> agree it's
>
> not ideal. For dirty-ring mode we could use dirty_ring_size as a reference;
> for dirty-bitmap
>
> mode there's no equivalent, so I don't have a good answer yet. I'd really
> appreciate any
>
> suggestions from the community on a better idea for the dirty-bitmap case.
I am thinking that we could use a default value (say 1 PAGESIZE/vcpu) and
add an ioctl to optionally increase this value. This way we don't require
a new interface to benefit from HDBSS, but allow users to tune it.
>
> Also, since we automatically enable HDBSS in the kernel, the check against
> HDBSS_MAX_ORDER is redundant. I'll remove it.
>
>
> > > +
> > > +/*
> > > + * Enable HDBSS for all vCPUs in the VM.
> > > + *
> > > + * Called from kvm_arm_hdbss_on_dirty_logging_change() which is invoked
> > > + * by kvm_arch_commit_memory_region() under kvm->slots_lock.
> > > + *
> > > + * If buffer allocation fails, HDBSS remains disabled and dirty tracking
> > > + * falls back to the traditional software-based approach (PTE write-protect
> > > + * + software dirty marking). This does not affect correctness; dirty
> > > + * logging remains functional without HDBSS.
> > > + */
> > > +static int kvm_arm_enable_hdbss_global(struct kvm *kvm)
> > > +{
> > > + int err;
> > > + unsigned long i;
> > > + unsigned int order;
> > > + struct kvm_vcpu *vcpu;
> > > +
> > > + if (!system_supports_hdbss())
> > > + return 0;
> > > +
> > > + if (kvm->dirty_ring_size) /* Don't support HDBSS in dirty ring mode */
> > > + return 0;
> > > +
> > > + if (kvm->arch.enable_hdbss) /* Already On */
> > > + return 0;
> > > +
> > > + /* Turn it on */
> > > + order = hdbss_auto_select_order(kvm);
> > > + kvm_for_each_vcpu(i, vcpu, kvm) {
> > > + err = kvm_arm_vcpu_alloc_hdbss(vcpu, order);
> > > + if (err)
> > > + goto error_alloc;
> > > + }
> > > +
> > > + kvm->arch.enable_hdbss = true;
> > > + kvm->arch.mmu.vtcr |= VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA;
> > > +
> > > + /*
> > > + * We should kick vcpus out of guest mode here to load new
> > > + * vtcr value to vtcr_el2 register when re-enter guest mode.
> > > + */
> > > + kvm_for_each_vcpu(i, vcpu, kvm)
> > > + kvm_vcpu_kick(vcpu);
> > > +
> > > + return 0;
> > > +
> > > +error_alloc:
> > > + kvm_for_each_vcpu(i, vcpu, kvm)
> > > + if (vcpu->arch.hdbss.base_phys)
> > > + kvm_arm_vcpu_free_hdbss(vcpu);
> > > +
> > > + pr_warn_once("kvm: failed to allocate HDBSS buffers (order=%u), "
> > > + "falling back to software dirty tracking\n", order);
> > > + return -ENOMEM;
> > > +}
> > > +
> > > +void kvm_arm_disable_hdbss_global(struct kvm *kvm)
> > > +{
> > > + unsigned long i;
> > > + struct kvm_vcpu *vcpu;
> > > +
> > > + if (!kvm->arch.enable_hdbss) /* Already Off */
> > > + return;
> > > +
> > > + /* Turn it off */
> > > + kvm->arch.mmu.vtcr &= ~(VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA);
> > > +
> > > + kvm_for_each_vcpu(i, vcpu, kvm)
> > > + kvm_arm_vcpu_free_hdbss(vcpu);
> > > +
> > > + kvm->arch.enable_hdbss = false;
> > > +}
> > > +
> > Okay, say the user requested it to be disabled, you change the global vtcr,
> > then free the hdbss on every vcpu.
> >
> > But the vcpus are still running, and since they will only disable this when
> > they go out of the guest, then in again, HDBSS will still be running,
> > right?
> >
> > If some page gets dirty in the between, would not the HDBSS try to write to
> > the already loaded buffer adress, and write to memory that have already
> > been freed here?
> >
>
> You're right — this is a race condition. I'll fix this in v5 by clearing
> VTCR_EL2_HDBSS from
>
> kvm->arch.mmu.vtcr first, then kicking all vCPUs to force them to exit guest
> mode and reload the config.
>
> Once all vCPUs are out of guest mode, it will be safe to free the HDBSS
> buffers.
>
That would be safer, indeed.
>
> > > +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging)
> > > +{
> > > + /*
> > > + * Called from kvm_arch_commit_memory_region() under kvm->slots_lock.
> > > + * All state transitions are serialized by slots_lock.
> > > + */
> > > + if (nr_memslots_logging > 0 && !kvm->arch.enable_hdbss)
> > > + kvm_arm_enable_hdbss_global(kvm);
> > > + else if (nr_memslots_logging == 0 && kvm->arch.enable_hdbss)
> > > + kvm_arm_disable_hdbss_global(kvm);
> > > +}
> > > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> > > index 949fb895add6..484f48dae000 100644
> > > --- a/arch/arm64/kvm/mmu.c
> > > +++ b/arch/arm64/kvm/mmu.c
> > > @@ -2588,6 +2588,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
> > > {
> > > bool log_dirty_pages = new && new->flags & KVM_MEM_LOG_DIRTY_PAGES;
> > >
> > > + kvm_arm_hdbss_on_dirty_logging_change(kvm,
> > > + atomic_read(&kvm->nr_memslots_dirty_logging));
> > > +
> > > /*
> > > * At this point memslot has been committed and there is an
> > > * allocated dirty_bitmap[], dirty pages will be tracked while the
> > > --
> > > 2.33.0
> > >
> > Okay, reading the above I remembered something really complicated:
> > We can't really enable HDBSS partially if we start with DBM set for all
> > pages. Once we enable HDBSS wit will track changes for all memslots.
> >
> > The only way to enable it partially would be to set DBM during the
> > dirty-bit tracking, which I recall being complicated for some reasons.
> >
> > Well, we have to think about the overall strategy before a next version.
> >
> > Thanks!
> > Leo
>
>
> Yes, I did consider this when I switched to global DBM injection in v4.
> There are
>
> indeed some scenarios that are harder to control:
>
>
>
> Firstly, for lazy split, if we add the DBM tag lazily during live migration
> dirty tracking (like v3 did),
>
> the first write to each page would trap. That trap serves two purposes: it
> gives us a chance to split
>
> hugepages on demand (lazy split), and it ensures the DBM addition happens at
> a safer, more controlled point.
It also allows us to do the dirty-tracking by slot, which is not possible
with the v4 approach.
>
> However, because v4 enables HDBSS and DBM upfront, we lose that initial
> trap. That's exactly why we
>
> now rely on your eager hugepage splitting patch as a mandatory dependency.
>
Correct.
>
>
> Secondly, I'm also concerned about whether global DBM injection could
> accidentally mark pages
>
> that shouldn't be tracked — for example, pages with special mappings.
Well, if we want to not track those pages, we have just to make sure we can
detect them and not mark them with the DBM bit.
> If
> that's possible, then the
>
> lazy approach (only adding DBM on the first write fault) would be safer
> because it only touches pages
>
> that are actually written to.
>
>
>
> So I'd like to ask: is avoiding the first-trap overhead worth the potential
> risks of global DBM injection?
>
> Or do you think the lazy approach is actually safer overall? I'd appreciate
> your thoughts on this trade-off.
>
Well, even though performance is important, the decision to set DBM bits
for all writtable pages at their mapping time was not driven by
performance, but instead by an issue with setting DBM while the VCPUs were
running. I have to rework what that was, and check if that is still an
issue, before we can even discuss what to do next :(
But the fact that the 'eager DBM setting' makes dirty-bit tracking start
global, instead of per-memslot, is something we have to consider as well.
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
2026-07-14 11:16 ` Leonardo Bras
@ 2026-07-14 14:33 ` Leonardo Bras
0 siblings, 0 replies; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 14:33 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 12:16:42PM +0100, Leonardo Bras wrote:
> On Tue, Jul 14, 2026 at 04:58:37PM +0800, Tian Zheng wrote:
> >
> > On 7/13/2026 10:50 PM, Leonardo Bras wrote:
> > > On Thu, Jul 09, 2026 at 06:40:26PM +0800, Tian Zheng wrote:
> > > > From: eillon <yezhenyu2@huawei.com>
> > > >
> > > > HDBSS buffers store per-page dirty state after the stage-2 page tables
> > > > have been split down to page granularity (chunk_size == PAGE_SIZE).
> > > chunk_size != PAGE_SIZE now, but that should change as well :)
> >
> >
> > Thanks, I'll clarify the comment in v5.
>
> By the discussion we are having in the HACDBS patchset, I think we can't
> assume the pages are split in the future. :\
>
> >
> >
> > >
> > > > When chunk_size == 0 the kernel may lazily skip splitting block mappings,
> > > > leaving the page table coarser than what HDBSS expects. Therefore,
> > > > enabling HDBSS requires disabling lazy split so that all block mappings
> > > > are eagerly broken down before the buffer starts recording.
> > > (See cover letter reply)
> > >
> > > > Add VM-level HDBSS enable/disable support. When dirty logging is
> > > > enabled on any memslot, HDBSS is automatically enabled. When dirty
> > > > logging is disabled on all memslots, HDBSS is automatically disabled.
> > > >
> > > > This includes:
> > > > - kvm_arm_enable_hdbss_global() to enable HDBSS for all vCPUs
> > > > - kvm_arm_disable_hdbss_global() to disable and free HDBSS buffers
> > > > - kvm_arm_hdbss_on_dirty_logging_change() for auto enable/disable
> > > > - kvm_arch_destroy_vm() cleanup path
> > > > - kvm_arch_commit_memory_region() integration
> > > >
> > > > Signed-off-by: Eillon <yezhenyu2@huawei.com>
> > > > Signed-off-by: Tian Zheng <zhengtian10@huawei.com>
> > > > ---
> > > > arch/arm64/include/asm/kvm_dirty_bit.h | 2 +
> > > > arch/arm64/kvm/arm.c | 8 ++
> > > > arch/arm64/kvm/dirty_bit.c | 105 +++++++++++++++++++++++++
> > > > arch/arm64/kvm/mmu.c | 3 +
> > > > 4 files changed, 118 insertions(+)
> > > >
> > > > diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > > index 4b28000e972f..a4cda8cdab24 100644
> > > > --- a/arch/arm64/include/asm/kvm_dirty_bit.h
> > > > +++ b/arch/arm64/include/asm/kvm_dirty_bit.h
> > > > @@ -23,5 +23,7 @@ int kvm_arm_vcpu_alloc_hdbss(struct kvm_vcpu *vcpu, unsigned int order);
> > > > void kvm_arm_vcpu_free_hdbss(struct kvm_vcpu *vcpu);
> > > > void kvm_flush_hdbss_buffer(struct kvm_vcpu *vcpu);
> > > > int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu);
> > > > +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging);
> > > > +void kvm_arm_disable_hdbss_global(struct kvm *kvm);
> > > >
> > > > #endif /* __ARM64_KVM_DIRTY_BIT_H__ */
> > > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > > index 566953a4e23a..536d94799ba8 100644
> > > > --- a/arch/arm64/kvm/arm.c
> > > > +++ b/arch/arm64/kvm/arm.c
> > > > @@ -317,6 +317,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
> > > > if (is_protected_kvm_enabled())
> > > > pkvm_destroy_hyp_vm(kvm);
> > > >
> > > > + /*
> > > > + * Userspace may destroy the VM without disabling dirty logging,
> > > > + * so the auto-disable path is never reached. Force disable HDBSS
> > > > + * here to ensure vCPU buffers are freed and prevent memory leaks.
> > > > + */
> > > > + if (kvm->arch.enable_hdbss)
> > > > + kvm_arm_disable_hdbss_global(kvm);
> > > > +
> > > > kvm_uninit_stage2_mmu(kvm);
> > > > kvm_destroy_mpidr_data(kvm);
> > > >
> > > > diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
> > > > index 002366337637..c5bf866c23ef 100644
> > > > --- a/arch/arm64/kvm/dirty_bit.c
> > > > +++ b/arch/arm64/kvm/dirty_bit.c
> > > > @@ -112,3 +112,108 @@ int kvm_handle_hdbss_fault(struct kvm_vcpu *vcpu)
> > > > return -EFAULT;
> > > > }
> > > > }
> > > > +
> > > > +static unsigned int hdbss_auto_select_order(struct kvm *kvm)
> > > > +{
> > > > + unsigned long npages = 0;
> > > > + struct kvm_memory_slot *memslot;
> > > > + int bkt;
> > > > +
> > > > + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm))
> > > > + npages += memslot->npages;
> > > > +
> > > > + if (npages <= 16384)
> > > > + return 0;
> > > > + else if (npages <= 262144)
> > > > + return 3;
> > > > + else if (npages <= 4194304)
> > > > + return 6;
> > > > + else
> > > > + return 9;
> > > > +}
> > > IIUC you are counting the amount of pages the VM has, and based on that
> > > allocating a size for the HDBSS buffer.
> > >
> > > A few notes here:
> > > - It's not really nice to use magic numbers around like this. If you
> > > actually want to use it, then use stuff like SZ_16K, SZ_256K, SZ_4M and
> > > so on.
> > > - You are returning magic numbers as well, why is it 0, 3, 6, or 9 here?
> > > It only makes sense if the person is reading HDBSSBR_EL2 documentation,
> > > which should not be necessary at this point. That's one reason I
> > > recommended to using sizes. If that was really the best way to use it,
> > > I would recommend using the defines that we get from sysreg, and you
> > > actually used before to set the maximum order on a previous patch.
> > > - Also, if you can return only valid values here, why do you check against
> > > the maximum value in that previous patch?
> > > - Also, are you using some undisclosed rule here? On 'order 0' the
> > > meanining is 4KB, which translate to 512 HDBSS entries. Why are you using
> > > it for any value under 16K? Same for 3-32KB-4kEntries you use for under
> > > 256K pages (and so on). If you are assuming a logical rule such as
> > > 'N pages would be ok with N/32 entries' it has to be described here at
> > > least.
> > > - Not sure VM size is the best way of doing that, since it will depend
> > > more on the dirtying rate than the actual size, and most VMs would just
> > > use the biggest size (4M x 4K pages is just 16GB). For instance with
> > > dirty_ring we can use the dirty_ring.size as a better option.
> > > (I know this is a hard one to estimate when using dirty-bitmap, though)
> >
> >
> > I'll replace the magic numbers and also add a comment in the next version
> > explaining
> >
> > the mapping between the thresholds and the order values.
> >
> >
> > On auto-choosing the size: VM memory size is a simple starting point, but I
> > agree it's
> >
> > not ideal. For dirty-ring mode we could use dirty_ring_size as a reference;
> > for dirty-bitmap
> >
> > mode there's no equivalent, so I don't have a good answer yet. I'd really
> > appreciate any
> >
> > suggestions from the community on a better idea for the dirty-bitmap case.
>
> I am thinking that we could use a default value (say 1 PAGESIZE/vcpu) and
> add an ioctl to optionally increase this value. This way we don't require
> a new interface to benefit from HDBSS, but allow users to tune it.
>
> >
> > Also, since we automatically enable HDBSS in the kernel, the check against
> > HDBSS_MAX_ORDER is redundant. I'll remove it.
> >
> >
> > > > +
> > > > +/*
> > > > + * Enable HDBSS for all vCPUs in the VM.
> > > > + *
> > > > + * Called from kvm_arm_hdbss_on_dirty_logging_change() which is invoked
> > > > + * by kvm_arch_commit_memory_region() under kvm->slots_lock.
> > > > + *
> > > > + * If buffer allocation fails, HDBSS remains disabled and dirty tracking
> > > > + * falls back to the traditional software-based approach (PTE write-protect
> > > > + * + software dirty marking). This does not affect correctness; dirty
> > > > + * logging remains functional without HDBSS.
> > > > + */
> > > > +static int kvm_arm_enable_hdbss_global(struct kvm *kvm)
> > > > +{
> > > > + int err;
> > > > + unsigned long i;
> > > > + unsigned int order;
> > > > + struct kvm_vcpu *vcpu;
> > > > +
> > > > + if (!system_supports_hdbss())
> > > > + return 0;
> > > > +
> > > > + if (kvm->dirty_ring_size) /* Don't support HDBSS in dirty ring mode */
> > > > + return 0;
> > > > +
> > > > + if (kvm->arch.enable_hdbss) /* Already On */
> > > > + return 0;
> > > > +
> > > > + /* Turn it on */
> > > > + order = hdbss_auto_select_order(kvm);
> > > > + kvm_for_each_vcpu(i, vcpu, kvm) {
> > > > + err = kvm_arm_vcpu_alloc_hdbss(vcpu, order);
> > > > + if (err)
> > > > + goto error_alloc;
> > > > + }
> > > > +
> > > > + kvm->arch.enable_hdbss = true;
> > > > + kvm->arch.mmu.vtcr |= VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA;
> > > > +
> > > > + /*
> > > > + * We should kick vcpus out of guest mode here to load new
> > > > + * vtcr value to vtcr_el2 register when re-enter guest mode.
> > > > + */
> > > > + kvm_for_each_vcpu(i, vcpu, kvm)
> > > > + kvm_vcpu_kick(vcpu);
> > > > +
> > > > + return 0;
> > > > +
> > > > +error_alloc:
> > > > + kvm_for_each_vcpu(i, vcpu, kvm)
> > > > + if (vcpu->arch.hdbss.base_phys)
> > > > + kvm_arm_vcpu_free_hdbss(vcpu);
> > > > +
> > > > + pr_warn_once("kvm: failed to allocate HDBSS buffers (order=%u), "
> > > > + "falling back to software dirty tracking\n", order);
> > > > + return -ENOMEM;
> > > > +}
> > > > +
> > > > +void kvm_arm_disable_hdbss_global(struct kvm *kvm)
> > > > +{
> > > > + unsigned long i;
> > > > + struct kvm_vcpu *vcpu;
> > > > +
> > > > + if (!kvm->arch.enable_hdbss) /* Already Off */
> > > > + return;
> > > > +
> > > > + /* Turn it off */
> > > > + kvm->arch.mmu.vtcr &= ~(VTCR_EL2_HD | VTCR_EL2_HDBSS | VTCR_EL2_HA);
> > > > +
> > > > + kvm_for_each_vcpu(i, vcpu, kvm)
> > > > + kvm_arm_vcpu_free_hdbss(vcpu);
> > > > +
> > > > + kvm->arch.enable_hdbss = false;
> > > > +}
> > > > +
> > > Okay, say the user requested it to be disabled, you change the global vtcr,
> > > then free the hdbss on every vcpu.
> > >
> > > But the vcpus are still running, and since they will only disable this when
> > > they go out of the guest, then in again, HDBSS will still be running,
> > > right?
> > >
> > > If some page gets dirty in the between, would not the HDBSS try to write to
> > > the already loaded buffer adress, and write to memory that have already
> > > been freed here?
> > >
> >
> > You're right — this is a race condition. I'll fix this in v5 by clearing
> > VTCR_EL2_HDBSS from
> >
> > kvm->arch.mmu.vtcr first, then kicking all vCPUs to force them to exit guest
> > mode and reload the config.
> >
> > Once all vCPUs are out of guest mode, it will be safe to free the HDBSS
> > buffers.
> >
>
> That would be safer, indeed.
>
> >
> > > > +void kvm_arm_hdbss_on_dirty_logging_change(struct kvm *kvm, int nr_memslots_logging)
> > > > +{
> > > > + /*
> > > > + * Called from kvm_arch_commit_memory_region() under kvm->slots_lock.
> > > > + * All state transitions are serialized by slots_lock.
> > > > + */
> > > > + if (nr_memslots_logging > 0 && !kvm->arch.enable_hdbss)
> > > > + kvm_arm_enable_hdbss_global(kvm);
> > > > + else if (nr_memslots_logging == 0 && kvm->arch.enable_hdbss)
> > > > + kvm_arm_disable_hdbss_global(kvm);
> > > > +}
> > > > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> > > > index 949fb895add6..484f48dae000 100644
> > > > --- a/arch/arm64/kvm/mmu.c
> > > > +++ b/arch/arm64/kvm/mmu.c
> > > > @@ -2588,6 +2588,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
> > > > {
> > > > bool log_dirty_pages = new && new->flags & KVM_MEM_LOG_DIRTY_PAGES;
> > > >
> > > > + kvm_arm_hdbss_on_dirty_logging_change(kvm,
> > > > + atomic_read(&kvm->nr_memslots_dirty_logging));
> > > > +
> > > > /*
> > > > * At this point memslot has been committed and there is an
> > > > * allocated dirty_bitmap[], dirty pages will be tracked while the
> > > > --
> > > > 2.33.0
> > > >
> > > Okay, reading the above I remembered something really complicated:
> > > We can't really enable HDBSS partially if we start with DBM set for all
> > > pages. Once we enable HDBSS wit will track changes for all memslots.
> > >
> > > The only way to enable it partially would be to set DBM during the
> > > dirty-bit tracking, which I recall being complicated for some reasons.
> > >
> > > Well, we have to think about the overall strategy before a next version.
> > >
> > > Thanks!
> > > Leo
> >
> >
> > Yes, I did consider this when I switched to global DBM injection in v4.
> > There are
> >
> > indeed some scenarios that are harder to control:
> >
> >
> >
> > Firstly, for lazy split, if we add the DBM tag lazily during live migration
> > dirty tracking (like v3 did),
> >
> > the first write to each page would trap. That trap serves two purposes: it
> > gives us a chance to split
> >
> > hugepages on demand (lazy split), and it ensures the DBM addition happens at
> > a safer, more controlled point.
>
> It also allows us to do the dirty-tracking by slot, which is not possible
> with the v4 approach.
>
> >
> > However, because v4 enables HDBSS and DBM upfront, we lose that initial
> > trap. That's exactly why we
> >
> > now rely on your eager hugepage splitting patch as a mandatory dependency.
> >
>
> Correct.
>
> >
> >
> > Secondly, I'm also concerned about whether global DBM injection could
> > accidentally mark pages
> >
> > that shouldn't be tracked — for example, pages with special mappings.
>
> Well, if we want to not track those pages, we have just to make sure we can
> detect them and not mark them with the DBM bit.
>
> > If
> > that's possible, then the
> >
> > lazy approach (only adding DBM on the first write fault) would be safer
> > because it only touches pages
> >
> > that are actually written to.
> >
> >
> >
> > So I'd like to ask: is avoiding the first-trap overhead worth the potential
> > risks of global DBM injection?
> >
> > Or do you think the lazy approach is actually safer overall? I'd appreciate
> > your thoughts on this trade-off.
> >
>
> Well, even though performance is important, the decision to set DBM bits
> for all writtable pages at their mapping time was not driven by
> performance, but instead by an issue with setting DBM while the VCPUs were
> running. I have to rework what that was, and check if that is still an
> issue, before we can even discuss what to do next :(
>
> But the fact that the 'eager DBM setting' makes dirty-bit tracking start
> global, instead of per-memslot, is something we have to consider as well.
>
Actually, I though more about that part, and we can achieve per-memslot
dirty-bit tracking with the eager DBM setting.
- On first fault,
- Read fault: we mark the writable PTE as writable-clean
- Write fault: we mark the writable PTE as writable-dirty
- In dirty-track enable, we clean the pages that are starting being tracked
- Only when pages go from writable-clean to writable-dirty they log HDBSS
The only obvious case, which should not happen that often, it that a
writable-clean page on a mesmslot that is not being tracked goes to
writable-dirty state. It will make to the HDBSS buffer, but we can just
check if the memslot is dirty-tracking before registering it to either the
dirty-bitmap or the dirty-ring.
While it will be using a entry that could be used for an actual tracking,
it is not supposed to happen that often in a real scenario, so it should
not impact performance that much.
So we only have to worry about the eager vs lazy splitting scenarios for
now.
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-09 10:40 [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Tian Zheng
` (5 preceding siblings ...)
2026-07-09 10:40 ` [PATCH v4 6/6] KVM: arm64: Add auto HDBSS enable/disable on dirty logging change Tian Zheng
@ 2026-07-13 10:31 ` Leonardo Bras
2026-07-13 16:27 ` Leonardo Bras
2026-07-14 9:37 ` Tian Zheng
6 siblings, 2 replies; 32+ messages in thread
From: Leonardo Bras @ 2026-07-13 10:31 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
> This series of patches add support to the Hardware Dirty state tracking
> Structure (HDBSS) feature, which is introduced by the ARM architecture
> in the DDI0601 (ID121123) version.
>
> The HDBSS feature is an extension to the architecture that enhances
> tracking translation table descriptors' dirty state, identified as
> FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
> page tracking, aiming to significantly reduce the overhead of scanning
> for dirty pages.
>
> The purpose of this feature is to make the execution overhead of live
> migration lower to both the guest and the host, compared to existing
> approaches (write-protect or search stage-2 tables).
>
> The required sysreg definitions for FEAT_HDBSS have been merged into
> arm64 /sysregs:
> [1/5] arm64/sysreg: Add HDBSS related register information
> https://git.kernel.org/arm64/c/72f7be0c2e30
>
>
> After these patches, the kernel automatically enables HDBSS when dirty
> logging is enabled on any memslot, and disables HDBSS when dirty logging
> is disabled on all memslots. This series does not support dirty ring
> mode.
>
> Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
> https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
On this, FYI, there have been some discussion on this:
https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
Oliver's suggestion is that we don't automatically enable eager splitting,
but instead we have different behaviours if the user enables it.
This is still under discussion there, but I think it can be useful reading.
>
> This dependency is mandatory. Unlike v3, where DBM was added lazily on
> the first dirty access via a page fault, v4 injects DBM into pgt->flags
> at stage-2 MMU initialization time. Combined with HDBSS being auto-enabled
> during migration setup, the first dirty access no longer triggers a page
> fault. Consequently, if lazy hugepage splitting were relied upon (which
> requires a page fault to trigger splitting), hugepages would never be
> split, leading to guest hang after migration. Leonardo's patch above
> ensures eager hugepage splitting is enabled (chunk_size != 0) when HDBSS
> is available, guaranteeing that hugepages are properly split before
> migration starts.
>
> v3: https://lore.kernel.org/linux-arm-kernel/20260225040421.2683931-1-zhengtian10@huawei.com/
>
> v3->v4 changes:
> - Merge sysreg definitions into the FEAT_HDBSS detection patch (was a
> separate patch in v3).
> - Add auto DBM (Dirty Bit Modifier) support as a new patch, suggested
> by Leonardo Bras. DBM is now controlled as a page-table level flag
> (KVM_PGTABLE_S2_DBM) rather than per-PTE. Note that DBM is injected
> at stage-2 MMU creation time, not lazily on first dirty access. This
> means the first write to a dirty-logged page does not generate a
> page fault, which is a key reason for the mandatory dependency on
> Leonardo's eager hugepage splitting patch (see Depends-on above).
> - Split the v3 "Enable HDBSS support and handle HDBSSF events" patch
> into three patches: per-vCPU buffer management, fault handling and
> buffer flush, and auto enable/disable on dirty logging change. This
> implements kernel-managed automatic HDBSS enable/disable.
> - Remove the KVM_CAP_ARM_HW_DIRTY_STATE_TRACK ioctl for manual HDBSS
> on/off. HDBSS is now automatically enabled/disabled based on dirty
> logging state via kvm_arch_commit_memory_region().
> - Change HDBSS buffer flush triggers to vcpu_put, check_vcpu_requests,
> and kvm_handle_guest_abort.
> - Store hdbss_order at VM level (kvm->arch.hdbss_order) instead of
> per-vCPU, since all vCPUs share the same order.
> - Document patch is not included in this version; will be sent in a
> follow-up series.
>
> Leonardo Bras (1):
> KVM: arm64: Enable eager hugepage splitting if HDBSS is available
>
> Tian Zheng (2):
> KVM: arm64: Add support for FEAT_HDBSS
> KVM: arm64: Add auto DBM support for hardware dirty tracking
>
> eillon (3):
> KVM: arm64: Add HDBSS per-vCPU buffer management
> KVM: arm64: Add HDBSS fault handling and buffer flush
> KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
>
> arch/arm64/include/asm/cpufeature.h | 5 +
> arch/arm64/include/asm/esr.h | 5 +
> arch/arm64/include/asm/kvm_dirty_bit.h | 29 ++++
> arch/arm64/include/asm/kvm_host.h | 14 ++
> arch/arm64/include/asm/kvm_pgtable.h | 4 +
> arch/arm64/include/asm/sysreg.h | 11 ++
> arch/arm64/kernel/cpufeature.c | 12 ++
> arch/arm64/kvm/Makefile | 1 +
> arch/arm64/kvm/arm.c | 29 ++++
> arch/arm64/kvm/dirty_bit.c | 219 +++++++++++++++++++++++++
> arch/arm64/kvm/hyp/pgtable.c | 35 +++-
> arch/arm64/kvm/hyp/vhe/switch.c | 15 ++
> arch/arm64/kvm/mmu.c | 19 ++-
> arch/arm64/kvm/reset.c | 4 +
> arch/arm64/tools/cpucaps | 1 +
> 15 files changed, 399 insertions(+), 4 deletions(-)
> create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
> create mode 100644 arch/arm64/kvm/dirty_bit.c
>
> --
> 2.33.0
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-13 10:31 ` [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Leonardo Bras
@ 2026-07-13 16:27 ` Leonardo Bras
2026-07-14 10:39 ` Tian Zheng
2026-07-14 9:37 ` Tian Zheng
1 sibling, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-13 16:27 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Mon, Jul 13, 2026 at 11:31:46AM +0100, Leonardo Bras wrote:
> On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
> > This series of patches add support to the Hardware Dirty state tracking
> > Structure (HDBSS) feature, which is introduced by the ARM architecture
> > in the DDI0601 (ID121123) version.
> >
> > The HDBSS feature is an extension to the architecture that enhances
> > tracking translation table descriptors' dirty state, identified as
> > FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
> > page tracking, aiming to significantly reduce the overhead of scanning
> > for dirty pages.
> >
> > The purpose of this feature is to make the execution overhead of live
> > migration lower to both the guest and the host, compared to existing
> > approaches (write-protect or search stage-2 tables).
> >
> > The required sysreg definitions for FEAT_HDBSS have been merged into
> > arm64 /sysregs:
> > [1/5] arm64/sysreg: Add HDBSS related register information
> > https://git.kernel.org/arm64/c/72f7be0c2e30
> >
> >
> > After these patches, the kernel automatically enables HDBSS when dirty
> > logging is enabled on any memslot, and disables HDBSS when dirty logging
> > is disabled on all memslots. This series does not support dirty ring
> > mode.
> >
> > Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
> > https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
>
> On this, FYI, there have been some discussion on this:
> https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
>
> Oliver's suggestion is that we don't automatically enable eager splitting,
> but instead we have different behaviours if the user enables it.
>
> This is still under discussion there, but I think it can be useful reading.
>
> >
> > This dependency is mandatory. Unlike v3, where DBM was added lazily on
> > the first dirty access via a page fault, v4 injects DBM into pgt->flags
> > at stage-2 MMU initialization time. Combined with HDBSS being auto-enabled
> > during migration setup, the first dirty access no longer triggers a page
> > fault. Consequently, if lazy hugepage splitting were relied upon (which
> > requires a page fault to trigger splitting), hugepages would never be
> > split, leading to guest hang after migration. Leonardo's patch above
> > ensures eager hugepage splitting is enabled (chunk_size != 0) when HDBSS
> > is available, guaranteeing that hugepages are properly split before
> > migration starts.
> >
> > v3: https://lore.kernel.org/linux-arm-kernel/20260225040421.2683931-1-zhengtian10@huawei.com/
> >
> > v3->v4 changes:
> > - Merge sysreg definitions into the FEAT_HDBSS detection patch (was a
> > separate patch in v3).
> > - Add auto DBM (Dirty Bit Modifier) support as a new patch, suggested
> > by Leonardo Bras. DBM is now controlled as a page-table level flag
> > (KVM_PGTABLE_S2_DBM) rather than per-PTE. Note that DBM is injected
> > at stage-2 MMU creation time, not lazily on first dirty access. This
> > means the first write to a dirty-logged page does not generate a
> > page fault, which is a key reason for the mandatory dependency on
> > Leonardo's eager hugepage splitting patch (see Depends-on above).
> > - Split the v3 "Enable HDBSS support and handle HDBSSF events" patch
> > into three patches: per-vCPU buffer management, fault handling and
> > buffer flush, and auto enable/disable on dirty logging change. This
> > implements kernel-managed automatic HDBSS enable/disable.
> > - Remove the KVM_CAP_ARM_HW_DIRTY_STATE_TRACK ioctl for manual HDBSS
> > on/off. HDBSS is now automatically enabled/disabled based on dirty
> > logging state via kvm_arch_commit_memory_region().
> > - Change HDBSS buffer flush triggers to vcpu_put, check_vcpu_requests,
> > and kvm_handle_guest_abort.
> > - Store hdbss_order at VM level (kvm->arch.hdbss_order) instead of
> > per-vCPU, since all vCPUs share the same order.
> > - Document patch is not included in this version; will be sent in a
> > follow-up series.
> >
> > Leonardo Bras (1):
> > KVM: arm64: Enable eager hugepage splitting if HDBSS is available
> >
> > Tian Zheng (2):
> > KVM: arm64: Add support for FEAT_HDBSS
> > KVM: arm64: Add auto DBM support for hardware dirty tracking
> >
> > eillon (3):
> > KVM: arm64: Add HDBSS per-vCPU buffer management
> > KVM: arm64: Add HDBSS fault handling and buffer flush
> > KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
> >
> > arch/arm64/include/asm/cpufeature.h | 5 +
> > arch/arm64/include/asm/esr.h | 5 +
> > arch/arm64/include/asm/kvm_dirty_bit.h | 29 ++++
> > arch/arm64/include/asm/kvm_host.h | 14 ++
> > arch/arm64/include/asm/kvm_pgtable.h | 4 +
> > arch/arm64/include/asm/sysreg.h | 11 ++
> > arch/arm64/kernel/cpufeature.c | 12 ++
> > arch/arm64/kvm/Makefile | 1 +
> > arch/arm64/kvm/arm.c | 29 ++++
> > arch/arm64/kvm/dirty_bit.c | 219 +++++++++++++++++++++++++
> > arch/arm64/kvm/hyp/pgtable.c | 35 +++-
> > arch/arm64/kvm/hyp/vhe/switch.c | 15 ++
> > arch/arm64/kvm/mmu.c | 19 ++-
> > arch/arm64/kvm/reset.c | 4 +
> > arch/arm64/tools/cpucaps | 1 +
> > 15 files changed, 399 insertions(+), 4 deletions(-)
> > create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
> > create mode 100644 arch/arm64/kvm/dirty_bit.c
> >
> > --
> > 2.33.0
> >
Also, please remember to include the base commit, so it can be applied for
testing/reviewing.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-13 16:27 ` Leonardo Bras
@ 2026-07-14 10:39 ` Tian Zheng
2026-07-14 11:20 ` Leonardo Bras
0 siblings, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 10:39 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/14/2026 12:27 AM, Leonardo Bras wrote:
> On Mon, Jul 13, 2026 at 11:31:46AM +0100, Leonardo Bras wrote:
>> On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
>>> This series of patches add support to the Hardware Dirty state tracking
>>> Structure (HDBSS) feature, which is introduced by the ARM architecture
>>> in the DDI0601 (ID121123) version.
>>>
>>> The HDBSS feature is an extension to the architecture that enhances
>>> tracking translation table descriptors' dirty state, identified as
>>> FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
>>> page tracking, aiming to significantly reduce the overhead of scanning
>>> for dirty pages.
>>>
>>> The purpose of this feature is to make the execution overhead of live
>>> migration lower to both the guest and the host, compared to existing
>>> approaches (write-protect or search stage-2 tables).
>>>
>>> The required sysreg definitions for FEAT_HDBSS have been merged into
>>> arm64 /sysregs:
>>> [1/5] arm64/sysreg: Add HDBSS related register information
>>> https://git.kernel.org/arm64/c/72f7be0c2e30
>>>
>>>
>>> After these patches, the kernel automatically enables HDBSS when dirty
>>> logging is enabled on any memslot, and disables HDBSS when dirty logging
>>> is disabled on all memslots. This series does not support dirty ring
>>> mode.
>>>
>>> Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
>>> https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
>> On this, FYI, there have been some discussion on this:
>> https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
>>
>> Oliver's suggestion is that we don't automatically enable eager splitting,
>> but instead we have different behaviours if the user enables it.
>>
>> This is still under discussion there, but I think it can be useful reading.
>>
>>> This dependency is mandatory. Unlike v3, where DBM was added lazily on
>>> the first dirty access via a page fault, v4 injects DBM into pgt->flags
>>> at stage-2 MMU initialization time. Combined with HDBSS being auto-enabled
>>> during migration setup, the first dirty access no longer triggers a page
>>> fault. Consequently, if lazy hugepage splitting were relied upon (which
>>> requires a page fault to trigger splitting), hugepages would never be
>>> split, leading to guest hang after migration. Leonardo's patch above
>>> ensures eager hugepage splitting is enabled (chunk_size != 0) when HDBSS
>>> is available, guaranteeing that hugepages are properly split before
>>> migration starts.
>>>
>>> v3: https://lore.kernel.org/linux-arm-kernel/20260225040421.2683931-1-zhengtian10@huawei.com/
>>>
>>> v3->v4 changes:
>>> - Merge sysreg definitions into the FEAT_HDBSS detection patch (was a
>>> separate patch in v3).
>>> - Add auto DBM (Dirty Bit Modifier) support as a new patch, suggested
>>> by Leonardo Bras. DBM is now controlled as a page-table level flag
>>> (KVM_PGTABLE_S2_DBM) rather than per-PTE. Note that DBM is injected
>>> at stage-2 MMU creation time, not lazily on first dirty access. This
>>> means the first write to a dirty-logged page does not generate a
>>> page fault, which is a key reason for the mandatory dependency on
>>> Leonardo's eager hugepage splitting patch (see Depends-on above).
>>> - Split the v3 "Enable HDBSS support and handle HDBSSF events" patch
>>> into three patches: per-vCPU buffer management, fault handling and
>>> buffer flush, and auto enable/disable on dirty logging change. This
>>> implements kernel-managed automatic HDBSS enable/disable.
>>> - Remove the KVM_CAP_ARM_HW_DIRTY_STATE_TRACK ioctl for manual HDBSS
>>> on/off. HDBSS is now automatically enabled/disabled based on dirty
>>> logging state via kvm_arch_commit_memory_region().
>>> - Change HDBSS buffer flush triggers to vcpu_put, check_vcpu_requests,
>>> and kvm_handle_guest_abort.
>>> - Store hdbss_order at VM level (kvm->arch.hdbss_order) instead of
>>> per-vCPU, since all vCPUs share the same order.
>>> - Document patch is not included in this version; will be sent in a
>>> follow-up series.
>>>
>>> Leonardo Bras (1):
>>> KVM: arm64: Enable eager hugepage splitting if HDBSS is available
>>>
>>> Tian Zheng (2):
>>> KVM: arm64: Add support for FEAT_HDBSS
>>> KVM: arm64: Add auto DBM support for hardware dirty tracking
>>>
>>> eillon (3):
>>> KVM: arm64: Add HDBSS per-vCPU buffer management
>>> KVM: arm64: Add HDBSS fault handling and buffer flush
>>> KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
>>>
>>> arch/arm64/include/asm/cpufeature.h | 5 +
>>> arch/arm64/include/asm/esr.h | 5 +
>>> arch/arm64/include/asm/kvm_dirty_bit.h | 29 ++++
>>> arch/arm64/include/asm/kvm_host.h | 14 ++
>>> arch/arm64/include/asm/kvm_pgtable.h | 4 +
>>> arch/arm64/include/asm/sysreg.h | 11 ++
>>> arch/arm64/kernel/cpufeature.c | 12 ++
>>> arch/arm64/kvm/Makefile | 1 +
>>> arch/arm64/kvm/arm.c | 29 ++++
>>> arch/arm64/kvm/dirty_bit.c | 219 +++++++++++++++++++++++++
>>> arch/arm64/kvm/hyp/pgtable.c | 35 +++-
>>> arch/arm64/kvm/hyp/vhe/switch.c | 15 ++
>>> arch/arm64/kvm/mmu.c | 19 ++-
>>> arch/arm64/kvm/reset.c | 4 +
>>> arch/arm64/tools/cpucaps | 1 +
>>> 15 files changed, 399 insertions(+), 4 deletions(-)
>>> create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
>>> create mode 100644 arch/arm64/kvm/dirty_bit.c
>>>
>>> --
>>> 2.33.0
>>>
> Also, please remember to include the base commit, so it can be applied for
> testing/reviewing.
Thank you for the reminder.
This patch series is based on:
- Commit: 0e35b9b6ec0f
- Description: Merge tag 'mm-hotfixes-stable-2026-07-06-17-49' of
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Welcome to test and review — please let me know if you encounter any
issues or have suggestions for improvement.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-14 10:39 ` Tian Zheng
@ 2026-07-14 11:20 ` Leonardo Bras
2026-07-14 13:29 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 11:20 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 06:39:48PM +0800, Tian Zheng wrote:
>
> On 7/14/2026 12:27 AM, Leonardo Bras wrote:
> > On Mon, Jul 13, 2026 at 11:31:46AM +0100, Leonardo Bras wrote:
> > > On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
> > > > This series of patches add support to the Hardware Dirty state tracking
> > > > Structure (HDBSS) feature, which is introduced by the ARM architecture
> > > > in the DDI0601 (ID121123) version.
> > > >
> > > > The HDBSS feature is an extension to the architecture that enhances
> > > > tracking translation table descriptors' dirty state, identified as
> > > > FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
> > > > page tracking, aiming to significantly reduce the overhead of scanning
> > > > for dirty pages.
> > > >
> > > > The purpose of this feature is to make the execution overhead of live
> > > > migration lower to both the guest and the host, compared to existing
> > > > approaches (write-protect or search stage-2 tables).
> > > >
> > > > The required sysreg definitions for FEAT_HDBSS have been merged into
> > > > arm64 /sysregs:
> > > > [1/5] arm64/sysreg: Add HDBSS related register information
> > > > https://git.kernel.org/arm64/c/72f7be0c2e30
> > > >
> > > >
> > > > After these patches, the kernel automatically enables HDBSS when dirty
> > > > logging is enabled on any memslot, and disables HDBSS when dirty logging
> > > > is disabled on all memslots. This series does not support dirty ring
> > > > mode.
> > > >
> > > > Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
> > > > https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
> > > On this, FYI, there have been some discussion on this:
> > > https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
> > >
> > > Oliver's suggestion is that we don't automatically enable eager splitting,
> > > but instead we have different behaviours if the user enables it.
> > >
> > > This is still under discussion there, but I think it can be useful reading.
> > >
> > > > This dependency is mandatory. Unlike v3, where DBM was added lazily on
> > > > the first dirty access via a page fault, v4 injects DBM into pgt->flags
> > > > at stage-2 MMU initialization time. Combined with HDBSS being auto-enabled
> > > > during migration setup, the first dirty access no longer triggers a page
> > > > fault. Consequently, if lazy hugepage splitting were relied upon (which
> > > > requires a page fault to trigger splitting), hugepages would never be
> > > > split, leading to guest hang after migration. Leonardo's patch above
> > > > ensures eager hugepage splitting is enabled (chunk_size != 0) when HDBSS
> > > > is available, guaranteeing that hugepages are properly split before
> > > > migration starts.
> > > >
> > > > v3: https://lore.kernel.org/linux-arm-kernel/20260225040421.2683931-1-zhengtian10@huawei.com/
> > > >
> > > > v3->v4 changes:
> > > > - Merge sysreg definitions into the FEAT_HDBSS detection patch (was a
> > > > separate patch in v3).
> > > > - Add auto DBM (Dirty Bit Modifier) support as a new patch, suggested
> > > > by Leonardo Bras. DBM is now controlled as a page-table level flag
> > > > (KVM_PGTABLE_S2_DBM) rather than per-PTE. Note that DBM is injected
> > > > at stage-2 MMU creation time, not lazily on first dirty access. This
> > > > means the first write to a dirty-logged page does not generate a
> > > > page fault, which is a key reason for the mandatory dependency on
> > > > Leonardo's eager hugepage splitting patch (see Depends-on above).
> > > > - Split the v3 "Enable HDBSS support and handle HDBSSF events" patch
> > > > into three patches: per-vCPU buffer management, fault handling and
> > > > buffer flush, and auto enable/disable on dirty logging change. This
> > > > implements kernel-managed automatic HDBSS enable/disable.
> > > > - Remove the KVM_CAP_ARM_HW_DIRTY_STATE_TRACK ioctl for manual HDBSS
> > > > on/off. HDBSS is now automatically enabled/disabled based on dirty
> > > > logging state via kvm_arch_commit_memory_region().
> > > > - Change HDBSS buffer flush triggers to vcpu_put, check_vcpu_requests,
> > > > and kvm_handle_guest_abort.
> > > > - Store hdbss_order at VM level (kvm->arch.hdbss_order) instead of
> > > > per-vCPU, since all vCPUs share the same order.
> > > > - Document patch is not included in this version; will be sent in a
> > > > follow-up series.
> > > >
> > > > Leonardo Bras (1):
> > > > KVM: arm64: Enable eager hugepage splitting if HDBSS is available
> > > >
> > > > Tian Zheng (2):
> > > > KVM: arm64: Add support for FEAT_HDBSS
> > > > KVM: arm64: Add auto DBM support for hardware dirty tracking
> > > >
> > > > eillon (3):
> > > > KVM: arm64: Add HDBSS per-vCPU buffer management
> > > > KVM: arm64: Add HDBSS fault handling and buffer flush
> > > > KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
> > > >
> > > > arch/arm64/include/asm/cpufeature.h | 5 +
> > > > arch/arm64/include/asm/esr.h | 5 +
> > > > arch/arm64/include/asm/kvm_dirty_bit.h | 29 ++++
> > > > arch/arm64/include/asm/kvm_host.h | 14 ++
> > > > arch/arm64/include/asm/kvm_pgtable.h | 4 +
> > > > arch/arm64/include/asm/sysreg.h | 11 ++
> > > > arch/arm64/kernel/cpufeature.c | 12 ++
> > > > arch/arm64/kvm/Makefile | 1 +
> > > > arch/arm64/kvm/arm.c | 29 ++++
> > > > arch/arm64/kvm/dirty_bit.c | 219 +++++++++++++++++++++++++
> > > > arch/arm64/kvm/hyp/pgtable.c | 35 +++-
> > > > arch/arm64/kvm/hyp/vhe/switch.c | 15 ++
> > > > arch/arm64/kvm/mmu.c | 19 ++-
> > > > arch/arm64/kvm/reset.c | 4 +
> > > > arch/arm64/tools/cpucaps | 1 +
> > > > 15 files changed, 399 insertions(+), 4 deletions(-)
> > > > create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
> > > > create mode 100644 arch/arm64/kvm/dirty_bit.c
> > > >
> > > > --
> > > > 2.33.0
> > > >
> > Also, please remember to include the base commit, so it can be applied for
> > testing/reviewing.
>
>
> Thank you for the reminder.
>
> This patch series is based on:
> - Commit: 0e35b9b6ec0f
> - Description: Merge tag 'mm-hotfixes-stable-2026-07-06-17-49' of
> git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
>
> Welcome to test and review — please let me know if you encounter any issues
> or have suggestions for improvement.
>
>
Thanks for sharing :)
Tip: for next version, you can automate that by adding
'--base=<base-commit>' to your git-format-patch command, and it will
automatically generate a base message at the end of your cover letter, such
as:
```
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
```
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-14 11:20 ` Leonardo Bras
@ 2026-07-14 13:29 ` Tian Zheng
0 siblings, 0 replies; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 13:29 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/14/2026 7:20 PM, Leonardo Bras wrote:
> On Tue, Jul 14, 2026 at 06:39:48PM +0800, Tian Zheng wrote:
>> On 7/14/2026 12:27 AM, Leonardo Bras wrote:
>>> On Mon, Jul 13, 2026 at 11:31:46AM +0100, Leonardo Bras wrote:
>>>> On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
>>>>> This series of patches add support to the Hardware Dirty state tracking
>>>>> Structure (HDBSS) feature, which is introduced by the ARM architecture
>>>>> in the DDI0601 (ID121123) version.
>>>>>
>>>>> The HDBSS feature is an extension to the architecture that enhances
>>>>> tracking translation table descriptors' dirty state, identified as
>>>>> FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
>>>>> page tracking, aiming to significantly reduce the overhead of scanning
>>>>> for dirty pages.
>>>>>
>>>>> The purpose of this feature is to make the execution overhead of live
>>>>> migration lower to both the guest and the host, compared to existing
>>>>> approaches (write-protect or search stage-2 tables).
>>>>>
>>>>> The required sysreg definitions for FEAT_HDBSS have been merged into
>>>>> arm64 /sysregs:
>>>>> [1/5] arm64/sysreg: Add HDBSS related register information
>>>>> https://git.kernel.org/arm64/c/72f7be0c2e30
>>>>>
>>>>>
>>>>> After these patches, the kernel automatically enables HDBSS when dirty
>>>>> logging is enabled on any memslot, and disables HDBSS when dirty logging
>>>>> is disabled on all memslots. This series does not support dirty ring
>>>>> mode.
>>>>>
>>>>> Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
>>>>> https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
>>>> On this, FYI, there have been some discussion on this:
>>>> https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
>>>>
>>>> Oliver's suggestion is that we don't automatically enable eager splitting,
>>>> but instead we have different behaviours if the user enables it.
>>>>
>>>> This is still under discussion there, but I think it can be useful reading.
>>>>
>>>>> This dependency is mandatory. Unlike v3, where DBM was added lazily on
>>>>> the first dirty access via a page fault, v4 injects DBM into pgt->flags
>>>>> at stage-2 MMU initialization time. Combined with HDBSS being auto-enabled
>>>>> during migration setup, the first dirty access no longer triggers a page
>>>>> fault. Consequently, if lazy hugepage splitting were relied upon (which
>>>>> requires a page fault to trigger splitting), hugepages would never be
>>>>> split, leading to guest hang after migration. Leonardo's patch above
>>>>> ensures eager hugepage splitting is enabled (chunk_size != 0) when HDBSS
>>>>> is available, guaranteeing that hugepages are properly split before
>>>>> migration starts.
>>>>>
>>>>> v3: https://lore.kernel.org/linux-arm-kernel/20260225040421.2683931-1-zhengtian10@huawei.com/
>>>>>
>>>>> v3->v4 changes:
>>>>> - Merge sysreg definitions into the FEAT_HDBSS detection patch (was a
>>>>> separate patch in v3).
>>>>> - Add auto DBM (Dirty Bit Modifier) support as a new patch, suggested
>>>>> by Leonardo Bras. DBM is now controlled as a page-table level flag
>>>>> (KVM_PGTABLE_S2_DBM) rather than per-PTE. Note that DBM is injected
>>>>> at stage-2 MMU creation time, not lazily on first dirty access. This
>>>>> means the first write to a dirty-logged page does not generate a
>>>>> page fault, which is a key reason for the mandatory dependency on
>>>>> Leonardo's eager hugepage splitting patch (see Depends-on above).
>>>>> - Split the v3 "Enable HDBSS support and handle HDBSSF events" patch
>>>>> into three patches: per-vCPU buffer management, fault handling and
>>>>> buffer flush, and auto enable/disable on dirty logging change. This
>>>>> implements kernel-managed automatic HDBSS enable/disable.
>>>>> - Remove the KVM_CAP_ARM_HW_DIRTY_STATE_TRACK ioctl for manual HDBSS
>>>>> on/off. HDBSS is now automatically enabled/disabled based on dirty
>>>>> logging state via kvm_arch_commit_memory_region().
>>>>> - Change HDBSS buffer flush triggers to vcpu_put, check_vcpu_requests,
>>>>> and kvm_handle_guest_abort.
>>>>> - Store hdbss_order at VM level (kvm->arch.hdbss_order) instead of
>>>>> per-vCPU, since all vCPUs share the same order.
>>>>> - Document patch is not included in this version; will be sent in a
>>>>> follow-up series.
>>>>>
>>>>> Leonardo Bras (1):
>>>>> KVM: arm64: Enable eager hugepage splitting if HDBSS is available
>>>>>
>>>>> Tian Zheng (2):
>>>>> KVM: arm64: Add support for FEAT_HDBSS
>>>>> KVM: arm64: Add auto DBM support for hardware dirty tracking
>>>>>
>>>>> eillon (3):
>>>>> KVM: arm64: Add HDBSS per-vCPU buffer management
>>>>> KVM: arm64: Add HDBSS fault handling and buffer flush
>>>>> KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
>>>>>
>>>>> arch/arm64/include/asm/cpufeature.h | 5 +
>>>>> arch/arm64/include/asm/esr.h | 5 +
>>>>> arch/arm64/include/asm/kvm_dirty_bit.h | 29 ++++
>>>>> arch/arm64/include/asm/kvm_host.h | 14 ++
>>>>> arch/arm64/include/asm/kvm_pgtable.h | 4 +
>>>>> arch/arm64/include/asm/sysreg.h | 11 ++
>>>>> arch/arm64/kernel/cpufeature.c | 12 ++
>>>>> arch/arm64/kvm/Makefile | 1 +
>>>>> arch/arm64/kvm/arm.c | 29 ++++
>>>>> arch/arm64/kvm/dirty_bit.c | 219 +++++++++++++++++++++++++
>>>>> arch/arm64/kvm/hyp/pgtable.c | 35 +++-
>>>>> arch/arm64/kvm/hyp/vhe/switch.c | 15 ++
>>>>> arch/arm64/kvm/mmu.c | 19 ++-
>>>>> arch/arm64/kvm/reset.c | 4 +
>>>>> arch/arm64/tools/cpucaps | 1 +
>>>>> 15 files changed, 399 insertions(+), 4 deletions(-)
>>>>> create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
>>>>> create mode 100644 arch/arm64/kvm/dirty_bit.c
>>>>>
>>>>> --
>>>>> 2.33.0
>>>>>
>>> Also, please remember to include the base commit, so it can be applied for
>>> testing/reviewing.
>>
>> Thank you for the reminder.
>>
>> This patch series is based on:
>> - Commit: 0e35b9b6ec0f
>> - Description: Merge tag 'mm-hotfixes-stable-2026-07-06-17-49' of
>> git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
>>
>> Welcome to test and review — please let me know if you encounter any issues
>> or have suggestions for improvement.
>>
>>
>
> Thanks for sharing :)
>
> Tip: for next version, you can automate that by adding
> '--base=<base-commit>' to your git-format-patch command, and it will
> automatically generate a base message at the end of your cover letter, such
> as:
>
> ```
> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
> ```
>
> Thanks!
> Leo
Got it! thx!
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-13 10:31 ` [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5 Leonardo Bras
2026-07-13 16:27 ` Leonardo Bras
@ 2026-07-14 9:37 ` Tian Zheng
2026-07-14 10:19 ` Leonardo Bras
1 sibling, 1 reply; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 9:37 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/13/2026 6:31 PM, Leonardo Bras wrote:
> On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
>> This series of patches add support to the Hardware Dirty state tracking
>> Structure (HDBSS) feature, which is introduced by the ARM architecture
>> in the DDI0601 (ID121123) version.
>>
>> The HDBSS feature is an extension to the architecture that enhances
>> tracking translation table descriptors' dirty state, identified as
>> FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
>> page tracking, aiming to significantly reduce the overhead of scanning
>> for dirty pages.
>>
>> The purpose of this feature is to make the execution overhead of live
>> migration lower to both the guest and the host, compared to existing
>> approaches (write-protect or search stage-2 tables).
>>
>> The required sysreg definitions for FEAT_HDBSS have been merged into
>> arm64 /sysregs:
>> [1/5] arm64/sysreg: Add HDBSS related register information
>> https://git.kernel.org/arm64/c/72f7be0c2e30
>>
>>
>> After these patches, the kernel automatically enables HDBSS when dirty
>> logging is enabled on any memslot, and disables HDBSS when dirty logging
>> is disabled on all memslots. This series does not support dirty ring
>> mode.
>>
>> Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
>> https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
> On this, FYI, there have been some discussion on this:
> https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
>
> Oliver's suggestion is that we don't automatically enable eager splitting,
> but instead we have different behaviours if the user enables it.
>
> This is still under discussion there, but I think it can be useful reading.
Thanks for the pointer — I've read through the discussion between you
and Oliver, and it's very helpful.
If I understand correctly, the proposed approach is to support both
modes, with DBM behavior depending
on the user's eager split setting:
- If users enable eager splitting (chunk_size != 0): use the v4 approach
— DBM is set globally.
- If users do NOT enable eager splitting (chunk_size == 0, i.e., lazy
split mode): use the v3 approach — DBM
is set lazily in user_mem_abort on the first write fault.
This makes sense to me. It gives users the flexibility to choose.
As I mentioned in my patch 6/6 reply
(https://lore.kernel.org/all/6e8b23bc-d420-4f5a-a921-5a5d64d84200@huawei.com/),
I do think the lazy DBM approach is safer overall if the first-fault
overhead is acceptable — it avoids accidentally marking
special mappings and naturally handles lazy split.
I'll keep an eye on the discussion and follow up once a conclusion is
reached.
Thanks!
Tian
>> This dependency is mandatory. Unlike v3, where DBM was added lazily on
>> the first dirty access via a page fault, v4 injects DBM into pgt->flags
>> at stage-2 MMU initialization time. Combined with HDBSS being auto-enabled
>> during migration setup, the first dirty access no longer triggers a page
>> fault. Consequently, if lazy hugepage splitting were relied upon (which
>> requires a page fault to trigger splitting), hugepages would never be
>> split, leading to guest hang after migration. Leonardo's patch above
>> ensures eager hugepage splitting is enabled (chunk_size != 0) when HDBSS
>> is available, guaranteeing that hugepages are properly split before
>> migration starts.
>>
>> v3: https://lore.kernel.org/linux-arm-kernel/20260225040421.2683931-1-zhengtian10@huawei.com/
>>
>> v3->v4 changes:
>> - Merge sysreg definitions into the FEAT_HDBSS detection patch (was a
>> separate patch in v3).
>> - Add auto DBM (Dirty Bit Modifier) support as a new patch, suggested
>> by Leonardo Bras. DBM is now controlled as a page-table level flag
>> (KVM_PGTABLE_S2_DBM) rather than per-PTE. Note that DBM is injected
>> at stage-2 MMU creation time, not lazily on first dirty access. This
>> means the first write to a dirty-logged page does not generate a
>> page fault, which is a key reason for the mandatory dependency on
>> Leonardo's eager hugepage splitting patch (see Depends-on above).
>> - Split the v3 "Enable HDBSS support and handle HDBSSF events" patch
>> into three patches: per-vCPU buffer management, fault handling and
>> buffer flush, and auto enable/disable on dirty logging change. This
>> implements kernel-managed automatic HDBSS enable/disable.
>> - Remove the KVM_CAP_ARM_HW_DIRTY_STATE_TRACK ioctl for manual HDBSS
>> on/off. HDBSS is now automatically enabled/disabled based on dirty
>> logging state via kvm_arch_commit_memory_region().
>> - Change HDBSS buffer flush triggers to vcpu_put, check_vcpu_requests,
>> and kvm_handle_guest_abort.
>> - Store hdbss_order at VM level (kvm->arch.hdbss_order) instead of
>> per-vCPU, since all vCPUs share the same order.
>> - Document patch is not included in this version; will be sent in a
>> follow-up series.
>>
>> Leonardo Bras (1):
>> KVM: arm64: Enable eager hugepage splitting if HDBSS is available
>>
>> Tian Zheng (2):
>> KVM: arm64: Add support for FEAT_HDBSS
>> KVM: arm64: Add auto DBM support for hardware dirty tracking
>>
>> eillon (3):
>> KVM: arm64: Add HDBSS per-vCPU buffer management
>> KVM: arm64: Add HDBSS fault handling and buffer flush
>> KVM: arm64: Add auto HDBSS enable/disable on dirty logging change
>>
>> arch/arm64/include/asm/cpufeature.h | 5 +
>> arch/arm64/include/asm/esr.h | 5 +
>> arch/arm64/include/asm/kvm_dirty_bit.h | 29 ++++
>> arch/arm64/include/asm/kvm_host.h | 14 ++
>> arch/arm64/include/asm/kvm_pgtable.h | 4 +
>> arch/arm64/include/asm/sysreg.h | 11 ++
>> arch/arm64/kernel/cpufeature.c | 12 ++
>> arch/arm64/kvm/Makefile | 1 +
>> arch/arm64/kvm/arm.c | 29 ++++
>> arch/arm64/kvm/dirty_bit.c | 219 +++++++++++++++++++++++++
>> arch/arm64/kvm/hyp/pgtable.c | 35 +++-
>> arch/arm64/kvm/hyp/vhe/switch.c | 15 ++
>> arch/arm64/kvm/mmu.c | 19 ++-
>> arch/arm64/kvm/reset.c | 4 +
>> arch/arm64/tools/cpucaps | 1 +
>> 15 files changed, 399 insertions(+), 4 deletions(-)
>> create mode 100644 arch/arm64/include/asm/kvm_dirty_bit.h
>> create mode 100644 arch/arm64/kvm/dirty_bit.c
>>
>> --
>> 2.33.0
>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-14 9:37 ` Tian Zheng
@ 2026-07-14 10:19 ` Leonardo Bras
2026-07-14 13:34 ` Tian Zheng
0 siblings, 1 reply; 32+ messages in thread
From: Leonardo Bras @ 2026-07-14 10:19 UTC (permalink / raw)
To: Tian Zheng
Cc: Leonardo Bras, maz, oupton, catalin.marinas, will, yuzenghui,
wangzhou1, yangjinqian1, caijian11, liuyonglong, yezhenyu2,
yubihong, linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel,
linux-kernel, seiden, suzuki.poulose
On Tue, Jul 14, 2026 at 05:37:58PM +0800, Tian Zheng wrote:
>
> On 7/13/2026 6:31 PM, Leonardo Bras wrote:
> > On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
> > > This series of patches add support to the Hardware Dirty state tracking
> > > Structure (HDBSS) feature, which is introduced by the ARM architecture
> > > in the DDI0601 (ID121123) version.
> > >
> > > The HDBSS feature is an extension to the architecture that enhances
> > > tracking translation table descriptors' dirty state, identified as
> > > FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
> > > page tracking, aiming to significantly reduce the overhead of scanning
> > > for dirty pages.
> > >
> > > The purpose of this feature is to make the execution overhead of live
> > > migration lower to both the guest and the host, compared to existing
> > > approaches (write-protect or search stage-2 tables).
> > >
> > > The required sysreg definitions for FEAT_HDBSS have been merged into
> > > arm64 /sysregs:
> > > [1/5] arm64/sysreg: Add HDBSS related register information
> > > https://git.kernel.org/arm64/c/72f7be0c2e30
> > >
> > >
> > > After these patches, the kernel automatically enables HDBSS when dirty
> > > logging is enabled on any memslot, and disables HDBSS when dirty logging
> > > is disabled on all memslots. This series does not support dirty ring
> > > mode.
> > >
> > > Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
> > > https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
> > On this, FYI, there have been some discussion on this:
> > https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
> >
> > Oliver's suggestion is that we don't automatically enable eager splitting,
> > but instead we have different behaviours if the user enables it.
> >
> > This is still under discussion there, but I think it can be useful reading.
>
>
> Thanks for the pointer — I've read through the discussion between you and
> Oliver, and it's very helpful.
>
>
> If I understand correctly, the proposed approach is to support both modes,
> with DBM behavior depending
>
> on the user's eager split setting:
I am still waiting for his feedback on that, but I suppose that's what he
meant.
>
> - If users enable eager splitting (chunk_size != 0): use the v4 approach —
> DBM is set globally.
>
> - If users do NOT enable eager splitting (chunk_size == 0, i.e., lazy split
> mode): use the v3 approach — DBM
>
> is set lazily in user_mem_abort on the first write fault.
>
>
> This makes sense to me. It gives users the flexibility to choose.
>
Yes, agree
>
> As I mentioned in my patch 6/6 reply (https://lore.kernel.org/all/6e8b23bc-d420-4f5a-a921-5a5d64d84200@huawei.com/),
>
> I do think the lazy DBM approach is safer overall if the first-fault
> overhead is acceptable — it avoids accidentally marking
>
> special mappings and naturally handles lazy split.
>
I remember a previous discussion in which was concluded that lazy marking
DBM carried some issues. I have to go back on that and check if we can take
care of that now.
>
> I'll keep an eye on the discussion and follow up once a conclusion is
> reached.
>
Thanks!
Leo
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 0/6] Support the FEAT_HDBSS introduced in Armv9.5
2026-07-14 10:19 ` Leonardo Bras
@ 2026-07-14 13:34 ` Tian Zheng
0 siblings, 0 replies; 32+ messages in thread
From: Tian Zheng @ 2026-07-14 13:34 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, will, yuzenghui, wangzhou1,
yangjinqian1, caijian11, liuyonglong, yezhenyu2, yubihong,
linuxarm, joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-kernel,
seiden, suzuki.poulose
On 7/14/2026 6:19 PM, Leonardo Bras wrote:
> On Tue, Jul 14, 2026 at 05:37:58PM +0800, Tian Zheng wrote:
>> On 7/13/2026 6:31 PM, Leonardo Bras wrote:
>>> On Thu, Jul 09, 2026 at 06:40:20PM +0800, Tian Zheng wrote:
>>>> This series of patches add support to the Hardware Dirty state tracking
>>>> Structure (HDBSS) feature, which is introduced by the ARM architecture
>>>> in the DDI0601 (ID121123) version.
>>>>
>>>> The HDBSS feature is an extension to the architecture that enhances
>>>> tracking translation table descriptors' dirty state, identified as
>>>> FEAT_HDBSS. This feature utilizes hardware assistance to achieve dirty
>>>> page tracking, aiming to significantly reduce the overhead of scanning
>>>> for dirty pages.
>>>>
>>>> The purpose of this feature is to make the execution overhead of live
>>>> migration lower to both the guest and the host, compared to existing
>>>> approaches (write-protect or search stage-2 tables).
>>>>
>>>> The required sysreg definitions for FEAT_HDBSS have been merged into
>>>> arm64 /sysregs:
>>>> [1/5] arm64/sysreg: Add HDBSS related register information
>>>> https://git.kernel.org/arm64/c/72f7be0c2e30
>>>>
>>>>
>>>> After these patches, the kernel automatically enables HDBSS when dirty
>>>> logging is enabled on any memslot, and disables HDBSS when dirty logging
>>>> is disabled on all memslots. This series does not support dirty ring
>>>> mode.
>>>>
>>>> Depends-on: "KVM: arm64: Enable eager hugepage splitting if HDBSS is available"
>>>> https://lore.kernel.org/linux-arm-kernel/20260629111820.1873540-3-leo.bras@arm.com/
>>> On this, FYI, there have been some discussion on this:
>>> https://lore.kernel.org/all/alETGFD2Ogx6N0HB@LeoBrasDK/
>>>
>>> Oliver's suggestion is that we don't automatically enable eager splitting,
>>> but instead we have different behaviours if the user enables it.
>>>
>>> This is still under discussion there, but I think it can be useful reading.
>>
>> Thanks for the pointer — I've read through the discussion between you and
>> Oliver, and it's very helpful.
>>
>>
>> If I understand correctly, the proposed approach is to support both modes,
>> with DBM behavior depending
>>
>> on the user's eager split setting:
> I am still waiting for his feedback on that, but I suppose that's what he
> meant.
>
>
>> - If users enable eager splitting (chunk_size != 0): use the v4 approach —
>> DBM is set globally.
>>
>> - If users do NOT enable eager splitting (chunk_size == 0, i.e., lazy split
>> mode): use the v3 approach — DBM
>>
>> is set lazily in user_mem_abort on the first write fault.
>>
>>
>> This makes sense to me. It gives users the flexibility to choose.
>>
> Yes, agree
>
>> As I mentioned in my patch 6/6 reply (https://lore.kernel.org/all/6e8b23bc-d420-4f5a-a921-5a5d64d84200@huawei.com/),
>>
>> I do think the lazy DBM approach is safer overall if the first-fault
>> overhead is acceptable — it avoids accidentally marking
>>
>> special mappings and naturally handles lazy split.
>>
> I remember a previous discussion in which was concluded that lazy marking
> DBM carried some issues. I have to go back on that and check if we can take
> care of that now.
>
>> I'll keep an eye on the discussion and follow up once a conclusion is
>> reached.
>>
> Thanks!
> Leo
All right, I'll support both split modes with different DBM methods in
the next version.
Please let me know once you've had a chance to revisit the previous lazy
DBM issues — happy to adjust if needed.
Thanks!
Tian
^ permalink raw reply [flat|nested] 32+ messages in thread