]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
qemu-img convert : use default cache=unsafe instead writeback
[qemu-server.git] / PVE / QemuServer.pm
index 4226f5031d3710db5d70a61b6044d27a2ebc4ded..2414fd86c861d9b25941641f461dfac9d827d71b 100644 (file)
@@ -30,7 +30,9 @@ use PVE::ProcFSTools;
 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;
@@ -321,6 +323,12 @@ EODESC
        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',
@@ -1684,7 +1692,10 @@ sub parse_net {
        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;
 }
 
@@ -1884,27 +1895,6 @@ sub parse_watchdog {
     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) = @_;
@@ -2645,7 +2635,7 @@ sub vmstatus {
 }
 
 sub foreach_drive {
-    my ($conf, $func) = @_;
+    my ($conf, $func, @param) = @_;
 
     foreach my $ds (valid_drive_names()) {
        next if !defined($conf->{$ds});
@@ -2653,7 +2643,7 @@ sub foreach_drive {
        my $drive = parse_drive($ds, $conf->{$ds});
        next if !$drive;
 
-       &$func($ds, $drive);
+       &$func($ds, $drive, @param);
     }
 }
 
@@ -2758,38 +2748,10 @@ sub config_to_command {
        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);
@@ -2867,31 +2829,8 @@ sub config_to_command {
     }
 
     # 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"}) {
@@ -3308,6 +3247,16 @@ sub vm_devices_list {
        }
     }
 
+    # 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;
 }
 
@@ -3325,6 +3274,14 @@ sub vm_deviceplug {
 
        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);
@@ -3420,6 +3377,15 @@ sub vm_deviceunplug {
 
        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);
@@ -3652,6 +3618,31 @@ sub qemu_netdevdel {
     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) = @_;
 
@@ -3880,6 +3871,7 @@ my $fast_plug_option = {
     'shares' => 1,
     'startup' => 1,
     'description' => 1,
+    'protection' => 1,
 };
 
 # hotplug changes in [PENDING]
@@ -3929,6 +3921,12 @@ sub vmconfig_hotplug_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);
@@ -3979,6 +3977,14 @@ sub vmconfig_hotplug_pending {
                } 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);
@@ -4354,19 +4360,48 @@ sub vm_start {
        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
@@ -4811,144 +4846,6 @@ sub pci_dev_group_bind_to_vfio {
     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 {
@@ -5039,12 +4936,13 @@ sub restore_update_config_line {
     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",
@@ -5058,7 +4956,7 @@ sub restore_update_config_line {
     } 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*$/) {
@@ -5707,7 +5605,7 @@ sub qemu_img_convert {
        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) {
@@ -5857,7 +5755,7 @@ sub clone_disk {
        $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) {