aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xc/lib/xc_linux_save.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xc/lib/xc_linux_save.c')
-rw-r--r--tools/xc/lib/xc_linux_save.c207
1 files changed, 150 insertions, 57 deletions
diff --git a/tools/xc/lib/xc_linux_save.c b/tools/xc/lib/xc_linux_save.c
index ceb5f02e15..857007fd3e 100644
--- a/tools/xc/lib/xc_linux_save.c
+++ b/tools/xc/lib/xc_linux_save.c
@@ -82,6 +82,67 @@ inline void set_bit ( int nr, volatile void * addr)
(1 << (nr % (sizeof(unsigned long)*8) ) );
}
+long long tv_to_us( struct timeval *new )
+{
+ return (new->tv_sec * 1000000) + new->tv_usec;
+}
+
+long long tvdelta( struct timeval *new, struct timeval *old )
+{
+ return ((new->tv_sec - old->tv_sec)*1000000 ) +
+ (new->tv_usec - old->tv_usec);
+}
+
+int track_cpu_usage_dom0( int xc_handle, int print )
+{
+ static struct timeval wall_last;
+ static long long cpu_last;
+
+ struct timeval wall_now;
+ long long cpu_now, wall_delta, cpu_delta;
+
+ gettimeofday(&wall_now, NULL);
+
+ cpu_now = xc_domain_get_cpu_usage( xc_handle, 0 )/1000;
+
+ wall_delta = tvdelta(&wall_now,&wall_last)/1000;
+ cpu_delta = (cpu_now - cpu_last)/1000;
+
+ if(print)
+ printf("Dom0 : wall delta %lldms, cpu delta %lldms : %d%%\n",
+ wall_delta, cpu_delta, (cpu_delta*100)/wall_delta);
+
+ cpu_last = cpu_now;
+ wall_last = wall_now;
+
+ return 0;
+}
+
+int track_cpu_usage_target( int xc_handle, u64 domid, int print )
+{
+ static struct timeval wall_last;
+ static long long cpu_last;
+
+ struct timeval wall_now;
+ long long cpu_now, wall_delta, cpu_delta;
+
+ gettimeofday(&wall_now, NULL);
+
+ cpu_now = xc_domain_get_cpu_usage( xc_handle, domid )/1000;
+
+ wall_delta = tvdelta(&wall_now,&wall_last)/1000;
+ cpu_delta = (cpu_now - cpu_last)/1000;
+
+ if(print)
+ printf("Target: wall delta %lldms, cpu delta %lldms : %d%%\n",
+ wall_delta, cpu_delta, (cpu_delta*100)/wall_delta);
+
+ cpu_last = cpu_now;
+ wall_last = wall_now;
+
+ return 0;
+}
+
int xc_linux_save(int xc_handle,
u64 domid,
@@ -95,10 +156,11 @@ int xc_linux_save(int xc_handle,
int verbose = flags & XCFLAGS_VERBOSE;
int live = flags & XCFLAGS_LIVE;
int debug = flags & XCFLAGS_DEBUG;
- int sent_last_iter, sent_this_iter, max_iters;
-
- /* Remember if we stopped the guest, so we can restart it on exit. */
- int we_stopped_it = 0;
+ int sent_last_iter, sent_this_iter, skip_this_iter;
+
+ /* Important tuning parameters */
+ int max_iters = 29; // limit us to 30 times round loop
+ int max_factor = 3; // never send more than 3x nr_pfns
/* The new domain's shared-info frame number. */
unsigned long shared_info_frame;
@@ -137,11 +199,15 @@ int xc_linux_save(int xc_handle,
/* number of pages we're dealing with */
unsigned long nr_pfns;
- /* bitmap of pages left to send */
- unsigned long *to_send, *to_fix;
-
-//live=0;
+ /* bitmap of pages:
+ - that should be sent this iteration (unless later marked as skip);
+ - to skip this iteration because already dirty;
+ - to fixup by sending at the end if not already resent; */
+ unsigned long *to_send, *to_skip, *to_fix;
+ int needed_to_fix = 0;
+ int total_sent = 0;
+
if ( mlock(&ctxt, sizeof(ctxt) ) )
{
PERROR("Unable to mlock ctxt");
@@ -149,39 +215,16 @@ int xc_linux_save(int xc_handle,
}
/* Ensure that the domain exists, and that it is stopped. */
- for ( ; ; )
- {
- op.cmd = DOM0_GETDOMAININFO;
- op.u.getdomaininfo.domain = (domid_t)domid;
- op.u.getdomaininfo.ctxt = &ctxt;
- if ( (do_dom0_op(xc_handle, &op) < 0) ||
- ((u64)op.u.getdomaininfo.domain != domid) )
- {
- PERROR("Could not get info on domain");
- goto out;
- }
-
- memcpy(name, op.u.getdomaininfo.name, sizeof(name));
- shared_info_frame = op.u.getdomaininfo.shared_info_frame;
-
- if ( op.u.getdomaininfo.state == DOMSTATE_STOPPED )
- break;
- we_stopped_it = 1;
-
- op.cmd = DOM0_STOPDOMAIN;
- op.u.stopdomain.domain = (domid_t)domid;
- if ( do_dom0_op(xc_handle, &op) != 0 )
- {
- we_stopped_it = 0;
- PERROR("Stopping target domain failed");
- goto out;
- }
-
- usleep(1000); // 1ms
- printf("Sleep for 1ms\n");
+ if ( xc_domain_stop_sync( xc_handle, domid, &op, &ctxt ) )
+ {
+ PERROR("Could not sync stop domain");
+ goto out;
}
+ memcpy(name, op.u.getdomaininfo.name, sizeof(name));
+ shared_info_frame = op.u.getdomaininfo.shared_info_frame;
+
/* A cheesy test to see whether the domain contains valid state. */
if ( ctxt.pt_base == 0 )
{
@@ -288,7 +331,6 @@ int xc_linux_save(int xc_handle,
last_iter = 0;
sent_last_iter = 1<<20; // 4GB's worth of pages
- max_iters = 9; // limit us to 10 time round loop
}
else
last_iter = 1;
@@ -300,12 +342,14 @@ int xc_linux_save(int xc_handle,
to_send = malloc( sz );
to_fix = calloc( 1, sz );
+ to_skip = malloc( sz );
- if (!to_send || !to_fix)
+ if (!to_send || !to_fix || !to_skip)
{
ERROR("Couldn't allocate to_send array");
goto out;
}
+
memset( to_send, 0xff, sz );
if ( mlock( to_send, sz ) )
@@ -313,6 +357,15 @@ int xc_linux_save(int xc_handle,
PERROR("Unable to mlock to_send");
return 1;
}
+
+ /* (to fix is local only) */
+
+ if ( mlock( to_skip, sz ) )
+ {
+ PERROR("Unable to mlock to_skip");
+ return 1;
+ }
+
}
@@ -369,8 +422,11 @@ int xc_linux_save(int xc_handle,
goto out;
}
- /* Now write out each data page, canonicalising page tables as we go... */
+ track_cpu_usage_dom0(xc_handle, 0);
+ track_cpu_usage_target( xc_handle, domid, 0);
+ /* Now write out each data page, canonicalising page tables as we go... */
+
while(1)
{
unsigned int prev_pc, batch, sent_this_iter;
@@ -378,6 +434,7 @@ int xc_linux_save(int xc_handle,
iter++;
sent_this_iter = 0;
+ skip_this_iter = 0;
prev_pc = 0;
verbose_printf("Saving memory pages: iter %d 0%%", iter);
@@ -391,6 +448,18 @@ int xc_linux_save(int xc_handle,
prev_pc = this_pc;
}
+ /* slightly wasteful to peek the whole array evey time,
+ but this is fast enough for the moment. */
+
+ if ( !last_iter &&
+ xc_shadow_control( xc_handle, domid,
+ DOM0_SHADOW_CONTROL_OP_PEEK,
+ to_skip, nr_pfns ) != nr_pfns )
+ {
+ ERROR("Error peeking shadow bitmap");
+ goto out;
+ }
+
/* load pfn_type[] with the mfn of all the pages we're doing in
this batch. */
@@ -404,15 +473,29 @@ int xc_linux_save(int xc_handle,
test_bit(n,to_send),
live_mfn_to_pfn_table[live_pfn_to_mfn_table[n]&0xFFFFF]);
+ if (!last_iter && test_bit(n, to_send) && test_bit(n, to_skip))
+ skip_this_iter++; // stats keeping
- if ( !test_bit(n, to_send ) &&
- !( last_iter && test_bit(n, to_fix ) ) ) continue;
+ if (! ( (test_bit(n, to_send) && !test_bit(n, to_skip)) ||
+ (test_bit(n, to_send) && last_iter) ||
+ (test_bit(n, to_fix) && last_iter) ) )
+ continue;
+
+ /* we get here if:
+ 1. page is marked to_send & hasn't already been re-dirtied
+ 2. (ignore to_skip in last iteration)
+ 3. add in pages that still need fixup (net bufs)
+ */
pfn_batch[batch] = n;
pfn_type[batch] = live_pfn_to_mfn_table[n];
if( pfn_type[batch] == 0x80000004 )
{
+ /* not currently in pusedo-physical map -- set bit
+ in to_fix that we must send this page in last_iter
+ unless its sent sooner anyhow */
+
set_bit( n, to_fix );
if( iter>1 )
DDPRINTF("Urk! netbuf race: iter %d, pfn %lx. mfn %lx\n",
@@ -422,6 +505,7 @@ int xc_linux_save(int xc_handle,
if ( last_iter && test_bit(n, to_fix ) && !test_bit(n, to_send ))
{
+ needed_to_fix++;
DPRINTF("Fix! iter %d, pfn %lx. mfn %lx\n",
iter,n,pfn_type[batch]);
}
@@ -567,9 +651,23 @@ int xc_linux_save(int xc_handle,
munmap(region_base, batch*PAGE_SIZE);
skip:
+
+ total_sent += sent_this_iter;
+
+ verbose_printf("\b\b\b\b100%% (pages sent= %d, skipped= %d )\n",
+ sent_this_iter, skip_this_iter );
+
+ track_cpu_usage_dom0(xc_handle, 1);
+ track_cpu_usage_target( xc_handle, domid, 1);
+
- verbose_printf("\b\b\b\b100%% (%d pages)\n", sent_this_iter );
-
+ if ( last_iter )
+ {
+ verbose_printf("Total pages sent= %d (%.2fx)\n",
+ total_sent, ((float)total_sent)/nr_pfns );
+ verbose_printf("(of which %d were fixups)\n", needed_to_fix );
+ }
+
if ( debug && last_iter )
{
int minusone = -1;
@@ -592,18 +690,21 @@ int xc_linux_save(int xc_handle,
if ( live )
{
- if ( ( sent_this_iter > (sent_last_iter * 0.95) ) ||
- (iter >= max_iters) || (sent_this_iter < 10) )
+ if (
+ // ( sent_this_iter > (sent_last_iter * 0.95) ) ||
+ (iter >= max_iters) ||
+ (sent_this_iter+skip_this_iter < 10) ||
+ (total_sent > nr_pfns*max_factor) )
{
DPRINTF("Start last iteration\n");
last_iter = 1;
- xc_domain_stop_sync( xc_handle, domid );
+ xc_domain_stop_sync( xc_handle, domid, &op, NULL );
}
if ( xc_shadow_control( xc_handle, domid,
- DOM0_SHADOW_CONTROL_OP_CLEAN,
+ DOM0_SHADOW_CONTROL_OP_CLEAN2,
to_send, nr_pfns ) != nr_pfns )
{
ERROR("Error flushing shadow PT");
@@ -674,14 +775,6 @@ int xc_linux_save(int xc_handle,
munmap(live_shinfo, PAGE_SIZE);
out:
- /* Restart the domain if we had to stop it to save its state. */
- if ( we_stopped_it )
- {
- printf("Restart domain\n");
- op.cmd = DOM0_STARTDOMAIN;
- op.u.startdomain.domain = (domid_t)domid;
- (void)do_dom0_op(xc_handle, &op);
- }
if ( pfn_type != NULL )
free(pfn_type);