# HG changeset patch # User David Scott # Date 1279905520 -3600 # Node ID 3bf70773971b9906e4d7355049b2cf7a88a13b23 # Parent 2580868fdc435e5c729f98bd707d018dafd3dc2a CA-43021: hook in 'sparse_dd' for improved VM.copy performance On local LVHD, VM.copies of freshly installed guests are much quicker: Guest Previous VM.copy time New VM.copy time Speedup ---------------------------------------------------------------- Debian Lenny 2:11 1:18 40% Windows 7 14:18 7:57 44% Signed-off-by: David Scott diff -r 2580868fdc43 -r 3bf70773971b ocaml/xapi/sm_fs_ops.ml --- a/ocaml/xapi/sm_fs_ops.ml Fri Jul 23 18:18:39 2010 +0100 +++ b/ocaml/xapi/sm_fs_ops.ml Fri Jul 23 18:18:40 2010 +0100 @@ -138,59 +138,48 @@ raise e ) -exception Cancelled -exception NonZero - -(* dd with sparseness check *) -let sparse_dd refresh_session ~__context sparse ifd ofd size bs = - let round v = int_of_float (v *. 50.0) in - let update = - let oldvalue = ref (-1.0) in - fun value -> - if round value <> round !oldvalue then begin - TaskHelper.exn_if_cancelling ~__context; - TaskHelper.operate_on_db_task ~__context - (fun self -> - Db.Task.set_progress ~__context ~self ~value); - end; - oldvalue := value - in - - let buf = String.create bs in - - let allzero s n = - try - for i=0 to n-1 do - if s.[i] <> '\000' then raise NonZero - done; - true - with NonZero -> false - in - - let rec do_block offset = - refresh_session (); - - update ((Int64.to_float offset) /. (Int64.to_float size)); - let remaining = Int64.sub size offset in - if remaining=0L - then () (* EOF *) - else - begin - let this_chunk = Int64.to_int (min remaining (Int64.of_int bs)) in - Unixext.really_read ifd buf 0 this_chunk; - begin - if sparse && (allzero buf this_chunk) - then - ignore(Unix.LargeFile.lseek ofd (Int64.of_int this_chunk) Unix.SEEK_CUR) - else - let n = Unix.write ofd buf 0 this_chunk in - (if n y <> x) !to_close) in + finally + (fun () -> + match Forkhelpers.with_logfile_fd "sparse_dd" + (fun log_fd -> + let pid = Forkhelpers.safe_close_and_exec None (Some pipe_write) (Some log_fd) [] + "/opt/xensource/libexec/sparse_dd" + ([ "-machine"; "-src"; infile; "-dest"; outfile; "-size"; Int64.to_string size ] @ + (if prezeroed then [ "-prezeroed" ] else [])) in + close pipe_write; + (* Read Progress: output from the binary *) + let buf = String.create 128 in + let finished = ref false in + while not (!finished) do + let n = Unix.read pipe_read buf 0 (String.length buf) in + if n = 0 then finished := true else debug "sparse_dd: %s" (String.sub buf 0 n); + try + Scanf.sscanf (String.sub buf 0 n) "Progress: %d" + (fun progress -> + TaskHelper.exn_if_cancelling ~__context; + TaskHelper.operate_on_db_task ~__context + (fun self -> Db.Task.set_progress ~__context ~self ~value:(float_of_int progress /. 100.)) + ) + with _ -> () + done; + match Forkhelpers.waitpid pid with + | (_, Unix.WEXITED 0) -> () + | (_, Unix.WEXITED n) -> error "sparse_dd exit: %d" n; failwith "sparse_dd" + ) with + | Forkhelpers.Success _ -> () + | Forkhelpers.Failure (log, exn) -> + error "Failure from sparse_dd: %s" log; + raise exn + ) + (fun () -> + close pipe_read; + close pipe_write) + (* SCTX-286: thin provisioning is thrown away over VDI.copy, VM.import(VM.export). Return true if the newly created vdi must have zeroes written into it; default to false @@ -226,38 +215,18 @@ let copy_vdi ~__context vdi_src vdi_dst = TaskHelper.set_cancellable ~__context; Helpers.call_api_functions ~__context (fun rpc session_id -> - let refresh_session = Xapi_session.consider_touching_session rpc session_id in (* Use the sparse copy unless we must write zeroes into the new VDI *) let sparse = not (must_write_zeroes_into_new_vdi ~__context vdi_dst) in let size = Db.VDI.get_virtual_size ~__context ~self:vdi_src in - let blocksize = 1024*1024 in - - debug "Sm_fs_ops.copy_vdi: copying %Ld in blocks of %d%s preserving sparseness" size blocksize (if sparse then "" else " NOT"); - - let dd = sparse_dd refresh_session ~__context sparse in with_block_attached_device __context rpc session_id vdi_src `RO (fun device_src -> with_block_attached_device __context rpc session_id vdi_dst `RW (fun device_dst -> - let ifd=Unix.openfile device_src [Unix.O_RDONLY] 0o600 - and ofd=Unix.openfile device_dst [Unix.O_WRONLY; Unix.O_SYNC] 0o600 in - finally - (fun () -> - try - dd ifd ofd size blocksize; - with - | Unix.Unix_error(Unix.EIO, _, _) -> - raise (Api_errors.Server_error (Api_errors.vdi_io_error, ["Device I/O error"])) - | e -> - debug "Caught exception %s" (ExnHelper.string_of_exn e); - log_backtrace ()) - (fun () -> - Unix.close ifd; - Unix.close ofd) + sparse_dd_new ~__context sparse device_src device_dst size; ) ) )