Re: [PATCH v3 10/11] KVM: arm64: Add hardware-accelerated dirty-ring cleaning routine
Leonardo Bras <[email protected]> Mon, 3 Aug 2026 17:07:36 +0100
| Newsgroups | dev.linux.lists.kvmarm,dev.linux.lists.sashiko-reviews,org.kernel.vger.kvm |
|---|---|
| Message-ID | <anC8yHF6xC693paS@LeoBrasDK> |
On Wed, Jul 29, 2026 at 11:21:24AM +0000, [email protected] wrote: > Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: > > New issues: > - [Critical] Hardware-accelerated dirty ring clear directly accesses EL2 system registers from the host kernel, causing fatal unhandled exceptions on nVHE and pKVM systems. > - [High] The `hw_entries` buffer is allocated using kmalloc, which does not guarantee the page alignment strictly required by the `SYS_HACDBSBR_EL2` hardware register. > > Pre-existing issues: > - [High] Writes to control-plane system registers are batched without an intervening `isb()`, violating ARM64 architectural synchronization requirements. > -- > > --- Patch [10]: [PATCH v3 10/11] KVM: arm64: Add hardware-accelerated dirty-ring cleaning routine --- > commit f9fc2a3f2ff348143923dea638ecd996e369580d > Author: Leonardo Bras <[email protected]> > > KVM: arm64: Add hardware-accelerated dirty-ring cleaning routine > > Implement arm64 version of kvm_arch_dirty_ring_clear() making use of > FEAT_HACDBS. > > It works by transversing the dirty-ring and converting its entries into > HDBSS entries based on the slot offset. > > The resulting HDBSS array is then fed to the HACDBS mechanism that walks > the pagetable marking writable-dirty pages as writable-clean. > > > diff --git a/arch/arm64/include/asm/kvm_dirty_bit.h b/arch/arm64/include/asm/kvm_dirty_bit.h > > index d368a23a84b30..f785a7aad9e88 100644 > > --- a/arch/arm64/include/asm/kvm_dirty_bit.h > > +++ b/arch/arm64/include/asm/kvm_dirty_bit.h > > @@ -15,6 +15,9 @@ int __kvm_arch_dirty_log_clear(struct kvm *kvm, > > unsigned long *bitmap, > > bool *flush); > > > > +int __kvm_arch_dirty_ring_clear(struct kvm *kvm, struct kvm_dirty_ring *ring, > > + int *nr_entries_reset); > > + > > static inline bool kvm_arch_dirty_clear_enabled(struct kvm *kvm) > > { > > return system_supports_hacdbs() && kvm->arch.mmu.pgt && > > (kvm->arch.mmu.pgt->flags & KVM_PGTABLE_S2_DBM); > > } > > [Severity: Critical] > Does this gating function also need to verify the VHE execution mode? it's already done before setting KVM_PGTABLE_S2_DBM. > > By checking only system_supports_hacdbs(), it appears this permits the > accelerated path to be invoked on nVHE and pKVM host systems. > > Since dirty_bit_clear() performs direct accesses to EL2 system registers > from the host kernel: > > arch/arm64/kvm/dirty_bit.c:dirty_bit_clear() { > ... > __load_stage2(&kvm->arch.mmu); > sysreg_clear_set_hcr(0, HCR_EL2_VM); > ... > } > > Wouldn't this trigger an unhandled hypervisor trap or undefined instruction > exception when triggered by userspace on nVHE systems? > > [ ... ] > > diff --git a/arch/arm64/kvm/dirty_bit.c b/arch/arm64/kvm/dirty_bit.c > > index d7e624f9b6308..ca9cfefe0893b 100644 > > --- a/arch/arm64/kvm/dirty_bit.c > > +++ b/arch/arm64/kvm/dirty_bit.c > [ ... ] > > @@ -359,3 +359,80 @@ int __kvm_arch_dirty_log_clear(struct kvm *kvm, > > > > return ret; > > } > > + > > +int __kvm_arch_dirty_ring_clear(struct kvm *kvm, struct kvm_dirty_ring *ring, > > + int *nr_entries_reset) > > +{ > > + u64 *hw_entries __free(kfree) = NULL; > > + u64 ttwl; > > + s64 cur_slot = S64_MAX; > > + int i, ret; > > + struct kvm_memory_slot *memslot; > > + > > + if (signal_pending(current)) > > + return -EINTR; > > + > > + ttwl = HDBSS_ENTRY_TTWL(KVM_PGTABLE_LAST_LEVEL); > > + > > + hw_entries = kmalloc(max(ring->size * sizeof(u64), PAGE_SIZE), GFP_KERNEL); > > [Severity: High] > Could this allocation cause the hardware accelerator to read from the wrong > memory address? > > When debugging features like SLUB debugging are enabled, kmalloc allocations > are not strictly page-aligned because of prepended metadata. > Argh, I was not aware of this. Will change it to use recommended allocators instead of kmalloc. > The SYS_HACDBSBR_EL2 hardware register requires a page-aligned physical > base address. When hacdbs_start() masks out the offset using > HACDBSBR_EL2_BADDR_MASK, an unaligned base address gets truncated. > > Will this result in the hardware reading from the start of the physical > page instead of the actual hw_entries array? It might be necessary to use > alloc_pages_exact() or __get_free_pages() here to guarantee alignment. > > > + if (!hw_entries) > > + return -ENOMEM; > > + > [ ... ] > > + if (i == 0) > > + return 0; > > + > > + write_lock(&kvm->mmu_lock); > > + ret = dirty_bit_clear(kvm, hw_entries, i); > > [Severity: High] > This is a pre-existing issue, but is it possible that the hardware routines > called here violate architectural synchronization requirements? > Already discussed before. > Looking at the underlying functions, writes to control-plane system > registers seem to be batched without an intervening isb() as the very next > instruction. > > For example, in hacdbs_start(): > > arch/arm64/kvm/dirty_bit.c:hacdbs_start() > ... > 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) | ... > ... > > And in hacdbs_sched_in(): > > arch/arm64/kvm/dirty_bit.c:hacdbs_sched_in() > ... > write_sysreg_s(save->save.vtcr_el2, SYS_VTCR_EL2); > write_sysreg_s(save->save.vttbr_el2, SYS_VTTBR_EL2); > isb(); > > Could this allow the CPU to execute the intervening instructions in an > architecturally undefined pipeline state? > > > + write_unlock(&kvm->mmu_lock); > > + > > + /* Set as invalid all successfully cleaned entries */ > [ ... ] > > -- > Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=10