# HG changeset patch # User David Scott # Date 1268739133 0 # Node ID bd84ea5e1a08e0d8019f8ab64a67b96a3a543548 # Parent 012936d9d680683cd83cdcd2cd772985de142c50 CA-37332: move essential suspend/resume-related code (specifically memory and PCI device handling) into Vmops so that it can be shared with checkpoint. Signed-off-by: David Scott diff -r 012936d9d680 -r bd84ea5e1a08 ocaml/xapi/vmops.ml --- a/ocaml/xapi/vmops.ml Tue Mar 16 11:30:25 2010 +0000 +++ b/ocaml/xapi/vmops.ml Tue Mar 16 11:32:13 2010 +0000 @@ -748,13 +748,19 @@ (** Get the suspend VDI, mount the disk, open the file and call _restore*. Guarantees to always unmount the VDI and, only if the restore succeeds, deletes the VDI (otherwise an exception from lower-level code propagates out *) -let restore ~__context ~xc ~xs ~self domid = +let restore ~__context ~xc ~xs ~self start_paused = + let memory_required_kib = Memory.kib_of_bytes_used (Memory_check.vm_compute_resume_memory ~__context self) in + let snapshot = Helpers.get_boot_record ~__context ~self in + (* CA-31759: we always use the live memory_target *) + let snapshot = { snapshot with API.vM_memory_target = Db.VM.get_memory_target ~__context ~self } in + + let reservation_id = Memory_control.reserve_memory ~__context ~xc ~xs ~kib:memory_required_kib in + let domid = create ~__context ~xc ~xs ~self ~reservation_id snapshot () in + Xapi_xenops_errors.handle_xenops_error (fun () -> let suspend_vdi = Db.VM.get_suspend_VDI ~__context ~self in - let snapshot = Helpers.get_boot_record ~__context ~self in - (* CA-31759: we always use the live memory_target *) - let snapshot = { snapshot with API.vM_memory_target = Db.VM.get_memory_target ~__context ~self } in + Sm_fs_ops.with_fs_vdi __context suspend_vdi (fun mount_point -> @@ -806,7 +812,13 @@ Helpers.call_api_functions ~__context (fun rpc session_id -> Client.VDI.destroy rpc session_id suspend_vdi); - Db.VM.set_suspend_VDI ~__context ~self ~value:Ref.null + Db.VM.set_suspend_VDI ~__context ~self ~value:Ref.null; + + Db.VM.set_domid ~__context ~self ~value:(Int64.of_int domid); + + debug "Vmops.restore: %s unpausing domain" (if start_paused then "not" else ""); + if not start_paused then Domain.unpause ~xc domid; + plug_pcidevs_noexn ~__context ~vm:self domid (pcidevs_of_vm ~__context ~vm:self); ) @@ -879,10 +891,33 @@ exception Domain_architecture_not_supported_in_suspend let suspend ~live ~progress_cb ~__context ~xc ~xs ~vm = - Xapi_xenops_errors.handle_xenops_error - (fun () -> - let uuid = Db.VM.get_uuid ~__context ~self:vm in - let hvm = Helpers.has_booted_hvm ~__context ~self:vm in + let uuid = Db.VM.get_uuid ~__context ~self:vm in + let hvm = Helpers.has_booted_hvm ~__context ~self:vm in + let domid = Helpers.domid_of_vm ~__context ~self:vm in + + Xapi_xenops_errors.handle_xenops_error + (fun () -> + with_xc_and_xs + (fun xc xs -> + let is_paused = Db.VM.get_power_state + ~__context ~self:vm = `Paused in + if is_paused then Domain.unpause ~xc domid; + let min = Db.VM.get_memory_dynamic_min ~__context ~self:vm in + let max = Db.VM.get_memory_dynamic_max ~__context ~self:vm in + let min = Int64.to_int (Int64.div min 1024L) in + let max = Int64.to_int (Int64.div max 1024L) in + try + (* Balloon down the guest as far as we can to force it to clear + unnecessary caches etc. *) + debug "suspend phase 0/4: asking guest to balloon down"; + Domain.set_memory_dynamic_range ~xs ~min ~max:min domid; + Memory_control.balance_memory ~__context ~xc ~xs; + + debug "suspend phase 1/4: hot-unplugging any PCI devices"; + let hvm = (Xc.domain_getinfo xc domid).Xc.hvm_guest in + if hvm then unplug_pcidevs_noexn ~__context ~vm domid (Device.PCI.list xc xs domid); + + let suspend_SR = Helpers.choose_suspend_sr ~__context ~vm in let required_space = get_suspend_space __context vm in Sm_fs_ops.with_new_fs_vdi __context @@ -891,7 +926,7 @@ ~sm_config:[Xapi_globs._sm_vm_hint, uuid] (fun vdi_ref mount_point -> let filename = sprintf "%s/suspend-image" mount_point in - debug "suspend: phase 1/2: opening suspend image file (%s)" + debug "suspend: phase 2/4: opening suspend image file (%s)" filename; (* NB if the suspend file already exists it will be *) (* overwritten. *) @@ -900,7 +935,7 @@ finally (fun () -> let domid = Helpers.domid_of_vm ~__context ~self:vm in - debug "suspend: phase 2/2: suspending to disk"; + debug "suspend: phase 3/4: suspending to disk"; with_xal (fun xal -> Domain.suspend ~xc ~xs ~hvm domid fd [] @@ -926,8 +961,25 @@ ~value:vdi_ref; ) (fun () -> Unix.close fd); - debug "suspend: complete") - ) + debug "suspend: complete"); + + debug "suspend phase 4/4: recording memory usage"; + (* Record the final memory usage of the VM, so *) + (* that we know how much memory to free before *) + (* attempting to resume this VM in future. *) + let di = with_xc (fun xc -> Xc.domain_getinfo xc domid) in + let final_memory_bytes = Memory.bytes_of_pages (Int64.of_nativeint di.Xc.total_memory_pages) in + debug "total_memory_pages=%Ld; storing target=%Ld" (Int64.of_nativeint di.Xc.total_memory_pages) final_memory_bytes; + (* CA-31759: avoid using the LBR to simplify upgrade *) + Db.VM.set_memory_target ~__context ~self:vm ~value:final_memory_bytes; + + with e -> + Domain.set_memory_dynamic_range ~xs ~min ~max domid; + Memory_control.balance_memory ~__context ~xc ~xs; + if is_paused then + (try Domain.pause ~xc domid with _ -> ()); + raise e + )) let resume ~__context ~xc ~xs ~vm = let domid = Helpers.domid_of_vm ~__context ~self:vm in diff -r 012936d9d680 -r bd84ea5e1a08 ocaml/xapi/xapi_vm.ml --- a/ocaml/xapi/xapi_vm.ml Tue Mar 16 11:30:25 2010 +0000 +++ b/ocaml/xapi/xapi_vm.ml Tue Mar 16 11:32:13 2010 +0000 @@ -724,24 +724,6 @@ ~reason:Xapi_hooks.reason__suspend ~vm; with_xc_and_xs (fun xc xs -> - let is_paused = Db.VM.get_power_state - ~__context ~self:vm = `Paused in - if is_paused then Domain.unpause ~xc domid; - let min = Db.VM.get_memory_dynamic_min ~__context ~self:vm in - let max = Db.VM.get_memory_dynamic_max ~__context ~self:vm in - let min = Int64.to_int (Int64.div min 1024L) in - let max = Int64.to_int (Int64.div max 1024L) in - try - (* Balloon down the guest as far as we can to force it to clear - unnecessary caches etc. *) - debug "suspend phase 0/4: asking guest to balloon down"; - Domain.set_memory_dynamic_range ~xs ~min ~max:min domid; - Memory_control.balance_memory ~__context ~xc ~xs; - - debug "suspend phase 1/4: hot-unplugging any PCI devices"; - let hvm = (Xc.domain_getinfo xc domid).Xc.hvm_guest in - if hvm then Vmops.unplug_pcidevs_noexn ~__context ~vm domid (Device.PCI.list xc xs domid); - debug "suspend phase 2/4: calling Vmops.suspend"; (* Call the memory image creating 90%, *) (* the device un-hotplug the final 10% *) @@ -750,24 +732,10 @@ TaskHelper.set_progress ~__context (x *. 0.9) ); - debug "suspend phase 3/4: recording memory usage"; - (* Record the final memory usage of the VM, so *) - (* that we know how much memory to free before *) - (* attempting to resume this VM in future. *) - let di = with_xc (fun xc -> Xc.domain_getinfo xc domid) in - let final_memory_bytes = Memory.bytes_of_pages (Int64.of_nativeint di.Xc.total_memory_pages) in - debug "total_memory_pages=%Ld; storing target=%Ld" (Int64.of_nativeint di.Xc.total_memory_pages) final_memory_bytes; - (* CA-31759: avoid using the LBR to simplify upgrade *) - Db.VM.set_memory_target ~__context ~self:vm ~value:final_memory_bytes; debug "suspend phase 4/4: destroying the domain"; Vmops.destroy ~clear_currently_attached:false ~__context ~xc ~xs ~self:vm domid `Suspended; - with e -> - Domain.set_memory_dynamic_range ~xs ~min ~max domid; - Memory_control.balance_memory ~__context ~xc ~xs; - if is_paused then - (try Domain.pause ~xc domid with _ -> ()); - raise e + ) ) ) () @@ -789,37 +757,13 @@ debug "resume: making sure the VM really is suspended"; assert_power_state_is ~__context ~vm ~expected:`Suspended; assert_ha_always_run_is_true ~__context ~vm; - let snapshot = Helpers.get_boot_record ~__context ~self:vm in - let memory_required_kib = - Memory.kib_of_bytes_used ( - Memory_check.vm_compute_resume_memory - ~__context vm) in -(* - Vmops.with_enough_memory ~__context ~xc ~xs ~memory_required_kib - (fun () -> -*) - debug "resume phase 0/3: allocating memory"; - let reservation_id = Memory_control.reserve_memory ~__context ~xc ~xs ~kib:memory_required_kib in - debug "resume phase 1/3: creating an empty domain"; - let domid = Vmops.create ~__context ~xc ~xs ~self:vm ~reservation_id - snapshot () in - debug "resume phase 2/3: executing Vmops.restore"; + (* vmops.restore guarantees that, if an exn occurs *) (* during execution, any disks that were attached/ *) (* activated have been detached/de-activated and *) (* the domain is destroyed. *) - Vmops.restore ~__context ~xc ~xs ~self:vm domid; - Db.VM.set_domid ~__context ~self:vm - ~value:(Int64.of_int domid); - - debug "resume phase 3/3: %s unpausing domain" - (if start_paused then "not" else ""); - if not start_paused then begin - Domain.unpause ~xc domid; - end; - - Vmops.plug_pcidevs_noexn ~__context ~vm domid (Vmops.pcidevs_of_vm ~__context ~vm); + Vmops.restore ~__context ~xc ~xs ~self:vm start_paused; (* VM is now resident here *) let localhost = Helpers.get_localhost ~__context in