On Mon, Sep 05, 2022 at 01:25:27PM +0200, Laszlo Ersek wrote:
> This concern arises because in the child process, only the one thread
> returning from fork() exists, thus if any locks are held by other threads
> in the parent process, those will never be released in the child process.
> 
> But such a fork() should be safe: existent code already starts e.g. nbdkit
> on the same call path, namely start_conversion_thread() ->
> start_conversion() -> start_nbd_server() -> start_nbdkit() -> fork().)

Yes, I think this ought to be safe.  The thread model of virt-p2v is
relatively simple.

> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1590721
> Signed-off-by: Laszlo Ersek <ler...@redhat.com>
> ---
>  generate-p2v-config.pl   | 45 ++++++++--------
>  p2v.h                    |  6 +++
>  cpuid.c                  | 39 ++++++++------
>  gui.c                    |  4 +-
>  main.c                   |  8 +--
>  physical-xml.c           | 54 ++++++++++----------
>  test-virt-p2v-cmdline.sh |  5 +-
>  7 files changed, 88 insertions(+), 73 deletions(-)
> 
> diff --git a/generate-p2v-config.pl b/generate-p2v-config.pl
> index 06a2e2c8b0e4..5c33f814e3e7 100755
> --- a/generate-p2v-config.pl
> +++ b/generate-p2v-config.pl
> @@ -110,16 +110,19 @@ my @fields = [
>      ],
>    ),
>    ConfigString->new(name => 'guestname'),
> -  ConfigInt->new(name => 'vcpus', value => 0),
> +  ConfigSection->new(
> +    name => 'vcpu',
> +    elements => [
> +      ConfigBool->new(name => 'dense_topo'),
> +      ConfigInt->new(name => 'cores', value => 0),
> +    ],
> +  ),
>    ConfigUInt64->new(name => 'memory'),
>    ConfigSection->new(
>      name => 'cpu',
>      elements => [
>        ConfigString->new(name => 'vendor'),
>        ConfigString->new(name => 'model'),
> -      ConfigUnsigned->new(name => 'sockets'),
> -      ConfigUnsigned->new(name => 'cores'),
> -      ConfigUnsigned->new(name => 'threads'),
>        ConfigBool->new(name => 'acpi'),
>        ConfigBool->new(name => 'apic'),
>        ConfigBool->new(name => 'pae'),
> @@ -231,11 +234,21 @@ The name of the guest that is created.  The default is 
> to try to
>  derive a name from the physical machine’s hostname (if possible) else
>  use a randomly generated name.",
>    ),
> -  "p2v.vcpus" => manual_entry->new(
> +  "p2v.vcpu.dense_topo" => manual_entry->new(
> +    shortopt => "", # ignored for booleans
> +    description => "
> +Copy the physical machine's CPU topology, densely populated, to the
> +guest.  Disabled by default.  If disabled, the C<p2v.vcpu.cores> setting
> +takes effect.",
> +  ),
> +  "p2v.vcpu.cores" => manual_entry->new(
>      shortopt => "N",
>      description => "
> -The number of virtual CPUs to give to the guest.  The default is to
> -use the same as the number of physical CPUs.",
> +This setting is ignored if C<p2v.vcpu.dense_topo> is enabled.
> +Otherwise, it specifies the flat number of vCPU cores to give to the
> +guest (placing all of those cores into a single socket, and exposing one
> +thread per core).  The default value is the number of online logical
> +processors on the physical machine.",
>    ),
>    "p2v.memory" => manual_entry->new(
>      shortopt => "n(M|G)",
> @@ -258,24 +271,6 @@ the same CPU vendor as the physical machine.",
>      description => "
>  The vCPU model, eg. \"IvyBridge\".  The default is to use the same
>  CPU model as the physical machine.",
> -  ),
> -  "p2v.cpu.sockets" => manual_entry->new(
> -    shortopt => "N",
> -    description => "
> -Number of vCPU sockets to use.  The default is to use the same as the
> -physical machine.",
> -  ),
> -  "p2v.cpu.cores" => manual_entry->new(
> -    shortopt => "N",
> -    description => "
> -Number of vCPU cores to use.  The default is to use the same as the
> -physical machine.",
> -  ),
> -  "p2v.cpu.threads" => manual_entry->new(
> -    shortopt => "N",
> -    description => "
> -Number of vCPU hyperthreads to use.  The default is to use the same
> -as the physical machine.",
>    ),
>    "p2v.cpu.acpi" => manual_entry->new(
>      shortopt => "", # ignored for booleans
> diff --git a/p2v.h b/p2v.h
> index 20c4ac7e506a..32dc55d94de5 100644
> --- a/p2v.h
> +++ b/p2v.h
> @@ -60,6 +60,12 @@ extern int feature_colours_option;
>  extern int force_colour;
>  
>  /* cpuid.c */
> +struct cpu_topo {
> +  unsigned sockets;
> +  unsigned cores;
> +  unsigned threads;
> +};
> +extern void get_cpu_topology (struct cpu_topo *topo);
>  extern void get_cpu_config (struct cpu_config *);
>  
>  /* rtc.c */
> diff --git a/cpuid.c b/cpuid.c
> index 84603bbb3374..31a1720dc23c 100644
> --- a/cpuid.c
> +++ b/cpuid.c
> @@ -154,22 +154,32 @@ get_vendor (char **lscpu, struct cpu_config *cpu)
>  }
>  
>  /**
> - * Read the CPU topology from lscpu output.
> + * Read the CPU topology from a separate lscpu invocation.
>   */
> -static void
> -get_topology (char **lscpu, struct cpu_config *cpu)
> +void
> +get_cpu_topology (struct cpu_topo *topo)
>  {
> -  const char *v;
> -
> -  v = get_field (lscpu, "Socket(s)");
> -  if (v)
> -    ignore_value (sscanf (v, "%u", &cpu->sockets));
> -  v = get_field (lscpu, "Core(s) per socket");
> -  if (v)
> -    ignore_value (sscanf (v, "%u", &cpu->cores));
> -  v = get_field (lscpu, "Thread(s) per core");
> -  if (v)
> -    ignore_value (sscanf (v, "%u", &cpu->threads));
> +
> +  CLEANUP_FREE_STRING_LIST char **lscpu = NULL;
> +
> +  lscpu = get_lscpu ();
> +
> +  if (lscpu != NULL) {
> +    const char *sockets, *cores, *threads;
> +
> +    sockets = get_field (lscpu, "Socket(s)");
> +    cores = get_field (lscpu, "Core(s) per socket");
> +    threads = get_field (lscpu, "Thread(s) per core");
> +    if (sockets && cores && threads) {
> +      ignore_value (sscanf (sockets, "%u", &topo->sockets));
> +      ignore_value (sscanf (cores, "%u", &topo->cores));
> +      ignore_value (sscanf (threads, "%u", &topo->threads));
> +      return;
> +    }
> +  }
> +  topo->sockets = 1;
> +  topo->cores = 1;
> +  topo->threads = 1;
>  }
>  
>  /**
> @@ -211,7 +221,6 @@ get_cpu_config (struct cpu_config *cpu)
>    lscpu = get_lscpu ();
>    if (lscpu != NULL) {
>      get_vendor (lscpu, cpu);
> -    get_topology (lscpu, cpu);
>      get_flags (lscpu, cpu);
>    }
>  
> diff --git a/gui.c b/gui.c
> index 5c4f1343095a..b4a9fed18410 100644
> --- a/gui.c
> +++ b/gui.c
> @@ -767,7 +767,7 @@ create_conversion_dialog (struct config *config)
>    set_alignment (vcpus_label, 1., 0.5);
>    vcpus_entry = gtk_entry_new ();
>    gtk_label_set_mnemonic_widget (GTK_LABEL (vcpus_label), vcpus_entry);
> -  snprintf (vcpus_str, sizeof vcpus_str, "%d", config->vcpus);
> +  snprintf (vcpus_str, sizeof vcpus_str, "%d", config->vcpu.cores);
>    gtk_entry_set_text (GTK_ENTRY (vcpus_entry), vcpus_str);
>    table_attach (target_tbl, vcpus_entry,
>                  1, 2, 1, 2, GTK_FILL, GTK_FILL, 1, 1);
> @@ -2010,7 +2010,7 @@ start_conversion_clicked (GtkWidget *w, gpointer data)
>      return;
>    }
>  
> -  config->vcpus = get_vcpus_from_conv_dlg ();
> +  config->vcpu.cores = get_vcpus_from_conv_dlg ();
>    config->memory = get_memory_from_conv_dlg ();
>  
>    /* Get the list of disks to be converted. */
> diff --git a/main.c b/main.c
> index 0ebb7291c7ce..6ac94fcb5f8e 100644
> --- a/main.c
> +++ b/main.c
> @@ -291,16 +291,18 @@ set_config_defaults (struct config *config)
>    }
>    config->guestname = strdup (hostname);
>  
> +  config->vcpu.dense_topo = false;
> +
>    /* Defaults for #vcpus and memory are taken from the physical machine. */
>    i = sysconf (_SC_NPROCESSORS_ONLN);
>    if (i == -1) {
>      perror ("sysconf: _SC_NPROCESSORS_ONLN");
> -    config->vcpus = 1;
> +    config->vcpu.cores = 1;
>    }
>    else if (i == 0)
> -    config->vcpus = 1;
> +    config->vcpu.cores = 1;
>    else
> -    config->vcpus = i;
> +    config->vcpu.cores = i;
>  
>    i = sysconf (_SC_PHYS_PAGES);
>    if (i == -1) {
> diff --git a/physical-xml.c b/physical-xml.c
> index 4e830ea7ee0c..c27869de1c35 100644
> --- a/physical-xml.c
> +++ b/physical-xml.c
> @@ -62,6 +62,7 @@ generate_physical_xml (struct config *config, struct 
> data_conn *data_conns,
>    uint64_t memkb;
>    CLEANUP_XMLFREETEXTWRITER xmlTextWriterPtr xo = NULL;
>    size_t i;
> +  struct cpu_topo topo;
>  
>    xo = xmlNewTextWriterFilename (filename, 0);
>    if (xo == NULL)
> @@ -104,34 +105,35 @@ generate_physical_xml (struct config *config, struct 
> data_conn *data_conns,
>        string_format ("%" PRIu64, memkb);
>      } end_element ();
>  
> -    single_element_format ("vcpu", "%d", config->vcpus);
> -
> -    if (config->cpu.vendor || config->cpu.model ||
> -        config->cpu.sockets || config->cpu.cores || config->cpu.threads) {
> -      /* https://libvirt.org/formatdomain.html#elementsCPU */
> -      start_element ("cpu") {
> -        attribute ("match", "minimum");
> -        if (config->cpu.vendor)
> -          single_element ("vendor", config->cpu.vendor);
> -        if (config->cpu.model) {
> -          start_element ("model") {
> -            attribute ("fallback", "allow");
> -            string (config->cpu.model);
> -          } end_element ();
> -        }
> -        if (config->cpu.sockets || config->cpu.cores || config->cpu.threads) 
> {
> -          start_element ("topology") {
> -            if (config->cpu.sockets)
> -              attribute_format ("sockets", "%u", config->cpu.sockets);
> -            if (config->cpu.cores)
> -              attribute_format ("cores", "%u", config->cpu.cores);
> -            if (config->cpu.threads)
> -              attribute_format ("threads", "%u", config->cpu.threads);
> -          } end_element ();
> -        }
> -      } end_element ();
> +    if (config->vcpu.dense_topo)
> +      get_cpu_topology (&topo);
> +    else {
> +      topo.sockets = 1;
> +      topo.cores = config->vcpu.cores;
> +      topo.threads = 1;
>      }
>  
> +    single_element_format ("vcpu", "%u",
> +                           topo.sockets * topo.cores * topo.threads);
> +
> +    /* https://libvirt.org/formatdomain.html#elementsCPU */
> +    start_element ("cpu") {
> +      attribute ("match", "minimum");
> +      if (config->cpu.vendor)
> +        single_element ("vendor", config->cpu.vendor);
> +      if (config->cpu.model) {
> +        start_element ("model") {
> +          attribute ("fallback", "allow");
> +          string (config->cpu.model);
> +        } end_element ();
> +      }
> +      start_element ("topology") {
> +        attribute_format ("sockets", "%u", topo.sockets);
> +        attribute_format ("cores", "%u", topo.cores);
> +        attribute_format ("threads", "%u", topo.threads);
> +      } end_element ();
> +    } end_element ();
> +
>      switch (config->rtc.basis) {
>      case BASIS_UNKNOWN:
>        /* Don't emit any <clock> element. */
> diff --git a/test-virt-p2v-cmdline.sh b/test-virt-p2v-cmdline.sh
> index cd76044195a3..37dc1662a593 100755
> --- a/test-virt-p2v-cmdline.sh
> +++ b/test-virt-p2v-cmdline.sh
> @@ -26,7 +26,7 @@ out=test-virt-p2v-cmdline.out
>  rm -f $out
>  
>  # The Linux kernel command line.
> -$VG virt-p2v --cmdline='p2v.server=localhost p2v.port=123 p2v.username=user 
> p2v.password=secret p2v.skip_test_connection p2v.name=test p2v.vcpus=4 
> p2v.memory=1G p2v.disks=sda,sdb,sdc p2v.removable=sdd 
> p2v.interfaces=eth0,eth1 p2v.o=local p2v.oa=sparse p2v.oc=qemu:///session 
> p2v.of=raw p2v.os=/var/tmp p2v.network=em1:wired,other 
> p2v.dump_config_and_exit' > $out
> +$VG virt-p2v --cmdline='p2v.server=localhost p2v.port=123 p2v.username=user 
> p2v.password=secret p2v.skip_test_connection p2v.name=test p2v.vcpu.cores=4 
> p2v.memory=1G p2v.disks=sda,sdb,sdc p2v.removable=sdd 
> p2v.interfaces=eth0,eth1 p2v.o=local p2v.oa=sparse p2v.oc=qemu:///session 
> p2v.of=raw p2v.os=/var/tmp p2v.network=em1:wired,other 
> p2v.dump_config_and_exit' > $out
>  
>  # For debugging purposes.
>  cat $out
> @@ -37,7 +37,8 @@ grep "^remote\.port.*123" $out
>  grep "^auth\.username.*user" $out
>  grep "^auth\.sudo.*false" $out
>  grep "^guestname.*test" $out
> -grep "^vcpus.*4" $out
> +grep "^vcpu.dense_topo.*false" $out
> +grep "^vcpu.cores.*4" $out
>  grep "^memory.*"$((1024*1024*1024)) $out
>  grep "^disks.*sda sdb sdc" $out
>  grep "^removable.*sdd" $out

Reviewed-by: Richard W.M. Jones <rjo...@redhat.com>

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
libguestfs lets you edit virtual machines.  Supports shell scripting,
bindings from many languages.  http://libguestfs.org
_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to