From: Claudio Imbrenda <imbrenda@linux.ibm.com>
To: "Carlos López" <clopez@suse.de>
Cc: kvm@vger.kernel.org, linux-mm@kvack.org, seanjc@google.com,
pbonzini@redhat.com, linux-s390@vger.kernel.org
Subject: Re: [RFC PATCH 0/4] KVM: x86/mmu: don't zap secondary MMU on permission upgrade
Date: Thu, 29 Oct 2026 17:52:03 +0100 [thread overview]
Message-ID: <20261029175203.5bdd51d7@p-imbrenda> (raw)
In-Reply-To: <20261029143336.390231-3-clopez@suse.de>
On Thu, 29 Oct 2026 15:33:33 +0100
Carlos López <clopez@suse.de> wrote:
> Prevent x86 KVM from over-zapping its secondary MMU on PTE permission
> upgrades, which leave the PFNs unchanged and do not revoke access.
> Permissions for SPTEs that KVM holds are updated on the next fault, so
> dropping them early gains nothing.
>
> Do this by adding a new MMU notifier flag to signal that a
> MMU_NOTIFY_PROTECTION_VMA strictly upgrades (loosens) permissions on a
> particular range.
>
> See below for benchmark results.
>
> Background
> ==========
>
> change_protection() pairs every protection change with an
> MMU_NOTIFY_PROTECTION_VMA invalidation, and KVM zaps its SPTEs in
> response, as it cannot tell whether permissions widened or narrowed.
> This is costly e.g. during a QEMU background snapshot, which relies on
> uffd-wp to write-protect guest RAM and then restore permissions as each
> page is saved. Without this series, the permission restore causes the
> guest to re-fault read-only memory needlessly.
>
> Note, the reverse situation is already handled for secondary MMUs
> that reuse the CPU page tables (e.g. ARM SMMU, IOMMU/ATS). They cannot
> refault, so they are notified of a permission upgrade on the CPU
> TLB-flush path [1].
>
> KVM, however, owns separate page tables and faults permissions on demand,
> so it needs no such notification (as Sean noted when that signalling
> was first proposed on the fault path [2]). This series applies the same
> principle to the change_protection() path, signalling the permission
> upgrade so KVM can avoid the unnecessary zap.
>
> RFC Considerations
> ==================
>
> 1. Having the zap skip in KVM's arch-independent code does not seem
> obviously correct, as s390 caches additional translation state
> that may not be rebuilt by refaults. Maybe an expert can correct
> me. The skip could be extended to arm64 and RISC-V, but I have not
> benchmarked the benefits and I'd like to get feedback first. Hence,
> x86-only for now.
>
> 2. change_protection() could simply not emit the invalidation at all
> for a pure permission upgrade (shared-page-table MMUs invalidate
> on the CPU TLB-flush path, as noted above, so they do not need it).
> But again, the s390 case could be broken, and even then, that would
> seem too much intrusion for KVM's benefit.
>
> 3. The flag reaches KVM as a bool on struct kvm_gfn_range.
> Alternatively, KVM could forward the notifier event, as Claudio
> Imbrenda suggested at LPC [3] and others have proposed in the past
> for different purposes [4] [5]. That, along with the event flags,
> could serve the same purpose. I'd be happy to take that approach if
> there is agreement that other consumers would want it.
>
> (The LPC talk recording has some audio issues so I am not too sure
> on what the final agreement was)
AFAIR the consensus was:
- adding the reason to the kvm kvm_gfn_range is fine.
- having a return value from the notifier to pass information back is
_not_ fine; I should add a new callback instead.
regarding this series:
on x86 you don't care what the new permissions are, because you're
updating when re-faulting (if I understood correctly).
for s390 it might be beneficial to know what the new permissions are;
of course we could look up the vma and find out, but since you are
already checking the permissions, maybe you can find a way to also
convey the new permission bits?
that would make our life on s390 easier :)
>
> Benchmarks
> ==========
>
> AMD EPYC 9654 96-Core Processor host running a recent Linux kernel
> (6b4b543fa187), with a 64GB, 64-vCPU guest running stress-ng --vm while
> the host takes a QEMU background snapshot. All results are aggregated
> over 5 runs, and compared between the stock kernel and one with this
> patch series. Guest is rebooted across each individual run, and NUMA
> balancing is disabled.
>
> pf/s and tlb/s (per-second deltas of KVM's pf_taken and remote_tlb_flush
> debugfs stats) clearly improve:
>
> guest pattern baseline patched
> -----------------------------------------------------------------------
> read64 (read) pf/s=950,015 tlb/s=1,440 pf/s=2,503 tlb/s=0.1
> rand-set (read+write) pf/s=1,004,353 tlb/s=862 pf/s=4,073 tlb/s=0.3
> write64 (write) pf/s=968,018 tlb/s=94 pf/s=2,292 tlb/s=0.3
>
> Guest read latency was sampled with a paced probe thread in the
> guest, reading one byte per page with an idle gap between reads, while
> stress-ng ran with read64. Each figure is the median across 5 runs, with
> the run-to-run min and max, in ns:
>
> metric baseline patched
> ------------------------------------------------------
> p50 (min max) 340 (270 380) 260 (250 260)
> p99 (min max) 19789 (16885 20200) 5207 (4076 5638)
> p99.9 (min max) 23445 (22845 24036) 6850 (6259 7110)
>
> The median improves modestly and the refault cost shows in the tail,
> where p99 drops ~3.8x.
>
> A separate stress-ng run with write64 and --verify showed no data
> corruption and no livelocks.
>
> References
> ==========
>
> [1] https://lore.kernel.org/linux-mm/cover.1eca029b8603ef4eebe5b41eae51facfc5920c41.1690292440.git-series.apopple@nvidia.com/
> [2] https://lore.kernel.org/linux-mm/ZGu1vsbscdO48V6h@google.com/
> [3] https://lpc.events/event/20/contributions/2552/
> [4] https://lore.kernel.org/all/20240801183453.57199-1-seanjc@google.com
> [5] https://lore.kernel.org/all/20231110003734.1014084-1-jackyli@google.com
>
> Carlos López (4):
> mm/mprotect: add MM_CP_PERM_UPGRADE
> mm/mmu_notifier: add MMU_NOTIFIER_RANGE_PERM_UPGRADE
> KVM: plumb MMU_NOTIFIER_RANGE_PERM_UPGRADE into kvm_gfn_range
> KVM: x86/mmu: don't zap the secondary MMU on permission upgrade
>
> arch/x86/kvm/mmu/mmu.c | 7 +++++++
> include/linux/kvm_host.h | 1 +
> include/linux/mm.h | 8 ++++++++
> include/linux/mmu_notifier.h | 5 +++++
> mm/hugetlb.c | 6 +++++-
> mm/mprotect.c | 20 +++++++++++++++++++-
> virt/kvm/kvm_main.c | 4 ++++
> 7 files changed, 49 insertions(+), 2 deletions(-)
>
prev parent reply other threads:[~2026-10-29 16:52 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 14:33 [RFC PATCH 0/4] KVM: x86/mmu: don't zap secondary MMU on permission upgrade Carlos López
2026-10-29 14:33 ` [RFC PATCH 1/4] mm/mprotect: add MM_CP_PERM_UPGRADE Carlos López
2026-10-29 14:33 ` [RFC PATCH 2/4] mm/mmu_notifier: add MMU_NOTIFIER_RANGE_PERM_UPGRADE Carlos López
2026-10-29 14:33 ` [RFC PATCH 3/4] KVM: plumb MMU_NOTIFIER_RANGE_PERM_UPGRADE into kvm_gfn_range Carlos López
2026-10-29 14:33 ` [RFC PATCH 4/4] KVM: x86/mmu: don't zap the secondary MMU on permission upgrade Carlos López
2026-10-29 16:52 ` Claudio Imbrenda [this message]
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=20261029175203.5bdd51d7@p-imbrenda \
--to=imbrenda@linux.ibm.com \
--cc=clopez@suse.de \
--cc=kvm@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-s390@vger.kernel.org \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.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