From 5458450dc996c2061a8bb0219435da726788eeca Mon Sep 17 00:00:00 2001 From: Thomas Sanders Date: Mon, 13 Jan 2014 13:58:46 +0000 Subject: [PATCH] CP-7327 nice and ionice config options for sparse_dd.conf Signed-off-by: Thomas Sanders (cherry picked from commit 1614b17016d2122ef0661c312648559ea67ba874) --- src/sparse_dd.conf | 19 ++++++++++ src/sparse_dd.ml | 86 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/sparse_dd.conf b/src/sparse_dd.conf index 71240f6..c659ae5 100644 --- a/src/sparse_dd.conf +++ b/src/sparse_dd.conf @@ -10,6 +10,25 @@ # user: do what the user asks # encryption-mode = user +# Priority settings for sparse_dd + +# Scheduling priority for the process: "niceness" as in the "nice" and +# "renice" commands. An integer in the range -20 (highest priority) +# to 19 (lowest priority, thus nicest to other processes). +# nice = 19 + +# IO scheduling class and priority for use with the "ionice" command. +# See "man ionice" for details, but ionice_class can be one of +# 0: none +# 1: real-time +# 2: best-effort +# 3: idle +# ionice_class = 3 +# If ionice_class is real-time or best-effort, then ionice_class_data +# can be used to specify priority within the class using an integer +# in the range 0 (highest priority) to 7 (lowest). +# ionice_class_data = 7 + # This reads directly from the source VHD file (when possible) # avoiding a round-trip through tapdisk and the kernel. # NB it is unsafe to use this if the files are being coalesced. diff --git a/src/sparse_dd.ml b/src/sparse_dd.ml index 2709623..a7afefb 100644 --- a/src/sparse_dd.ml +++ b/src/sparse_dd.ml @@ -8,6 +8,9 @@ let config_file = "/etc/sparse_dd.conf" let vhd_search_path = "/dev/mapper" +let ionice_cmd = "/usr/bin/ionice" +let renice_cmd = "/usr/bin/renice" + type encryption_mode = | Always | Never @@ -23,6 +26,11 @@ let encryption_mode_of_string = function | x -> failwith (Printf.sprintf "Unknown encryption mode %s. Use always, never or user." x) let encryption_mode = ref User +(* Niceness: strings that may or may not be valid ints. *) +let nice = ref None +let ionice_class = ref None +let ionice_class_data = ref None + let base = ref None let src = ref None let dest = ref None @@ -38,9 +46,17 @@ let string_opt = function let machine_readable_progress = ref false -let options = [ +let options = + let str_option name var_ref description = + name, Arg.String (fun x -> var_ref := Some x), (fun () -> string_opt !var_ref), description + in + [ "unbuffered", Arg.Bool (fun b -> File.use_unbuffered := b), (fun () -> string_of_bool !File.use_unbuffered), "use unbuffered I/O via O_DIRECT"; "encryption-mode", Arg.String (fun x -> encryption_mode := encryption_mode_of_string x), (fun () -> string_of_encryption_mode !encryption_mode), "how to use encryption"; + (* Want to ignore bad values for "nice" etc. so not using Arg.Int *) + str_option "nice" nice "If supplied, the scheduling priority will be set using this value as argument to the 'nice' command."; + str_option "ionice_class" ionice_class "If supplied, the io scheduling class will be set using this value as -c argument to the 'ionice' command."; + str_option "ionice_class_data" ionice_class_data "If supplied, the io scheduling class data will be set using this value as -n argument to the 'ionice' command."; "experimental-reads-bypass-tapdisk", Arg.Set experimental_reads_bypass_tapdisk, (fun () -> string_of_bool !experimental_reads_bypass_tapdisk), "bypass tapdisk and read directly from the underlying vhd file"; "experimental-writes-bypass-tapdisk", Arg.Set experimental_writes_bypass_tapdisk, (fun () -> string_of_bool !experimental_writes_bypass_tapdisk), "bypass tapdisk and write directly to the underlying vhd file"; "base", Arg.String (fun x -> base := Some x), (fun () -> string_opt !base), "base disk to search for differences from"; @@ -218,6 +234,74 @@ let _ = let size = !size in let base = !base in + (* Helper function to bring an int into valid range *) + let clip v min max descr = + if v < min then ( + warn "Value %d is too low for %s. Using %d instead." v descr min; + min + ) else if v > max then ( + warn "Value %d is too high for %s. Using %d instead." v descr max; + max + ) else v + in + + ( + let parse_as_int str_opt int_opt_ref opt_name = + match str_opt with + | None -> () + | Some str -> + try + int_opt_ref := Some (int_of_string str) + with _ -> + error "Ignoring invalid value for %s: %s" opt_name str + in + + (* renice this process if specified *) + let n_ref = ref None in + parse_as_int !nice n_ref "nice"; + (match !n_ref with + | None -> () + | Some n -> ( + (* Run command like: renice -n priority -p pid *) + let n = clip n (-20) 19 "nice" in + let pid = string_of_int (Unix.getpid ()) in + let (stdout, stderr) = Forkhelpers.execute_command_get_output renice_cmd ["-n"; string_of_int n; "-p"; pid] + in () + ) + ); + + (* Possibly run command like: ionice -c class -n classdata -p pid *) + let c_ref = ref None in + let cd_ref = ref None in + parse_as_int !ionice_class c_ref "ionice_class"; + parse_as_int !ionice_class_data cd_ref "ionice_class_data"; + + match !c_ref with + | None -> () + | Some c -> + let pid = string_of_int (Unix.getpid ()) in + let ionice args = + let (stdout, stderr) = Forkhelpers.execute_command_get_output ionice_cmd args + in () + in + let class_only c = + ionice ["-c"; string_of_int c; "-p"; pid] + in + let class_and_data c n = + ionice ["-c"; string_of_int c; "-n"; string_of_int n; "-p"; pid] + in + match c with + | 0 | 3 -> + class_only c + | 1 | 2 -> ( + match !cd_ref with + | None -> class_only c + | Some n -> + let n = clip n 0 7 "ionice classdata" in + class_and_data c n) + | _ -> error "Cannot use ionice due to invalid class value: %d" c + ); + debug "src = %s; dest = %s; base = %s; size = %Ld" src dest (Opt.default "None" base) size; let src_vhd = vhd_of_device src in let dest_vhd = vhd_of_device dest in