Blame SOURCES/0028-output-Permit-output-modes-to-wait-on-the-local-NBD-.patch

c1a9fa
From 96efdcf54c887ae88d54332df12a5f5dd962fd0a Mon Sep 17 00:00:00 2001
c1a9fa
From: "Richard W.M. Jones" <rjones@redhat.com>
c1a9fa
Date: Fri, 15 Jul 2022 11:25:45 +0100
c1a9fa
Subject: [PATCH] output: Permit output modes to wait on the local NBD server
c1a9fa
c1a9fa
Output.output_to_local_file is used by several output modes that write
c1a9fa
to local files or devices.  It launches an instance of qemu-nbd or
c1a9fa
nbdkit connected to the local file.
c1a9fa
c1a9fa
Previously we unconditionally added an On_exit handler to kill the NBD
c1a9fa
server.  This is usually safe because nbdcopy --flush has guaranteed
c1a9fa
that the data was written through to permanent storage, and so killing
c1a9fa
the NBD server is just there to prevent orphaned processes.
c1a9fa
c1a9fa
However for output to RHV (-o rhv) we actually need the NBD server to
c1a9fa
be cleaned up before we exit.  See the analysis here:
c1a9fa
c1a9fa
https://bugzilla.redhat.com/show_bug.cgi?id=1953286#c26
c1a9fa
c1a9fa
Allow an alternate strategy of waiting for the NBD server to exit
c1a9fa
during virt-v2v shutdown.
c1a9fa
c1a9fa
We only need this in virt-v2v so implement it here instead of pushing
c1a9fa
it all the way into the On_exit module.
c1a9fa
c1a9fa
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
c1a9fa
(cherry picked from commit e2a1a7b4dfb6a9e44260da10a7e7029c09753b5c)
c1a9fa
---
c1a9fa
 output/output.ml  | 91 ++++++++++++++++++++++++++++-------------------
c1a9fa
 output/output.mli | 17 +++++++--
c1a9fa
 2 files changed, 69 insertions(+), 39 deletions(-)
c1a9fa
c1a9fa
diff --git a/output/output.ml b/output/output.ml
c1a9fa
index 496c32b6..8f83a324 100644
c1a9fa
--- a/output/output.ml
c1a9fa
+++ b/output/output.ml
c1a9fa
@@ -69,7 +69,10 @@ let error_if_disk_count_gt dir n =
c1a9fa
   if Sys.file_exists socket then
c1a9fa
     error (f_"this output module doesn't support copying more than %d disks") n
c1a9fa
 
c1a9fa
+type on_exit_kill = Kill | KillAndWait
c1a9fa
+
c1a9fa
 let output_to_local_file ?(changeuid = fun f -> f ()) ?(compressed = false)
c1a9fa
+      ?(on_exit_kill = Kill)
c1a9fa
       output_alloc output_format filename size socket =
c1a9fa
   (* Check nbdkit is installed and has the required plugin. *)
c1a9fa
   if not (Nbdkit.is_installed ()) then
c1a9fa
@@ -94,46 +97,60 @@ let output_to_local_file ?(changeuid = fun f -> f ()) ?(compressed = false)
c1a9fa
     fun () -> g#disk_create ?preallocation filename output_format size
c1a9fa
   );
c1a9fa
 
c1a9fa
-  match output_format with
c1a9fa
-  | "raw" ->
c1a9fa
-     let cmd = Nbdkit.create "file" in
c1a9fa
-     Nbdkit.add_arg cmd "file" filename;
c1a9fa
-     if Nbdkit.version nbdkit_config >= (1, 22, 0) then (
c1a9fa
-       let cmd = Nbdkit.add_arg cmd "cache" "none" in
c1a9fa
-       cmd
c1a9fa
-     );
c1a9fa
-     let _, pid = Nbdkit.run_unix socket cmd in
c1a9fa
+  let pid =
c1a9fa
+    match output_format with
c1a9fa
+    | "raw" ->
c1a9fa
+       let cmd = Nbdkit.create "file" in
c1a9fa
+       Nbdkit.add_arg cmd "file" filename;
c1a9fa
+       if Nbdkit.version nbdkit_config >= (1, 22, 0) then (
c1a9fa
+         let cmd = Nbdkit.add_arg cmd "cache" "none" in
c1a9fa
+         cmd
c1a9fa
+       );
c1a9fa
+       let _, pid = Nbdkit.run_unix socket cmd in
c1a9fa
+       pid
c1a9fa
 
c1a9fa
-     (* --exit-with-parent should ensure nbdkit is cleaned
c1a9fa
-      * up when we exit, but it's not supported everywhere.
c1a9fa
-      *)
c1a9fa
-     On_exit.kill pid
c1a9fa
+    | "qcow2" ->
c1a9fa
+       let cmd =
c1a9fa
+         if compressed then (
c1a9fa
+           let qemu_quote str = String.replace str "," ",," in
c1a9fa
+           let image_opts = [ "driver=compress";
c1a9fa
+                              "file.driver=qcow2";
c1a9fa
+                              "file.file.driver=file";
c1a9fa
+                              "file.file.filename=" ^ qemu_quote filename ] in
c1a9fa
+           let image_opts = String.concat "," image_opts in
c1a9fa
+           let cmd = QemuNBD.create image_opts in
c1a9fa
+           QemuNBD.set_image_opts cmd true;
c1a9fa
+           cmd
c1a9fa
+         )
c1a9fa
+         else (* not compressed *) (
c1a9fa
+           let cmd = QemuNBD.create filename in
c1a9fa
+           QemuNBD.set_format cmd (Some "qcow2");
c1a9fa
+           cmd
c1a9fa
+         ) in
c1a9fa
+       QemuNBD.set_snapshot cmd false;
c1a9fa
+       let _, pid = QemuNBD.run_unix socket cmd in
c1a9fa
+       pid
c1a9fa
 
c1a9fa
-  | "qcow2" ->
c1a9fa
-     let cmd =
c1a9fa
-       if compressed then (
c1a9fa
-         let qemu_quote str = String.replace str "," ",," in
c1a9fa
-         let image_opts = [ "driver=compress";
c1a9fa
-                            "file.driver=qcow2";
c1a9fa
-                            "file.file.driver=file";
c1a9fa
-                            "file.file.filename=" ^ qemu_quote filename ] in
c1a9fa
-         let image_opts = String.concat "," image_opts in
c1a9fa
-         let cmd = QemuNBD.create image_opts in
c1a9fa
-         QemuNBD.set_image_opts cmd true;
c1a9fa
-         cmd
c1a9fa
-       )
c1a9fa
-       else (* not compressed *) (
c1a9fa
-         let cmd = QemuNBD.create filename in
c1a9fa
-         QemuNBD.set_format cmd (Some "qcow2");
c1a9fa
-         cmd
c1a9fa
-       ) in
c1a9fa
-     QemuNBD.set_snapshot cmd false;
c1a9fa
-     let _, pid = QemuNBD.run_unix socket cmd in
c1a9fa
-     On_exit.kill pid
c1a9fa
+    | _ ->
c1a9fa
+       error (f_"output mode only supports raw or qcow2 format (format: %s)")
c1a9fa
+         output_format in
c1a9fa
+
c1a9fa
+  match on_exit_kill with
c1a9fa
+  | Kill ->
c1a9fa
+    (* Kill the NBD server on exit.  (For nbdkit we use --exit-with-parent
c1a9fa
+     * but it's not supported everywhere).
c1a9fa
+     *)
c1a9fa
+    On_exit.kill pid
c1a9fa
 
c1a9fa
-  | _ ->
c1a9fa
-     error (f_"output mode only supports raw or qcow2 format (format: %s)")
c1a9fa
-       output_format
c1a9fa
+  | KillAndWait ->
c1a9fa
+     On_exit.f (
c1a9fa
+       fun () ->
c1a9fa
+         kill pid Sys.sigterm;
c1a9fa
+         (* Errors from the NBD server don't matter.  On successful
c1a9fa
+          * completion we've already committed the data to disk.
c1a9fa
+          *)
c1a9fa
+         ignore (waitpid [] pid)
c1a9fa
+     )
c1a9fa
 
c1a9fa
 let disk_path os name i =
c1a9fa
   let outdisk = sprintf "%s/%s-sd%s" os name (drive_name i) in
c1a9fa
diff --git a/output/output.mli b/output/output.mli
c1a9fa
index c1f0f53d..c4486311 100644
c1a9fa
--- a/output/output.mli
c1a9fa
+++ b/output/output.mli
c1a9fa
@@ -83,14 +83,27 @@ val error_if_disk_count_gt : string -> int -> unit
c1a9fa
     "in[n]" in the v2v directory [dir].  If the socket exists, [error] is
c1a9fa
     called. *)
c1a9fa
 
c1a9fa
+type on_exit_kill = Kill | KillAndWait
c1a9fa
+
c1a9fa
 val output_to_local_file : ?changeuid:((unit -> unit) -> unit) ->
c1a9fa
-                           ?compressed:bool ->
c1a9fa
+                           ?compressed:bool -> ?on_exit_kill:on_exit_kill ->
c1a9fa
                            Types.output_allocation ->
c1a9fa
                            string -> string -> int64 -> string ->
c1a9fa
                            unit
c1a9fa
 (** When an output mode wants to create a local file with a
c1a9fa
     particular format (only "raw" or "qcow2" allowed) then
c1a9fa
-    this common function can be used. *)
c1a9fa
+    this common function can be used.
c1a9fa
+
c1a9fa
+    Optional parameter [?on_exit_kill] controls how the NBD server
c1a9fa
+    is cleaned up.  The default is {!Kill} which registers an
c1a9fa
+    {!On_exit.kill} handler that kills (but does not wait for)
c1a9fa
+    the server when virt-v2v exits.  Most callers should use this.
c1a9fa
+
c1a9fa
+    Setting [~on_exit_kill:KillAndWait] should be used if the NBD
c1a9fa
+    server must fully exit before we continue with the rest of
c1a9fa
+    virt-v2v shut down.  This is only necessary if some other action
c1a9fa
+    (such as unmounting a host filesystem or removing a host device)
c1a9fa
+    depends on the NBD server releasing resources. *)
c1a9fa
 
c1a9fa
 val disk_path : string -> string -> int -> string
c1a9fa
 (** For [-o disk|qemu], return the output disk name of the i'th disk,