]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
cleanup: whitespaces & style
[qemu-server.git] / PVE / QemuServer.pm
index fc064c5ed06b9f555c6d55458d772d1d6c0cf580..0b866cdda406a48ebdbe46a7de5bced74ebdd662 100644 (file)
@@ -30,10 +30,17 @@ 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;
 
+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();
@@ -68,6 +75,13 @@ PVE::JSONSchema::register_standard_option('pve-snapshot-name', {
     maxLength => 40,
 });
 
+PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
+    type => 'string',
+    enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
+    description => "The drive's backing file's data format.",
+    optional => 1,
+});
+
 #no warnings 'redefine';
 
 sub cgroups_write {
@@ -133,7 +147,6 @@ my $cpu_fmt = {
        description => "Emulated CPU type.",
        type => 'string',
        enum => [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ],
-       format_description => 'cputype',
        default => 'kvm64',
        default_key => 1,
     },
@@ -197,18 +210,20 @@ my $confdesc = {
     cpulimit => {
        optional => 1,
        type => 'number',
-       description => "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has total of '2' CPU time. Value '0' indicates no CPU limit.",
+       description => "Limit of CPU usage.",
+        verbose_description => "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has total of '2' CPU time. Value '0' indicates no CPU limit.",
        minimum => 0,
        maximum => 128,
-       default => 0,
+        default => 0,
     },
     cpuunits => {
        optional => 1,
        type => 'integer',
-       description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
+        description => "CPU weight for a VM.",
+       verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
        minimum => 0,
        maximum => 500000,
-       default => 1000,
+       default => 1024,
     },
     memory => {
        optional => 1,
@@ -234,7 +249,7 @@ my $confdesc = {
     keyboard => {
        optional => 1,
        type => 'string',
-       description => "Keybord layout for vnc server. Default is read from the datacenter configuration file.",
+       description => "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.conf' configuration file.",
        enum => PVE::Tools::kvmkeymaplist(),
        default => 'en-us',
     },
@@ -246,7 +261,7 @@ my $confdesc = {
     scsihw => {
        optional => 1,
        type => 'string',
-       description => "scsi controller model",
+       description => "SCSI controller model",
        enum => [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
        default => 'lsi',
     },
@@ -258,25 +273,24 @@ my $confdesc = {
     ostype => {
        optional => 1,
        type => 'string',
-        enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 l24 l26 solaris)],
-       description => <<EODESC,
-Used to enable special optimization/features for specific
-operating systems:
-
-other  => unspecified OS
-wxp    => Microsoft Windows XP
-w2k    => Microsoft Windows 2000
-w2k3   => Microsoft Windows 2003
-w2k8   => Microsoft Windows 2008
-wvista => Microsoft Windows Vista
-win7   => Microsoft Windows 7
-win8   => Microsoft Windows 8/2012
-l24    => Linux 2.4 Kernel
-l26    => Linux 2.6/3.X Kernel
-solaris => solaris/opensolaris/openindiania kernel
-
-other|l24|l26|solaris                       ... no special behaviour
-wxp|w2k|w2k3|w2k8|wvista|win7|win8  ... use --localtime switch
+        enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 l24 l26 solaris)],
+       description => "Specify guest operating system.",
+       verbose_description => <<EODESC,
+Specify guest operating system. This is used to enable special
+optimization/features for specific operating systems:
+
+[horizontal]
+other;; unspecified OS
+wxp;; Microsoft Windows XP
+w2k;; Microsoft Windows 2000
+w2k3;; Microsoft Windows 2003
+w2k8;; Microsoft Windows 2008
+wvista;; Microsoft Windows Vista
+win7;; Microsoft Windows 7
+win8;; Microsoft Windows 8/2012
+l24;; Linux 2.4 Kernel
+l26;; Linux 2.6/3.X Kernel
+solaris;; Solaris/OpenSolaris/OpenIndiania kernel
 EODESC
     },
     boot => {
@@ -319,6 +333,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',
@@ -363,7 +383,8 @@ EODESC
     vga => {
        optional => 1,
        type => 'string',
-       description => "Select the VGA type. If you want to use high resolution" .
+       description => "Select the VGA type.",
+        verbose_description => "Select the VGA type. If you want to use high resolution" .
            " modes (>= 1280x1024x16) then you should use the options " .
            "'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and " .
            "'cirrus' for other OS types. The 'qxl' option enables the SPICE " .
@@ -376,7 +397,8 @@ EODESC
     watchdog => {
        optional => 1,
        type => 'string', format => 'pve-qm-watchdog',
-       description => "Create a virtual hardware watchdog device. Once enabled" .
+       description => "Create a virtual hardware watchdog device.",
+       verbose_description => "Create a virtual hardware watchdog device. Once enabled" .
            " (by a guest action), the watchdog must be periodically polled " .
            "by an agent inside the guest or else the watchdog will reset " .
            "the guest (or execute the respective action specified)",
@@ -399,7 +421,8 @@ EODESC
     args => {
        optional => 1,
        type => 'string',
-       description => <<EODESCR,
+       description => "Arbitrary arguments passed to kvm.",
+       verbose_description => <<EODESCR,
 Arbitrary arguments passed to kvm, for example:
 
 args: -no-reboot -no-hpet
@@ -411,7 +434,8 @@ EODESCR
        optional => 1,
        type => 'boolean',
        default => 1,
-       description => "Enable/disable the USB tablet device. This device is " .
+       description => "Enable/disable the USB tablet device.",
+       verbose_description => "Enable/disable the USB tablet device. This device is " .
            "usually needed to allow absolute mouse positioning with VNC. " .
            "Else the mouse runs out of sync with normal VNC clients. " .
            "If you're running lots of console-only guests on one host, " .
@@ -434,8 +458,8 @@ EODESCR
     },
     cdrom => {
        optional => 1,
-       type => 'string', format => 'pve-qm-drive',
-       typetext => 'volume',
+       type => 'string', format => 'pve-qm-ide',
+       typetext => '<volume>',
        description => "This is an alias for option -ide2",
     },
     cpu => {
@@ -475,7 +499,7 @@ EODESCR
     protection => {
        optional => 1,
        type => 'boolean',
-       description => "Sets the protection flag of the VM. This will prevent the remove operation.",
+       description => "Sets the protection flag of the VM. This will disable the remove VM and remove disk operations.",
        default => 0,
     },
     bios => {
@@ -519,32 +543,30 @@ my $MAX_HOSTPCI_DEVICES = 4;
 my $MAX_SERIAL_PORTS = 4;
 my $MAX_PARALLEL_PORTS = 3;
 my $MAX_NUMA = 8;
-my $MAX_MEM = 4194304;
-my $STATICMEM = 1024;
 
 my $numa_fmt = {
     cpus => {
        type => "string",
        pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
-       description => "CPUs accessing this numa node.",
+       description => "CPUs accessing this NUMA node.",
        format_description => "id[-id];...",
     },
     memory => {
        type => "number",
-       description => "Amount of memory this numa node provides.",
+       description => "Amount of memory this NUMA node provides.",
        optional => 1,
     },
     hostnodes => {
        type => "string",
        pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
-       description => "host numa nodes to use",
+       description => "Host NUMA nodes to use.",
        format_description => "id[-id];...",
        optional => 1,
     },
     policy => {
        type => 'string',
        enum => [qw(preferred bind interleave)],
-       description => "numa allocation policy.",
+       description => "NUMA allocation policy.",
        optional => 1,
     },
 };
@@ -552,7 +574,7 @@ PVE::JSONSchema::register_format('pve-qm-numanode', $numa_fmt);
 my $numadesc = {
     optional => 1,
     type => 'string', format => $numa_fmt,
-    description => "numa topology",
+    description => "NUMA topology.",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-numanode", $numadesc);
 
@@ -565,25 +587,39 @@ my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000',  'pcnet',  'virtio',
                      'e1000-82540em', 'e1000-82544gc', 'e1000-82545em'];
 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
 
+my $net_fmt_bridge_descr = <<__EOD__;
+Bridge to attach the network device to. The Proxmox VE standard bridge
+is called 'vmbr0'.
+
+If you do not specify a bridge, we create a kvm user (NATed) network
+device, which provides DHCP and DNS services. The following addresses
+are used:
+
+ 10.0.2.2   Gateway
+ 10.0.2.3   DNS Server
+ 10.0.2.4   SMB Server
+
+The DHCP server assign addresses to the guest starting from 10.0.2.15.
+__EOD__
+
 my $net_fmt = {
     macaddr => {
        type => 'string',
        pattern => qr/[0-9a-f]{2}(?::[0-9a-f]{2}){5}/i,
-       description => "MAC address",
+       description => "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
        format_description => "XX:XX:XX:XX:XX:XX",
        optional => 1,
     },
     model => {
        type => 'string',
-       description => 'Network Card Model.',
-       format_description => 'model',
+       description => "Network Card Model. The 'virtio' model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use 'e1000'.",
         enum => $nic_model_list,
         default_key => 1,
     },
     (map { $_ => { keyAlias => 'model', alias => 'macaddr' }} @$nic_model_list),
     bridge => {
        type => 'string',
-       description => 'Bridge to attach the network device to.',
+       description => $net_fmt_bridge_descr,
        format_description => 'bridge',
        optional => 1,
     },
@@ -596,12 +632,12 @@ my $net_fmt = {
     rate => {
        type => 'number',
        minimum => 0,
-       description => 'Rate limit in mbps as floating point number.',
+       description => "Rate limit in mbps (megabytes per second) as floating point number.",
        optional => 1,
     },
     tag => {
        type => 'integer',
-       minimum => 2, maximum => 4094,
+       minimum => 1, maximum => 4094,
        description => 'VLAN tag to apply to packets on this interface.',
        optional => 1,
     },
@@ -619,35 +655,17 @@ my $net_fmt = {
     },
     link_down => {
        type => 'boolean',
-       description => 'Whether this interface should be DISconnected (like pulling the plug).',
+       description => 'Whether this interface should be disconnected (like pulling the plug).',
        optional => 1,
     },
 };
+
 my $netdesc = {
     optional => 1,
     type => 'string', format => $net_fmt,
-    description => <<EODESCR,
-Specify network devices.
-
-MODEL is one of: $nic_model_list_txt
-
-XX:XX:XX:XX:XX:XX should be an unique MAC address. This is
-automatically generated if not specified.
-
-The bridge parameter can be used to automatically add the interface to a bridge device. The Proxmox VE standard bridge is called 'vmbr0'.
-
-Option 'rate' is used to limit traffic bandwidth from and to this interface. It is specified as floating point number, unit is 'Megabytes per second'.
-
-If you specify no bridge, we create a kvm 'user' (NATed) network device, which provides DHCP and DNS services. The following addresses are used:
-
-10.0.2.2   Gateway
-10.0.2.3   DNS Server
-10.0.2.4   SMB Server
-
-The DHCP server assign addresses to the guest starting from 10.0.2.15.
-
-EODESCR
+    description => "Specify network devices.",
 };
+
 PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
 
 for (my $i = 0; $i < $MAX_NETS; $i++)  {
@@ -721,13 +739,7 @@ my %drivedesc_base = (
        description => "The drive's cache mode",
        optional => 1,
     },
-    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,
-    },
+    format => get_standard_option('pve-qm-image-format'),
     size => {
        type => 'string',
        format => 'disk-size',
@@ -809,38 +821,49 @@ my %queues_fmt = (
 );
 
 my $add_throttle_desc = sub {
-    my ($key, $type, $what, $unit, $longunit) = @_;
-    $drivedesc_base{$key} = {
+    my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
+    my $d = {
        type => $type,
        format_description => $unit,
-       description => "Maximum $what speed in $longunit per second.",
+       description => "Maximum $what in $longunit.",
        optional => 1,
     };
+    $d->{minimum} = $minimum if defined($minimum);
+    $drivedesc_base{$key} = $d;
 };
 # throughput: (leaky bucket)
-$add_throttle_desc->('bps',     'integer', 'r/w speed',   'bps',  'bytes');
-$add_throttle_desc->('bps_rd',  'integer', 'read speed',  'bps',  'bytes');
-$add_throttle_desc->('bps_wr',  'integer', 'write speed', 'bps',  'bytes');
-$add_throttle_desc->('mbps',    'number',  'r/w speed',   'mbps', 'megabytes');
-$add_throttle_desc->('mbps_rd', 'number',  'read speed',  'mbps', 'megabytes');
-$add_throttle_desc->('mbps_wr', 'number',  'write speed', 'mbps', 'megabytes');
-$add_throttle_desc->('iops',    'integer', 'r/w I/O',     'iops', 'operations');
-$add_throttle_desc->('iops_rd', 'integer', 'read I/O',    'iops', 'operations');
-$add_throttle_desc->('iops_wr', 'integer', 'write I/O',   'iops', 'operations');
+$add_throttle_desc->('bps',     'integer', 'r/w speed',   'bps',  'bytes per second');
+$add_throttle_desc->('bps_rd',  'integer', 'read speed',  'bps',  'bytes per second');
+$add_throttle_desc->('bps_wr',  'integer', 'write speed', 'bps',  'bytes per second');
+$add_throttle_desc->('mbps',    'number',  'r/w speed',   'mbps', 'megabytes per second');
+$add_throttle_desc->('mbps_rd', 'number',  'read speed',  'mbps', 'megabytes per second');
+$add_throttle_desc->('mbps_wr', 'number',  'write speed', 'mbps', 'megabytes per second');
+$add_throttle_desc->('iops',    'integer', 'r/w I/O',     'iops', 'operations per second');
+$add_throttle_desc->('iops_rd', 'integer', 'read I/O',    'iops', 'operations per second');
+$add_throttle_desc->('iops_wr', 'integer', 'write I/O',   'iops', 'operations per second');
 
 # pools: (pool of IO before throttling starts taking effect)
-$add_throttle_desc->('mbps_max',    'number',  'unthrottled r/w pool',       'mbps', 'megabytes');
-$add_throttle_desc->('mbps_rd_max', 'number',  'unthrottled read pool',      'mbps', 'megabytes');
-$add_throttle_desc->('mbps_wr_max', 'number',  'unthrottled write pool',     'mbps', 'megabytes');
-$add_throttle_desc->('iops_max',    'integer', 'unthrottled r/w I/O pool',   'iops', 'operations');
-$add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool',  'iops', 'operations');
-$add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations');
+$add_throttle_desc->('mbps_max',    'number',  'unthrottled r/w pool',       'mbps', 'megabytes per second');
+$add_throttle_desc->('mbps_rd_max', 'number',  'unthrottled read pool',      'mbps', 'megabytes per second');
+$add_throttle_desc->('mbps_wr_max', 'number',  'unthrottled write pool',     'mbps', 'megabytes per second');
+$add_throttle_desc->('iops_max',    'integer', 'unthrottled r/w I/O pool',   'iops', 'operations per second');
+$add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool',  'iops', 'operations per second');
+$add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
+
+# burst lengths
+$add_throttle_desc->('bps_max_length',  'integer', 'length of I/O bursts',       'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_rd_length',   'integer', 'length of read I/O bursts',  'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_wr_length',   'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts',       'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_rd_length',  'integer', 'length of read I/O bursts',  'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_wr_length',  'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
 
 my $ide_fmt = {
     %drivedesc_base,
     %rerror_fmt,
     %model_fmt,
 };
+PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
 
 my $idedesc = {
     optional => 1,
@@ -892,41 +915,64 @@ my $alldrive_fmt = {
     %queues_fmt,
 };
 
-my $usb_fmt = {
-    host => {
+my $efidisk_fmt = {
+    volume => { alias => 'file' },
+    file => {
+       type => 'string',
+       format => 'pve-volume-id-or-qm-path',
        default_key => 1,
-       type => 'string', format => 'pve-qm-usb-device',
-       format_description => 'HOSTUSBDEVICE|spice',
-       description => 'The Host USB device or port or the value spice',
+       format_description => 'volume',
+       description => "The drive's backing volume.",
     },
-    usb3 => {
+    format => get_standard_option('pve-qm-image-format'),
+    size => {
+       type => 'string',
+       format => 'disk-size',
+       format_description => 'DiskSize',
+       description => "Disk size. This is purely informational and has no effect.",
        optional => 1,
-       type => 'boolean',
-#      format_description => 'yes|no',
-       description => 'Specifies whether if given host option is a USB3 device or port',
     },
 };
 
-my $usbdesc = {
+my $efidisk_desc = {
     optional => 1,
-    type => 'string', format => $usb_fmt,
-    description => <<EODESCR,
-Configure an USB device (n is 0 to 4). This can be used to
-pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
+    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,
+       type => 'string', format => 'pve-qm-usb-device',
+       format_description => 'HOSTUSBDEVICE|spice',
+        description => <<EODESCR,
+The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
 
-'bus-port(.port)*' (decimal numbers) or
-'vendor_id:product_id' (hexadeciaml numbers) or
-'spice'
+ 'bus-port(.port)*' (decimal numbers) or
+ 'vendor_id:product_id' (hexadeciaml numbers) or
+ 'spice'
 
 You can use the 'lsusb -t' command to list existing usb devices.
 
 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
 The value 'spice' can be used to add a usb redirection devices for spice.
-
-The 'usb3' option determines whether the device is a USB3 device or not (this does currently not work reliably with spice redirection and is then ignored).
-
 EODESCR
+    },
+    usb3 => {
+       optional => 1,
+       type => 'boolean',
+       description => "Specifies whether if given host option is a USB3 device or port (this does currently not work reliably with spice redirection and is then ignored).",
+        default => 0,
+    },
+};
+
+my $usbdesc = {
+    optional => 1,
+    type => 'string', format => $usb_fmt,
+    description => "Configure an USB device (n is 0 to 4).",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
 
@@ -938,20 +984,30 @@ my $hostpci_fmt = {
        type => 'string',
        pattern => qr/$PCIRE(;$PCIRE)*/,
        format_description => 'HOSTPCIID[;HOSTPCIID2...]',
-       description => "The PCI ID of a host's PCI device or a list of PCI virtual functions of the host.",
+       description => <<EODESCR,
+Host PCI device pass through. The PCI ID of a host's PCI device or a list 
+of PCI virtual functions of the host. HOSTPCIID syntax is:
+
+'bus:dev.func' (hexadecimal numbers)
+
+You can us the 'lspci' command to list existing PCI devices.
+EODESCR
     },
     rombar => {
        type => 'boolean',
+        description =>  "Specify whether or not the device's ROM will be visible in the guest's memory map.",
        optional => 1,
        default => 1,
     },
     pcie => {
        type => 'boolean',
+        description =>  "Choose the PCI-express bus (needs the 'q35' machine model).",
        optional => 1,
        default => 0,
     },
     'x-vga' => {
        type => 'boolean',
+        description =>  "Enable vfio-vga device support.",
        optional => 1,
        default => 0,
     },
@@ -961,18 +1017,14 @@ PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
 my $hostpcidesc = {
         optional => 1,
         type => 'string', format => 'pve-qm-hostpci',
-        description => <<EODESCR,
-Map host pci devices. HOSTPCIDEVICE syntax is:
-
-'bus:dev.func' (hexadecimal numbers)
+        description => "Map host PCI devices into guest.",
+       verbose_description =>  <<EODESCR,
+Map host PCI devices into guest.
 
-You can us the 'lspci' command to list existing pci devices.
+NOTE: This option allows direct access to host hardware. So it is no longer 
+possible to migrate such machines - use with special care.
 
-The 'rombar' option determines whether or not the device's ROM will be visible in the guest's memory map (default is 'on').
-
-NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
-
-Experimental: user reported problems with this option.
+CAUTION: Experimental! User reported problems with this option.
 EODESCR
 };
 PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc);
@@ -981,12 +1033,15 @@ my $serialdesc = {
        optional => 1,
        type => 'string',
        pattern => '(/dev/.+|socket)',
-       description =>  <<EODESCR,
-Create a serial device inside the VM (n is 0 to 3), and pass through a host serial device (i.e. /dev/ttyS0), or create a unix socket on the host side (use 'qm terminal' to open a terminal connection).
+       description =>  "Create a serial device inside the VM (n is 0 to 3)",
+       verbose_description =>  <<EODESCR,
+Create a serial device inside the VM (n is 0 to 3), and pass through a
+host serial device (i.e. /dev/ttyS0), or create a unix socket on the
+host side (use 'qm terminal' to open a terminal connection).
 
 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care.
 
-Experimental: user reported problems with this option.
+CAUTION: Experimental! User reported problems with this option.
 EODESCR
 };
 
@@ -994,12 +1049,13 @@ my $paralleldesc= {
        optional => 1,
        type => 'string',
         pattern => '/dev/parport\d+|/dev/usb/lp\d+',
-       description =>  <<EODESCR,
+       description =>  "Map host parallel devices (n is 0 to 2).",
+       verbose_description =>  <<EODESCR,
 Map host parallel devices (n is 0 to 2).
 
 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
-Experimental: user reported problems with this option.
+CAUTION: Experimental! User reported problems with this option.
 EODESCR
 };
 
@@ -1035,6 +1091,9 @@ for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++)  {
     $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;
 }
@@ -1042,7 +1101,7 @@ for (my $i = 0; $i < $MAX_USB_DEVICES; $i++)  {
 my $unuseddesc = {
     optional => 1,
     type => 'string', format => 'pve-volume-id',
-    description => "Reference to unused volumes.",
+    description => "Reference to unused volumes. This is used internally, and should not be modified manually.",
 };
 
 for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++)  {
@@ -1096,7 +1155,8 @@ sub valid_drive_names {
     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 {
@@ -1125,6 +1185,7 @@ sub os_list_description {
        wvista => 'Windows Vista',
        win7 => 'Windows 7',
        win8 => 'Windows 8/2012',
+       win10 => 'Windows 10/2016',
        l24 => 'Linux 2.4',
        l26 => 'Linux 2.6',
     };
@@ -1225,7 +1286,7 @@ sub parse_hotplug_features {
        if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
            $res->{$1} = 1;
        } else {
-           warn "ignoring unknown hotplug feature '$feature'\n";
+           die "invalid hotplug feature '$feature'\n";
        }
     }
     return $res;
@@ -1282,6 +1343,22 @@ sub parse_drive {
            $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0));
        }
     }
+
+    # can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
+    for my $requirement (
+       [bps_max_length => 'mbps_max'],
+       [bps_rd_max_length => 'mbps_rd_max'],
+       [bps_wr_max_length => 'mbps_wr_max'],
+       [iops_max_length => 'iops_max'],
+       [iops_rd_max_length => 'iops_rd_max'],
+       [iops_wr_max_length => 'iops_wr_max']) {
+       my ($option, $requires) = @$requirement;
+       if ($res->{$option} && !$res->{$requires}) {
+           warn "$option requires $requires\n";
+           ++$error;
+       }
+    }
+
     return undef if $error;
 
     return undef if $res->{mbps_rd} && $res->{mbps};
@@ -1618,6 +1695,28 @@ sub print_netdev_full {
     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) = @_;
 
@@ -1659,7 +1758,12 @@ sub parse_hostpci {
     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";
@@ -1677,7 +1781,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;
 }
 
@@ -1794,42 +1901,49 @@ my $smbios1_fmt = {
        type => 'string',
        pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
        format_description => 'UUID',
+        description => "Set SMBIOS1 UUID.",
        optional => 1,
     },
     version => {
        type => 'string',
        pattern => '\S+',
        format_description => 'string',
+        description => "Set SMBIOS1 version.",
        optional => 1,
     },
     serial => {
        type => 'string',
        pattern => '\S+',
        format_description => 'string',
+        description => "Set SMBIOS1 serial number.",
        optional => 1,
     },
     manufacturer => {
        type => 'string',
        pattern => '\S+',
        format_description => 'string',
+        description => "Set SMBIOS1 manufacturer.",
        optional => 1,
     },
     product => {
        type => 'string',
        pattern => '\S+',
        format_description => 'string',
+        description => "Set SMBIOS1 product ID.",
        optional => 1,
     },
     sku => {
        type => 'string',
        pattern => '\S+',
        format_description => 'string',
+        description => "Set SMBIOS1 SKU string.",
        optional => 1,
     },
     family => {
        type => 'string',
        pattern => '\S+',
        format_description => 'string',
+        description => "Set SMBIOS1 family string.",
        optional => 1,
     },
 };
@@ -1870,27 +1984,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) = @_;
@@ -1941,12 +2034,6 @@ sub check_type {
         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;
        }
@@ -1995,7 +2082,11 @@ sub destroy_vm {
        my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
        return if !$path || !$owner || ($owner != $vmid);
 
-       PVE::Storage::vdisk_free($storecfg, $volid);
+       eval {
+           PVE::Storage::vdisk_free($storecfg, $volid);
+       };
+       warn "Could not remove disk '$volid', check manually: $@" if $@;
+
     });
 
     if ($keep_empty_config) {
@@ -2094,8 +2185,9 @@ sub parse_vm_config {
            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;
@@ -2106,11 +2198,7 @@ sub parse_vm_config {
                    }
                }
 
-               if ($key eq 'cdrom') {
-                   $conf->{ide2} = $value;
-               } else {
-                   $conf->{$key} = $value;
-               }
+               $conf->{$key} = $value;
            }
        }
     }
@@ -2620,7 +2708,7 @@ sub vmstatus {
        $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
     }
 
-    $qmpclient->queue_execute(undef, 1);
+    $qmpclient->queue_execute(undef, 2);
 
     foreach my $vmid (keys %$list) {
        next if $opt_vmid && ($vmid ne $opt_vmid);
@@ -2630,50 +2718,8 @@ sub vmstatus {
     return $res;
 }
 
-sub foreach_dimm {
-    my ($conf, $vmid, $memory, $sockets, $func) = @_;
-
-    my $dimm_id = 0;
-    my $current_size = 1024;
-    my $dimm_size = 512;
-    return if $current_size == $memory;
-
-    for (my $j = 0; $j < 8; $j++) {
-       for (my $i = 0; $i < 32; $i++) {
-           my $name = "dimm${dimm_id}";
-           $dimm_id++;
-           my $numanode = $i % $sockets;
-           $current_size += $dimm_size;
-           &$func($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory);
-           return  $current_size if $current_size >= $memory;
-       }
-       $dimm_size *= 2;
-    }
-}
-
-sub foreach_reverse_dimm {
-    my ($conf, $vmid, $memory, $sockets, $func) = @_;
-
-    my $dimm_id = 253;
-    my $current_size = 4177920;
-    my $dimm_size = 65536;
-    return if $current_size == $memory;
-
-    for (my $j = 0; $j < 8; $j++) {
-       for (my $i = 0; $i < 32; $i++) {
-           my $name = "dimm${dimm_id}";
-           $dimm_id--;
-           my $numanode = $i % $sockets;
-           $current_size -= $dimm_size;
-           &$func($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory);
-           return  $current_size if $current_size <= $memory;
-       }
-       $dimm_size /= 2;
-    }
-}
-
 sub foreach_drive {
-    my ($conf, $func) = @_;
+    my ($conf, $func, @param) = @_;
 
     foreach my $ds (valid_drive_names()) {
        next if !defined($conf->{$ds});
@@ -2681,12 +2727,12 @@ sub foreach_drive {
        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 = {};
 
@@ -2713,7 +2759,7 @@ sub foreach_volid {
     }
 
     foreach my $volid (keys %$volhash) {
-       &$func($volid, $volhash->{$volid});
+       &$func($volid, $volhash->{$volid}, @param);
     }
 }
 
@@ -2739,6 +2785,8 @@ sub config_to_command {
     my $kvmver = kvm_user_version();
     my $vernum = 0; # unknown
     my $ostype = $conf->{ostype};
+    my $winversion = windows_version($ostype);
+
     if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
        $vernum = $1*1000000+$2*1000;
     } elsif ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) {
@@ -2758,19 +2806,6 @@ sub config_to_command {
     my $cpuunits = defined($conf->{cpuunits}) ?
             $conf->{cpuunits} : $defaults->{cpuunits};
 
-    push @$cmd, '/usr/bin/systemd-run';
-    push @$cmd, '--scope';
-    push @$cmd, '--slice', "qemu";
-    push @$cmd, '--unit', $vmid;
-    # set KillMode=none, so that systemd don't kill those scopes
-    # at shutdown (pve-manager service should stop the VMs instead)
-    push @$cmd, '-p', "KillMode=none";
-    push @$cmd, '-p', "CPUShares=$cpuunits";
-    if ($conf->{cpulimit}) {
-       my $cpulimit = int($conf->{cpulimit} * 100);
-       push @$cmd, '-p', "CPUQuota=$cpulimit\%";
-    }
-
     push @$cmd, '/usr/bin/kvm';
 
     push @$cmd, '-id', $vmid;
@@ -2791,59 +2826,57 @@ sub config_to_command {
     }
 
     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);
     $vga = 'qxl' if $qxlnum;
 
     if (!$vga) {
-       if ($conf->{ostype} && ($conf->{ostype} eq 'win8' ||
-                               $conf->{ostype} eq 'win7' ||
-                               $conf->{ostype} eq 'w2k8')) {
-           $vga = 'std';
-       } else {
-           $vga = 'cirrus';
-       }
+       $vga = $winversion >= 6 ? 'std' : 'cirrus';
     }
 
     # enable absolute mouse coordinates (needed by vnc)
@@ -2859,6 +2892,8 @@ sub config_to_command {
     push @$devices, '-device', print_tabletdevice_full($conf) if $tablet;
 
     my $kvm_off = 0;
+    my $gpu_passthrough;
+
     # host pci devices
     for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
        my $d = parse_hostpci($conf->{"hostpci$i"});
@@ -2878,9 +2913,8 @@ sub config_to_command {
            $xvga = ',x-vga=on';
            $kvm_off = 1;
            $vga = 'none';
-           if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8') {
-               push @$cpuFlags , 'hv_vendor_id=proxmox';
-           }
+           $gpu_passthrough = 1;
+
            if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
                $xvga = "";
            }
@@ -2908,31 +2942,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"}) {
@@ -2977,8 +2988,18 @@ sub config_to_command {
     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};
@@ -2990,7 +3011,7 @@ sub config_to_command {
        $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;
 
@@ -3011,35 +3032,18 @@ sub config_to_command {
     my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
     my $useLocaltime = $conf->{localtime};
 
-    if ($ostype) {
-       # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26, solaris
-
-       if ($ostype =~ m/^w/) { # windows
-           $useLocaltime = 1 if !defined($conf->{localtime});
+    if ($winversion >= 5) { # windows
+       $useLocaltime = 1 if !defined($conf->{localtime});
 
-           # use time drift fix when acpi is enabled
-           if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
-               $tdf = 1 if !defined($conf->{tdf});
-           }
-       }
-
-       if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8' ||
-           $ostype eq 'wvista') {
-           push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
-           push @$cmd, '-no-hpet';
-           if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
-               push @$cpuFlags , 'hv_spinlocks=0x1fff' if !$nokvm;
-               push @$cpuFlags , 'hv_vapic' if !$nokvm;
-               push @$cpuFlags , 'hv_time' if !$nokvm;
-
-           } else {
-               push @$cpuFlags , 'hv_spinlocks=0xffff' if !$nokvm;
-           }
+       # use time drift fix when acpi is enabled
+       if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
+           $tdf = 1 if !defined($conf->{tdf});
        }
+    }
 
-       if ($ostype eq 'win7' || $ostype eq 'win8') {
-           push @$cpuFlags , 'hv_relaxed' if !$nokvm;
-       }
+    if ($winversion >= 6) {
+       push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
+       push @$cmd, '-no-hpet';
     }
 
     push @$rtcFlags, 'driftfix=slew' if $tdf;
@@ -3083,6 +3087,8 @@ sub config_to_command {
        push @$cpuFlags , '+kvm_pv_eoi' if !$nokvm;
     }
 
+    add_hyperv_enlighments($cpuFlags, $winversion, $machine_type, $kvmver, $nokvm, $conf->{bios}, $gpu_passthrough);
+
     push @$cpuFlags, 'enforce' if $cpu ne 'host' && !$nokvm;
 
     push @$cpuFlags, 'kvm=off' if $kvm_off;
@@ -3097,105 +3103,8 @@ sub config_to_command {
 
     push @$cmd, '-cpu', $cpu;
 
-    my $memory = $conf->{memory} || $defaults->{memory};
-    my $static_memory = 0;
-    my $dimm_memory = 0;
-
-    if ($hotplug_features->{memory}) {
-       die "Numa need to be enabled for memory hotplug\n" if !$conf->{numa};
-       die "Total memory is bigger than ${MAX_MEM}MB\n" if $memory > $MAX_MEM;
-       $static_memory = $STATICMEM;
-       die "minimum memory must be ${static_memory}MB\n" if($memory < $static_memory);
-       $dimm_memory = $memory - $static_memory;
-       push @$cmd, '-m', "size=${static_memory},slots=255,maxmem=${MAX_MEM}M";
-
-    } else {
-
-       $static_memory = $memory;
-       push @$cmd, '-m', $static_memory;
-    }
-
-    if ($conf->{numa}) {
-
-       my $numa_totalmemory = undef;
-       for (my $i = 0; $i < $MAX_NUMA; $i++) {
-           next if !$conf->{"numa$i"};
-           my $numa = parse_numa($conf->{"numa$i"});
-           next if !$numa;
-           # memory
-           die "missing numa node$i memory value\n" if !$numa->{memory};
-           my $numa_memory = $numa->{memory};
-           $numa_totalmemory += $numa_memory;
-           my $numa_object = "memory-backend-ram,id=ram-node$i,size=${numa_memory}M";
-
-           # cpus
-           my $cpulists = $numa->{cpus};
-           die "missing numa node$i cpus\n" if !defined($cpulists);
-           my $cpus = join(',', map {
-               my ($start, $end) = @$_;
-               defined($end) ? "$start-$end" : $start
-           } @$cpulists);
-
-           # hostnodes
-           my $hostnodelists = $numa->{hostnodes};
-           if (defined($hostnodelists)) {
-               my $hostnodes;
-               foreach my $hostnoderange (@$hostnodelists) {
-                   my ($start, $end) = @$hostnoderange;
-                   $hostnodes .= ',' if $hostnodes;
-                   $hostnodes .= $start;
-                   $hostnodes .= "-$end" if defined($end);
-                   $end //= $start;
-                   for (my $i = $start; $i <= $end; ++$i ) {
-                       die "host numa node$i don't exist\n" if ! -d "/sys/devices/system/node/node$i/";
-                   }
-               }
-
-               # policy
-               my $policy = $numa->{policy};
-               die "you need to define a policy for hostnode $hostnodes\n" if !$policy;
-               $numa_object .= ",host-nodes=$hostnodes,policy=$policy";
-           }
-
-           push @$cmd, '-object', $numa_object;
-           push @$cmd, '-numa', "node,nodeid=$i,cpus=$cpus,memdev=ram-node$i";
-       }
-
-       die "total memory for NUMA nodes must be equal to vm static memory\n"
-           if $numa_totalmemory && $numa_totalmemory != $static_memory;
-
-       #if no custom tology, we split memory and cores across numa nodes
-       if(!$numa_totalmemory) {
-
-           my $numa_memory = ($static_memory / $sockets) . "M";
-
-           for (my $i = 0; $i < $sockets; $i++)  {
-
-               my $cpustart = ($cores * $i);
-               my $cpuend = ($cpustart + $cores - 1) if $cores && $cores > 1;
-               my $cpus = $cpustart;
-               $cpus .= "-$cpuend" if $cpuend;
-
-               push @$cmd, '-object', "memory-backend-ram,size=$numa_memory,id=ram-node$i";
-               push @$cmd, '-numa', "node,nodeid=$i,cpus=$cpus,memdev=ram-node$i";
-           }
-       }
-    }
-
-    if ($hotplug_features->{memory}) {
-       foreach_dimm($conf, $vmid, $memory, $sockets, sub {
-           my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
-           push @$cmd, "-object" , "memory-backend-ram,id=mem-$name,size=${dimm_size}M";
-           push @$cmd, "-device", "pc-dimm,id=$name,memdev=mem-$name,node=$numanode";
-
-           #if dimm_memory is not aligned to dimm map
-           if($current_size > $memory) {
-                $conf->{memory} = $current_size;
-                PVE::QemuConfig->write_config($vmid, $conf);
-           }
-       });
-    }
-
+    PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
+    
     push @$cmd, '-S' if $conf->{freeze};
 
     # set keyboard layout
@@ -3219,7 +3128,7 @@ sub config_to_command {
 
     if ($qxlnum) {
        if ($qxlnum > 1) {
-           if ($conf->{ostype} && $conf->{ostype} =~ m/^w/){
+           if ($winversion){
                for(my $i = 1; $i < $qxlnum; $i++){
                    my $pciaddr = print_pci_addr("vga$i", $bridges);
                    push @$cmd, '-device', "qxl,id=vga$i,ram_size=67108864,vram_size=33554432$pciaddr";
@@ -3324,6 +3233,11 @@ sub config_to_command {
            $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);
@@ -3440,6 +3354,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;
 }
 
@@ -3457,6 +3381,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);
@@ -3552,6 +3484,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);
@@ -3784,9 +3725,36 @@ 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) = @_;
 
+    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};
@@ -3799,108 +3767,70 @@ sub qemu_cpu_hotplug {
        if $vcpus > $maxcpus;
 
     my $currentvcpus = $conf->{vcpus} || $maxcpus;
-    die "online cpu unplug is not yet possible\n"
-       if $vcpus < $currentvcpus;
-
-    my $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
-    die "vcpus in running vm is different than configuration\n"
-       if scalar(@{$currentrunningvcpus}) != $currentvcpus;
-
-    for (my $i = $currentvcpus; $i < $vcpus; $i++) {
-       vm_mon_cmd($vmid, "cpu-add", id => int($i));
-    }
-}
-
-sub qemu_memory_hotplug {
-    my ($vmid, $conf, $defaults, $opt, $value) = @_;
-
-    return $value if !check_running($vmid);
-
-    my $memory = $conf->{memory} || $defaults->{memory};
-    $value = $defaults->{memory} if !$value;
-    return $value if $value == $memory;
 
-    my $static_memory = $STATICMEM;
-    my $dimm_memory = $memory - $static_memory;
-
-    die "memory can't be lower than $static_memory MB" if $value < $static_memory;
-    die "you cannot add more memory than $MAX_MEM MB!\n" if $memory > $MAX_MEM;
-
-
-    my $sockets = 1;
-    $sockets = $conf->{sockets} if $conf->{sockets};
+    if ($vcpus < $currentvcpus) {
 
-    if($value > $memory) {
-
-       foreach_dimm($conf, $vmid, $value, $sockets, sub {
-           my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
-
-               return if $current_size <= $conf->{memory};
-
-               eval { vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) };
-               if (my $err = $@) {
-                   eval { qemu_objectdel($vmid, "mem-$name"); };
-                   die $err;
-               }
-
-               eval { vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) };
-               if (my $err = $@) {
-                   eval { qemu_objectdel($vmid, "mem-$name"); };
-                   die $err;
-               }
-               #update conf after each succesful module hotplug
-               $conf->{memory} = $current_size;
-               PVE::QemuConfig->write_config($vmid, $conf);
-       });
-
-    } else {
-
-       foreach_reverse_dimm($conf, $vmid, $value, $sockets, sub {
-           my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
-
-               return if $current_size >= $conf->{memory};
-               print "try to unplug memory dimm $name\n";
+       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;
-               while (1) {
-                   eval { qemu_devicedel($vmid, $name) };
-                   sleep 3;
-                   my $dimm_list = qemu_dimm_list($vmid);
-                   last if !$dimm_list->{$name};
-                   raise_param_exc({ $name => "error unplug memory module" }) if $retry > 5;
+               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 succesful module unplug
-               $conf->{memory} = $current_size;
-
-               eval { qemu_objectdel($vmid, "mem-$name"); };
+               #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;
     }
-}
 
-sub qemu_dimm_list {
-    my ($vmid) = @_;
+    my $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
+    die "vcpus in running vm does not match its configuration\n"
+       if scalar(@{$currentrunningvcpus}) != $currentvcpus;
 
-    my $dimmarray = vm_mon_cmd_nocheck($vmid, "query-memory-devices");
-    my $dimms = {};
+    if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
 
-    foreach my $dimm (@$dimmarray) {
+       for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
+           my $cpustr = print_cpu_device($conf, $i);
+           qemu_deviceadd($vmid, $cpustr);
 
-        $dimms->{$dimm->{data}->{id}}->{id} = $dimm->{data}->{id};
-        $dimms->{$dimm->{data}->{id}}->{node} = $dimm->{data}->{node};
-        $dimms->{$dimm->{data}->{id}}->{addr} = $dimm->{data}->{addr};
-        $dimms->{$dimm->{data}->{id}}->{size} = $dimm->{data}->{size};
-        $dimms->{$dimm->{data}->{id}}->{slot} = $dimm->{data}->{slot};
+           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));
+       }
     }
-    return $dimms;
 }
 
 sub qemu_block_set_io_throttle {
     my ($vmid, $deviceid,
        $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
-       $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max) = @_;
+       $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
+       $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
+       $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
 
     return if !check_running($vmid) ;
 
@@ -3916,7 +3846,13 @@ sub qemu_block_set_io_throttle {
        bps_wr_max => int($bps_wr_max),
        iops_max => int($iops_max),
        iops_rd_max => int($iops_rd_max),
-       iops_wr_max => int($iops_wr_max)
+       iops_wr_max => int($iops_wr_max),
+       bps_max_length => int($bps_max_length),
+       bps_rd_max_length => int($bps_rd_max_length),
+       bps_wr_max_length => int($bps_wr_max_length),
+       iops_max_length => int($iops_max_length),
+       iops_rd_max_length => int($iops_rd_max_length),
+       iops_wr_max_length => int($iops_wr_max_length),
     );
 
 }
@@ -4059,11 +3995,11 @@ sub qemu_volume_snapshot_delete {
 
     my $running = check_running($vmid);
 
-    return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
-
-    return if !$running;
-
-    vm_mon_cmd($vmid, "delete-drive-snapshot", device => $deviceid, name => $snap);
+    if ($running && do_snapshots_with_qemu($storecfg, $volid)){
+       vm_mon_cmd($vmid, "delete-drive-snapshot", device => $deviceid, name => $snap);
+    } else {
+       PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
+    }
 }
 
 sub set_migration_caps {
@@ -4098,6 +4034,7 @@ my $fast_plug_option = {
     'shares' => 1,
     'startup' => 1,
     'description' => 1,
+    'protection' => 1,
 };
 
 # hotplug changes in [PENDING]
@@ -4147,6 +4084,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);
@@ -4164,7 +4107,7 @@ sub vmconfig_hotplug_pending {
                vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
            } elsif ($opt =~ m/^memory$/) {
                die "skip\n" if !$hotplug_features->{memory};
-               qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
+               PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
            } elsif ($opt eq 'cpuunits') {
                cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits});
            } elsif ($opt eq 'cpulimit') {
@@ -4197,6 +4140,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);
@@ -4221,7 +4172,7 @@ sub vmconfig_hotplug_pending {
                                     $vmid, $opt, $value, 1);
            } elsif ($opt =~ m/^memory$/) { #dimms
                die "skip\n" if !$hotplug_features->{memory};
-               $value = qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
+               $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
            } elsif ($opt eq 'cpuunits') {
                cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt});
            } elsif ($opt eq 'cpulimit') {
@@ -4423,7 +4374,7 @@ sub vmconfig_update_disk {
                    # update existing disk
 
                    # skip non hotpluggable value
-                   if (&$safe_num_ne($drive->{discard}, $old_drive->{discard}) ||
+                   if (&$safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
                        &$safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
                        &$safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
                        &$safe_string_ne($drive->{cache}, $old_drive->{cache})) {
@@ -4442,7 +4393,13 @@ sub vmconfig_update_disk {
                        &$safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
                        &$safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
                        &$safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
-                       &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max})) {
+                       &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
+                       &$safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
+                       &$safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
+                       &$safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
+                       &$safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
+                       &$safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
+                       &$safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
 
                        qemu_block_set_io_throttle($vmid,"drive-$opt",
                                                   ($drive->{mbps} || 0)*1024*1024,
@@ -4456,7 +4413,13 @@ sub vmconfig_update_disk {
                                                   ($drive->{mbps_wr_max} || 0)*1024*1024,
                                                   $drive->{iops_max} || 0,
                                                   $drive->{iops_rd_max} || 0,
-                                                  $drive->{iops_wr_max} || 0);
+                                                  $drive->{iops_wr_max} || 0,
+                                                  $drive->{bps_max_length} || 1,
+                                                  $drive->{bps_rd_max_length} || 1,
+                                                  $drive->{bps_wr_max_length} || 1,
+                                                  $drive->{iops_max_length} || 1,
+                                                  $drive->{iops_rd_max_length} || 1,
+                                                  $drive->{iops_wr_max_length} || 1);
 
                    }
 
@@ -4486,7 +4449,7 @@ sub vmconfig_update_disk {
 
 sub vm_start {
     my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
-       $forcemachine, $spice_ticket) = @_;
+       $forcemachine, $spice_ticket, $migration_network, $migration_type, $targetstorage) = @_;
 
     PVE::QemuConfig->lock_config($vmid, sub {
        my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
@@ -4507,6 +4470,54 @@ sub vm_start {
        # set environment variable useful inside network script
        $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
 
+       my $local_volumes = {};
+
+       if ($targetstorage) {
+           foreach_drive($conf, sub {
+               my ($ds, $drive) = @_;
+
+               return if drive_is_cdrom($drive);
+
+               my $volid = $drive->{file};
+
+               return if !$volid;
+
+               my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
+
+               my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+               return if $scfg->{shared};
+               $local_volumes->{$ds} = [$volid, $storeid, $volname];
+           });
+
+           my $format = undef;
+
+           foreach my $opt (sort keys %$local_volumes) {
+
+               my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
+               my $drive = parse_drive($opt, $conf->{$opt});
+
+               #if remote storage is specified, use default format
+               if ($targetstorage && $targetstorage ne "1") {
+                   $storeid = $targetstorage;
+                   my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
+                   $format = $defFormat;
+               } else {
+                   #else we use same format than original
+                   my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+                   $format = qemu_img_format($scfg, $volid);
+               }
+
+               my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, ($drive->{size}/1024));
+               my $newdrive = $drive;
+               $newdrive->{format} = $format;
+               $newdrive->{file} = $newvolid;
+               my $drivestr = PVE::QemuServer::print_drive($vmid, $newdrive);
+               $local_volumes->{$opt} = $drivestr;
+               #pass drive to conf for command line
+               $conf->{$opt} = $drivestr;
+           }
+       }
+
        my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
        my $migrate_port = 0;
@@ -4516,15 +4527,44 @@ sub vm_start {
                my $localip = "localhost";
                my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
                my $nodename = PVE::INotify::nodename();
-               if ($datacenterconf->{migration_unsecure}) {
+
+               if (!defined($migration_type)) {
+                   if (defined($datacenterconf->{migration}->{type})) {
+                       $migration_type = $datacenterconf->{migration}->{type};
+                   } else {
+                       $migration_type = 'secure';
+                   }
+               }
+
+               if ($migration_type eq 'insecure') {
+                   my $migrate_network_addr = PVE::Cluster::get_local_migration_ip($migration_network);
+                   if ($migrate_network_addr) {
+                       $localip = $migrate_network_addr;
+                   } else {
                        $localip = PVE::Cluster::remote_node_ip($nodename, 1);
-                       $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
+                   }
+
+                   $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
                }
+
                my $pfamily = PVE::Tools::get_host_address_family($nodename);
                $migrate_port = PVE::Tools::next_migrate_port($pfamily);
                $migrate_uri = "tcp:${localip}:${migrate_port}";
                push @$cmd, '-incoming', $migrate_uri;
                push @$cmd, '-S';
+
+           } elsif ($statefile eq 'unix') {
+               # 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
+               my $socket_addr = "/run/qemu-server/$vmid.migrate";
+               unlink $socket_addr;
+
+               $migrate_uri = "unix:$socket_addr";
+
+               push @$cmd, '-incoming', $migrate_uri;
+               push @$cmd, '-S';
+
            } else {
                push @$cmd, '-loadstate', $statefile;
            }
@@ -4556,8 +4596,51 @@ sub vm_start {
            eval  { run_command($cmd); };
        }
 
-       eval  { run_command($cmd, timeout => $statefile ? undef : 30,
-                   umask => 0077); };
+       my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
+                                                 : $defaults->{cpuunits};
+
+       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
@@ -4572,8 +4655,27 @@ sub vm_start {
            warn $@ if $@;
        }
 
-       if ($migratedfrom) {
+       #start nbd server for storage migration
+       if ($targetstorage) {
+           my $nodename = PVE::INotify::nodename();
+           my $migrate_network_addr = PVE::Cluster::get_local_migration_ip($migration_network);
+           my $localip = $migrate_network_addr ? $migrate_network_addr : PVE::Cluster::remote_node_ip($nodename, 1);
+           my $pfamily = PVE::Tools::get_host_address_family($nodename);
+           $migrate_port = PVE::Tools::next_migrate_port($pfamily);
+
+           vm_mon_cmd_nocheck($vmid, "nbd-server-start", addr => { type => 'inet', data => { host => "${localip}", port => "${migrate_port}" } } );
+
+           $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
+
+           foreach my $opt (sort keys %$local_volumes) {
+               my $volid = $local_volumes->{$opt};
+               vm_mon_cmd_nocheck($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
+               my $migrate_storage_uri = "nbd:${localip}:${migrate_port}:exportname=drive-$opt";
+               print "storage migration listens on $migrate_storage_uri volume:$volid\n";
+           }
+       }
 
+       if ($migratedfrom) {
            eval {
                set_migration_caps($vmid);
            };
@@ -4588,7 +4690,6 @@ sub vm_start {
            }
 
        } else {
-
            if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) {
                vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
                    if $conf->{balloon};
@@ -4679,7 +4780,7 @@ sub vm_commandline {
 
     my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
 
-    return join(' ', @$cmd);
+    return PVE::Tools::cmd2string($cmd);
 }
 
 sub vm_reset {
@@ -5002,144 +5103,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 {
@@ -5148,11 +5111,11 @@ 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;
@@ -5230,12 +5193,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",
@@ -5249,10 +5213,10 @@ 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*$/) {
+    } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
        my $virtdev = $1;
        my $value = $3;
        my $di = parse_drive($virtdev, $value);
@@ -5554,7 +5518,10 @@ sub restore_vma_archive {
                # 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;
+                   }
                }
            });
 
@@ -5898,7 +5865,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) {
@@ -5936,91 +5903,222 @@ sub qemu_img_format {
 }
 
 sub qemu_drive_mirror {
-    my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized) = @_;
+    my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $skipcomplete, $qga) = @_;
 
-    my $storecfg = PVE::Storage::config();
-    my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid);
+    $jobs = {} if !$jobs;
+
+    my $qemu_target;
+    my $format;
+    $jobs->{"drive-$drive"} = {};
+
+    if ($dst_volid =~ /^nbd:(localhost|[\d\.]+|\[[\d\.:a-fA-F]+\]):(\d+):exportname=(\S+)/) {
+       my $server = $1;
+       my $port = $2;
+       my $exportname = $3;
+
+       $format = "nbd";
+       my $unixsocket = "/run/qemu-server/$vmid.mirror-drive-$drive";
+       $qemu_target = "nbd+unix:///$exportname?socket=$unixsocket";
+       my $cmd = ['socat', "UNIX-LISTEN:$unixsocket,fork", "TCP:$server:$2,connect-timeout=1"];
+
+       my $pid = fork();
+       if (!defined($pid)) {
+           die "forking socat tunnel failed";
+       } elsif ($pid == 0) {
+           exec(@$cmd);
+           exit(-1);
+       } else {
+           $jobs->{"drive-$drive"}->{pid} = $pid;
 
-    my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
+           my $timeout = 0;
+           while (1) {
+               last if -S $unixsocket; 
+               die if $timeout > 5;
+               $timeout++;
+               sleep 1;
+           }
+       }
+    } else {
+       my $storecfg = PVE::Storage::config();
+       my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid);
+
+       my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
 
-    my $format = qemu_img_format($dst_scfg, $dst_volname);
+       $format = qemu_img_format($dst_scfg, $dst_volname);
 
-    my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
+       my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
 
-    my $qemu_target = $is_zero_initialized ? "zeroinit:$dst_path" : $dst_path;
+       $qemu_target = $is_zero_initialized ? "zeroinit:$dst_path" : $dst_path;
+    }
 
     my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target };
     $opts->{format} = $format if $format;
 
-    print "drive mirror is starting (scanning bitmap) : this step can take some minutes/hours, depend of disk size and storage speed\n";
+    print "drive mirror is starting for drive-$drive\n";
 
-    my $finish_job = sub {
-       while (1) {
-           my $stats = vm_mon_cmd($vmid, "query-block-jobs");
-           my $stat = @$stats[0];
-           last if !$stat;
-           sleep 1;
-       }
-    };
+    eval { vm_mon_cmd($vmid, "drive-mirror", %$opts); }; #if a job already run for this device,it's throw an error
+
+    if (my $err = $@) {
+       eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
+       die "mirroring error: $err";
+    }
+
+    qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
+}
+
+sub qemu_drive_mirror_monitor {
+    my ($vmid, $vmiddst, $jobs, $skipcomplete, $qga) = @_;
 
     eval {
-    vm_mon_cmd($vmid, "drive-mirror", %$opts);
+       my $err_complete = 0;
+
        while (1) {
+           die "storage migration timed out\n" if $err_complete > 300;
+
            my $stats = vm_mon_cmd($vmid, "query-block-jobs");
-           my $stat = @$stats[0];
-           die "mirroring job seem to have die. Maybe do you have bad sectors?" if !$stat;
-           die "error job is not mirroring" if $stat->{type} ne "mirror";
 
-           my $busy = $stat->{busy};
-           my $ready = $stat->{ready};
+           my $running_mirror_jobs = {};
+           foreach my $stat (@$stats) {
+               next if $stat->{type} ne 'mirror';
+               $running_mirror_jobs->{$stat->{device}} = $stat;
+           }
+
+           my $readycounter = 0;
+
+           foreach my $job (keys %$jobs) {
+
+               if(defined($jobs->{$job}->{complete}) && !defined($running_mirror_jobs->{$job})) {
+                   print "$job : finished\n";
+                   delete $jobs->{$job};
+                   next;
+               }
+
+               die "$job: mirroring has been cancelled. Maybe do you have bad sectors?" if !defined($running_mirror_jobs->{$job});
 
-           if (my $total = $stat->{len}) {
-               my $transferred = $stat->{offset} || 0;
-               my $remaining = $total - $transferred;
-               my $percent = sprintf "%.2f", ($transferred * 100 / $total);
+               my $busy = $running_mirror_jobs->{$job}->{busy};
+               my $ready = $running_mirror_jobs->{$job}->{ready};
+               if (my $total = $running_mirror_jobs->{$job}->{len}) {
+                   my $transferred = $running_mirror_jobs->{$job}->{offset} || 0;
+                   my $remaining = $total - $transferred;
+                   my $percent = sprintf "%.2f", ($transferred * 100 / $total);
 
-               print "transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n";
+                   print "$job: transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n";
+               }
+
+               $readycounter++ if $running_mirror_jobs->{$job}->{ready} eq 'true';
            }
 
+           last if scalar(keys %$jobs) == 0;
 
-           if ($stat->{ready} eq 'true') {
+           if ($readycounter == scalar(keys %$jobs)) {
+               print "all mirroring jobs are ready \n";
+               last if $skipcomplete; #do the complete later
 
-               last if $vmiddst != $vmid;
+               if ($vmiddst && $vmiddst != $vmid) {
+                   if ($qga) {
+                       print "freeze filesystem\n";
+                       eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-freeze"); };
+                   } else {
+                       print "suspend vm\n";
+                       eval { PVE::QemuServer::vm_suspend($vmid, 1); };
+                   }
+
+                   # if we clone a disk for a new target vm, we don't switch the disk
+                   PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs);
+
+                   if ($qga) {
+                       print "unfreeze filesystem\n";
+                       eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-thaw"); };
+                   } else {
+                       print "resume vm\n";
+                       eval {  PVE::QemuServer::vm_resume($vmid, 1, 1); };
+                   }
 
-               # try to switch the disk if source and destination are on the same guest
-               eval { vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive") };
-               if (!$@) {
-                   &$finish_job();
                    last;
+               } else {
+
+                   foreach my $job (keys %$jobs) {
+                       # try to switch the disk if source and destination are on the same guest
+                       print "$job : Try to complete block job\n";
+
+                       eval { vm_mon_cmd($vmid, "block-job-complete", device => $job) };
+                       if ($@ =~ m/cannot be completed/) {
+                           print "$job : block job cannot be complete. Try again \n";
+                           $err_complete++;
+                       }else {
+                           print "$job : complete ok : flushing pending writes\n";
+                           $jobs->{$job}->{complete} = 1;
+                           eval { qemu_blockjobs_finish_tunnel($vmid, $job, $jobs->{$job}->{pid}) } ;
+                       }
+                   }
                }
-               die $@ if $@ !~ m/cannot be completed/;
            }
            sleep 1;
        }
-
-
     };
     my $err = $@;
 
-    my $cancel_job = sub {
-       vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive");
-       &$finish_job();
-    };
-
     if ($err) {
-       eval { &$cancel_job(); };
+       eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
        die "mirroring error: $err";
     }
 
-    if ($vmiddst != $vmid) {
-       # if we clone a disk for a new target vm, we don't switch the disk
-       &$cancel_job(); # so we call block-job-cancel
+}
+
+sub qemu_blockjobs_cancel {
+    my ($vmid, $jobs) = @_;
+
+    foreach my $job (keys %$jobs) {
+       print "$job: try to cancel block job\n";
+       eval { vm_mon_cmd($vmid, "block-job-cancel", device => $job); };
+       $jobs->{$job}->{cancel} = 1;
     }
+
+    while (1) {
+       my $stats = vm_mon_cmd($vmid, "query-block-jobs");
+
+       my $running_jobs = {};
+       foreach my $stat (@$stats) {
+           $running_jobs->{$stat->{device}} = $stat;
+       }
+
+       foreach my $job (keys %$jobs) {
+
+           if(defined($jobs->{$job}->{cancel}) && !defined($running_jobs->{$job})) {
+               print "$job : finished\n";
+               eval { qemu_blockjobs_finish_tunnel($vmid, $job, $jobs->{$job}->{pid}) } ;
+               delete $jobs->{$job};
+           }
+       }
+
+       last if scalar(keys %$jobs) == 0;
+
+       sleep 1;
+    }
+}
+
+sub qemu_blockjobs_finish_tunnel {
+   my ($vmid, $job, $cpid) = @_;
+
+   return if !$cpid;
+
+   for (my $i = 1; $i < 20; $i++) {
+       my $waitpid = waitpid($cpid, WNOHANG);
+       last if (defined($waitpid) && ($waitpid == $cpid));
+       if ($i == 10) {
+           kill(15, $cpid);
+       } elsif ($i >= 15) {
+           kill(9, $cpid);
+       }
+       sleep (1);
+    }
+    unlink "/run/qemu-server/$vmid.mirror-$job";
 }
 
 sub clone_disk {
     my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
-       $newvmid, $storage, $format, $full, $newvollist) = @_;
+       $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga) = @_;
 
     my $newvolid;
 
@@ -6029,6 +6127,7 @@ sub clone_disk {
        $newvolid = PVE::Storage::vdisk_clone($storecfg,  $drive->{file}, $newvmid, $snapname);
        push @$newvollist, $newvolid;
     } else {
+
        my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
        $storeid = $storage if $storage;
 
@@ -6048,13 +6147,20 @@ 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) {
            qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
        } else {
-           qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit);
+
+           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, $jobs, $skipcomplete, $qga);
        }
     }
 
@@ -6085,6 +6191,13 @@ sub get_current_qemu_machine {
     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) = @_;
 
@@ -6191,6 +6304,52 @@ sub scsihw_infos {
     return ($maxdev, $controller, $controller_prefix);
 }
 
+sub add_hyperv_enlighments {
+    my ($cpuFlags, $winversion, $machine_type, $kvmver, $nokvm, $bios, $gpu_passthrough) = @_;
+
+    return if $nokvm;
+    return if $winversion < 6;
+    return if $bios && $bios eq 'ovmf' && $winversion < 8;
+
+    push @$cpuFlags , 'hv_vendor_id=proxmox' if $gpu_passthrough;
+
+    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';
+    }
+}
+
+sub windows_version {
+    my ($ostype) = @_;
+
+    return 0 if !$ostype;
+
+    my $winversion = 0;
+
+    if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
+        $winversion = 5;
+    } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
+        $winversion = 6;
+    } elsif ($ostype =~ m/^win(\d+)$/) {
+        $winversion = $1;
+    }
+
+    return $winversion;
+}
+
 # bash completion helper
 
 sub complete_backup_archives {
@@ -6264,4 +6423,10 @@ sub complete_storage {
     return $res;
 }
 
+sub nbd_stop {
+    my ($vmid) = @_;
+
+    vm_mon_cmd($vmid, 'nbd-server-stop');
+}
+
 1;