[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 3 of 9] x86/mm: Refactor possibly deadlocking get_gfn calls
> At 14:51 -0500 on 01 Feb (1328107915), Andres Lagar-Cavilla wrote: >> xen/arch/x86/hvm/emulate.c | 35 +++++++-------- >> xen/arch/x86/mm/mem_sharing.c | 28 +++++++------ >> xen/include/asm-x86/p2m.h | 91 >> +++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 122 insertions(+), 32 deletions(-) >> >> >> When calling get_gfn multiple times on different gfn's in the same >> function, we >> can easily deadlock if p2m lookups are locked. Thus, refactor these >> calls to >> enforce simple deadlock-avoidance rules: >> - Lowest-numbered domain first >> - Lowest-numbered gfn first > > This is a good idea, and I like the get_two_gfns() abstraction, but: > - I think the two_gfns struct should proabbly just live on the stack > instead of malloc()ing it up every time. It's not very big. > - the implementation of get_two_gfns() seems to be very complex; I'm > sure it could be simplified. At the very least, you could avoid a > bit of duplication by just deciding once which order to do the gets > in and then running all the setu and get code once. Reasonable. Will do both, repost later. Thanks! Andres > > Cheers, > > Tim. > >> Signed-off-by: Andres Lagar-Cavilla <andres@xxxxxxxxxxxxxxx> >> >> diff -r 27031a8a4eff -r 3de7e43b130a xen/arch/x86/hvm/emulate.c >> --- a/xen/arch/x86/hvm/emulate.c >> +++ b/xen/arch/x86/hvm/emulate.c >> @@ -660,12 +660,13 @@ static int hvmemul_rep_movs( >> { >> struct hvm_emulate_ctxt *hvmemul_ctxt = >> container_of(ctxt, struct hvm_emulate_ctxt, ctxt); >> - unsigned long saddr, daddr, bytes, sgfn, dgfn; >> + unsigned long saddr, daddr, bytes; >> paddr_t sgpa, dgpa; >> uint32_t pfec = PFEC_page_present; >> - p2m_type_t p2mt; >> + p2m_type_t sp2mt, dp2mt; >> int rc, df = !!(ctxt->regs->eflags & X86_EFLAGS_DF); >> char *buf; >> + struct two_gfns *tg; >> >> rc = hvmemul_virtual_to_linear( >> src_seg, src_offset, bytes_per_rep, reps, hvm_access_read, >> @@ -693,26 +694,25 @@ static int hvmemul_rep_movs( >> if ( rc != X86EMUL_OKAY ) >> return rc; >> >> - /* XXX In a fine-grained p2m locking scenario, we need to sort this >> - * get_gfn's, or else we might deadlock */ >> - sgfn = sgpa >> PAGE_SHIFT; >> - (void)get_gfn(current->domain, sgfn, &p2mt); >> - if ( !p2m_is_ram(p2mt) && !p2m_is_grant(p2mt) ) >> + tg = get_two_gfns(current->domain, sgpa >> PAGE_SHIFT, &sp2mt, >> NULL, NULL, >> + current->domain, dgpa >> PAGE_SHIFT, &dp2mt, >> NULL, NULL, >> + p2m_guest); >> + if ( !tg ) >> + return X86EMUL_UNHANDLEABLE; >> + >> + if ( !p2m_is_ram(sp2mt) && !p2m_is_grant(sp2mt) ) >> { >> rc = hvmemul_do_mmio( >> sgpa, reps, bytes_per_rep, dgpa, IOREQ_READ, df, NULL); >> - put_gfn(current->domain, sgfn); >> + put_two_gfns(&tg); >> return rc; >> } >> >> - dgfn = dgpa >> PAGE_SHIFT; >> - (void)get_gfn(current->domain, dgfn, &p2mt); >> - if ( !p2m_is_ram(p2mt) && !p2m_is_grant(p2mt) ) >> + if ( !p2m_is_ram(dp2mt) && !p2m_is_grant(dp2mt) ) >> { >> rc = hvmemul_do_mmio( >> dgpa, reps, bytes_per_rep, sgpa, IOREQ_WRITE, df, NULL); >> - put_gfn(current->domain, sgfn); >> - put_gfn(current->domain, dgfn); >> + put_two_gfns(&tg); >> return rc; >> } >> >> @@ -730,8 +730,7 @@ static int hvmemul_rep_movs( >> */ >> if ( ((dgpa + bytes_per_rep) > sgpa) && (dgpa < (sgpa + bytes)) ) >> { >> - put_gfn(current->domain, sgfn); >> - put_gfn(current->domain, dgfn); >> + put_two_gfns(&tg); >> return X86EMUL_UNHANDLEABLE; >> } >> >> @@ -743,8 +742,7 @@ static int hvmemul_rep_movs( >> buf = xmalloc_bytes(bytes); >> if ( buf == NULL ) >> { >> - put_gfn(current->domain, sgfn); >> - put_gfn(current->domain, dgfn); >> + put_two_gfns(&tg); >> return X86EMUL_UNHANDLEABLE; >> } >> >> @@ -757,8 +755,7 @@ static int hvmemul_rep_movs( >> rc = hvm_copy_to_guest_phys(dgpa, buf, bytes); >> >> xfree(buf); >> - put_gfn(current->domain, sgfn); >> - put_gfn(current->domain, dgfn); >> + put_two_gfns(&tg); >> >> if ( rc == HVMCOPY_gfn_paged_out ) >> return X86EMUL_RETRY; >> diff -r 27031a8a4eff -r 3de7e43b130a xen/arch/x86/mm/mem_sharing.c >> --- a/xen/arch/x86/mm/mem_sharing.c >> +++ b/xen/arch/x86/mm/mem_sharing.c >> @@ -718,11 +718,13 @@ int mem_sharing_share_pages(struct domai >> int ret = -EINVAL; >> mfn_t smfn, cmfn; >> p2m_type_t smfn_type, cmfn_type; >> + struct two_gfns *tg; >> >> - /* XXX if sd == cd handle potential deadlock by ordering >> - * the get_ and put_gfn's */ >> - smfn = get_gfn(sd, sgfn, &smfn_type); >> - cmfn = get_gfn(cd, cgfn, &cmfn_type); >> + tg = get_two_gfns(sd, sgfn, &smfn_type, NULL, &smfn, >> + cd, cgfn, &cmfn_type, NULL, &cmfn, >> + p2m_query); >> + if ( !tg ) >> + return -ENOMEM; >> >> /* This tricky business is to avoid two callers deadlocking if >> * grabbing pages in opposite client/source order */ >> @@ -819,8 +821,7 @@ int mem_sharing_share_pages(struct domai >> ret = 0; >> >> err_out: >> - put_gfn(cd, cgfn); >> - put_gfn(sd, sgfn); >> + put_two_gfns(&tg); >> return ret; >> } >> >> @@ -834,11 +835,13 @@ int mem_sharing_add_to_physmap(struct do >> struct gfn_info *gfn_info; >> struct p2m_domain *p2m = p2m_get_hostp2m(cd); >> p2m_access_t a; >> - >> - /* XXX if sd == cd handle potential deadlock by ordering >> - * the get_ and put_gfn's */ >> - smfn = get_gfn_query(sd, sgfn, &smfn_type); >> - cmfn = get_gfn_type_access(p2m, cgfn, &cmfn_type, &a, p2m_query, >> NULL); >> + struct two_gfns *tg; >> + >> + tg = get_two_gfns(sd, sgfn, &smfn_type, NULL, &smfn, >> + cd, cgfn, &cmfn_type, &a, &cmfn, >> + p2m_query); >> + if ( !tg ) >> + return -ENOMEM; >> >> /* Get the source shared page, check and lock */ >> ret = XEN_DOMCTL_MEM_SHARING_S_HANDLE_INVALID; >> @@ -886,8 +889,7 @@ int mem_sharing_add_to_physmap(struct do >> err_unlock: >> mem_sharing_page_unlock(spage); >> err_out: >> - put_gfn(cd, cgfn); >> - put_gfn(sd, sgfn); >> + put_two_gfns(&tg); >> return ret; >> } >> >> diff -r 27031a8a4eff -r 3de7e43b130a xen/include/asm-x86/p2m.h >> --- a/xen/include/asm-x86/p2m.h >> +++ b/xen/include/asm-x86/p2m.h >> @@ -378,6 +378,97 @@ static inline unsigned long mfn_to_gfn(s >> return mfn_x(mfn); >> } >> >> +/* Deadlock-avoidance scheme when calling get_gfn on different gfn's */ >> +struct two_gfns { >> + struct domain *first_domain; >> + unsigned long first_gfn; >> + struct domain *second_domain; >> + unsigned long second_gfn; >> +}; >> + >> +#define assign_pointers(dest, source) >> \ >> +do { >> \ >> + dest ## _mfn = (source ## mfn) ? (source ## mfn) : &__ ## dest ## >> _mfn; \ >> + dest ## _a = (source ## a) ? (source ## a) : &__ ## dest ## >> _a; \ >> + dest ## _t = (source ## t) ? (source ## t) : &__ ## dest ## >> _t; \ >> +} while(0) >> + >> +/* Returns mfn, type and access for potential caller consumption, but >> any >> + * of those can be NULL */ >> +static inline struct two_gfns *get_two_gfns(struct domain *rd, unsigned >> long rgfn, >> + p2m_type_t *rt, p2m_access_t *ra, mfn_t *rmfn, struct domain >> *ld, >> + unsigned long lgfn, p2m_type_t *lt, p2m_access_t *la, mfn_t >> *lmfn, >> + p2m_query_t q) >> +{ >> + mfn_t *first_mfn, *second_mfn, __first_mfn, __second_mfn; >> + p2m_access_t *first_a, *second_a, __first_a, __second_a; >> + p2m_type_t *first_t, *second_t, __first_t, __second_t; >> + >> + struct two_gfns *rval = xmalloc(struct two_gfns); >> + if ( !rval ) >> + return NULL; >> + >> + if ( rd == ld ) >> + { >> + rval->first_domain = rval->second_domain = rd; >> + >> + /* Sort by gfn */ >> + if (rgfn <= lgfn) >> + { >> + rval->first_gfn = rgfn; >> + rval->second_gfn = lgfn; >> + assign_pointers(first, r); >> + assign_pointers(second, l); >> + } else { >> + rval->first_gfn = lgfn; >> + rval->second_gfn = rgfn; >> + assign_pointers(first, l); >> + assign_pointers(second, r); >> + } >> + } else { >> + /* Sort by domain */ >> + if ( rd->domain_id <= ld->domain_id ) >> + { >> + rval->first_domain = rd; >> + rval->first_gfn = rgfn; >> + rval->second_domain = ld; >> + rval->second_gfn = lgfn; >> + assign_pointers(first, r); >> + assign_pointers(second, l); >> + } else { >> + rval->first_domain = ld; >> + rval->first_gfn = lgfn; >> + rval->second_domain = rd; >> + rval->second_gfn = rgfn; >> + assign_pointers(first, l); >> + assign_pointers(second, r); >> + } >> + } >> + >> + /* Now do the gets */ >> + *first_mfn = >> get_gfn_type_access(p2m_get_hostp2m(rval->first_domain), >> + rval->first_gfn, first_t, >> first_a, q, NULL); >> + *second_mfn = >> get_gfn_type_access(p2m_get_hostp2m(rval->second_domain), >> + rval->second_gfn, second_t, >> second_a, q, NULL); >> + >> + return rval; >> +} >> + >> +static inline void put_two_gfns(struct two_gfns **arg_ptr) >> +{ >> + struct two_gfns *arg; >> + >> + if ( !arg_ptr || !(*arg_ptr) ) >> + return; >> + >> + arg = *arg_ptr; >> + put_gfn(arg->second_domain, arg->second_gfn); >> + put_gfn(arg->first_domain, arg->first_gfn); >> + >> + xfree(arg); >> + *arg_ptr = NULL; >> +} >> + >> /* Init the datastructures for later use by the p2m code */ >> int p2m_init(struct domain *d); >> >> >> _______________________________________________ >> Xen-devel mailing list >> Xen-devel@xxxxxxxxxxxxxxxxxxx >> http://lists.xensource.com/xen-devel > _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |