From: Leonardo Bras <leo.bras@arm.com>
To: Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>, Marc Zyngier <maz@kernel.org>,
Oliver Upton <oupton@kernel.org>, Fuad Tabba <tabba@google.com>,
Joey Gouly <joey.gouly@arm.com>,
Steffen Eiden <seiden@linux.ibm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Paolo Bonzini <pbonzini@redhat.com>,
James Morse <james.morse@arm.com>,
Leonardo Bras <leo.bras@arm.com>,
Yang Shi <yang@os.amperecomputing.com>,
mrigendrachaubey <mrigendra.chaubey@gmail.com>,
Thomas Huth <thuth@redhat.com>, Zeng Heng <zengheng4@huawei.com>,
Ryan Roberts <ryan.roberts@arm.com>,
Mark Brown <broonie@kernel.org>,
Sascha Bischoff <Sascha.Bischoff@arm.com>,
Yicong Yang <yangyicong@hisilicon.com>,
Kevin Brodsky <kevin.brodsky@arm.com>,
James Clark <james.clark@linaro.org>,
Raghavendra Rao Ananta <rananta@google.com>,
Yeoreum Yun <yeoreum.yun@arm.com>,
Mark Rutland <mark.rutland@arm.com>,
Anshuman Khandual <anshuman.khandual@arm.com>,
Tian Zheng <zhengtian10@huawei.com>
Cc: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, kvmarm@lists.linux.dev,
kvm@vger.kernel.org
Subject: [PATCH v3 04/11] KVM: arm64: dirty_bit: Add base FEAT_HACDBS cleaning routine
Date: Wed, 29 Jul 2026 11:45:39 +0100 [thread overview]
Message-ID: <20260729104548.3439958-5-leo.bras@arm.com> (raw)
In-Reply-To: <20260729104548.3439958-1-leo.bras@arm.com>
Introduce the basic cleaning routine that is going to be used for both
dirty-bitmap and dirty-ring routines.
It sets the required registers with the input buffer, and wait for
HACDBS to finish, which means either the task is done, or there was some
error during processing.
It is ran with preemption disabled, as a task being scheduled in could
change the translation registers used by HACDBS and end up corrupting the
current dirty-bit tracking and the sched-in task's S2 pagetables.
Signed-off-by: Leonardo Bras <leo.bras@arm.com>
---
arch/arm64/kvm/dirty_bit.c | 176 +++++++++++++++++++++++++++++++++++++
1 file changed, 176 insertions(+)
diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c
index 32fe938d6bf7..33826bbd16d9 100644
--- a/arch/arm64/kvm/dirty_bit.c
+++ b/arch/arm64/kvm/dirty_bit.c
@@ -1,16 +1,192 @@
// 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>
/* 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)
+
+struct hacdbs_save {
+ u64 hacdbsbr_el2;
+ u64 hacdbscons_el2;
+ u64 vttbr_el2;
+ u64 vtcr_el2;
+ bool hcr_vm_set;
+};
+
+struct hacdbs_sched_save {
+ struct preempt_notifier notifier;
+ struct hacdbs_save save;
+};
+
+static int hacdbs_last_entry(int size)
+{
+ u64 cons = read_sysreg_s(SYS_HACDBSCONS_EL2);
+ int index = FIELD_GET(HACDBSCONS_EL2_INDEX, cons);
+
+ switch (FIELD_GET(HACDBSCONS_EL2_ERR_REASON, cons)) {
+ case HACDBSCONS_EL2_ERR_REASON_NOF:
+ return size;
+ case HACDBSCONS_EL2_ERR_REASON_IPAHACF:
+ /* When size not a power of two >= 4k, exit with reserved TTLW */
+ if (index >= size)
+ return size;
+
+ fallthrough;
+ default:
+ /* In case of error, INDEX should point the faulty entry */
+ return index;
+ }
+}
+
+static void hacdbs_start(u64 *hw_entries, int size)
+{
+ u64 br;
+ /* Each entry is 8 bytes */
+ int size_b = size * sizeof(hw_entries[0]);
+ int size_p2 = max(roundup_pow_of_two(size_b), PAGE_SIZE);
+
+ /* If not using the full size of the array, put a stop entry at the end */
+ if (size_b < size_p2)
+ hw_entries[size] = HDBSS_ENTRY_VALID | HDBSS_ENTRY_TTWL_RESV;
+
+ sysreg_clear_set_s(SYS_HACDBSCONS_EL2,
+ HACDBSCONS_EL2_ERR_REASON | HACDBSCONS_EL2_INDEX, 0);
+
+ br = (virt_to_phys(hw_entries) & HACDBSBR_EL2_BADDR_MASK) |
+ FIELD_PREP(HACDBSBR_EL2_SZ, ilog2(size_p2) - 12) |
+ FIELD_PREP(HACDBSBR_EL2_EN, 1);
+
+ /* All writes to the array have to complete before starting */
+ dsb(ishst);
+
+ write_sysreg_s(br, SYS_HACDBSBR_EL2);
+ isb();
+}
+
+static int hacdbs_stop(int size)
+{
+ int idx;
+
+ write_sysreg_s(0, SYS_HACDBSBR_EL2);
+ isb();
+
+ idx = hacdbs_last_entry(size);
+
+ return idx;
+}
+
+static void hacdbs_sched_in(struct preempt_notifier *notifier, int cpu)
+{
+ struct hacdbs_sched_save *save = container_of(notifier,
+ struct hacdbs_sched_save,
+ notifier);
+
+ write_sysreg_s(save->save.vtcr_el2, SYS_VTCR_EL2);
+ write_sysreg_s(save->save.vttbr_el2, SYS_VTTBR_EL2);
+ isb();
+
+ if (save->save.hcr_vm_set) {
+ sysreg_clear_set_hcr(0, HCR_EL2_VM);
+ isb();
+ }
+
+ write_sysreg_s(save->save.hacdbscons_el2, SYS_HACDBSCONS_EL2);
+ write_sysreg_s(save->save.hacdbsbr_el2, SYS_HACDBSBR_EL2);
+ isb();
+}
+
+static void hacdbs_sched_out(struct preempt_notifier *notifier,
+ struct task_struct *next)
+{
+ struct hacdbs_sched_save *save = container_of(notifier,
+ struct hacdbs_sched_save,
+ notifier);
+
+ if (read_sysreg_s(SYS_HACDBSBR_EL2) & HACDBSBR_EL2_EN) {
+ save->save.hacdbsbr_el2 = HACDBSBR_EL2_EN;
+ sysreg_clear_set_s(SYS_HACDBSBR_EL2, HACDBSBR_EL2_EN, 0);
+ isb();
+ } else {
+ save->save.hacdbsbr_el2 = 0;
+ }
+
+ save->save.hacdbscons_el2 = read_sysreg_s(SYS_HACDBSCONS_EL2);
+ save->save.hacdbsbr_el2 |= read_sysreg_s(SYS_HACDBSBR_EL2);
+ save->save.vttbr_el2 = read_sysreg_s(SYS_VTTBR_EL2);
+ save->save.vtcr_el2 = read_sysreg_s(SYS_VTCR_EL2);
+
+ if (read_sysreg_s(SYS_HCR_EL2) & HCR_EL2_VM) {
+ sysreg_clear_set_hcr(HCR_EL2_VM, 0);
+ isb();
+ save->save.hcr_vm_set = true;
+ } else {
+ save->save.hcr_vm_set = false;
+ }
+}
+
+static struct preempt_ops hacdbs_preempt_ops = {
+ .sched_in = hacdbs_sched_in,
+ .sched_out = hacdbs_sched_out,
+};
+
+/*
+ * Clears dirty-bits for an array of pages (hw_entries) using HACDBS
+ * Returns the number of items cleaned from the array. If returns value < size,
+ * there was an error in the processing.
+ */
+static int dirty_bit_clear(struct kvm *kvm, u64 *hw_entries, int size)
+{
+ int ret;
+ u64 cons;
+ struct hacdbs_sched_save save;
+
+ preempt_notifier_init(&save.notifier, &hacdbs_preempt_ops);
+ preempt_disable();
+ preempt_notifier_register(&save.notifier);
+ preempt_enable();
+
+ __load_stage2(&kvm->arch.mmu);
+ sysreg_clear_set_hcr(0, HCR_EL2_VM);
+ isb();
+
+ hacdbs_start(hw_entries, size);
+
+ do {
+ cons = read_sysreg_s(SYS_HACDBSCONS_EL2);
+ if (FIELD_GET(HACDBSCONS_EL2_ERR_REASON, cons))
+ break;
+
+ if (FIELD_GET(HACDBSCONS_EL2_INDEX, cons) >= size)
+ break;
+
+ udelay(1);
+ } while (true);
+
+ ret = hacdbs_stop(size);
+
+ sysreg_clear_set_hcr(HCR_EL2_VM, 0);
+ isb();
+
+ /*
+ * No DSB is needed here, as kvm_flush_remote_tlbs_memslot() that happens
+ * later in generic dirty-cleaning code already performs a DSB before
+ * doing the TLBI.
+ */
+
+ preempt_disable();
+ preempt_notifier_unregister(&save.notifier);
+ preempt_enable();
+
+ return ret;
+}
--
2.55.0
next prev parent reply other threads:[~2026-07-29 10:46 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-29 10:45 [PATCH v3 00/11] KVM Dirty-bit cleaning hw accelerator (HACDBS) Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 01/11] KVM: arm64: HDBSS bits Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 02/11] arm64/cpufeature: Add system-wide FEAT_HACDBS detection Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 03/11] arm64/sysreg: Add HACDBS consumer and base registers Leonardo Bras
2026-07-29 10:45 ` Leonardo Bras [this message]
2026-07-29 10:45 ` [PATCH v3 05/11] kvm: Add arch-generic interface for hw-accelerated dirty-bitmap cleaning Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 06/11] KVM: arm64: Add hardware-accelerated dirty-bitmap cleaning routine Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 07/11] KVM: arm64: Dirty-bitmap: avoid splitting previously split blocks Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 08/11] kvm/dirty_ring: Introduce get_memslot and move helpers to header Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 09/11] kvm/dirty_ring: Add arch-generic interface for hw-accelerated dirty-ring cleaning Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 10/11] KVM: arm64: Add hardware-accelerated dirty-ring cleaning routine Leonardo Bras
2026-07-29 10:45 ` [PATCH v3 11/11] KVM: arm64: Enable KVM_HW_DIRTY_BIT Leonardo Bras
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260729104548.3439958-5-leo.bras@arm.com \
--to=leo.bras@arm.com \
--cc=Sascha.Bischoff@arm.com \
--cc=anshuman.khandual@arm.com \
--cc=broonie@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=james.clark@linaro.org \
--cc=james.morse@arm.com \
--cc=joey.gouly@arm.com \
--cc=kevin.brodsky@arm.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=maz@kernel.org \
--cc=mrigendra.chaubey@gmail.com \
--cc=oupton@kernel.org \
--cc=pbonzini@redhat.com \
--cc=rananta@google.com \
--cc=ryan.roberts@arm.com \
--cc=seiden@linux.ibm.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=thuth@redhat.com \
--cc=will@kernel.org \
--cc=yang@os.amperecomputing.com \
--cc=yangyicong@hisilicon.com \
--cc=yeoreum.yun@arm.com \
--cc=yuzenghui@huawei.com \
--cc=zengheng4@huawei.com \
--cc=zhengtian10@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox