]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
fix efidisks on storages with minimum sizes bigger than OVMF_VARS.fd
[qemu-server.git] / PVE / QemuServer.pm
index 2ef8bff5581192730e5894960a514f4d8968245f..611fb4509e00d207931ccf153458b333dcd0ff30 100644 (file)
@@ -545,7 +545,7 @@ EODESCR
        optional => 1,
        description => "Emulated CPU type.",
        type => 'string',
-       format => $PVE::QemuServer::CPUConfig::cpu_fmt,
+       format => 'pve-vm-cpu-conf',
     },
     parent => get_standard_option('pve-snapshot-name', {
        optional => 1,
@@ -1091,10 +1091,6 @@ for my $key (keys %{$PVE::QemuServer::Drive::drivedesc_hash}) {
     $confdesc->{$key} = $PVE::QemuServer::Drive::drivedesc_hash->{$key};
 }
 
-for (my $i = 0; $i < $PVE::QemuServer::Drive::MAX_UNUSED_DISKS; $i++) {
-    $confdesc->{"unused$i"} = $PVE::QemuServer::Drive::unuseddesc;
-}
-
 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++)  {
     $confdesc->{"usb$i"} = $usbdesc;
 }
@@ -2962,6 +2958,8 @@ sub config_to_command {
     my $version_guard = sub {
        my ($major, $minor, $pve) = @_;
        return 0 if !min_version($machine_version, $major, $minor, $pve);
+       my $max_pve = PVE::QemuServer::Machine::get_pve_version("$major.$minor");
+       return 1 if min_version($machine_version, $major, $minor, $max_pve+1);
        $required_pve_version = $pve if $pve && $pve > $required_pve_version;
        return 1;
     };
@@ -3053,8 +3051,14 @@ sub config_to_command {
            $format = 'raw';
        }
 
+       my $size_str = "";
+
+       if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
+           $size_str = ",size=" . (-s $ovmf_vars);
+       }
+
        push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
-       push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
+       push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0$size_str,file=$path";
     }
 
     # load q35 config
@@ -4708,7 +4712,7 @@ sub vmconfig_update_disk {
 sub vm_start {
     my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
        $forcemachine, $spice_ticket, $migration_network, $migration_type,
-       $targetstorage, $timeout, $nbd_protocol_version) = @_;
+       $targetstorage, $timeout, $nbd_protocol_version, $replicated_volumes) = @_;
 
     PVE::QemuConfig->lock_config($vmid, sub {
        my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
@@ -4762,6 +4766,12 @@ sub vm_start {
            foreach my $opt (sort keys %$local_volumes) {
 
                my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
+               if ($replicated_volumes->{$volid}) {
+                   # re-use existing, replicated volume with bitmap on source side
+                   $local_volumes->{$opt} = $conf->{${opt}};
+                   print "re-using replicated volume: $opt - $volid\n";
+                   next;
+               }
                my $drive = parse_drive($opt, $conf->{$opt});
 
                # If a remote storage is specified and the format of the original
@@ -5008,11 +5018,7 @@ sub vm_start {
            foreach my $opt (sort keys %$local_volumes) {
                my $drivestr = $local_volumes->{$opt};
                mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
-               if ($nbd_protocol_version > 0 && $migration_type eq 'secure') {
-                   print "storage migration listens on $migrate_storage_uri:exportname=drive-$opt volume:$drivestr\n";
-               } else {
-                   print "storage migration listens on $migrate_storage_uri:exportname=drive-$opt volume:$drivestr\n";
-               }
+               print "storage migration listens on $migrate_storage_uri:exportname=drive-$opt volume:$drivestr\n";
            }
        }
 
@@ -6535,7 +6541,7 @@ sub qemu_img_format {
 }
 
 sub qemu_drive_mirror {
-    my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit) = @_;
+    my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
 
     $jobs = {} if !$jobs;
 
@@ -6562,6 +6568,12 @@ sub qemu_drive_mirror {
     my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target };
     $opts->{format} = $format if $format;
 
+    if (defined($src_bitmap)) {
+       $opts->{sync} = 'incremental';
+       $opts->{bitmap} = $src_bitmap;
+       print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
+    }
+
     if (defined($bwlimit)) {
        $opts->{speed} = $bwlimit * 1024;
        print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
@@ -6580,10 +6592,14 @@ sub qemu_drive_mirror {
     qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $completion, $qga);
 }
 
+# $completion can be either
+# 'complete': wait until all jobs are ready, block-job-complete them (default)
+# 'cancel': wait until all jobs are ready, block-job-cancel them
+# 'skip': wait until all jobs are ready, return with block jobs in ready state
 sub qemu_drive_mirror_monitor {
     my ($vmid, $vmiddst, $jobs, $completion, $qga) = @_;
 
-    $completion //= 'wait'; # same semantic as with 'skipcomplete' before
+    $completion //= 'complete';
 
     eval {
        my $err_complete = 0;
@@ -6659,9 +6675,9 @@ sub qemu_drive_mirror_monitor {
                        print "$job: Completing block job...\n";
 
                        my $op;
-                       if ($completion eq 'wait') {
+                       if ($completion eq 'complete') {
                            $op = 'block-job-complete';
-                       } elsif ($completion eq 'wait_noswap') {
+                       } elsif ($completion eq 'cancel') {
                            $op = 'block-job-cancel';
                        } else {
                            die "invalid completion value: $completion\n";
@@ -6722,7 +6738,7 @@ sub qemu_blockjobs_cancel {
 
 sub clone_disk {
     my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
-       $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
+       $newvmid, $storage, $format, $full, $newvollist, $jobs, $completion, $qga, $bwlimit, $conf) = @_;
 
     my $newvolid;
 
@@ -6745,6 +6761,8 @@ sub clone_disk {
            $name .= ".$dst_format" if $dst_format ne 'raw';
            $snapname = undef;
            $size = PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE;
+       } elsif ($drivename eq 'efidisk0') {
+           $size = get_efivars_size($conf);
        }
        $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024));
        push @$newvollist, $newvolid;
@@ -6758,7 +6776,16 @@ sub clone_disk {
        my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
        if (!$running || $snapname) {
            # TODO: handle bwlimits
-           qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
+           if ($drivename eq 'efidisk0') {
+               # the relevant data on the efidisk may be smaller than the source
+               # e.g. on RBD/ZFS, so we use dd to copy only the amount
+               # that is given by the OVMF_VARS.fd
+               my $src_path = PVE::Storage::path($storecfg, $drive->{file});
+               my $dst_path = PVE::Storage::path($storecfg, $newvolid);
+               run_command(['qemu-img', 'dd', '-n', '-O', $dst_format, "bs=1", "count=$size", "if=$src_path", "of=$dst_path"]);
+           } else {
+               qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
+           }
        } else {
 
            my $kvmver = get_running_qemu_version ($vmid);
@@ -6767,7 +6794,7 @@ sub clone_disk {
                    if $drive->{iothread};
            }
 
-           qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga, $bwlimit);
+           qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $completion, $qga, $bwlimit);
        }
     }
 
@@ -6810,6 +6837,26 @@ sub qemu_use_old_bios_files {
     return ($use_old_bios_files, $machine_type);
 }
 
+sub get_efivars_size {
+    my ($conf) = @_;
+    my $arch = get_vm_arch($conf);
+    my (undef, $ovmf_vars) = get_ovmf_files($arch);
+    die "uefi vars image '$ovmf_vars' not found\n" if ! -f $ovmf_vars;
+    return -s $ovmf_vars;
+}
+
+sub update_efidisk_size {
+    my ($conf) = @_;
+
+    return if !defined($conf->{efidisk0});
+
+    my $disk = PVE::QemuServer::parse_drive('efidisk0', $conf->{efidisk0});
+    $disk->{size} = get_efivars_size($conf);
+    $conf->{efidisk0} = print_drive($disk);
+
+    return;
+}
+
 sub create_efidisk($$$$$) {
     my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;