use PVE::QemuConfig;
use PVE::QMPClient;
use PVE::RPCEnvironment;
+use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr);
use PVE::QemuServer::Memory;
+use PVE::QemuServer::USB qw(parse_usb_device);
use Time::HiRes qw(gettimeofday);
use File::Copy qw(copy);
use URI::Escape;
+my $OVMF_CODE = '/usr/share/kvm/OVMF_CODE-pure-efi.fd';
+my $OVMF_VARS = '/usr/share/kvm/OVMF_VARS-pure-efi.fd';
+my $OVMF_IMG = '/usr/share/kvm/OVMF-pure-efi.fd';
+
my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
description => "Enable/disable NUMA.",
default => 0,
},
+ hugepages => {
+ optional => 1,
+ type => 'string',
+ description => "Enable/disable hugepages memory.",
+ enum => [qw(any 2 1024)],
+ },
vcpus => {
optional => 1,
type => 'integer',
},
cdrom => {
optional => 1,
- type => 'string', format => 'pve-qm-drive',
+ type => 'string', format => 'pve-qm-ide',
typetext => 'volume',
description => "This is an alias for option -ide2",
},
%rerror_fmt,
%model_fmt,
};
+PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
my $idedesc = {
optional => 1,
%queues_fmt,
};
+my $efidisk_fmt = {
+ volume => { alias => 'file' },
+ file => {
+ type => 'string',
+ format => 'pve-volume-id-or-qm-path',
+ default_key => 1,
+ format_description => 'volume',
+ description => "The drive's backing volume.",
+ },
+ format => {
+ type => 'string',
+ format_description => 'image format',
+ enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
+ description => "The drive's backing file's data format.",
+ optional => 1,
+ },
+ size => {
+ type => 'string',
+ format => 'disk-size',
+ format_description => 'DiskSize',
+ description => "Disk size. This is purely informational and has no effect.",
+ optional => 1,
+ },
+};
+
+my $efidisk_desc = {
+ optional => 1,
+ type => 'string', format => $efidisk_fmt,
+ description => "Configure a Disk for storing EFI vars",
+};
+
+PVE::JSONSchema::register_standard_option("pve-qm-efidisk", $efidisk_desc);
+
my $usb_fmt = {
host => {
default_key => 1,
$confdesc->{"virtio$i"} = $virtiodesc;
}
+$drivename_hash->{efidisk0} = 1;
+$confdesc->{efidisk0} = $efidisk_desc;
+
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
$confdesc->{"usb$i"} = $usbdesc;
}
return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
(map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
(map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))),
- (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))));
+ (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))),
+ 'efidisk0');
}
sub is_valid_drivename {
return $netdev;
}
+
+sub print_cpu_device {
+ my ($conf, $id) = @_;
+
+ my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
+ my $cpu = $nokvm ? "qemu64" : "kvm64";
+ 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 $sockets = 1;
+ $sockets = $conf->{sockets} if $conf->{sockets};
+ my $cores = $conf->{cores} || 1;
+
+ my $current_core = ($id - 1) % $cores;
+ my $current_socket = int(($id - $current_core)/$cores);
+
+ return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
+}
+
sub drive_is_cdrom {
my ($drive) = @_;
delete $res->{host};
foreach my $id (@idlist) {
if ($id =~ /^$PCIRE$/) {
- push @{$res->{pciid}}, { id => $1, function => ($2//'0') };
+ if (defined($2)) {
+ push @{$res->{pciid}}, { id => $1, function => $2 };
+ } else {
+ my $pcidevices = lspci($1);
+ $res->{pciid} = $pcidevices->{$1};
+ }
} else {
# should have been caught by parse_property_string already
die "failed to parse PCI id: $id\n";
warn $@;
return undef;
}
- $res->{macaddr} = PVE::Tools::random_ether_addr() if !defined($res->{macaddr});
+ if (!defined($res->{macaddr})) {
+ my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
+ $res->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
+ }
return $res;
}
return $res;
}
-sub parse_usb_device {
- my ($value) = @_;
-
- return undef if !$value;
-
- my $res = {};
- if ($value =~ m/^(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) {
- $res->{vendorid} = $2;
- $res->{productid} = $4;
- } elsif ($value =~ m/^(\d+)\-(\d+(\.\d+)*)$/) {
- $res->{hostbus} = $1;
- $res->{hostport} = $2;
- } elsif ($value =~ m/^spice$/i) {
- $res->{spice} = 1;
- } else {
- return undef;
- }
-
- return $res;
-}
-
PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
sub verify_usb_device {
my ($value, $noerr) = @_;
die "type check ('number') failed - got '$value'\n";
} elsif ($type eq 'string') {
if (my $fmt = $confdesc->{$key}->{format}) {
- if ($fmt eq 'pve-qm-drive') {
- # special case - we need to pass $key to parse_drive()
- my $drive = parse_drive($key, $value);
- return $value if $drive;
- die "unable to parse drive options\n";
- }
PVE::JSONSchema::check_format($fmt, $value);
return $value;
}
if ($@) {
warn "vm $vmid - unable to parse value of '$key' - $@";
} else {
+ $key = 'ide2' if $key eq 'cdrom';
my $fmt = $confdesc->{$key}->{format};
- if ($fmt && $fmt eq 'pve-qm-drive') {
+ if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
my $v = parse_drive($key, $value);
if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
$v->{file} = $volid;
}
}
- if ($key eq 'cdrom') {
- $conf->{ide2} = $value;
- } else {
- $conf->{$key} = $value;
- }
+ $conf->{$key} = $value;
}
}
}
}
sub foreach_drive {
- my ($conf, $func) = @_;
+ my ($conf, $func, @param) = @_;
foreach my $ds (valid_drive_names()) {
next if !defined($conf->{$ds});
my $drive = parse_drive($ds, $conf->{$ds});
next if !$drive;
- &$func($ds, $drive);
+ &$func($ds, $drive, @param);
}
}
sub foreach_volid {
- my ($conf, $func) = @_;
+ my ($conf, $func, @param) = @_;
my $volhash = {};
}
foreach my $volid (keys %$volhash) {
- &$func($volid, $volhash->{$volid});
+ &$func($volid, $volhash->{$volid}, @param);
}
}
}
if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
- my $ovmfvar = "OVMF_VARS-pure-efi.fd";
- my $ovmfvar_src = "/usr/share/kvm/$ovmfvar";
- my $ovmfvar_dst = "/tmp/$vmid-$ovmfvar";
- PVE::Tools::file_copy($ovmfvar_src, $ovmfvar_dst, 256*1024);
- push @$cmd, '-drive', "if=pflash,format=raw,readonly,file=/usr/share/kvm/OVMF-pure-efi.fd";
- push @$cmd, '-drive', "if=pflash,format=raw,file=$ovmfvar_dst";
- }
-
- if ($q35) {
- # the q35 chipset support native usb2, so we enable usb controller
- # by default for this machine type
- push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
- } else {
- $pciaddr = print_pci_addr("piix3", $bridges);
- push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2";
+ my $ovmfbase;
- my $use_usb2 = 0;
- for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
- next if !$conf->{"usb$i"};
- my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
- next if !$d || $d->{usb3}; # do not add usb2 controller if we have only usb3 devices
- $use_usb2 = 1;
+ # prefer the OVMF_CODE variant
+ if (-f $OVMF_CODE) {
+ $ovmfbase = $OVMF_CODE;
+ } elsif (-f $OVMF_IMG) {
+ $ovmfbase = $OVMF_IMG;
}
- # include usb device config
- push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
- }
- # add usb3 controller if needed
-
- my $use_usb3 = 0;
- for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
- next if !$conf->{"usb$i"};
- my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
- next if !$d || !$d->{usb3};
- $use_usb3 = 1;
+ die "no uefi base img found\n" if !$ovmfbase;
+ push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmfbase";
+
+ if (defined($conf->{efidisk0}) && ($ovmfbase eq $OVMF_CODE)) {
+ my $d = PVE::JSONSchema::parse_property_string($efidisk_fmt, $conf->{efidisk0});
+ my $format = $d->{format} // 'raw';
+ my $path;
+ my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
+ if ($storeid) {
+ $path = PVE::Storage::path($storecfg, $d->{file});
+ my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+ $format = qemu_img_format($scfg, $volname);
+ } else {
+ $path = $d->{file};
+ $format = "raw";
+ }
+ push @$cmd, '-drive', "if=pflash,unit=1,id=drive-efidisk0,format=$format,file=$path";
+ } elsif ($ovmfbase eq $OVMF_CODE) {
+ warn "using uefi without permanent efivars disk\n";
+ my $ovmfvar_dst = "/tmp/$vmid-ovmf.fd";
+ PVE::Tools::file_copy($OVMF_VARS, $ovmfvar_dst, 256*1024);
+ push @$cmd, '-drive', "if=pflash,unit=1,format=raw,file=$ovmfvar_dst";
+ } else {
+ # if the base img is not OVMF_CODE, we do not have to bother
+ # to create/use a vars image, since it will not be used anyway
+ # this can only happen if someone manually deletes the OVMF_CODE image
+ # or has an old pve-qemu-kvm version installed.
+ # both should not happen, but we ignore it here
+ }
}
- $pciaddr = print_pci_addr("xhci", $bridges);
- push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr" if $use_usb3;
+ # add usb controllers
+ my @usbcontrollers = PVE::QemuServer::USB::get_usb_controllers($conf, $bridges, $q35, $usbdesc->{format}, $MAX_USB_DEVICES);
+ push @$devices, @usbcontrollers if @usbcontrollers;
my $vga = $conf->{vga};
my $qxlnum = vga_conf_has_spice($vga);
}
# usb devices
- for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
- next if !$conf->{"usb$i"};
- my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
- next if !$d;
-
- # if it is a usb3 device, attach it to the xhci controller, else omit the bus option
- my $usbbus = '';
- if (defined($d->{usb3}) && $d->{usb3}) {
- $usbbus = ',bus=xhci.0';
- }
-
- if (defined($d->{host})) {
- $d = parse_usb_device($d->{host});
- if (defined($d->{vendorid}) && defined($d->{productid})) {
- push @$devices, '-device', "usb-host$usbbus,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
- } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
- push @$devices, '-device', "usb-host$usbbus,hostbus=$d->{hostbus},hostport=$d->{hostport}";
- } elsif (defined($d->{spice}) && $d->{spice}) {
- # usb redir support for spice, currently no usb3
- push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
- push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
- }
- }
- }
-
+ my @usbdevices = PVE::QemuServer::USB::get_usb_devices($conf, $usbdesc->{format}, $MAX_USB_DEVICES);
+ push @$devices, @usbdevices if @usbdevices;
# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
if (my $path = $conf->{"serial$i"}) {
die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
if ($allowed_vcpus < $maxcpus);
- push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
+ if($hotplug_features->{cpu} && qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 7)) {
+
+ push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
+ for (my $i = 2; $i <= $vcpus; $i++) {
+ my $cpustr = print_cpu_device($conf,$i);
+ push @$cmd, '-device', $cpustr;
+ }
+
+ } else {
+ push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
+ }
push @$cmd, '-nodefaults';
my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
$i++;
}
- push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000";
+ push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;
push @$cpuFlags , 'hv_vapic' if !$nokvm;
push @$cpuFlags , 'hv_time' if !$nokvm;
+ if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 6)) {
+ push @$cpuFlags , 'hv_reset' if !$nokvm;
+ push @$cpuFlags , 'hv_vpindex' if !$nokvm;
+ push @$cpuFlags , 'hv_runtime' if !$nokvm;
+ }
+
} else {
push @$cpuFlags , 'hv_spinlocks=0xffff' if !$nokvm;
}
$ahcicontroller->{$controller}=1;
}
+ if ($drive->{interface} eq 'efidisk') {
+ # this will be added somewhere else
+ return;
+ }
+
my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
push @$devices, '-drive',$drive_cmd;
push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
}
}
+ # for usb devices there is no query-usb
+ # but we can iterate over the entries in
+ # qom-list path=/machine/peripheral
+ my $resperipheral = vm_mon_cmd($vmid, 'qom-list', path => '/machine/peripheral');
+ foreach my $per (@$resperipheral) {
+ if ($per->{name} =~ m/^usb\d+$/) {
+ $devices->{$per->{name}} = 1;
+ }
+ }
+
return $devices;
}
qemu_deviceadd($vmid, print_tabletdevice_full($conf));
+ } elsif ($deviceid =~ m/^usb(\d+)$/) {
+
+ die "usb hotplug currently not reliable\n";
+ # since we can't reliably hot unplug all added usb devices
+ # and usb passthrough disables live migration
+ # we disable usb hotplugging for now
+ qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device));
+
} elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
qemu_iothread_add($vmid, $deviceid, $device);
qemu_devicedel($vmid, $deviceid);
+ } elsif ($deviceid =~ m/^usb\d+$/) {
+
+ die "usb hotplug currently not reliable\n";
+ # when unplugging usb devices this way,
+ # there may be remaining usb controllers/hubs
+ # so we disable it for now
+ qemu_devicedel($vmid, $deviceid);
+ qemu_devicedelverify($vmid, $deviceid);
+
} elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
qemu_devicedel($vmid, $deviceid);
vm_mon_cmd($vmid, "netdev_del", id => $deviceid);
}
+sub qemu_usb_hotplug {
+ my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
+
+ return if !$device;
+
+ # remove the old one first
+ vm_deviceunplug($vmid, $conf, $deviceid);
+
+ # check if xhci controller is necessary and available
+ if ($device->{usb3}) {
+
+ my $devicelist = vm_devices_list($vmid);
+
+ if (!$devicelist->{xhci}) {
+ my $pciaddr = print_pci_addr("xhci");
+ qemu_deviceadd($vmid, "nec-usb-xhci,id=xhci$pciaddr");
+ }
+ }
+ my $d = parse_usb_device($device->{host});
+ $d->{usb3} = $device->{usb3};
+
+ # add the new one
+ vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d);
+}
+
sub qemu_cpu_hotplug {
my ($vmid, $conf, $vcpus) = @_;
+ my $machine_type = PVE::QemuServer::get_current_qemu_machine($vmid);
+
my $sockets = 1;
$sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
$sockets = $conf->{sockets} if $conf->{sockets};
if $vcpus > $maxcpus;
my $currentvcpus = $conf->{vcpus} || $maxcpus;
- die "online cpu unplug is not yet possible\n"
- if $vcpus < $currentvcpus;
+
+ if ($vcpus < $currentvcpus) {
+
+ if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
+
+ for (my $i = $currentvcpus; $i > $vcpus; $i--) {
+ qemu_devicedel($vmid, "cpu$i");
+ my $retry = 0;
+ my $currentrunningvcpus = undef;
+ while (1) {
+ $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
+ last if scalar(@{$currentrunningvcpus}) == $i-1;
+ raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
+ $retry++;
+ sleep 1;
+ }
+ #update conf after each succesfull cpu unplug
+ $conf->{vcpus} = scalar(@{$currentrunningvcpus});
+ PVE::QemuConfig->write_config($vmid, $conf);
+ }
+ } else {
+ die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
+ }
+
+ return;
+ }
my $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
- die "vcpus in running vm is different than configuration\n"
+ die "vcpus in running vm does not match its configuration\n"
if scalar(@{$currentrunningvcpus}) != $currentvcpus;
- for (my $i = $currentvcpus; $i < $vcpus; $i++) {
- vm_mon_cmd($vmid, "cpu-add", id => int($i));
+ if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
+
+ for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
+ my $cpustr = print_cpu_device($conf, $i);
+ qemu_deviceadd($vmid, $cpustr);
+
+ my $retry = 0;
+ my $currentrunningvcpus = undef;
+ while (1) {
+ $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
+ last if scalar(@{$currentrunningvcpus}) == $i;
+ raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
+ sleep 1;
+ $retry++;
+ }
+ #update conf after each succesfull cpu hotplug
+ $conf->{vcpus} = scalar(@{$currentrunningvcpus});
+ PVE::QemuConfig->write_config($vmid, $conf);
+ }
+ } else {
+
+ for (my $i = $currentvcpus; $i < $vcpus; $i++) {
+ vm_mon_cmd($vmid, "cpu-add", id => int($i));
+ }
}
}
'shares' => 1,
'startup' => 1,
'description' => 1,
+ 'protection' => 1,
};
# hotplug changes in [PENDING]
} else {
vm_deviceunplug($vmid, $conf, $opt);
}
+ } elsif ($opt =~ m/^usb\d+/) {
+ die "skip\n";
+ # since we cannot reliably hot unplug usb devices
+ # we are disabling it
+ die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
+ vm_deviceunplug($vmid, $conf, $opt);
} elsif ($opt eq 'vcpus') {
die "skip\n" if !$hotplug_features->{cpu};
qemu_cpu_hotplug($vmid, $conf, undef);
} elsif ($value == 0) {
vm_deviceunplug($vmid, $conf, $opt);
}
+ } elsif ($opt =~ m/^usb\d+$/) {
+ die "skip\n";
+ # since we cannot reliably hot unplug usb devices
+ # we are disabling it
+ die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
+ die "skip\n" if !$d;
+ qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d);
} elsif ($opt eq 'vcpus') {
die "skip\n" if !$hotplug_features->{cpu};
qemu_cpu_hotplug($vmid, $conf, $value);
# should be default for secure migrations as a ssh TCP forward
# tunnel is not deterministic reliable ready and fails regurarly
# to set up in time, so use UNIX socket forwards
- $migrate_uri = "unix:/run/qemu-server/$vmid.migrate";
+ my $socket_addr = "/run/qemu-server/$vmid.migrate";
+ unlink $socket_addr;
+
+ $migrate_uri = "unix:$socket_addr";
push @$cmd, '-incoming', $migrate_uri;
push @$cmd, '-S';
my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
: $defaults->{cpuunits};
- eval {
- my %properties = (
- Slice => 'qemu.slice',
- KillMode => 'none',
- CPUShares => $cpuunits
- );
- if (my $cpulimit = $conf->{cpulimit}) {
- $properties{CPUQuota} = int($cpulimit * 100);
- }
- $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
- PVE::Tools::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
- run_command($cmd, timeout => $statefile ? undef : 30, umask => 0077);
- };
+ my %run_params = (timeout => $statefile ? undef : 30, umask => 0077);
+
+ my %properties = (
+ Slice => 'qemu.slice',
+ KillMode => 'none',
+ CPUShares => $cpuunits
+ );
+
+ if (my $cpulimit = $conf->{cpulimit}) {
+ $properties{CPUQuota} = int($cpulimit * 100);
+ }
+ $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
+
+ if ($conf->{hugepages}) {
+
+ my $code = sub {
+ my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
+ my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
+
+ PVE::QemuServer::Memory::hugepages_mount();
+ PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
+
+ eval {
+ PVE::Tools::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
+ run_command($cmd, %run_params);
+ };
+
+ if (my $err = $@) {
+ PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology);
+ die $err;
+ }
+
+ PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology);
+ };
+ eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
+
+ } else {
+ eval {
+ PVE::Tools::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
+ run_command($cmd, %run_params);
+ };
+ }
if (my $err = $@) {
# deactivate volumes if start fails
my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
- return join(' ', @$cmd);
+ return PVE::Tools::cmd2string($cmd);
}
sub vm_reset {
return 1;
}
-sub print_pci_addr {
- my ($id, $bridges) = @_;
-
- my $res = '';
- my $devices = {
- piix3 => { bus => 0, addr => 1 },
- #addr2 : first videocard
- balloon0 => { bus => 0, addr => 3 },
- watchdog => { bus => 0, addr => 4 },
- scsihw0 => { bus => 0, addr => 5 },
- 'pci.3' => { bus => 0, addr => 5 }, #can also be used for virtio-scsi-single bridge
- scsihw1 => { bus => 0, addr => 6 },
- ahci0 => { bus => 0, addr => 7 },
- qga0 => { bus => 0, addr => 8 },
- spice => { bus => 0, addr => 9 },
- virtio0 => { bus => 0, addr => 10 },
- virtio1 => { bus => 0, addr => 11 },
- virtio2 => { bus => 0, addr => 12 },
- virtio3 => { bus => 0, addr => 13 },
- virtio4 => { bus => 0, addr => 14 },
- virtio5 => { bus => 0, addr => 15 },
- hostpci0 => { bus => 0, addr => 16 },
- hostpci1 => { bus => 0, addr => 17 },
- net0 => { bus => 0, addr => 18 },
- net1 => { bus => 0, addr => 19 },
- net2 => { bus => 0, addr => 20 },
- net3 => { bus => 0, addr => 21 },
- net4 => { bus => 0, addr => 22 },
- net5 => { bus => 0, addr => 23 },
- vga1 => { bus => 0, addr => 24 },
- vga2 => { bus => 0, addr => 25 },
- vga3 => { bus => 0, addr => 26 },
- hostpci2 => { bus => 0, addr => 27 },
- hostpci3 => { bus => 0, addr => 28 },
- #addr29 : usb-host (pve-usb.cfg)
- 'pci.1' => { bus => 0, addr => 30 },
- 'pci.2' => { bus => 0, addr => 31 },
- 'net6' => { bus => 1, addr => 1 },
- 'net7' => { bus => 1, addr => 2 },
- 'net8' => { bus => 1, addr => 3 },
- 'net9' => { bus => 1, addr => 4 },
- 'net10' => { bus => 1, addr => 5 },
- 'net11' => { bus => 1, addr => 6 },
- 'net12' => { bus => 1, addr => 7 },
- 'net13' => { bus => 1, addr => 8 },
- 'net14' => { bus => 1, addr => 9 },
- 'net15' => { bus => 1, addr => 10 },
- 'net16' => { bus => 1, addr => 11 },
- 'net17' => { bus => 1, addr => 12 },
- 'net18' => { bus => 1, addr => 13 },
- 'net19' => { bus => 1, addr => 14 },
- 'net20' => { bus => 1, addr => 15 },
- 'net21' => { bus => 1, addr => 16 },
- 'net22' => { bus => 1, addr => 17 },
- 'net23' => { bus => 1, addr => 18 },
- 'net24' => { bus => 1, addr => 19 },
- 'net25' => { bus => 1, addr => 20 },
- 'net26' => { bus => 1, addr => 21 },
- 'net27' => { bus => 1, addr => 22 },
- 'net28' => { bus => 1, addr => 23 },
- 'net29' => { bus => 1, addr => 24 },
- 'net30' => { bus => 1, addr => 25 },
- 'net31' => { bus => 1, addr => 26 },
- 'xhci' => { bus => 1, addr => 27 },
- 'virtio6' => { bus => 2, addr => 1 },
- 'virtio7' => { bus => 2, addr => 2 },
- 'virtio8' => { bus => 2, addr => 3 },
- 'virtio9' => { bus => 2, addr => 4 },
- 'virtio10' => { bus => 2, addr => 5 },
- 'virtio11' => { bus => 2, addr => 6 },
- 'virtio12' => { bus => 2, addr => 7 },
- 'virtio13' => { bus => 2, addr => 8 },
- 'virtio14' => { bus => 2, addr => 9 },
- 'virtio15' => { bus => 2, addr => 10 },
- 'virtioscsi0' => { bus => 3, addr => 1 },
- 'virtioscsi1' => { bus => 3, addr => 2 },
- 'virtioscsi2' => { bus => 3, addr => 3 },
- 'virtioscsi3' => { bus => 3, addr => 4 },
- 'virtioscsi4' => { bus => 3, addr => 5 },
- 'virtioscsi5' => { bus => 3, addr => 6 },
- 'virtioscsi6' => { bus => 3, addr => 7 },
- 'virtioscsi7' => { bus => 3, addr => 8 },
- 'virtioscsi8' => { bus => 3, addr => 9 },
- 'virtioscsi9' => { bus => 3, addr => 10 },
- 'virtioscsi10' => { bus => 3, addr => 11 },
- 'virtioscsi11' => { bus => 3, addr => 12 },
- 'virtioscsi12' => { bus => 3, addr => 13 },
- 'virtioscsi13' => { bus => 3, addr => 14 },
- 'virtioscsi14' => { bus => 3, addr => 15 },
- 'virtioscsi15' => { bus => 3, addr => 16 },
- 'virtioscsi16' => { bus => 3, addr => 17 },
- 'virtioscsi17' => { bus => 3, addr => 18 },
- 'virtioscsi18' => { bus => 3, addr => 19 },
- 'virtioscsi19' => { bus => 3, addr => 20 },
- 'virtioscsi20' => { bus => 3, addr => 21 },
- 'virtioscsi21' => { bus => 3, addr => 22 },
- 'virtioscsi22' => { bus => 3, addr => 23 },
- 'virtioscsi23' => { bus => 3, addr => 24 },
- 'virtioscsi24' => { bus => 3, addr => 25 },
- 'virtioscsi25' => { bus => 3, addr => 26 },
- 'virtioscsi26' => { bus => 3, addr => 27 },
- 'virtioscsi27' => { bus => 3, addr => 28 },
- 'virtioscsi28' => { bus => 3, addr => 29 },
- 'virtioscsi29' => { bus => 3, addr => 30 },
- 'virtioscsi30' => { bus => 3, addr => 31 },
-
- };
-
- if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
- my $addr = sprintf("0x%x", $devices->{$id}->{addr});
- my $bus = $devices->{$id}->{bus};
- $res = ",bus=pci.$bus,addr=$addr";
- $bridges->{$bus} = 1 if $bridges;
- }
- return $res;
-
-}
-
-sub print_pcie_addr {
- my ($id) = @_;
-
- my $res = '';
- my $devices = {
- hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 },
- hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 },
- hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 },
- hostpci3 => { bus => "ich9-pcie-port-4", addr => 0 },
- };
-
- if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
- my $addr = sprintf("0x%x", $devices->{$id}->{addr});
- my $bus = $devices->{$id}->{bus};
- $res = ",bus=$bus,addr=$addr";
- }
- return $res;
-
-}
-
# vzdump restore implementaion
sub tar_archive_read_firstfile {
die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
# try to detect archive type first
- my $pid = open (TMP, "tar tf '$archive'|") ||
+ my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
die "unable to open file '$archive'\n";
- my $firstfile = <TMP>;
+ my $firstfile = <$fh>;
kill 15, $pid;
- close TMP;
+ close $fh;
die "ERROR: archive contaions no data\n" if !$firstfile;
chomp $firstfile;
return if $line =~ m/^parent:/;
return if $line =~ m/^template:/; # restored VM is never a template
+ my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
# try to convert old 1.X settings
my ($id, $ind, $ethcfg) = ($1, $2, $3);
foreach my $devconfig (PVE::Tools::split_list($ethcfg)) {
my ($model, $macaddr) = split(/\=/, $devconfig);
- $macaddr = PVE::Tools::random_ether_addr() if !$macaddr || $unique;
+ $macaddr = PVE::Tools::random_ether_addr($dc->{mac_prefix}) if !$macaddr || $unique;
my $net = {
model => $model,
bridge => "vmbr$ind",
} elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
my ($id, $netstr) = ($1, $2);
my $net = parse_net($netstr);
- $net->{macaddr} = PVE::Tools::random_ether_addr() if $net->{macaddr};
+ $net->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix}) if $net->{macaddr};
$netstr = print_net($net);
print $outfd "$id: $netstr\n";
- } elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
+ } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
my $virtdev = $1;
my $value = $3;
my $di = parse_drive($virtdev, $value);
# Note: only delete disk we want to restore
# other volumes will become unused
if ($virtdev_hash->{$ds}) {
- PVE::Storage::vdisk_free($cfg, $volid);
+ eval { PVE::Storage::vdisk_free($cfg, $volid); };
+ if (my $err = $@) {
+ warn $err;
+ }
}
});
my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
my $cmd = [];
- push @$cmd, '/usr/bin/qemu-img', 'convert', '-t', 'writeback', '-p', '-n';
+ push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
push @$cmd, '-s', $snapname if($snapname && $src_format eq "qcow2");
push @$cmd, '-f', $src_format, '-O', $dst_format, $src_path;
if ($is_zero_initialized) {
$newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $format, undef, ($size/1024));
push @$newvollist, $newvolid;
- PVE::Storage::activate_volumes($storecfg, $newvollist);
+ PVE::Storage::activate_volumes($storecfg, [$newvolid]);
my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
if (!$running || $snapname) {
qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
} else {
+
+ my $kvmver = get_running_qemu_version ($vmid);
+ if (!qemu_machine_feature_enabled (undef, $kvmver, 2, 7)) {
+ die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
+ if $drive->{iothread};
+ }
+
qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit);
}
}
return $current || $default || 'pc';
}
+sub get_running_qemu_version {
+ my ($vmid) = @_;
+ my $cmd = { execute => 'query-version', arguments => {} };
+ my $res = vm_qmp_command($vmid, $cmd);
+ return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
+}
+
sub qemu_machine_feature_enabled {
my ($machine, $kvmver, $version_major, $version_minor) = @_;