[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


  • To: "Tim Deegan" <tim@xxxxxxx>
  • From: "Andres Lagar-Cavilla" <andres@xxxxxxxxxxxxxxxx>
  • Date: Thu, 2 Feb 2012 05:44:40 -0800
  • Cc: andres@xxxxxxxxxxxxxx, xen-devel@xxxxxxxxxxxxxxxxxxx, olaf@xxxxxxxxx, adin@xxxxxxxxxxxxxx
  • Delivery-date: Thu, 02 Feb 2012 13:44:53 +0000
  • Domainkey-signature: a=rsa-sha1; c=nofws; d=lagarcavilla.org; h=message-id :in-reply-to:references:date:subject:from:to:cc:reply-to :mime-version:content-type:content-transfer-encoding; q=dns; s= lagarcavilla.org; b=DJTSZzyt7boRbs9+PZAAdvnrxUHASbHSTh7VXmi70oyM gBFHnCNhQHZxXb209DvizOAk9y9sXHRUVEUYKbRHi81i1EHTFRYWvLqTybBU5UuD 7I1AeDf4pd1gRcQ3U8ZajLOj555i+PVZ3s7gyuUUMkIyaSXxP3ajgkI539/k2TE=
  • List-id: Xen developer discussion <xen-devel.lists.xensource.com>

> 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


 


Rackspace

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