KVM TLB flushing improvements (for radix)
Subsystems (2)
Nicholas Piggin
reviewerv2 cover KVM TLB flushing improvements (for radix)
This series moves some of the radix mode TLB flushing into powerpc/mm, which allows them them to implement the powerpc:tlbie tracepoints for KVM invalidations.
This also fixes a partition scoped page fault performance issue that was found by looking at partition scoped tlbie traces.
Since v1:
- Fixed a bug where I mixed up PRS values, leading to guest page fault hangs.
- Fixed up the hash cases that still need to be done in real-mode.
- Dropped the hash changes including the interesting case of a hash tlbie issued by a radix host, for mixed mode support.
This has survived some stress testing over the weekend now, so it should be ready for wider review.
Thanks,
Nick
Nicholas Piggin (5):
KVM: PPC: Book3S HV: radix use correct tlbie sequence in
kvmppc_radix_tlbie_page
powerpc/mm/radix: implement LPID based TLB flushes to be used by KVM
KVM: PPC: Book3S HV: radix use the Linux TLB flush function in
kvmppc_radix_tlbie_page
KVM: PPC: Book3S HV: radix handle process scoped LPID flush in C, with
relocation on
KVM: PPC: Book3S HV: radix do not clear partition scoped page table
when page fault races with other vCPUs.
.../include/asm/book3s/64/tlbflush-radix.h | 6 + arch/powerpc/kvm/book3s_64_mmu_radix.c | 78 ++++----- arch/powerpc/kvm/book3s_hv.c | 26 +++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 13 +- arch/powerpc/mm/tlb-radix.c | 160 ++++++++++++++++++ 5 files changed, 238 insertions(+), 45 deletions(-)
Signature · 1 line
2.17.0
Nicholas Piggin
reviewerPATCH v2 1/5 KVM: PPC: Book3S HV: radix use correct tlbie sequence in kvmppc_radix_tlbie_page
The standard eieio ; tlbsync ; ptesync must follow tlbie to ensure it is ordered with respect to subsequent operations.
--- arch/powerpc/kvm/book3s_64_mmu_radix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kvm/book3s_64_mmu_radix.c +1 -1
@@ -160,7 +160,7 @@ static void kvmppc_radix_tlbie_page(struct kvm *kvm, unsigned long addr,if (cpu_has_feature (CPU_FTR_P9_TLBIE_BUG )) asm volatile(PPC_TLBIE_5(%0, %1, 0, 0, 1) : : "r" (addr), "r" (kvm->arch.lpid) : "memory"); - asmvolatile ("ptesync" : : :"memory" ); + asmvolatile ("eieio ; tlbsync ; ptesync" : : :"memory" ); } unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep,
Signature · 1 line
2.17.0
Nicholas Piggin
reviewerPATCH v2 2/5 powerpc/mm/radix: implement LPID based TLB flushes to be used by KVM
In reply to Nicholas PigginImplement a local TLB flush for invalidating an LPID with variants for process or partition scope. And a global TLB flush for invalidating a partition scoped page of an LPID.
These will be used by KVM in subsequent patches.
--- .../include/asm/book3s/64/tlbflush-radix.h | 6 + arch/powerpc/mm/tlb-radix.c | 160 ++++++++++++++++++ 2 files changed, 166 insertions(+)
arch/powerpc/include/asm/book3s/64/tlbflush-radix.h +6 -0
@@ -51,4 +51,10 @@ extern void radix__flush_tlb_all(void);extern void radix__flush_tlb_pte_p9_dd1 (unsigned long old_pte,struct mm_struct *mm,unsigned long address); +extern void radix__flush_tlb_lpid_page (unsigned int lpid, +unsigned long addr, +unsigned long page_size); +extern void radix__local_flush_tlb_lpid (unsigned int lpid); +extern void radix__local_flush_tlb_lpid_guest (unsigned int lpid); +#endif
arch/powerpc/mm/tlb-radix.c +160 -0
196 lines not on this page · raw message
Signature · 1 line
2.17.0
Nicholas Piggin
reviewerPATCH v2 3/5 KVM: PPC: Book3S HV: radix use the Linux TLB flush function in kvmppc_radix_tlbie_pag
In reply to Nicholas PigginThis has the advantage of consolidating TLB flush code in fewer places, and it also implements powerpc:tlbie trace events.
1GB pages should be handled without further modification.
--- arch/powerpc/kvm/book3s_64_mmu_radix.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-)
arch/powerpc/kvm/book3s_64_mmu_radix.c +7 -19
@@ -139,28 +139,16 @@ int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,return 0 ; } -#ifdef CONFIG_PPC_64K_PAGES -#define MMU_BASE_PSIZE MMU_PAGE_64K -#else -#define MMU_BASE_PSIZE MMU_PAGE_4K -#endif -static void kvmppc_radix_tlbie_page (struct kvm *kvm,unsigned long addr,unsigned int pshift) { -int psize =MMU_BASE_PSIZE ; - -if (pshift >=PMD_SHIFT ) - psize =MMU_PAGE_2M ; - addr &= ~0xfffUL ; - addr |= mmu_psize_defs[psize].ap <<5 ; - asmvolatile ("ptesync" : : :"memory" ); - asm volatile(PPC_TLBIE_5(%0, %1, 0, 0, 1) - : : "r" (addr), "r" (kvm->arch.lpid) : "memory"); -if (cpu_has_feature (CPU_FTR_P9_TLBIE_BUG )) - asm volatile(PPC_TLBIE_5(%0, %1, 0, 0, 1) - : : "r" (addr), "r" (kvm->arch.lpid) : "memory"); - asmvolatile ("eieio ; tlbsync ; ptesync" : : :"memory" ); +unsigned long psize =PAGE_SIZE ; + +if (pshift) + psize =1UL << pshift; + + addr &= ~(psize -1 ); +radix__flush_tlb_lpid_page (kvm->arch.lpid, addr, psize); } unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep,
Signature · 1 line
2.17.0
Nicholas Piggin
reviewerPATCH v2 4/5 KVM: PPC: Book3S HV: radix handle process scoped LPID flush in C, with relocation on
In reply to Nicholas PigginThe radix guest code can has fewer restrictions about what context it can run in, so move this flushing out of assembly and have it use the Linux TLB flush implementations introduced previously.
This allows powerpc:tlbie trace events to be used.
This changes the tlbiel sequence to only execute RIC=2 flush once on the first set flushed, which matches the rest of the Linux flushing. This does not change semantics of the flush.
--- arch/powerpc/kvm/book3s_hv.c | 26 +++++++++++++++++++++++++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 13 ++++++------- 2 files changed, 32 insertions(+), 7 deletions(-)
arch/powerpc/kvm/book3s_hv.c +26 -0
@@ -2901,6 +2901,32 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc)for (sub =0 ; sub < core_info.n_subcores; ++sub)spin_unlock (&core_info.vc[sub]->lock); +if (kvm_is_radix (vc->kvm)) { +int tmp = pcpu; + +/* +* Do we need to flush the process scoped TLB for the LPAR? +* +* On POWER9, individual threads can come in here, but the +* TLB is shared between the 4 threads in a core, hence +* invalidating on one thread invalidates for all. +* Thus we make all 4 threads use the same bit here. +* +* Hash must be flushed in realmode in order to use tlbiel. +*/ +mtspr (SPRN_LPID , vc->kvm->arch.lpid); +isync (); + +if (cpu_has_feature (CPU_FTR_ARCH_300 )) + tmp &= ~0x3UL ; + +if (cpumask_test_cpu (tmp, &vc->kvm->arch.need_tlb_flush)) { +radix__local_flush_tlb_lpid_guest (vc->kvm->arch.lpid); +/* Clear the bit after the TLB flush */ +cpumask_clear_cpu (tmp, &vc->kvm->arch.need_tlb_flush); + } + } +/* * Interrupts will be enabled once we get into the guest, * so tell lockdep that we're about to enable interrupts.
arch/powerpc/kvm/book3s_hv_rmhandlers.S +6 -7
@@ -636,6 +636,10 @@ kvmppc_hv_entry: /* Primary thread switches to guest partition. */ cmpwi r6,0 bne 10f + + /* Radix has already switched LPID and flushed core TLB */ + bne cr7,22f + lwz r7,KVM_LPID(r9) BEGIN_FTR_SECTION ld r6,KVM_SDR1(r9)
@@ -647,7 +651,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300) mtspr SPRN_LPID,r7 isync - /* See if we need to flush the TLB */ + /* See if we need to flush the TLB. Hash has to be done in RM */ lhz r6,PACAPACAINDEX(r13) /* test_bit(cpu, need_tlb_flush) */ BEGIN_FTR_SECTION /*
@@ -674,15 +678,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) li r7,0x800 /* IS field = 0b10 */ ptesync li r0,0 /* RS for P9 version of tlbiel */ - bne cr7, 29f 28: tlbiel r7 /* On P9, rs=0, RIC=0, PRS=0, R=0 */ addi r7,r7,0x1000 bdnz 28b - b 30f -29: PPC_TLBIEL(7,0,2,1,1) /* for radix, RIC=2, PRS=1, R=1 */ - addi r7,r7,0x1000 - bdnz 29b -30: ptesync + ptesync 23: ldarx r7,0,r6 /* clear the bit after TLB flushed */ andc r7,r7,r8 stdcx. r7,0,r6
Signature · 1 line
2.17.0
Nicholas Piggin
reviewerPATCH v2 5/5 KVM: PPC: Book3S HV: radix do not clear partition scoped page table when page fault r
In reply to Nicholas PigginWhen running a SMP radix guest, KVM can get into page fault / tlbie storms -- hundreds of thousands to the same address from different threads -- due to partition scoped page faults invalidating the page table entry if it was found to be already set up by a racing CPU.
What can happen is that guest threads can hit page faults for the same addresses, this can happen when KSM or THP takes out a commonly used page. gRA zero (the interrupt vectors and important kernel text) was a common one. Multiple CPUs will page fault and contend on the same lock, when one CPU sets up the page table and releases the lock, the next will find the new entry and invalidate it before installing its own, which causes other page faults which invalidate that entry, etc.
The solution to this is to avoid invalidating the entry or flushing TLBs in case of a race. The pte may still need bits updated, but those are to add R/C or relax access restrictions so no flush is required.
This solves the page fault / tlbie storms.
--- arch/powerpc/kvm/book3s_64_mmu_radix.c | 52 ++++++++++++++++---------- 1 file changed, 33 insertions(+), 19 deletions(-)
arch/powerpc/kvm/book3s_64_mmu_radix.c +33 -19
@@ -199,7 +199,6 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa,pud_t *pud, *new_pud =NULL ;pmd_t *pmd, *new_pmd =NULL ;pte_t *ptep, *new_ptep =NULL ; -unsigned long old;int ret;/* Traverse the guest's 2nd-level tree, allocate new levels needed */
@@ -243,6 +242,7 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, pmd =pmd_offset (pud, gpa);if (pmd_is_leaf (*pmd)) {unsigned long lgpa = gpa &PMD_MASK ; +pte_t old_pte = *pmdp_ptep (pmd);/* * If we raced with another CPU which has just put
@@ -252,18 +252,22 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, ret = -EAGAIN ; goto out_unlock; } -/* Valid 2MB page here already, remove it */ - old =kvmppc_radix_update_pte (kvm,pmdp_ptep (pmd), - ~0UL ,0 , lgpa,PMD_SHIFT ); -kvmppc_radix_tlbie_page (kvm, lgpa,PMD_SHIFT ); -if (old & _PAGE_DIRTY) { -unsigned long gfn = lgpa >>PAGE_SHIFT ; -struct kvm_memory_slot *memslot; - memslot =gfn_to_memslot (kvm, gfn); -if (memslot && memslot->dirty_bitmap) -kvmppc_update_dirty_map (memslot, - gfn,PMD_SIZE ); + +/* PTE was previously valid, so update it */ +if (pte_val (old_pte) =pte_val (pte)) { + ret = -EAGAIN ; + goto out_unlock; } + +/* Make sure we're weren't trying to take bits away */ +WARN_ON_ONCE (pte_pfn (old_pte) !=pte_pfn (pte)); +WARN_ON_ONCE ((pte_val (old_pte) & ~pte_val (pte)) & + (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE)); + +kvmppc_radix_update_pte (kvm,pmdp_ptep (pmd), +0 ,pte_val (pte), lgpa,PMD_SHIFT ); + ret =0 ; + goto out_unlock; }else if (level =1 && !pmd_none (*pmd)) {/* * There's a page table page here, but we wanted
@@ -274,6 +278,8 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, goto out_unlock; }if (level =0 ) { +pte_t old_pte; +if (pmd_none (*pmd)) {if (!new_ptep) goto out_unlock;
@@ -281,13 +287,21 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, new_ptep =NULL ; } ptep =pte_offset_kernel (pmd, gpa); -if (pte_present (*ptep)) { -/* PTE was previously valid, so invalidate it */ - old =kvmppc_radix_update_pte (kvm, ptep, _PAGE_PRESENT, -0 , gpa,0 ); -kvmppc_radix_tlbie_page (kvm, gpa,0 ); -if (old & _PAGE_DIRTY) -mark_page_dirty (kvm, gpa >>PAGE_SHIFT ); + old_pte = *ptep; +if (pte_present (old_pte)) { +/* PTE was previously valid, so update it */ +if (pte_val (old_pte) =pte_val (pte)) { + ret = -EAGAIN ; + goto out_unlock; + } + +/* Make sure we're weren't trying to take bits away */ +WARN_ON_ONCE (pte_pfn (old_pte) !=pte_pfn (pte)); +WARN_ON_ONCE ((pte_val (old_pte) & ~pte_val (pte)) & + (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE)); + +kvmppc_radix_update_pte (kvm, ptep,0 , +pte_val (pte), gpa,0 ); }kvmppc_radix_set_pte_at (kvm, gpa, ptep, pte); }else {
Signature · 1 line
2.17.0
Nicholas Piggin
reviewerRe: [PATCH v2 5/5] KVM: PPC: Book3S HV: radix do not clear partition scoped page table when page fau
Quoted 15 lines · On Mon, 16 Apr 2018 14:32:40 +1000 Nicholas Piggin <[email protected]> wrote:
When running a SMP radix guest, KVM can get into page fault / tlbie storms -- hundreds of thousands to the same address from different threads -- due to partition scoped page faults invalidating the page table entry if it was found to be already set up by a racing CPU.
What can happen is that guest threads can hit page faults for the same addresses, this can happen when KSM or THP takes out a commonly used page. gRA zero (the interrupt vectors and important kernel text) was a common one. Multiple CPUs will page fault and contend on the same lock, when one CPU sets up the page table and releases the lock, the next will find the new entry and invalidate it before installing its own, which causes other page faults which invalidate that entry, etc.
Quoted 6 lines
The solution to this is to avoid invalidating the entry or flushing TLBs in case of a race. The pte may still need bits updated, but those are to add R/C or relax access restrictions so no flush is required.
This solves the page fault / tlbie storms.
Oh, I didn't notice "KVM: PPC: Book3S HV: Radix page fault handler optimizations" does much the same thing as this one and it's been merged upstream now.
That also adds a partition scoped PWC flush that I'll add to powerpc/mm, so I'll rebase this series.
Thanks,
Nick