WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH] hap dirty vram tracking

To: xen-devel <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] hap dirty vram tracking
From: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
Date: Thu, 4 Jun 2009 19:21:54 +0100
Delivery-date: Thu, 04 Jun 2009 11:20:33 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Thunderbird 2.0.0.14 (X11/20080505)
Hi all,
currently HAP systems suffer a significant performance loss when a vnc
client is connect or the sdl interface is used, because HAP is lacking
an implementation of track_dirty_vram.
As a consequence qemu always tries to update the whole screen because it
does not know which areas of the screen have been updated by the guest.

This patch implements track_dirty_vram for HAP enabling the logdirty
mechanism only in a specific gfn range and adding a
paging_log_dirty_range function that returns the log dirty bitmap in a
requested range.

Paging_log_dirty_range is different from paging_log_dirty_op because
operates on a range and also because it does not pause the domain. In
order not to lose any update I moved clean_dirty_bitmap at the beginning
of the function before evaluating the logdirty bitmap.
The bitmap is still safe because it is protected by the logdirty lock.

Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>

---

diff -r 50cf07f42fdd xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c    Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/arch/x86/hvm/hvm.c    Thu Jun 04 18:47:04 2009 +0100
@@ -34,6 +34,7 @@
 #include <xen/event.h>
 #include <xen/paging.h>
 #include <asm/shadow.h>
+#include <asm/hap.h>
 #include <asm/current.h>
 #include <asm/e820.h>
 #include <asm/io.h>
@@ -2653,12 +2654,13 @@
             goto param_fail2;
 
         rc = -EINVAL;
-        if ( !shadow_mode_enabled(d))
-            goto param_fail2;
         if ( d->vcpu[0] == NULL )
             goto param_fail2;
 
-        rc = shadow_track_dirty_vram(d, a.first_pfn, a.nr, a.dirty_bitmap);
+        if ( shadow_mode_enabled(d) )
+            rc = shadow_track_dirty_vram(d, a.first_pfn, a.nr, a.dirty_bitmap);
+        else
+            rc = hap_track_dirty_vram(d, a.first_pfn, a.nr, a.dirty_bitmap);
 
     param_fail2:
         rcu_unlock_domain(d);
diff -r 50cf07f42fdd xen/arch/x86/mm/hap/hap.c
--- a/xen/arch/x86/mm/hap/hap.c Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/arch/x86/mm/hap/hap.c Thu Jun 04 18:47:04 2009 +0100
@@ -52,8 +52,140 @@
 #define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg))
 
 /************************************************/
+/*          HAP VRAM TRACKING SUPPORT           */
+/************************************************/
+
+int hap_enable_vram_tracking(struct domain *d)
+{
+    int i;
+
+    if ( !d->dirty_vram )
+        return -EINVAL;
+
+    /* turn on PG_log_dirty bit in paging mode */
+    hap_lock(d);
+    d->arch.paging.mode |= PG_log_dirty;
+    hap_unlock(d);
+
+    /* set l1e entries of P2M table to be read-only. */
+    for (i = d->dirty_vram->begin_pfn; i < d->dirty_vram->end_pfn; i++)
+        p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty);
+
+    flush_tlb_mask(&d->domain_dirty_cpumask);
+    return 0;
+}
+
+int hap_disable_vram_tracking(struct domain *d)
+{
+    int i;
+
+    if ( !d->dirty_vram )
+        return -EINVAL;
+
+    hap_lock(d);
+    d->arch.paging.mode &= ~PG_log_dirty;
+    hap_unlock(d);
+
+    /* set l1e entries of P2M table with normal mode */
+    for (i = d->dirty_vram->begin_pfn; i < d->dirty_vram->end_pfn; i++)
+        p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty);
+
+    flush_tlb_mask(&d->domain_dirty_cpumask);
+    return 0;
+}
+
+void hap_clean_vram_tracking(struct domain *d)
+{
+    int i;
+
+    if ( !d->dirty_vram )
+        return;
+
+    /* set l1e entries of P2M table to be read-only. */
+    for (i = d->dirty_vram->begin_pfn; i < d->dirty_vram->end_pfn; i++)
+        p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty);
+
+    flush_tlb_mask(&d->domain_dirty_cpumask);
+}
+
+void hap_vram_tracking_init(struct domain *d)
+{
+    paging_log_dirty_init(d, hap_enable_vram_tracking,
+                          hap_disable_vram_tracking,
+                          hap_clean_vram_tracking);
+}
+
+int hap_track_dirty_vram(struct domain *d,
+                         unsigned long begin_pfn,
+                         unsigned long nr,
+                         XEN_GUEST_HANDLE_64(uint8) dirty_bitmap)
+{
+    long rc = 0;
+
+    if ( nr )
+    {
+        if ( paging_mode_log_dirty(d) && d->dirty_vram )
+        {
+            if ( begin_pfn != d->dirty_vram->begin_pfn ||
+                 begin_pfn + nr != d->dirty_vram->end_pfn )
+            {
+                paging_log_dirty_disable(d);
+                d->dirty_vram->begin_pfn = begin_pfn;
+                d->dirty_vram->end_pfn = begin_pfn + nr;
+                rc = paging_log_dirty_enable(d);
+                if (rc != 0)
+                    goto param_fail;
+            }
+        }
+        else if ( !paging_mode_log_dirty(d) && !d->dirty_vram )
+        {
+            rc -ENOMEM;
+            if ( (d->dirty_vram = xmalloc(struct sh_dirty_vram)) == NULL )
+                goto param_fail;
+
+            d->dirty_vram->begin_pfn = begin_pfn;
+            d->dirty_vram->end_pfn = begin_pfn + nr;
+            hap_vram_tracking_init(d);
+            rc = paging_log_dirty_enable(d);
+            if (rc != 0)
+                goto param_fail;
+        }
+        else
+        {
+            if ( !paging_mode_log_dirty(d) && d->dirty_vram )
+                rc = -EINVAL;
+            else
+                rc = -ENODATA;
+            goto param_fail;
+        }
+        /* get the bitmap */
+        rc = paging_log_dirty_range(d, begin_pfn, nr, dirty_bitmap);
+    }
+    else
+    {
+        if ( paging_mode_log_dirty(d) && d->dirty_vram ) {
+            rc = paging_log_dirty_disable(d);
+            xfree(d->dirty_vram);
+            d->dirty_vram = NULL;
+        } else
+            rc = 0;
+    }
+
+    return rc;
+
+param_fail:
+    if ( d->dirty_vram )
+    {
+        xfree(d->dirty_vram);
+        d->dirty_vram = NULL;
+    }
+    return rc;
+}
+
+/************************************************/
 /*            HAP LOG DIRTY SUPPORT             */
 /************************************************/
+
 /* hap code to call when log_dirty is enable. return 0 if no problem found. */
 int hap_enable_log_dirty(struct domain *d)
 {
@@ -84,6 +216,21 @@
     /* set l1e entries of P2M table to be read-only. */
     p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
     flush_tlb_mask(&d->domain_dirty_cpumask);
+}
+
+void hap_logdirty_init(struct domain *d)
+{
+    if ( paging_mode_log_dirty(d) && d->dirty_vram )
+    {
+        paging_log_dirty_disable(d);
+        xfree(d->dirty_vram);
+        d->dirty_vram = NULL;    
+    }
+
+    /* Reinitialize logdirty mechanism */
+    paging_log_dirty_init(d, hap_enable_log_dirty,
+                          hap_disable_log_dirty,
+                          hap_clean_dirty_bitmap);
 }
 
 /************************************************/
@@ -390,10 +537,6 @@
 {
     hap_lock_init(d);
     INIT_PAGE_LIST_HEAD(&d->arch.paging.hap.freelist);
-
-    /* This domain will use HAP for log-dirty mode */
-    paging_log_dirty_init(d, hap_enable_log_dirty, hap_disable_log_dirty,
-                          hap_clean_dirty_bitmap);
 }
 
 /* return 0 for success, -errno for failure */
diff -r 50cf07f42fdd xen/arch/x86/mm/paging.c
--- a/xen/arch/x86/mm/paging.c  Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/arch/x86/mm/paging.c  Thu Jun 04 18:47:04 2009 +0100
@@ -453,6 +453,157 @@
     return rv;
 }
 
+int paging_log_dirty_range(struct domain *d,
+                            unsigned long begin_pfn,
+                            unsigned long nr,
+                            XEN_GUEST_HANDLE_64(uint8) dirty_bitmap)
+{
+    int rv = 0;
+    unsigned long pages = 0;
+    mfn_t *l4, *l3, *l2;
+    unsigned long *l1;
+    int b1, b2, b3, b4;
+    int i2, i3, i4;
+
+    d->arch.paging.log_dirty.clean_dirty_bitmap(d);
+    log_dirty_lock(d);
+
+    PAGING_DEBUG(LOGDIRTY, "log-dirty-range: dom %u faults=%u dirty=%u\n",
+                 d->domain_id,
+                 d->arch.paging.log_dirty.fault_count,
+                 d->arch.paging.log_dirty.dirty_count);
+
+    if ( !mfn_valid(d->arch.paging.log_dirty.top) )
+    {
+        rv = -EINVAL; /* perhaps should be ENOMEM? */
+        goto out;
+    }
+
+    if ( unlikely(d->arch.paging.log_dirty.failed_allocs) ) {
+        printk("%s: %d failed page allocs while logging dirty pages\n",
+               __FUNCTION__, d->arch.paging.log_dirty.failed_allocs);
+        rv = -ENOMEM;
+        goto out;
+    }
+    
+    if ( !d->arch.paging.log_dirty.fault_count &&
+         !d->arch.paging.log_dirty.dirty_count ) {
+        int size = (nr + BITS_PER_LONG - 1) / BITS_PER_LONG;
+        unsigned long zeroes[size];
+        memset(zeroes, 0x00, size * BYTES_PER_LONG);
+        rv = 0;
+        if ( copy_to_guest_offset(dirty_bitmap, 0, (uint8_t *) zeroes,
+                                  size * BYTES_PER_LONG) != 0 )
+            rv = -EFAULT;
+        goto out;
+    }
+    d->arch.paging.log_dirty.fault_count = 0;
+    d->arch.paging.log_dirty.dirty_count = 0;
+
+    b1 = L1_LOGDIRTY_IDX(begin_pfn);
+    b2 = L2_LOGDIRTY_IDX(begin_pfn);
+    b3 = L3_LOGDIRTY_IDX(begin_pfn);
+    b4 = L4_LOGDIRTY_IDX(begin_pfn);
+    l4 = map_domain_page(mfn_x(d->arch.paging.log_dirty.top));
+
+    for ( i4 = b4;
+          (pages < nr) && (i4 < LOGDIRTY_NODE_ENTRIES);
+          i4++ )
+    {
+        l3 = mfn_valid(l4[i4]) ? map_domain_page(mfn_x(l4[i4])) : NULL;
+        for ( i3 = b3;
+              (pages < nr) && (i3 < LOGDIRTY_NODE_ENTRIES);
+              i3++ )
+        {
+            l2 = ((l3 && mfn_valid(l3[i3])) ?
+                  map_domain_page(mfn_x(l3[i3])) : NULL);
+            for ( i2 = b2;
+                  (pages < nr) && (i2 < LOGDIRTY_NODE_ENTRIES);
+                  i2++ )
+            {
+                static unsigned long zeroes[PAGE_SIZE/BYTES_PER_LONG];
+                unsigned int bytes = PAGE_SIZE;
+                uint8_t *s;
+                l1 = ((l2 && mfn_valid(l2[i2])) ?
+                      map_domain_page(mfn_x(l2[i2])) : zeroes);
+
+                s = ((uint8_t*)l1) + (b1 >> 3);
+                bytes -= b1 >> 3;
+
+                if ( likely(((nr - pages + 7) >> 3) < bytes) )
+                    bytes = (unsigned int)((nr - pages + 7) >> 3);
+
+                /* begin_pfn is not 32K aligned, hence we have to bit
+                 * shift the bitmap */
+                if ( b1 & 0x7 )
+                {
+                    int i, j;
+                    uint32_t *l = (uint32_t*) s;
+                    int bits = b1 & 0x7;
+                    int bitmask = (1 << bits) - 1;
+                    int size = (bytes + BYTES_PER_LONG - 1) / BYTES_PER_LONG;
+                    unsigned long bitmap[size];
+                    static unsigned long printed = 0;
+
+                    if ( printed != begin_pfn )
+                    {
+                        dprintk(XENLOG_DEBUG, "%s: begin_pfn %lx is not 32K 
aligned!\n",
+                                __FUNCTION__, begin_pfn);
+                        printed = begin_pfn;
+                    }
+
+                    for ( i = 0; i < size - 1; i++, l++ ) {
+                        bitmap[i] = ((*l) >> bits) |
+                            (((*((uint8_t*)(l + 1))) & bitmask) << (sizeof(*l) 
* 8 - bits));
+                    }
+                    s = (uint8_t*) l;
+                    size = BYTES_PER_LONG - ((b1 >> 3) & 0x3);
+                    bitmap[i] = 0;
+                    for ( j = 0; j < size; j++, s++ )
+                        bitmap[i] |= (*s) << (j * 8);
+                    bitmap[i] = (bitmap[i] >> bits) | (bitmask << (size * 8 - 
bits));
+                    if ( copy_to_guest_offset(dirty_bitmap, (pages >> 3),
+                                (uint8_t*) bitmap, bytes) != 0 )
+                    {
+                        rv = -EFAULT;
+                        goto out;
+                    }    
+                }
+                else
+                {
+                    if ( copy_to_guest_offset(dirty_bitmap, pages >> 3,
+                                              s, bytes) != 0 )
+                    {
+                        rv = -EFAULT;
+                        goto out;
+                    }
+                }
+
+                if ( l1 != zeroes )
+                    clear_page(l1);
+                pages += bytes << 3;
+                if ( l1 != zeroes )
+                    unmap_domain_page(l1);
+                b1 = b1 & 0x7;
+            }
+            b2 = 0;
+            if ( l2 )
+                unmap_domain_page(l2);
+        }
+        b3 = 0;
+        if ( l3 )
+            unmap_domain_page(l3);
+    }
+    unmap_domain_page(l4);
+
+    log_dirty_unlock(d);
+
+    return rv;
+
+ out:
+    log_dirty_unlock(d);
+    return rv;
+}
 
 /* Note that this function takes three function pointers. Callers must supply
  * these functions for log dirty code to call. This function usually is
@@ -554,11 +705,17 @@
     switch ( sc->op )
     {
     case XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY:
+        if ( hap_enabled(d) )
+            hap_logdirty_init(d);
         return paging_log_dirty_enable(d);
 
     case XEN_DOMCTL_SHADOW_OP_ENABLE:
         if ( sc->mode & XEN_DOMCTL_SHADOW_ENABLE_LOG_DIRTY )
+        {
+            if ( hap_enabled(d) )
+                hap_logdirty_init(d);
             return paging_log_dirty_enable(d);
+        }
 
     case XEN_DOMCTL_SHADOW_OP_OFF:
         if ( paging_mode_log_dirty(d) )
diff -r 50cf07f42fdd xen/arch/x86/mm/shadow/private.h
--- a/xen/arch/x86/mm/shadow/private.h  Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/arch/x86/mm/shadow/private.h  Thu Jun 04 18:47:04 2009 +0100
@@ -590,17 +590,6 @@
 }
 
 /**************************************************************************/
-/* VRAM dirty tracking support */
-
-struct sh_dirty_vram {
-    unsigned long begin_pfn;
-    unsigned long end_pfn;
-    paddr_t *sl1ma;
-    uint8_t *dirty_bitmap;
-    s_time_t last_dirty;
-};
-
-/**************************************************************************/
 /* Shadow-page refcounting. */
 
 void sh_destroy_shadow(struct vcpu *v, mfn_t smfn);
diff -r 50cf07f42fdd xen/include/asm-x86/hap.h
--- a/xen/include/asm-x86/hap.h Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/include/asm-x86/hap.h Thu Jun 04 18:47:04 2009 +0100
@@ -91,6 +91,11 @@
 void  hap_final_teardown(struct domain *d);
 void  hap_teardown(struct domain *d);
 void  hap_vcpu_init(struct vcpu *v);
+void  hap_logdirty_init(struct domain *d);
+int   hap_track_dirty_vram(struct domain *d,
+                           unsigned long begin_pfn,
+                           unsigned long nr,
+                           XEN_GUEST_HANDLE_64(uint8) dirty_bitmap);
 
 extern struct paging_mode hap_paging_real_mode;
 extern struct paging_mode hap_paging_protected_mode;
diff -r 50cf07f42fdd xen/include/asm-x86/paging.h
--- a/xen/include/asm-x86/paging.h      Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/include/asm-x86/paging.h      Thu Jun 04 18:47:05 2009 +0100
@@ -139,6 +139,12 @@
 /* free log dirty bitmap resource */
 void paging_free_log_dirty_bitmap(struct domain *d);
 
+/* get the dirty bitmap for a specific range of pfns */
+int paging_log_dirty_range(struct domain *d,
+                           unsigned long begin_pfn,
+                           unsigned long nr,
+                           XEN_GUEST_HANDLE_64(uint8) dirty_bitmap);
+
 /* enable log dirty */
 int paging_log_dirty_enable(struct domain *d);
 
diff -r 50cf07f42fdd xen/include/xen/sched.h
--- a/xen/include/xen/sched.h   Thu Jun 04 10:57:39 2009 +0100
+++ b/xen/include/xen/sched.h   Thu Jun 04 18:47:05 2009 +0100
@@ -165,6 +165,17 @@
 #define domain_unlock(d) spin_unlock_recursive(&(d)->domain_lock)
 #define domain_is_locked(d) spin_is_locked(&(d)->domain_lock)
 
+/* VRAM dirty tracking support */
+
+struct sh_dirty_vram {
+    unsigned long begin_pfn;
+    unsigned long end_pfn;
+    paddr_t *sl1ma;
+    uint8_t *dirty_bitmap;
+    s_time_t last_dirty;
+};
+
+/**************************************************************************/
 struct domain
 {
     domid_t          domain_id;

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH] hap dirty vram tracking, Stefano Stabellini <=