On Thu, Mar 11, 2021 at 02:09:37PM +0530, Bharata B Rao wrote: > Enable support for process-scoped invalidations from nested > guests and partition-scoped invalidations for nested guests. > > Process-scoped invalidations for any level of nested guests > are handled by implementing H_RPT_INVALIDATE handler in the > nested guest exit path in L0. > > Partition-scoped invalidation requests are forwarded to the > right nested guest, handled there and passed down to L0 > for eventual handling. > > Signed-off-by: Bharata B Rao <bharata@xxxxxxxxxxxxx> > Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@xxxxxxxxxxxxx> > [Nested guest partition-scoped invalidation changes] Reviewed-by: David Gibson <david@xxxxxxxxxxxxxxxxxxxxx> > --- > arch/powerpc/include/asm/kvm_book3s.h | 3 + > arch/powerpc/kvm/book3s_hv.c | 71 +++++++++++++++++- > arch/powerpc/kvm/book3s_hv_nested.c | 104 ++++++++++++++++++++++++++ > 3 files changed, 175 insertions(+), 3 deletions(-) > > diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h > index 2f5f919f6cd3..de8fc5a4d19c 100644 > --- a/arch/powerpc/include/asm/kvm_book3s.h > +++ b/arch/powerpc/include/asm/kvm_book3s.h > @@ -305,6 +305,9 @@ void kvmhv_set_ptbl_entry(unsigned int lpid, u64 dw0, u64 dw1); > void kvmhv_release_all_nested(struct kvm *kvm); > long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu); > long kvmhv_do_nested_tlbie(struct kvm_vcpu *vcpu); > +long do_h_rpt_invalidate_pat(struct kvm_vcpu *vcpu, unsigned long lpid, > + unsigned long type, unsigned long pg_sizes, > + unsigned long start, unsigned long end); > int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, > u64 time_limit, unsigned long lpcr); > void kvmhv_save_hv_regs(struct kvm_vcpu *vcpu, struct hv_guest_state *hr); > diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c > index 5d008468347c..03755389efd1 100644 > --- a/arch/powerpc/kvm/book3s_hv.c > +++ b/arch/powerpc/kvm/book3s_hv.c > @@ -922,6 +922,46 @@ static int kvmppc_get_yield_count(struct kvm_vcpu *vcpu) > return yield_count; > } > > +/* > + * H_RPT_INVALIDATE hcall handler for nested guests. > + * > + * Handles only nested process-scoped invalidation requests in L0. > + */ > +static int kvmppc_nested_h_rpt_invalidate(struct kvm_vcpu *vcpu) > +{ > + unsigned long type = kvmppc_get_gpr(vcpu, 6); > + unsigned long pid, pg_sizes, start, end, psize; > + struct kvm_nested_guest *gp; > + struct mmu_psize_def *def; > + > + /* > + * The partition-scoped invalidations aren't handled here in L0. > + */ > + if (type & H_RPTI_TYPE_NESTED) > + return RESUME_HOST; > + > + pid = kvmppc_get_gpr(vcpu, 4); > + pg_sizes = kvmppc_get_gpr(vcpu, 7); > + start = kvmppc_get_gpr(vcpu, 8); > + end = kvmppc_get_gpr(vcpu, 9); > + > + gp = kvmhv_get_nested(vcpu->kvm, vcpu->kvm->arch.lpid, false); > + if (!gp) > + goto out; > + > + for (psize = 0; psize < MMU_PAGE_COUNT; psize++) { > + def = &mmu_psize_defs[psize]; > + if (pg_sizes & def->h_rpt_pgsize) > + do_h_rpt_invalidate_prt(pid, gp->shadow_lpid, type, > + (1UL << def->shift), psize, > + start, end); > + } > + kvmhv_put_nested(gp); > +out: > + kvmppc_set_gpr(vcpu, 3, H_SUCCESS); > + return RESUME_GUEST; > +} > + > static long kvmppc_h_rpt_invalidate(struct kvm_vcpu *vcpu, > unsigned long id, unsigned long target, > unsigned long type, unsigned long pg_sizes, > @@ -938,10 +978,18 @@ static long kvmppc_h_rpt_invalidate(struct kvm_vcpu *vcpu, > > /* > * Partition-scoped invalidation for nested guests. > - * Not yet supported > */ > - if (type & H_RPTI_TYPE_NESTED) > - return H_P3; > + if (type & H_RPTI_TYPE_NESTED) { > + if (!nesting_enabled(vcpu->kvm)) > + return H_FUNCTION; > + > + /* Support only cores as target */ > + if (target != H_RPTI_TARGET_CMMU) > + return H_P2; > + > + return do_h_rpt_invalidate_pat(vcpu, id, type, pg_sizes, > + start, end); > + } > > /* > * Process-scoped invalidation for L1 guests. > @@ -1636,6 +1684,23 @@ static int kvmppc_handle_nested_exit(struct kvm_vcpu *vcpu) > if (!xics_on_xive()) > kvmppc_xics_rm_complete(vcpu, 0); > break; > + case BOOK3S_INTERRUPT_SYSCALL: > + { > + unsigned long req = kvmppc_get_gpr(vcpu, 3); > + > + /* > + * The H_RPT_INVALIDATE hcalls issued by nested > + * guests for process-scoped invalidations when > + * GTSE=0, are handled here in L0. > + */ > + if (req == H_RPT_INVALIDATE) { > + r = kvmppc_nested_h_rpt_invalidate(vcpu); > + break; > + } > + > + r = RESUME_HOST; > + break; > + } > default: > r = RESUME_HOST; > break; > diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3s_hv_nested.c > index 0cd0e7aad588..adcc8e26ef22 100644 > --- a/arch/powerpc/kvm/book3s_hv_nested.c > +++ b/arch/powerpc/kvm/book3s_hv_nested.c > @@ -1191,6 +1191,110 @@ long kvmhv_do_nested_tlbie(struct kvm_vcpu *vcpu) > return H_SUCCESS; > } > > +static long do_tlb_invalidate_nested_tlb(struct kvm_vcpu *vcpu, > + unsigned long lpid, > + unsigned long page_size, > + unsigned long ap, > + unsigned long start, > + unsigned long end) > +{ > + unsigned long addr = start; > + int ret; > + > + do { > + ret = kvmhv_emulate_tlbie_tlb_addr(vcpu, lpid, ap, > + get_epn(addr)); > + if (ret) > + return ret; > + addr += page_size; > + } while (addr < end); > + > + return ret; > +} > + > +static long do_tlb_invalidate_nested_all(struct kvm_vcpu *vcpu, > + unsigned long lpid, unsigned long ric) > +{ > + struct kvm *kvm = vcpu->kvm; > + struct kvm_nested_guest *gp; > + > + gp = kvmhv_get_nested(kvm, lpid, false); > + if (gp) { > + kvmhv_emulate_tlbie_lpid(vcpu, gp, ric); > + kvmhv_put_nested(gp); > + } > + return H_SUCCESS; > +} > + > +/* > + * Performs partition-scoped invalidations for nested guests > + * as part of H_RPT_INVALIDATE hcall. > + */ > +long do_h_rpt_invalidate_pat(struct kvm_vcpu *vcpu, unsigned long lpid, > + unsigned long type, unsigned long pg_sizes, > + unsigned long start, unsigned long end) > +{ > + struct kvm_nested_guest *gp; > + long ret; > + unsigned long psize, ap; > + > + /* > + * If L2 lpid isn't valid, we need to return H_PARAMETER. > + * > + * However, nested KVM issues a L2 lpid flush call when creating > + * partition table entries for L2. This happens even before the > + * corresponding shadow lpid is created in HV which happens in > + * H_ENTER_NESTED call. Since we can't differentiate this case from > + * the invalid case, we ignore such flush requests and return success. > + */ > + gp = kvmhv_find_nested(vcpu->kvm, lpid); > + if (!gp) > + return H_SUCCESS; > + > + /* > + * A flush all request can be handled by a full lpid flush only. > + */ > + if ((type & H_RPTI_TYPE_NESTED_ALL) == H_RPTI_TYPE_NESTED_ALL) > + return do_tlb_invalidate_nested_all(vcpu, lpid, RIC_FLUSH_ALL); > + > +#if 0 > + /* > + * We don't need to handle a PWC flush like process table here, > + * because intermediate partition scoped table in nested guest doesn't > + * really have PWC. Only level we have PWC is in L0 and for nested > + * invalidate at L0 we always do kvm_flush_lpid() which does > + * radix__flush_all_lpid(). For range invalidate at any level, we > + * are not removing the higher level page tables and hence there is > + * no PWC invalidate needed. > + */ > + if (type & H_RPTI_TYPE_PWC) { > + ret = do_tlb_invalidate_nested_all(vcpu, lpid, RIC_FLUSH_PWC); > + if (ret) > + return H_P4; > + } > +#endif > + > + if (start == 0 && end == -1) > + return do_tlb_invalidate_nested_all(vcpu, lpid, RIC_FLUSH_TLB); > + > + if (type & H_RPTI_TYPE_TLB) { > + struct mmu_psize_def *def; > + > + for (psize = 0; psize < MMU_PAGE_COUNT; psize++) { > + def = &mmu_psize_defs[psize]; > + if (!(pg_sizes & def->h_rpt_pgsize)) > + continue; > + > + ret = do_tlb_invalidate_nested_tlb(vcpu, lpid, > + (1UL << def->shift), > + ap, start, end); > + if (ret) > + return H_P4; > + } > + } > + return H_SUCCESS; > +} > + > /* Used to convert a nested guest real address to a L1 guest real address */ > static int kvmhv_translate_addr_nested(struct kvm_vcpu *vcpu, > struct kvm_nested_guest *gp, -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
Attachment:
signature.asc
Description: PGP signature