[Libguestfs] [v2v PATCH 1/9] types: introduce the "gcaps_default_cpu" field

Laszlo Ersek
2022-04-20T16:23:25Z

Conversion will set "gcaps_default_cpu" to true iff the guest OS is
capable of running on QEMU's default VCPU model.

This capability will only matter for the QEMU and libvirt output modules,
where, in case the capability is false *and* the source hypervisor does
not specify a VCPU model, we'll have to manually present the guest OS with
a VCPU that looks as close as possible to a physical CPU.  (In that case,
we'll specify host-passthrough.)

The management applications targeted by the RHV and OpenStack output
modules have their own explicit VCPU defaults, overriding the QEMU default
model even in case the source hypervisor does not specify a VCPU model;
those modules will ignore this capability therefore.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2076013
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
 lib/types.mli              | 14 ++++++++++++++
 convert/convert_linux.ml   |  1 +
 convert/convert_windows.ml |  1 +
 lib/types.ml               |  3 +++
 4 files changed, 19 insertions(+)

diff --git a/lib/types.mli b/lib/types.mli
index 37238cd75519..87544a0c59eb 100644
--- a/lib/types.mli
+++ b/lib/types.mli
@@ -251,52 +251,66 @@ val string_of_target_firmware : target_firmware ->
string

 (** {2 Target NICs} *)

 type target_nics = source_nic list

 (** {2 Guest capabilities} *)

 type guestcaps = {
   gcaps_block_bus : guestcaps_block_type;
   gcaps_net_bus : guestcaps_net_type;
   (** Best block device and network device guest can access.  These are
       determined during conversion by inspecting the guest (and in some cases
       conversion can actually enhance these by installing drivers).  Thus this
       is not known until after conversion. *)

   gcaps_virtio_rng : bool;      (** Guest supports virtio-rng. *)
   gcaps_virtio_balloon : bool;  (** Guest supports virtio balloon. *)
   gcaps_isa_pvpanic : bool;     (** Guest supports ISA pvpanic device. *)
   gcaps_virtio_socket : bool;   (** Guest supports virtio socket. *)
   gcaps_machine : guestcaps_machine; (** Machine model. *)
   gcaps_arch : string;          (** Architecture that KVM must emulate. *)
   gcaps_acpi : bool;            (** True if guest supports acpi. *)

   gcaps_virtio_1_0 : bool;
   (** The guest supports the virtio devices that it does at the virtio-1.0
       protocol level. *)
+
+  gcaps_default_cpu : bool;
+  (** True iff the guest OS is capable of running on QEMU's default VCPU
model.
+
+      This capability only matters for the QEMU and libvirt output modules,
+      where, in case the capability is false *and* the source hypervisor does
+      not specify a VCPU model, we must manually present the guest OS with a
+      VCPU that looks as close as possible to a physical CPU.  (In that case,
we
+      specify host-passthrough.)
+
+      The management applications targeted by the RHV and OpenStack output
+      modules have their own explicit VCPU defaults, overriding the QEMU
default
+      model even in case the source hypervisor does not specify a VCPU model;
+      those modules ignore this capability therefore.  Refer to RHBZ#2076013.
*)
 }
 (** Guest capabilities after conversion.  eg. Was virtio found or installed? *)

 and guestcaps_block_type = Virtio_blk | IDE
 and guestcaps_net_type = Virtio_net | E1000 | RTL8139
 and guestcaps_machine = I440FX | Q35 | Virt

 val string_of_guestcaps : guestcaps -> string

 (** {2 Guest buses} *)

 type target_buses = {
   target_virtio_blk_bus : target_bus_slot array;
   target_ide_bus : target_bus_slot array;
   target_scsi_bus : target_bus_slot array;
   target_floppy_bus : target_bus_slot array;
 }
 (** Mapping of fixed and removable disks to buses.

     As shown in the diagram below, there are (currently) four buses
     attached to the target VM.  Each contains a chain of fixed or
     removable disks.  Slots can also be empty.

     We try to assign disks to the same slot number as they would
     occupy on the source, although that is not always possible.

diff --git a/convert/convert_linux.ml b/convert/convert_linux.ml
index c2bbce03637f..93b3922cac60 100644
--- a/convert/convert_linux.ml
+++ b/convert/convert_linux.ml
@@ -151,52 +151,53 @@ let convert (g : G.guestfs) source inspect
keep_serial_console _                if major <= 4 then I440FX else Q35
             | ("sles"|"suse-based"|"opensuse"),
major ->
               if major < 10 then I440FX else Q35
             |
("debian"|"ubuntu"|"linuxmint"|"kalilinux"),
major ->
               if major < 4 then I440FX else Q35

             (* reasonable default for all modern Linux kernels *)
             | _, _ -> Q35
            ), true
         )

       | _ -> Virt, true
     in

     (* Return guest capabilities from the convert () function. *)
     let guestcaps = {
       gcaps_block_bus = block_type;
       gcaps_net_bus = net_type;
       gcaps_virtio_rng = kernel.ki_supports_virtio_rng;
       gcaps_virtio_balloon = kernel.ki_supports_virtio_balloon;
       gcaps_isa_pvpanic = kernel.ki_supports_isa_pvpanic;
       gcaps_virtio_socket = kernel.ki_supports_virtio_socket;
       gcaps_machine = machine;
       gcaps_arch = Utils.kvm_arch inspect.i_arch;
       gcaps_acpi = acpi;
       gcaps_virtio_1_0 = virtio_1_0;
+      gcaps_default_cpu = true;
     } in

     guestcaps

   and augeas_grub_configuration ()      if bootloader#set_augeas_configuration
() then
       Linux.augeas_reload g

   and unconfigure_xen ()      (* Remove kmod-xenpv-* (RHEL 3). *)
     let xenmods        List.filter_map (
         fun { G.app2_name = name } ->
           if name = "kmod-xenpv" || String.is_prefix name
"kmod-xenpv-" then
             Some name
           else
             None
       ) inspect.i_apps in
     Linux.remove g inspect xenmods;

     (* Undo related nastiness if kmod-xenpv was installed. *)
     if xenmods <> [] then (
       (* kmod-xenpv modules may have been manually copied to other kernels.
        * Hunt them down and destroy them.
        *)
       let dirs = g#find "/lib/modules" in
diff --git a/convert/convert_windows.ml b/convert/convert_windows.ml
index b53312a9aba4..34a5044dd338 100644
--- a/convert/convert_windows.ml
+++ b/convert/convert_windows.ml
@@ -282,52 +282,53 @@ let convert (g : G.guestfs) _ inspect _ static_ips
Libosinfo_utils.os_support_of_osinfo_device_list
                (devices @ best_drv_devs) in
            (if q35 then Q35 else I440FX), vio10
          with
          | Not_found ->
            (* Pivot on the year 2007.  Any Windows version from earlier than
             * 2007 should use i440fx, anything 2007 or newer should use q35.
             * Luckily this coincides almost exactly with the release of NT 6.
             *)
            (if inspect.i_major_version < 6 then I440FX else Q35), true
         )
       | _ -> Virt, true
     in

     (* Return guest capabilities from the convert () function. *)
     let guestcaps = {
       gcaps_block_bus = block_driver;
       gcaps_net_bus = net_driver;
       gcaps_virtio_rng = virtio_rng_supported;
       gcaps_virtio_balloon = virtio_ballon_supported;
       gcaps_isa_pvpanic = isa_pvpanic_supported;
       gcaps_virtio_socket = virtio_socket_supported;
       gcaps_machine = machine;
       gcaps_arch = Utils.kvm_arch inspect.i_arch;
       gcaps_acpi = true;
       gcaps_virtio_1_0 = virtio_1_0;
+      gcaps_default_cpu = true;
     } in

     guestcaps

   and configure_firstboot ()      (* Note that pnp_wait.exe must be the first
firstboot script as it
      * suppresses PnP for all following scripts.
      *)
     let tool_path = virt_tools_data_dir () // "pnp_wait.exe" in
     if Sys.file_exists tool_path then
       configure_wait_pnp tool_path
     else
       debug (f_"%s is missing.  Firstboot scripts may conflict with
PnP.")
         tool_path;

     (* Install VMDP unconditionally, if available, but don't
      * warn about it if not.
      *)
     let tool_path = virt_tools_data_dir () // "vmdp.exe" in
     if Sys.file_exists tool_path then
       configure_vmdp tool_path;

     (* Install QEMU Guest Agent unconditionally and warn if missing *)
     let qemu_ga_files = Windows_virtio.copy_qemu_ga g inspect in
     if qemu_ga_files <> [] then
       configure_qemu_ga qemu_ga_files
diff --git a/lib/types.ml b/lib/types.ml
index 92ed0e52513d..7ffb868b1fbf 100644
--- a/lib/types.ml
+++ b/lib/types.ml
@@ -383,92 +383,95 @@ let string_of_target t          target format: %s
 "
     (match t.target_file with
      | TargetFile s -> "[file] " ^ s
      | TargetURI s -> "[qemu] " ^ s)
     t.target_format

 type target_firmware = TargetBIOS | TargetUEFI

 let string_of_target_firmware = function
   | TargetBIOS -> "bios"
   | TargetUEFI -> "uefi"

 type target_nics = source_nic list

 type guestcaps = {
   gcaps_block_bus : guestcaps_block_type;
   gcaps_net_bus : guestcaps_net_type;
   gcaps_virtio_rng : bool;
   gcaps_virtio_balloon : bool;
   gcaps_isa_pvpanic : bool;
   gcaps_virtio_socket : bool;
   gcaps_machine : guestcaps_machine;
   gcaps_arch : string;
   gcaps_acpi : bool;
   gcaps_virtio_1_0 : bool;
+  gcaps_default_cpu : bool;
 }
 and guestcaps_block_type = Virtio_blk | IDE
 and guestcaps_net_type = Virtio_net | E1000 | RTL8139
 and guestcaps_machine = I440FX | Q35 | Virt

 let string_of_block_type = function
   | Virtio_blk -> "virtio-blk"
   | IDE -> "ide"
 let string_of_net_type = function
   | Virtio_net -> "virtio-net"
   | E1000 -> "e1000"
   | RTL8139 -> "rtl8139"
 let string_of_machine = function
   | I440FX -> "i440fx"
   | Q35 -> "q35"
   | Virt -> "virt"

 let string_of_guestcaps gcaps    sprintf "\
            gcaps_block_bus = %s\n\
            gcaps_net_bus = %s\n\
            gcaps_virtio_rng = %b\n\
            gcaps_virtio_balloon = %b\n\
            gcaps_isa_pvpanic = %b\n\
            gcaps_virtio_socket = %b\n\
            gcaps_machine = %s\n\
            gcaps_arch = %s\n\
            gcaps_acpi = %b\n\
            gcaps_virtio_1_0 = %b\n\
+           gcaps_default_cpu = %b\n\
           "
   (string_of_block_type gcaps.gcaps_block_bus)
   (string_of_net_type gcaps.gcaps_net_bus)
   gcaps.gcaps_virtio_rng
   gcaps.gcaps_virtio_balloon
   gcaps.gcaps_isa_pvpanic
   gcaps.gcaps_virtio_socket
   (string_of_machine gcaps.gcaps_machine)
   gcaps.gcaps_arch
   gcaps.gcaps_acpi
   gcaps.gcaps_virtio_1_0
+  gcaps.gcaps_default_cpu

 type target_buses = {
   target_virtio_blk_bus : target_bus_slot array;
   target_ide_bus : target_bus_slot array;
   target_scsi_bus : target_bus_slot array;
   target_floppy_bus : target_bus_slot array;
 }

 and target_bus_slot    | BusSlotEmpty
   | BusSlotDisk of source_disk
   | BusSlotRemovable of source_removable

 let string_of_target_bus_slots bus_name slots    let slots      Array.mapi (
       fun slot_nr slot ->
         sprintf "%s slot %d:\n" bus_name slot_nr ^
           (match slot with
            | BusSlotEmpty -> "\t(slot empty)\n"
            | BusSlotDisk d -> string_of_source_disk d
            | BusSlotRemovable r -> string_of_source_removable r ^
"\n"
           )
     ) slots in
   String.concat "" (Array.to_list slots)

--
2.19.1.3.g30247aa5d201