[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 3/3] x86/HVM: defer p2m TLB flushes across a grant unmap batch



A grant unmap by a translated caller also flushes the guest TLBs:
p2m_remove_page() sets need_flush of the host p2m, and put_gfn()
flushes when it drops the last level of the p2m lock. Each flush sends
an IPI to every other CPU in d->dirty_cpumask and waits for all of
them, so a PVH dom0 with busy vCPUs pays a round of IPIs for every page
it unmaps.

Defer that flush to the end of the batch, still before the pages are
released. Under the gfn lock of get_gfn_query() only the removal can
set need_flush, as the query neither allocates nor unshares, so no
other change's flush is deferred. p2m_free_ptp() still flushes before
it frees a table.

The final flush reaches the CPUs in d->dirty_cpumask at that time. A
CPU that left the mask in between has flushed already, as
__context_switch() clears it from the mask only after write_ptbase()
has flushed the guest TLBs. With EPT, each change also marks every CPU
for an INVEPT before its next VM entry.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Luca Di Maio <luca@xxxxxxxxx>

---
 xen/arch/x86/hvm/grant_table.c      | 6 ++++++
 xen/arch/x86/include/asm/hvm/vcpu.h | 1 +
 2 files changed, 7 insertions(+)

diff --git a/xen/arch/x86/hvm/grant_table.c b/xen/arch/x86/hvm/grant_table.c
index 1d7f9b19f9..e340712dd6 100644
--- a/xen/arch/x86/hvm/grant_table.c
+++ b/xen/arch/x86/hvm/grant_table.c
@@ -41,6 +41,7 @@ int replace_grant_p2m_mapping(uint64_t addr, mfn_t frame,
     p2m_type_t type;
     mfn_t old_mfn;
     struct domain *d = current->domain;
+    struct p2m_domain *p2m = p2m_get_hostp2m(d);
     struct hvm_grant_unmap_batch *batch = &current->arch.hvm.grant_unmap_batch;
     int rc;
 
@@ -72,6 +73,8 @@ int replace_grant_p2m_mapping(uint64_t addr, mfn_t frame,
         batch->iommu_flags |= this_cpu(iommu_skipped_flush_flags);
         batch->gfn_lo = min(batch->gfn_lo, gfn);
         batch->gfn_hi = max(batch->gfn_hi, gfn);
+        batch->tlb_flush |= p2m->need_flush;
+        p2m->need_flush = false;
     }
 
     if ( rc )
@@ -95,10 +98,13 @@ void grant_p2m_unmap_batch_begin(void)
 int grant_p2m_unmap_batch_end(void)
 {
     struct domain *d = current->domain;
+    struct p2m_domain *p2m = p2m_get_hostp2m(d);
     struct hvm_grant_unmap_batch *batch = &current->arch.hvm.grant_unmap_batch;
     unsigned int order;
 
     batch->open = false;
+    if ( batch->tlb_flush )
+        p2m->tlb_flush(p2m);
 
     if ( !batch->iommu_flags )
         return 0;
diff --git a/xen/arch/x86/include/asm/hvm/vcpu.h 
b/xen/arch/x86/include/asm/hvm/vcpu.h
index 12b7e9b707..9638e50e91 100644
--- a/xen/arch/x86/include/asm/hvm/vcpu.h
+++ b/xen/arch/x86/include/asm/hvm/vcpu.h
@@ -111,6 +111,7 @@ struct altp2mvcpu {
 /* Per vCPU, as the guest copies between the unmaps can sleep. */
 struct hvm_grant_unmap_batch {
     bool          open;
+    bool          tlb_flush;
     unsigned int  iommu_flags;
     unsigned long gfn_lo, gfn_hi;
 };
-- 
2.54.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.