[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH RFC v2 3/3] xen: rework paging_log_dirty_op to work with hvm guests



On 02/04/15 11:26, Roger Pau Monne wrote:
> When the caller of paging_log_dirty_op is a hvm guest Xen would choke when
> trying to copy the dirty bitmap to the guest because the paging lock is
> already held.

Are you sure? Presumably you get an mm lock ordering violation, because
paging_log_dirty_op() should take the target domains paging lock, rather
than your own (which is prohibited by the current check at the top of
paging_domctl()).

Unfortunately, dropping the paging_lock() here is unsafe, as it will
result in corruption of the logdirty bitmap from non-domain sources such
as HVMOP_modified_memory.

I will need to find some time with a large pot of coffee and a
whiteboard, but I suspect it might actually be safe to alter the current
mm_lock() enforcement to maintain independent levels for a source and
destination domain.

Up until now, the toolstack domain has always been PV (with very little
in the way of locking), and I don't believe our current locking model is
suitable for an HVM domain performing toolstack operations on another,
where both the source and destination need locking.

~Andrew

>
> Fix this by independently mapping each page of the guest bitmap as needed
> without the paging lock held.
>
> Signed-off-by: Roger Pau Monnà <roger.pau@xxxxxxxxxx>
> Cc: Tim Deegan <tim@xxxxxxx>
> Cc: Jan Beulich <jbeulich@xxxxxxxx>
> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
> ---
>  xen/arch/x86/mm/paging.c | 99 
> +++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 89 insertions(+), 10 deletions(-)
>
> diff --git a/xen/arch/x86/mm/paging.c b/xen/arch/x86/mm/paging.c
> index b54d76a..4dcf942 100644
> --- a/xen/arch/x86/mm/paging.c
> +++ b/xen/arch/x86/mm/paging.c
> @@ -397,6 +397,53 @@ int paging_mfn_is_dirty(struct domain *d, mfn_t gmfn)
>      return rv;
>  }
>  
> +static inline void *map_dirty_bitmap(XEN_GUEST_HANDLE_64(uint8) dirty_bitmap,
> +                                     unsigned long pages,
> +                                     struct page_info **page,
> +                                     unsigned long *mapped_page)
> +{
> +    p2m_type_t p2mt;
> +    uint32_t pfec;
> +    unsigned long gfn;
> +
> +    gfn = paging_gva_to_gfn(current,
> +                            (paddr_t)(((char *)dirty_bitmap.p) + (pages >> 
> 3)),
> +                            &pfec);
> +    if ( gfn == INVALID_GFN )
> +        return NULL;
> +
> +    *page = get_page_from_gfn(current->domain, gfn, &p2mt, P2M_UNSHARE);
> +
> +    if ( p2m_is_paging(p2mt) )
> +    {
> +        put_page(*page);
> +        p2m_mem_paging_populate(current->domain, gfn);
> +        return NULL;
> +    }
> +    if ( p2m_is_shared(p2mt) )
> +    {
> +        put_page(*page);
> +        return NULL;
> +    }
> +    if ( p2m_is_grant(p2mt) )
> +    {
> +        put_page(*page);
> +        return NULL;
> +    }
> +
> +    *mapped_page = pages;
> +    return __map_domain_page(*page);
> +}
> +
> +static inline void unmap_dirty_bitmap(void *addr, struct page_info *page)
> +{
> +    if ( addr != NULL )
> +    {
> +        unmap_domain_page(addr);
> +        put_page(page);
> +    }
> +}
> +
>  
>  /* Read a domain's log-dirty bitmap and stats.  If the operation is a CLEAN,
>   * clear the bitmap and stats as well. */
> @@ -409,9 +456,23 @@ static int paging_log_dirty_op(struct domain *d,
>      mfn_t *l4 = NULL, *l3 = NULL, *l2 = NULL;
>      unsigned long *l1 = NULL;
>      int i4, i3, i2;
> +    uint8_t *dirty_bitmap = NULL;
> +    struct page_info *page;
> +    unsigned long index_mapped = 0;
>  
>      if ( !resuming )
>          domain_pause(d);
> +
> +    dirty_bitmap = map_dirty_bitmap(sc->dirty_bitmap,
> +                                    resuming ?
> +                                        
> d->arch.paging.preempt.log_dirty.done :
> +                                        0,
> +                                    &page, &index_mapped);
> +    if ( dirty_bitmap == NULL )
> +    {
> +        domain_unpause(d);
> +        return -EFAULT;
> +    }
>      paging_lock(d);
>  
>      if ( !d->arch.paging.preempt.dom )
> @@ -448,21 +509,23 @@ static int paging_log_dirty_op(struct domain *d,
>          goto out;
>      }
>  
> -    l4 = paging_map_log_dirty_bitmap(d);
>      i4 = d->arch.paging.preempt.log_dirty.i4;
>      i3 = d->arch.paging.preempt.log_dirty.i3;
> +    i2 = 0;
>      pages = d->arch.paging.preempt.log_dirty.done;
>  
> + again:
> +    l4 = paging_map_log_dirty_bitmap(d);
> +
>      for ( ; (pages < sc->pages) && (i4 < LOGDIRTY_NODE_ENTRIES); i4++, i3 = 
> 0 )
>      {
>          l3 = (l4 && mfn_valid(l4[i4])) ? map_domain_page(mfn_x(l4[i4])) : 
> NULL;
> -        for ( ; (pages < sc->pages) && (i3 < LOGDIRTY_NODE_ENTRIES); i3++ )
> +        for ( ; (pages < sc->pages) && (i3 < LOGDIRTY_NODE_ENTRIES);
> +             i3++, i2 = 0 )
>          {
>              l2 = ((l3 && mfn_valid(l3[i3])) ?
>                    map_domain_page(mfn_x(l3[i3])) : NULL);
> -            for ( i2 = 0;
> -                  (pages < sc->pages) && (i2 < LOGDIRTY_NODE_ENTRIES);
> -                  i2++ )
> +            for ( ; (pages < sc->pages) && (i2 < LOGDIRTY_NODE_ENTRIES); 
> i2++ )
>              {
>                  unsigned int bytes = PAGE_SIZE;
>                  l1 = ((l2 && mfn_valid(l2[i2])) ?
> @@ -471,11 +534,25 @@ static int paging_log_dirty_op(struct domain *d,
>                      bytes = (unsigned int)((sc->pages - pages + 7) >> 3);
>                  if ( likely(peek) )
>                  {
> -                    if ( (l1 ? copy_to_guest_offset(sc->dirty_bitmap,
> -                                                    pages >> 3, (uint8_t 
> *)l1,
> -                                                    bytes)
> -                             : clear_guest_offset(sc->dirty_bitmap,
> -                                                  pages >> 3, bytes)) != 0 )
> +                    if ( (pages >> 3) >= (index_mapped >> 3) + 4096 ) {
> +                        /* We need to map next page */
> +                        paging_unlock(d);
> +                        unmap_dirty_bitmap(dirty_bitmap, page);
> +                        dirty_bitmap = map_dirty_bitmap(sc->dirty_bitmap, 
> pages,
> +                                                        &page, 
> &index_mapped);
> +                        paging_lock(d);
> +                        if ( dirty_bitmap == NULL )
> +                        {
> +                            rv = -EFAULT;
> +                            goto out;
> +                        }
> +                        goto again;
> +                    }
> +                    BUG_ON(((pages >> 3) % PAGE_SIZE) + bytes > PAGE_SIZE);
> +                    if ( (l1 ? memcpy(dirty_bitmap + ((pages >> 3) % 
> PAGE_SIZE),
> +                                      (uint8_t *)l1, bytes)
> +                             : memset(dirty_bitmap + ((pages >> 3) % 
> PAGE_SIZE),
> +                                      0, bytes)) == NULL )
>                      {
>                          rv = -EFAULT;
>                          goto out;
> @@ -549,12 +626,14 @@ static int paging_log_dirty_op(struct domain *d,
>           * paging modes (shadow or hap).  Safe because the domain is paused. 
> */
>          d->arch.paging.log_dirty.clean_dirty_bitmap(d);
>      }
> +    unmap_dirty_bitmap(dirty_bitmap, page);
>      domain_unpause(d);
>      return rv;
>  
>   out:
>      d->arch.paging.preempt.dom = NULL;
>      paging_unlock(d);
> +    unmap_dirty_bitmap(dirty_bitmap, page);
>      domain_unpause(d);
>  
>      if ( l1 )


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

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