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;
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',
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) = @_;
}
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);
}
}
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 $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;
- }
- # 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;
- }
-
- $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"}) {
}
}
+ # 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) = @_;
'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);
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
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 {
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*$/) {
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) {