[pve-devel] [PATCH v3 qemu-server 05/12] Add CPUConfig file and migrate some helpers
Stefan Reiter
s.reiter at proxmox.com
Tue Oct 15 16:12:27 CEST 2019
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.
qemu_machine_feature_enabled is moved to avoid a cyclic module dependency.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
v5: remove unnecessary reformatting
v3: mention qemu_machine_feature_enabled in commit msg
PVE/QemuServer.pm | 242 +---------------------------------
PVE/QemuServer/CPUConfig.pm | 255 ++++++++++++++++++++++++++++++++++++
PVE/QemuServer/Makefile | 1 +
3 files changed, 258 insertions(+), 240 deletions(-)
create mode 100644 PVE/QemuServer/CPUConfig.pm
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 4447afc..e7758cb 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -33,6 +33,7 @@ use PVE::QemuConfig;
use PVE::QMPClient;
use PVE::RPCEnvironment;
use PVE::GuestHelpers;
+use PVE::QemuServer::CPUConfig qw(qemu_machine_feature_enabled print_cpu_device get_cpu_options);
use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port);
use PVE::QemuServer::Memory;
use PVE::QemuServer::USB qw(parse_usb_device);
@@ -116,108 +117,6 @@ mkdir $var_run_tmpdir;
my $lock_dir = "/var/lock/qemu-server";
mkdir $lock_dir;
-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,
@@ -606,7 +505,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,
@@ -2136,26 +2035,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',
@@ -3649,62 +3528,6 @@ sub query_understood_cpu_flags {
return \@flags;
}
-sub get_cpu_options {
- my ($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $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 $conf->{ostype} && $conf->{ostype} eq 'solaris';
-
- push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
-
- push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
-
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 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_type, $kvmver, $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) = @_;
@@ -7325,28 +7148,6 @@ sub get_running_qemu_version {
return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
}
-sub qemu_machine_feature_enabled {
- my ($machine, $kvmver, $version_major, $version_minor) = @_;
-
- my $current_major;
- my $current_minor;
-
- if ($machine && $machine =~ m/^((?:pc(-i440fx|-q35)?|virt)-(\d+)\.(\d+))/) {
-
- $current_major = $3;
- $current_minor = $4;
-
- } elsif ($kvmver =~ m/^(\d+)\.(\d+)/) {
-
- $current_major = $1;
- $current_minor = $2;
- }
-
- return 1 if $current_major > $version_major ||
- ($current_major == $version_major &&
- $current_minor >= $version_minor);
-}
-
sub qemu_machine_pxe {
my ($vmid, $conf) = @_;
@@ -7432,45 +7233,6 @@ sub scsihw_infos {
return ($maxdev, $controller, $controller_prefix);
}
-sub add_hyperv_enlightenments {
- my ($cpuFlags, $winversion, $machine_type, $kvmver, $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 (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
- push @$cpuFlags , 'hv_spinlocks=0x1fff';
- push @$cpuFlags , 'hv_vapic';
- push @$cpuFlags , 'hv_time';
- } else {
- push @$cpuFlags , 'hv_spinlocks=0xffff';
- }
-
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 6)) {
- push @$cpuFlags , 'hv_reset';
- push @$cpuFlags , 'hv_vpindex';
- push @$cpuFlags , 'hv_runtime';
- }
-
- if ($winversion >= 7) {
- push @$cpuFlags , 'hv_relaxed';
-
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 12)) {
- push @$cpuFlags , 'hv_synic';
- push @$cpuFlags , 'hv_stimer';
- }
-
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 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..c139445
--- /dev/null
+++ b/PVE/QemuServer/CPUConfig.pm
@@ -0,0 +1,255 @@
+package PVE::QemuServer::CPUConfig;
+
+use strict;
+use warnings;
+
+use PVE::JSONSchema;
+
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(
+print_cpu_device
+get_cpu_options
+qemu_machine_feature_enabled
+);
+
+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, $machine_type, $kvm_off, $kvmver, $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 $conf->{ostype} && $conf->{ostype} eq 'solaris';
+
+ push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+
+ push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
+
+ if (qemu_machine_feature_enabled ($machine_type, $kvmver, 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_type, $kvmver, $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_type, $kvmver, $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 (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
+ push @$cpuFlags , 'hv_spinlocks=0x1fff';
+ push @$cpuFlags , 'hv_vapic';
+ push @$cpuFlags , 'hv_time';
+ } else {
+ push @$cpuFlags , 'hv_spinlocks=0xffff';
+ }
+
+ if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 6)) {
+ push @$cpuFlags , 'hv_reset';
+ push @$cpuFlags , 'hv_vpindex';
+ push @$cpuFlags , 'hv_runtime';
+ }
+
+ if ($winversion >= 7) {
+ push @$cpuFlags , 'hv_relaxed';
+
+ if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 12)) {
+ push @$cpuFlags , 'hv_synic';
+ push @$cpuFlags , 'hv_stimer';
+ }
+
+ if (qemu_machine_feature_enabled ($machine_type, $kvmver, 3, 1)) {
+ push @$cpuFlags , 'hv_ipi';
+ }
+ }
+}
+
+sub qemu_machine_feature_enabled {
+ my ($machine, $kvmver, $version_major, $version_minor) = @_;
+
+ my $current_major;
+ my $current_minor;
+
+ if ($machine && $machine =~ m/^((?:pc(-i440fx|-q35)?|virt)-(\d+)\.(\d+))/) {
+
+ $current_major = $3;
+ $current_minor = $4;
+
+ } elsif ($kvmver =~ m/^(\d+)\.(\d+)/) {
+
+ $current_major = $1;
+ $current_minor = $2;
+ }
+
+ return 1 if $current_major > $version_major ||
+ ($current_major == $version_major &&
+ $current_minor >= $version_minor);
+}
+
+1;
diff --git a/PVE/QemuServer/Makefile b/PVE/QemuServer/Makefile
index afc39a3..a3054f1 100644
--- a/PVE/QemuServer/Makefile
+++ b/PVE/QemuServer/Makefile
@@ -5,6 +5,7 @@ SOURCES=PCI.pm \
OVF.pm \
Cloudinit.pm \
Agent.pm \
+ CPUConfig.pm \
.PHONY: install
install: ${SOURCES}
--
2.20.1
More information about the pve-devel
mailing list