[pve-devel] [PATCH v7 qemu-server 02/10] Add CPUConfig file and migrate some helpers

Stefan Reiter s.reiter at proxmox.com
Thu Jan 16 16:40:48 CET 2020


The package will be used for custom CPU models as a SectionConfig, hence
the name. For now we simply move some CPU related helper functions and
declarations over from QemuServer to reduce clutter there.

Exports are to avoid changing all call sites, functions have useful
names on their own.

Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
 PVE/QemuServer.pm           | 219 +---------------------------------
 PVE/QemuServer/CPUConfig.pm | 232 ++++++++++++++++++++++++++++++++++++
 PVE/QemuServer/Makefile     |   1 +
 3 files changed, 235 insertions(+), 217 deletions(-)
 create mode 100644 PVE/QemuServer/CPUConfig.pm

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 9ef3b71..63fe25c 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -43,6 +43,7 @@ use PVE::QMPClient;
 use PVE::QemuConfig;
 use PVE::QemuServer::Helpers qw(min_version config_aware_timeout);
 use PVE::QemuServer::Cloudinit;
+use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
 use PVE::QemuServer::Machine;
 use PVE::QemuServer::Memory;
 use PVE::QemuServer::Monitor qw(mon_cmd);
@@ -114,108 +115,6 @@ sub nodename {
     return $nodename_cache;
 }
 
-my $cpu_vendor_list = {
-    # Intel CPUs
-    486 => 'GenuineIntel',
-    pentium => 'GenuineIntel',
-    pentium2  => 'GenuineIntel',
-    pentium3  => 'GenuineIntel',
-    coreduo => 'GenuineIntel',
-    core2duo => 'GenuineIntel',
-    Conroe  => 'GenuineIntel',
-    Penryn  => 'GenuineIntel',
-    Nehalem  => 'GenuineIntel',
-    'Nehalem-IBRS'  => 'GenuineIntel',
-    Westmere => 'GenuineIntel',
-    'Westmere-IBRS' => 'GenuineIntel',
-    SandyBridge => 'GenuineIntel',
-    'SandyBridge-IBRS' => 'GenuineIntel',
-    IvyBridge => 'GenuineIntel',
-    'IvyBridge-IBRS' => 'GenuineIntel',
-    Haswell => 'GenuineIntel',
-    'Haswell-IBRS' => 'GenuineIntel',
-    'Haswell-noTSX' => 'GenuineIntel',
-    'Haswell-noTSX-IBRS' => 'GenuineIntel',
-    Broadwell => 'GenuineIntel',
-    'Broadwell-IBRS' => 'GenuineIntel',
-    'Broadwell-noTSX' => 'GenuineIntel',
-    'Broadwell-noTSX-IBRS' => 'GenuineIntel',
-    'Skylake-Client' => 'GenuineIntel',
-    'Skylake-Client-IBRS' => 'GenuineIntel',
-    'Skylake-Server' => 'GenuineIntel',
-    'Skylake-Server-IBRS' => 'GenuineIntel',
-    'Cascadelake-Server' => 'GenuineIntel',
-    KnightsMill => 'GenuineIntel',
-
-
-    # AMD CPUs
-    athlon => 'AuthenticAMD',
-    phenom  => 'AuthenticAMD',
-    Opteron_G1  => 'AuthenticAMD',
-    Opteron_G2  => 'AuthenticAMD',
-    Opteron_G3  => 'AuthenticAMD',
-    Opteron_G4  => 'AuthenticAMD',
-    Opteron_G5  => 'AuthenticAMD',
-    EPYC => 'AuthenticAMD',
-    'EPYC-IBPB' => 'AuthenticAMD',
-
-    # generic types, use vendor from host node
-    host => 'default',
-    kvm32 => 'default',
-    kvm64 => 'default',
-    qemu32 => 'default',
-    qemu64 => 'default',
-    max => 'default',
-};
-
-my @supported_cpu_flags = (
-    'pcid',
-    'spec-ctrl',
-    'ibpb',
-    'ssbd',
-    'virt-ssbd',
-    'amd-ssbd',
-    'amd-no-ssb',
-    'pdpe1gb',
-    'md-clear',
-    'hv-tlbflush',
-    'hv-evmcs',
-    'aes'
-);
-my $cpu_flag = qr/[+-](@{[join('|', @supported_cpu_flags)]})/;
-
-my $cpu_fmt = {
-    cputype => {
-	description => "Emulated CPU type.",
-	type => 'string',
-	enum => [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ],
-	default => 'kvm64',
-	default_key => 1,
-    },
-    hidden => {
-	description => "Do not identify as a KVM virtual machine.",
-	type => 'boolean',
-	optional => 1,
-	default => 0
-    },
-    'hv-vendor-id' => {
-	type => 'string',
-	pattern => qr/[a-zA-Z0-9]{1,12}/,
-	format_description => 'vendor-id',
-	description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
-	optional => 1,
-    },
-    flags => {
-	description => "List of additional CPU flags separated by ';'."
-		     . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
-		     . " Currently supported flags: @{[join(', ', @supported_cpu_flags)]}.",
-	format_description => '+FLAG[;-FLAG...]',
-	type => 'string',
-	pattern => qr/$cpu_flag(;$cpu_flag)*/,
-	optional => 1,
-    },
-};
-
 my $watchdog_fmt = {
     model => {
 	default_key => 1,
@@ -611,7 +510,7 @@ EODESCR
 	optional => 1,
 	description => "Emulated CPU type.",
 	type => 'string',
-	format => $cpu_fmt,
+	format => $PVE::QemuServer::CPUConfig::cpu_fmt,
     },
     parent => get_standard_option('pve-snapshot-name', {
 	optional => 1,
@@ -2121,26 +2020,6 @@ sub print_netdev_full {
     return $netdev;
 }
 
-
-sub print_cpu_device {
-    my ($conf, $id) = @_;
-
-    my $kvm = $conf->{kvm} // 1;
-    my $cpu = $kvm ? "kvm64" : "qemu64";
-    if (my $cputype = $conf->{cpu}) {
-	my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
-	    or die "Cannot parse cpu description: $cputype\n";
-	$cpu = $cpuconf->{cputype};
-    }
-
-    my $cores = $conf->{cores} || 1;
-
-    my $current_core = ($id - 1) % $cores;
-    my $current_socket = int(($id - 1 - $current_core)/$cores);
-
-    return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
-}
-
 my $vga_map = {
     'cirrus' => 'cirrus-vga',
     'std' => 'VGA',
@@ -3516,61 +3395,6 @@ sub query_understood_cpu_flags {
     return \@flags;
 }
 
-sub get_cpu_options {
-    my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
-
-    my $cpuFlags = [];
-    my $ostype = $conf->{ostype};
-
-    my $cpu = $kvm ? "kvm64" : "qemu64";
-    if ($arch eq 'aarch64') {
-	$cpu = 'cortex-a57';
-    }
-    my $hv_vendor_id;
-    if (my $cputype = $conf->{cpu}) {
-	my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
-	    or die "Cannot parse cpu description: $cputype\n";
-	$cpu = $cpuconf->{cputype};
-	$kvm_off = 1 if $cpuconf->{hidden};
-	$hv_vendor_id = $cpuconf->{'hv-vendor-id'};
-
-	if (defined(my $flags = $cpuconf->{flags})) {
-	    push @$cpuFlags, split(";", $flags);
-	}
-    }
-
-    push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64';
-
-    push @$cpuFlags , '-x2apic' if $ostype && $ostype eq 'solaris';
-
-    push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
-
-    push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
-
-    if (min_version($machine_version, 2, 3) && $arch eq 'x86_64') {
-
-	push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
-	push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
-    }
-
-    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_version, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
-
-    push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
-
-    push @$cpuFlags, 'kvm=off' if $kvm_off;
-
-    if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) {
-	push @$cpuFlags, "vendor=${cpu_vendor}"
-	    if $cpu_vendor ne 'default';
-    } elsif ($arch ne 'aarch64') {
-	die "internal error"; # should not happen
-    }
-
-    $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
-
-    return ('-cpu', $cpu);
-}
-
 sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
 
@@ -7285,45 +7109,6 @@ sub scsihw_infos {
     return ($maxdev, $controller, $controller_prefix);
 }
 
-sub add_hyperv_enlightenments {
-    my ($cpuFlags, $winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
-
-    return if $winversion < 6;
-    return if $bios && $bios eq 'ovmf' && $winversion < 8;
-
-    if ($gpu_passthrough || defined($hv_vendor_id)) {
-	$hv_vendor_id //= 'proxmox';
-	push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
-    }
-
-    if (min_version($machine_version, 2, 3)) {
-	push @$cpuFlags , 'hv_spinlocks=0x1fff';
-	push @$cpuFlags , 'hv_vapic';
-	push @$cpuFlags , 'hv_time';
-    } else {
-	push @$cpuFlags , 'hv_spinlocks=0xffff';
-    }
-
-    if (min_version($machine_version, 2, 6)) {
-	push @$cpuFlags , 'hv_reset';
-	push @$cpuFlags , 'hv_vpindex';
-	push @$cpuFlags , 'hv_runtime';
-    }
-
-    if ($winversion >= 7) {
-	push @$cpuFlags , 'hv_relaxed';
-
-	if (min_version($machine_version, 2, 12)) {
-	    push @$cpuFlags , 'hv_synic';
-	    push @$cpuFlags , 'hv_stimer';
-	}
-
-	if (min_version($machine_version, 3, 1)) {
-	    push @$cpuFlags , 'hv_ipi';
-	}
-    }
-}
-
 sub windows_version {
     my ($ostype) = @_;
 
diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm
new file mode 100644
index 0000000..86febe8
--- /dev/null
+++ b/PVE/QemuServer/CPUConfig.pm
@@ -0,0 +1,232 @@
+package PVE::QemuServer::CPUConfig;
+
+use strict;
+use warnings;
+
+use PVE::JSONSchema;
+use PVE::QemuServer::Helpers qw(min_version);
+
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(
+print_cpu_device
+get_cpu_options
+);
+
+my $cpu_vendor_list = {
+    # Intel CPUs
+    486 => 'GenuineIntel',
+    pentium => 'GenuineIntel',
+    pentium2 => 'GenuineIntel',
+    pentium3 => 'GenuineIntel',
+    coreduo => 'GenuineIntel',
+    core2duo => 'GenuineIntel',
+    Conroe => 'GenuineIntel',
+    Penryn => 'GenuineIntel',
+    Nehalem => 'GenuineIntel',
+    'Nehalem-IBRS' => 'GenuineIntel',
+    Westmere => 'GenuineIntel',
+    'Westmere-IBRS' => 'GenuineIntel',
+    SandyBridge => 'GenuineIntel',
+    'SandyBridge-IBRS' => 'GenuineIntel',
+    IvyBridge => 'GenuineIntel',
+    'IvyBridge-IBRS' => 'GenuineIntel',
+    Haswell => 'GenuineIntel',
+    'Haswell-IBRS' => 'GenuineIntel',
+    'Haswell-noTSX' => 'GenuineIntel',
+    'Haswell-noTSX-IBRS' => 'GenuineIntel',
+    Broadwell => 'GenuineIntel',
+    'Broadwell-IBRS' => 'GenuineIntel',
+    'Broadwell-noTSX' => 'GenuineIntel',
+    'Broadwell-noTSX-IBRS' => 'GenuineIntel',
+    'Skylake-Client' => 'GenuineIntel',
+    'Skylake-Client-IBRS' => 'GenuineIntel',
+    'Skylake-Server' => 'GenuineIntel',
+    'Skylake-Server-IBRS' => 'GenuineIntel',
+    'Cascadelake-Server' => 'GenuineIntel',
+    KnightsMill => 'GenuineIntel',
+
+    # AMD CPUs
+    athlon => 'AuthenticAMD',
+    phenom => 'AuthenticAMD',
+    Opteron_G1 => 'AuthenticAMD',
+    Opteron_G2 => 'AuthenticAMD',
+    Opteron_G3 => 'AuthenticAMD',
+    Opteron_G4 => 'AuthenticAMD',
+    Opteron_G5 => 'AuthenticAMD',
+    EPYC => 'AuthenticAMD',
+    'EPYC-IBPB' => 'AuthenticAMD',
+
+    # generic types, use vendor from host node
+    host => 'default',
+    kvm32 => 'default',
+    kvm64 => 'default',
+    qemu32 => 'default',
+    qemu64 => 'default',
+    max => 'default',
+};
+
+my @supported_cpu_flags = (
+    'pcid',
+    'spec-ctrl',
+    'ibpb',
+    'ssbd',
+    'virt-ssbd',
+    'amd-ssbd',
+    'amd-no-ssb',
+    'pdpe1gb',
+    'md-clear',
+    'hv-tlbflush',
+    'hv-evmcs',
+    'aes'
+);
+my $cpu_flag = qr/[+-](@{[join('|', @supported_cpu_flags)]})/;
+
+our $cpu_fmt = {
+    cputype => {
+	description => "Emulated CPU type.",
+	type => 'string',
+	enum => [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ],
+	default => 'kvm64',
+	default_key => 1,
+    },
+    hidden => {
+	description => "Do not identify as a KVM virtual machine.",
+	type => 'boolean',
+	optional => 1,
+	default => 0
+    },
+    'hv-vendor-id' => {
+	type => 'string',
+	pattern => qr/[a-zA-Z0-9]{1,12}/,
+	format_description => 'vendor-id',
+	description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
+	optional => 1,
+    },
+    flags => {
+	description => "List of additional CPU flags separated by ';'."
+		     . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
+		     . " Currently supported flags: @{[join(', ', @supported_cpu_flags)]}.",
+	format_description => '+FLAG[;-FLAG...]',
+	type => 'string',
+	pattern => qr/$cpu_flag(;$cpu_flag)*/,
+	optional => 1,
+    },
+};
+
+# Print a QEMU device node for a given VM configuration for hotplugging CPUs
+sub print_cpu_device {
+    my ($conf, $id) = @_;
+
+    my $kvm = $conf->{kvm} // 1;
+    my $cpu = $kvm ? "kvm64" : "qemu64";
+    if (my $cputype = $conf->{cpu}) {
+	my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
+	    or die "Cannot parse cpu description: $cputype\n";
+	$cpu = $cpuconf->{cputype};
+    }
+
+    my $cores = $conf->{cores} || 1;
+
+    my $current_core = ($id - 1) % $cores;
+    my $current_socket = int(($id - 1 - $current_core)/$cores);
+
+    return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
+}
+
+# Calculate QEMU's '-cpu' argument from a given VM configuration
+sub get_cpu_options {
+    my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
+
+    my $cpuFlags = [];
+    my $ostype = $conf->{ostype};
+
+    my $cpu = $kvm ? "kvm64" : "qemu64";
+    if ($arch eq 'aarch64') {
+	$cpu = 'cortex-a57';
+    }
+    my $hv_vendor_id;
+    if (my $cputype = $conf->{cpu}) {
+	my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
+	    or die "Cannot parse cpu description: $cputype\n";
+	$cpu = $cpuconf->{cputype};
+	$kvm_off = 1 if $cpuconf->{hidden};
+	$hv_vendor_id = $cpuconf->{'hv-vendor-id'};
+
+	if (defined(my $flags = $cpuconf->{flags})) {
+	    push @$cpuFlags, split(";", $flags);
+	}
+    }
+
+    push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64';
+
+    push @$cpuFlags , '-x2apic' if $ostype && $ostype eq 'solaris';
+
+    push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+
+    push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
+
+    if (min_version($machine_version, 2, 3) && $arch eq 'x86_64') {
+
+	push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
+	push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
+    }
+
+    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_version, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
+
+    push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
+
+    push @$cpuFlags, 'kvm=off' if $kvm_off;
+
+    if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) {
+	push @$cpuFlags, "vendor=${cpu_vendor}"
+	    if $cpu_vendor ne 'default';
+    } elsif ($arch ne 'aarch64') {
+	die "internal error"; # should not happen
+    }
+
+    $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
+
+    return ('-cpu', $cpu);
+}
+
+sub add_hyperv_enlightenments {
+    my ($cpuFlags, $winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
+
+    return if $winversion < 6;
+    return if $bios && $bios eq 'ovmf' && $winversion < 8;
+
+    if ($gpu_passthrough || defined($hv_vendor_id)) {
+	$hv_vendor_id //= 'proxmox';
+	push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
+    }
+
+    if (min_version($machine_version, 2, 3)) {
+	push @$cpuFlags , 'hv_spinlocks=0x1fff';
+	push @$cpuFlags , 'hv_vapic';
+	push @$cpuFlags , 'hv_time';
+    } else {
+	push @$cpuFlags , 'hv_spinlocks=0xffff';
+    }
+
+    if (min_version($machine_version, 2, 6)) {
+	push @$cpuFlags , 'hv_reset';
+	push @$cpuFlags , 'hv_vpindex';
+	push @$cpuFlags , 'hv_runtime';
+    }
+
+    if ($winversion >= 7) {
+	push @$cpuFlags , 'hv_relaxed';
+
+	if (min_version($machine_version, 2, 12)) {
+	    push @$cpuFlags , 'hv_synic';
+	    push @$cpuFlags , 'hv_stimer';
+	}
+
+	if (min_version($machine_version, 3, 1)) {
+	    push @$cpuFlags , 'hv_ipi';
+	}
+    }
+}
+
+1;
diff --git a/PVE/QemuServer/Makefile b/PVE/QemuServer/Makefile
index 670105a..6a49626 100644
--- a/PVE/QemuServer/Makefile
+++ b/PVE/QemuServer/Makefile
@@ -8,6 +8,7 @@ SOURCES=PCI.pm		\
 	Helpers.pm	\
 	Monitor.pm	\
 	Machine.pm	\
+	CPUConfig.pm	\
 
 .PHONY: install
 install: ${SOURCES}
-- 
2.20.1





More information about the pve-devel mailing list