KVM: PPC: Book3S HV: Fix physical address calculations
Subsystems (3)
PATCH 1/2 KVM: PPC: Book3S HV: Fix physical address calculations
This fixes a bug in kvmppc_do_h_enter() where the physical address for a page can be calculated incorrectly if transparent huge pages (THP) are active. Until THP came along, it was true that if we encountered a large (16M) page in kvmppc_do_h_enter(), then the associated memslot must be 16M aligned for both its guest physical address and the userspace address, and the physical address calculations in kvmppc_do_h_enter() assumed that. With THP, that is no longer true.
In the case where we are using MMU notifiers and the page size that we get from the Linux page tables is larger than the page being mapped by the guest, we need to fill in some low-order bits of the physical address. Without THP, these bits would be the same in the guest physical address (gpa) and the host virtual address (hva). With THP, they can be different, and we need to use the bits from hva rather than gpa.
In the case where we are not using MMU notifiers, the host physical address we get from the memslot->arch.slot_phys[] array already includes the low-order bits down to the PAGE_SIZE level, even if we are using large pages. Thus we can simplify the calculation in this case to just add in the remaining bits in the case where PAGE_SIZE is 64k and the guest is mapping a 4k page.
The same bug exists in kvmppc_book3s_hv_page_fault(). The basic fix is to use psize (the page size from the HPTE) rather than pte_size (the page size from the Linux PTE) when updating the HPTE low word in r. That means that pfn needs to be computed to PAGE_SIZE granularity even if the Linux PTE is a huge page PTE. That can be arranged simply by doing the page_to_pfn() before setting page to the head of the compound page. If psize is less than PAGE_SIZE, then we need to make sure we only update the bits from PAGE_SIZE upwards, in order not to lose any sub-page offset bits in r. On the other hand, if psize is greater than PAGE_SIZE, we need to make sure we don't bring in non-zero low order bits in pfn, hence we mask (pfn << PAGE_SHIFT) with ~(psize - 1).
--- arch/powerpc/kvm/book3s_64_mmu_hv.c | 12 +++++++++--- arch/powerpc/kvm/book3s_hv_rm_mmu.c | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-)
arch/powerpc/kvm/book3s_64_mmu_hv.c +9 -3
@@ -665,6 +665,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,return -EFAULT ; }else { page = pages[0 ]; + pfn =page_to_pfn (page);if (PageHuge (page)) { page =compound_head (page); pte_size <<=compound_order (page);
@@ -689,7 +690,6 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, }rcu_read_unlock_sched (); } - pfn =page_to_pfn (page); } ret = -EFAULT ;
@@ -707,8 +707,14 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, r = (r & ~(HPTE_R_W |HPTE_R_I |HPTE_R_G )) |HPTE_R_M ; } -/* Set the HPTE to point to pfn */ - r = (r & ~(HPTE_R_PP0 - pte_size)) | (pfn <<PAGE_SHIFT ); +/* +* Set the HPTE to point to pfn. +* Since the pfn is at PAGE_SIZE granularity, make sure we +* don't mask out lower-order bits if psize < PAGE_SIZE. +*/ +if (psize <PAGE_SIZE ) + psize =PAGE_SIZE ; + r = (r & ~(HPTE_R_PP0 - psize)) | ((pfn <<PAGE_SHIFT ) & ~(psize -1 ));if (hpte_is_writable (r) && !write_ok) r =hpte_make_readonly (r); ret =RESUME_GUEST ;
arch/powerpc/kvm/book3s_hv_rm_mmu.c +2 -2
@@ -225,6 +225,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags, is_io = pa & (HPTE_R_I |HPTE_R_W ); pte_size =PAGE_SIZE << (pa &KVMPPC_PAGE_ORDER_MASK ); pa &=PAGE_MASK ; + pa |= gpa & ~PAGE_MASK ; }else {/* Translate to host virtual address */ hva =__gfn_to_hva_memslot (memslot, gfn);
@@ -238,13 +239,12 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags, ptel =hpte_make_readonly (ptel); is_io =hpte_cache_bits (pte_val (pte)); pa =pte_pfn (pte) <<PAGE_SHIFT ; + pa |= hva & (pte_size -1 ); } }if (pte_size < psize)return H_PARAMETER ; -if (pa && pte_size > psize) - pa |= gpa & (pte_size -1 ); ptel &= ~(HPTE_R_PP0 - psize); ptel |= pa;
Signature · 1 line
1.8.4.2
PATCH 2/2 KVM: PPC: Book3S HV: Refine barriers in guest entry/exit
Some users have reported instances of the host hanging with secondary threads of a core waiting for the primary thread to exit the guest, and the primary thread stuck in nap mode. This prompted a review of the memory barriers in the guest entry/exit code, and this is the result. Most of these changes are the suggestions of Dean Burdick <[email protected]>.
The barriers between updating napping_threads and reading the entry_exit_count on the one hand, and updating entry_exit_count and reading napping_threads on the other, need to be isync not lwsync, since we need to ensure that either the napping_threads update or the entry_exit_count update get seen. It is not sufficient to order the load vs. lwarx, as lwsync does; we need to order the load vs. the stwcx., so we need isync.
In addition, we need a full sync before sending IPIs to wake other threads from nap, to ensure that the write to the entry_exit_count is visible before the IPI occurs.
--- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
arch/powerpc/kvm/book3s_hv_rmhandlers.S +10 -6
@@ -153,7 +153,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) 13: b machine_check_fwnmi - /* * We come in here when wakened from nap mode on a secondary hw thread. * Relocation is off and most register values are lost.
@@ -224,6 +223,11 @@ kvm_start_guest: /* Clear our vcpu pointer so we don't come back in early */ li r0, 0 std r0, HSTATE_KVM_VCPU(r13) + /* + * Make sure we clear HSTATE_KVM_VCPU(r13) before incrementing + * the nap_count, because once the increment to nap_count is + * visible we could be given another vcpu. + */ lwsync /* Clear any pending IPI - we're an offline thread */ ld r5, HSTATE_XICS_PHYS(r13)
@@ -241,7 +245,6 @@ kvm_start_guest: /* increment the nap count and then go to nap mode */ ld r4, HSTATE_KVM_VCORE(r13) addi r4, r4, VCORE_NAP_COUNT - lwsync /* make previous updates visible */ 51: lwarx r3, 0, r4 addi r3, r3, 1 stwcx. r3, 0, r4
@@ -990,14 +993,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201) */ /* Increment the threads-exiting-guest count in the 0xff00 bits of vcore->entry_exit_count */ - lwsync ld r5,HSTATE_KVM_VCORE(r13) addi r6,r5,VCORE_ENTRY_EXIT 41: lwarx r3,0,r6 addi r0,r3,0x100 stwcx. r0,0,r6 bne 41b - lwsync + isync /* order stwcx. vs. reading napping_threads */ /* * At this point we have an interrupt that we have to pass
@@ -1030,6 +1032,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201) sld r0,r0,r4 andc. r3,r3,r0 /* no sense IPI'ing ourselves */ beq 43f + /* Order entry/exit update vs. IPIs */ + sync mulli r4,r4,PACA_SIZE /* get paca for thread 0 */ subf r6,r4,r13 42: andi. r0,r3,1
@@ -1638,10 +1642,10 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206) bge kvm_cede_exit stwcx. r4,0,r6 bne 31b + /* order napping_threads update vs testing entry_exit_count */ + isync li r0,1 stb r0,HSTATE_NAPPING(r13) - /* order napping_threads update vs testing entry_exit_count */ - lwsync mr r4,r3 lwz r7,VCORE_ENTRY_EXIT(r5) cmpwi r7,0x100
Signature · 1 line
1.8.4.2