shadow: drop guest VRAM write access after some idleness
If the video RAM has been kept clean for at least 2 seconds, we can
afford taking the time to drop guest write access, which allows us to
save the dirty bit scanning entirely until we get a guest page handle.
diff -r 3bc6ad3beafc -r 98b06d404e6b xen/arch/x86/mm/shadow/common.c
--- a/xen/arch/x86/mm/shadow/common.c Fri May 02 14:59:35 2008 +0100
+++ b/xen/arch/x86/mm/shadow/common.c Thu May 08 12:50:34 2008 +0100
@@ -2871,6 +2871,8 @@ int shadow_track_dirty_vram(struct domai
unsigned long end_pfn = begin_pfn + nr;
unsigned long dirty_size = (nr + 7) / 8;
int flush_tlb = 0;
+ unsigned long i;
+ p2m_type_t t;
if (end_pfn < begin_pfn
|| begin_pfn > d->arch.p2m->max_mapped_pfn
@@ -2881,7 +2883,8 @@ int shadow_track_dirty_vram(struct domai
if ( d->dirty_vram && (!nr ||
( begin_pfn != d->dirty_vram->begin_pfn
- || end_pfn != d->dirty_vram->end_pfn )) ) {
+ || end_pfn != d->dirty_vram->end_pfn )) )
+ {
/* Different tracking, tear the previous down. */
gdprintk(XENLOG_INFO, "stopping tracking VRAM %lx - %lx\n",
d->dirty_vram->begin_pfn, d->dirty_vram->end_pfn);
xfree(d->dirty_vram->sl1ma);
@@ -2890,17 +2893,16 @@ int shadow_track_dirty_vram(struct domai
d->dirty_vram = NULL;
}
- if ( !nr ) {
+ if ( !nr )
+ {
rc = 0;
goto out;
}
/* This should happen seldomly (Video mode change),
* no need to be careful. */
- if ( !d->dirty_vram ) {
- unsigned long i;
- p2m_type_t t;
-
+ if ( !d->dirty_vram )
+ {
/* Just recount from start. */
for ( i = begin_pfn; i < end_pfn; i++ )
flush_tlb |= sh_remove_all_mappings(d->vcpu[0], gfn_to_mfn(d, i,
&t));
@@ -2921,10 +2923,20 @@ int shadow_track_dirty_vram(struct domai
goto out_sl1ma;
memset(d->dirty_vram->dirty_bitmap, 0, dirty_size);
+ d->dirty_vram->last_dirty = NOW();
+
/* Tell the caller that this time we could not track dirty bits. */
rc = -ENODATA;
- } else {
- int i;
+ }
+ else if (d->dirty_vram->last_dirty == -1)
+ {
+ /* still completely clean, just copy our empty bitmap */
+ rc = -EFAULT;
+ if ( copy_to_guest(dirty_bitmap, d->dirty_vram->dirty_bitmap,
dirty_size) == 0 )
+ rc = 0;
+ }
+ else
+ {
#ifdef __i386__
unsigned long map_mfn = INVALID_MFN;
void *map_sl1p = NULL;
@@ -2932,26 +2944,29 @@ int shadow_track_dirty_vram(struct domai
/* Iterate over VRAM to track dirty bits. */
for ( i = 0; i < nr; i++ ) {
- p2m_type_t t;
mfn_t mfn = gfn_to_mfn(d, begin_pfn + i, &t);
struct page_info *page = mfn_to_page(mfn);
u32 count_info = page->u.inuse.type_info & PGT_count_mask;
int dirty = 0;
paddr_t sl1ma = d->dirty_vram->sl1ma[i];
- switch (count_info) {
+ switch (count_info)
+ {
case 0:
/* No guest reference, nothing to track. */
break;
case 1:
/* One guest reference. */
- if ( sl1ma == INVALID_PADDR ) {
+ if ( sl1ma == INVALID_PADDR )
+ {
/* We don't know which sl1e points to this, too bad. */
dirty = 1;
/* TODO: Heuristics for finding the single mapping of
* this gmfn */
flush_tlb |= sh_remove_all_mappings(d->vcpu[0],
gfn_to_mfn(d, begin_pfn + i, &t));
- } else {
+ }
+ else
+ {
/* Hopefully the most common case: only one mapping,
* whose dirty bit we can use. */
l1_pgentry_t *sl1e;
@@ -2970,7 +2985,8 @@ int shadow_track_dirty_vram(struct domai
sl1e = maddr_to_virt(sl1ma);
#endif
- if ( l1e_get_flags(*sl1e) & _PAGE_DIRTY ) {
+ if ( l1e_get_flags(*sl1e) & _PAGE_DIRTY )
+ {
dirty = 1;
/* Note: this is atomic, so we may clear a
* _PAGE_ACCESSED set by another processor. */
@@ -2987,7 +3003,10 @@ int shadow_track_dirty_vram(struct domai
}
if ( dirty )
+ {
d->dirty_vram->dirty_bitmap[i / 8] |= 1 << (i % 8);
+ d->dirty_vram->last_dirty = NOW();
+ }
}
#ifdef __i386__
@@ -2998,6 +3017,14 @@ int shadow_track_dirty_vram(struct domai
rc = -EFAULT;
if ( copy_to_guest(dirty_bitmap, d->dirty_vram->dirty_bitmap,
dirty_size) == 0 ) {
memset(d->dirty_vram->dirty_bitmap, 0, dirty_size);
+ if (d->dirty_vram->last_dirty + SECONDS(2) < NOW())
+ {
+ /* was clean for more than two seconds, try to disable guest
+ * write access */
+ for ( i = begin_pfn; i < end_pfn; i++ )
+ flush_tlb |= sh_remove_write_access(d->vcpu[0],
gfn_to_mfn(d, i, &t), 1, 0);
+ d->dirty_vram->last_dirty = -1;
+ }
rc = 0;
}
}
diff -r 3bc6ad3beafc -r 98b06d404e6b xen/arch/x86/mm/shadow/multi.c
--- a/xen/arch/x86/mm/shadow/multi.c Fri May 02 14:59:35 2008 +0100
+++ b/xen/arch/x86/mm/shadow/multi.c Thu May 08 12:50:34 2008 +0100
@@ -870,6 +870,17 @@ _sh_propagate(struct vcpu *v,
}
}
+ if ( unlikely((level == 1) && d->dirty_vram
+ && d->dirty_vram->last_dirty == -1
+ && gfn_x(target_gfn) >= d->dirty_vram->begin_pfn
+ && gfn_x(target_gfn) < d->dirty_vram->end_pfn) )
+ {
+ if ( ft & FETCH_TYPE_WRITE )
+ d->dirty_vram->last_dirty = NOW();
+ else
+ sflags &= ~_PAGE_RW;
+ }
+
/* Read-only memory */
if ( p2mt == p2m_ram_ro )
sflags &= ~_PAGE_RW;
@@ -1320,8 +1331,10 @@ static inline void shadow_vram_put_l1e(s
* just hope it will remain. */
}
}
- if ( dirty )
+ if ( dirty ) {
d->dirty_vram->dirty_bitmap[i / 8] |= 1 << (i % 8);
+ d->dirty_vram->last_dirty = NOW();
+ }
}
}
diff -r 3bc6ad3beafc -r 98b06d404e6b xen/arch/x86/mm/shadow/private.h
--- a/xen/arch/x86/mm/shadow/private.h Fri May 02 14:59:35 2008 +0100
+++ b/xen/arch/x86/mm/shadow/private.h Thu May 08 12:50:34 2008 +0100
@@ -536,6 +536,7 @@ struct sh_dirty_vram {
unsigned long end_pfn;
paddr_t *sl1ma;
uint8_t *dirty_bitmap;
+ s_time_t last_dirty;
};
/**************************************************************************/
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|