@@ -5,6 +5,7 @@ use propolis::inventory::Entity;
5
5
use propolis:: migrate:: {
6
6
MigrateCtx , MigrateStateError , Migrator , PayloadOffer , PayloadOffers ,
7
7
} ;
8
+ use propolis:: vmm;
8
9
use slog:: { error, info, trace, warn} ;
9
10
use std:: convert:: TryInto ;
10
11
use std:: io;
@@ -108,6 +109,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin + Send> DestinationProtocol<T> {
108
109
self . ram_push ( & step) . await
109
110
}
110
111
MigratePhase :: DeviceState => self . device_state ( ) . await ,
112
+ MigratePhase :: TimeData => self . time_data ( ) . await ,
111
113
MigratePhase :: RamPull => self . ram_pull ( ) . await ,
112
114
MigratePhase :: ServerState => self . server_state ( ) . await ,
113
115
MigratePhase :: Finish => self . finish ( ) . await ,
@@ -129,6 +131,11 @@ impl<T: AsyncRead + AsyncWrite + Unpin + Send> DestinationProtocol<T> {
129
131
// pre- and post-pause steps.
130
132
self . run_phase ( MigratePhase :: RamPushPrePause ) . await ?;
131
133
self . run_phase ( MigratePhase :: RamPushPostPause ) . await ?;
134
+
135
+ // Import of the time data *must* be done before we import device
136
+ // state: the proper functioning of device timers depends on an adjusted
137
+ // boot_hrtime.
138
+ self . run_phase ( MigratePhase :: TimeData ) . await ?;
132
139
self . run_phase ( MigratePhase :: DeviceState ) . await ?;
133
140
self . run_phase ( MigratePhase :: RamPull ) . await ?;
134
141
self . run_phase ( MigratePhase :: ServerState ) . await ?;
@@ -321,6 +328,110 @@ impl<T: AsyncRead + AsyncWrite + Unpin + Send> DestinationProtocol<T> {
321
328
self . import_device ( & target, & device, & migrate_ctx) ?;
322
329
}
323
330
}
331
+ self . send_msg ( codec:: Message :: Okay ) . await
332
+ }
333
+
334
+ // Get the guest time data from the source, make updates to it based on the
335
+ // new host, and write the data out to bhvye.
336
+ async fn time_data ( & mut self ) -> Result < ( ) , MigrateError > {
337
+ // Read time data sent by the source and deserialize
338
+ let raw: String = match self . read_msg ( ) . await ? {
339
+ codec:: Message :: Serialized ( encoded) => encoded,
340
+ msg => {
341
+ error ! ( self . log( ) , "time data: unexpected message: {msg:?}" ) ;
342
+ return Err ( MigrateError :: UnexpectedMessage ) ;
343
+ }
344
+ } ;
345
+ info ! ( self . log( ) , "VMM Time Data: {:?}" , raw) ;
346
+ let time_data_src: vmm:: time:: VmTimeData = ron:: from_str ( & raw )
347
+ . map_err ( |e| {
348
+ MigrateError :: TimeData ( format ! (
349
+ "VMM Time Data deserialization error: {}" ,
350
+ e
351
+ ) )
352
+ } ) ?;
353
+ probes:: migrate_time_data_before!( || {
354
+ (
355
+ time_data_src. guest_freq,
356
+ time_data_src. guest_tsc,
357
+ time_data_src. boot_hrtime,
358
+ )
359
+ } ) ;
360
+
361
+ // Take a snapshot of the host hrtime/wall clock time, then adjust
362
+ // time data appropriately.
363
+ let vmm_hdl = {
364
+ let instance_guard = self . vm_controller . instance ( ) . lock ( ) ;
365
+ & instance_guard. machine ( ) . hdl . clone ( )
366
+ } ;
367
+ let ( dst_hrt, dst_wc) = vmm:: time:: host_time_snapshot ( vmm_hdl)
368
+ . map_err ( |e| {
369
+ MigrateError :: TimeData ( format ! (
370
+ "could not read host time: {}" ,
371
+ e
372
+ ) )
373
+ } ) ?;
374
+ let ( time_data_dst, adjust) =
375
+ vmm:: time:: adjust_time_data ( time_data_src, dst_hrt, dst_wc)
376
+ . map_err ( |e| {
377
+ MigrateError :: TimeData ( format ! (
378
+ "could not adjust VMM Time Data: {}" ,
379
+ e
380
+ ) )
381
+ } ) ?;
382
+
383
+ // In case import fails, log adjustments made to time data and fire
384
+ // dtrace probe first
385
+ if adjust. migrate_delta_negative {
386
+ warn ! (
387
+ self . log( ) ,
388
+ "Found negative wall clock delta between target import \
389
+ and source export:\n \
390
+ - source wall clock time: {:?}\n \
391
+ - target wall clock time: {:?}\n ",
392
+ time_data_src. wall_clock( ) ,
393
+ dst_wc
394
+ ) ;
395
+ }
396
+ info ! (
397
+ self . log( ) ,
398
+ "Time data adjustments:\n \
399
+ - guest TSC freq: {} Hz = {} GHz\n \
400
+ - guest uptime ns: {:?}\n \
401
+ - migration time delta: {:?}\n \
402
+ - guest_tsc adjustment = {} + {} = {}\n \
403
+ - boot_hrtime adjustment = {} ---> {} - {} = {}\n \
404
+ - dest highres clock time: {}\n \
405
+ - dest wall clock time: {:?}",
406
+ time_data_dst. guest_freq,
407
+ time_data_dst. guest_freq as f64 / vmm:: time:: NS_PER_SEC as f64 ,
408
+ adjust. guest_uptime_ns,
409
+ adjust. migrate_delta,
410
+ time_data_src. guest_tsc,
411
+ adjust. guest_tsc_delta,
412
+ time_data_dst. guest_tsc,
413
+ time_data_src. boot_hrtime,
414
+ dst_hrt,
415
+ adjust. boot_hrtime_delta,
416
+ time_data_dst. boot_hrtime,
417
+ dst_hrt,
418
+ dst_wc
419
+ ) ;
420
+ probes:: migrate_time_data_after!( || {
421
+ (
422
+ time_data_dst. guest_freq,
423
+ time_data_dst. guest_tsc,
424
+ time_data_dst. boot_hrtime,
425
+ adjust. guest_uptime_ns,
426
+ adjust. migrate_delta. as_nanos( ) as u64 ,
427
+ adjust. migrate_delta_negative,
428
+ )
429
+ } ) ;
430
+
431
+ // Import the adjusted time data
432
+ vmm:: time:: import_time_data ( vmm_hdl, time_data_dst) . map_err ( |e| {
433
+ MigrateError :: TimeData ( format ! ( "VMM Time Data import error: {}" , e) )
434
+ } ) ?;
324
435
325
436
self . send_msg ( codec:: Message :: Okay ) . await
326
437
}
0 commit comments