use PVE::QemuServer::Helpers qw(config_aware_timeout min_version windows_version);
use PVE::QemuServer::Cloudinit;
use PVE::QemuServer::CGroup;
-use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
+use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options get_cpu_bitness is_native_arch);
use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
use PVE::QemuServer::Machine;
use PVE::QemuServer::Memory qw(get_current_memory);
maximum => 512,
},
clipboard => {
- description => 'Enable a specific clipboard. If not set, depending on'
- .' the display type the SPICE one will be added.',
+ description => 'Enable a specific clipboard. If not set, depending on the display type the'
+ .' SPICE one will be added. Migration with VNC clipboard is not yet supported!',
type => 'string',
enum => ['vnc'],
optional => 1,
}
}
-sub scsi_inquiry {
- my($fh, $noerr) = @_;
-
- my $SG_IO = 0x2285;
- my $SG_GET_VERSION_NUM = 0x2282;
-
- my $versionbuf = "\x00" x 8;
- my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
- if (!$ret) {
- die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
- return;
- }
- my $version = unpack("I", $versionbuf);
- if ($version < 30000) {
- die "scsi generic interface too old\n" if !$noerr;
- return;
- }
-
- my $buf = "\x00" x 36;
- my $sensebuf = "\x00" x 8;
- my $cmd = pack("C x3 C x1", 0x12, 36);
-
- # see /usr/include/scsi/sg.h
- my $sg_io_hdr_t = "i i C C s I P P P I I i P C C C C S S i I I";
-
- my $packet = pack(
- $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
- );
-
- $ret = ioctl($fh, $SG_IO, $packet);
- if (!$ret) {
- die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
- return;
- }
-
- my @res = unpack($sg_io_hdr_t, $packet);
- if ($res[17] || $res[18]) {
- die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
- return;
- }
-
- my $res = {};
- $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
-
- $res->{removable} = $res->{removable} & 128 ? 1 : 0;
- $res->{type} &= 0x1F;
-
- return $res;
-}
-
-sub path_is_scsi {
- my ($path) = @_;
-
- my $fh = IO::File->new("+<$path") || return;
- my $res = scsi_inquiry($fh, 1);
- close($fh);
-
- return $res;
-}
-
sub print_tabletdevice_full {
my ($conf, $arch) = @_;
my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
my $unit = $drive->{index} % $maxdev;
- my $devicetype = 'hd';
- my $path = '';
- if (drive_is_cdrom($drive)) {
- $devicetype = 'cd';
- } else {
- if ($drive->{file} =~ m|^/|) {
- $path = $drive->{file};
- if (my $info = path_is_scsi($path)) {
- if ($info->{type} == 0 && $drive->{scsiblock}) {
- $devicetype = 'block';
- } elsif ($info->{type} == 1) { # tape
- $devicetype = 'generic';
- }
- }
- } else {
- $path = PVE::Storage::path($storecfg, $drive->{file});
- }
- # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
- my $version = extract_version($machine_type, kvm_user_version());
- if ($path =~ m/^iscsi\:\/\// &&
- !min_version($version, 4, 1)) {
- $devicetype = 'generic';
- }
- }
+ my $machine_version = extract_version($machine_type, kvm_user_version());
+ my $devicetype = PVE::QemuServer::Drive::get_scsi_devicetype(
+ $drive, $storecfg, $machine_version);
if (!$conf->{scsihw} || $conf->{scsihw} =~ m/^lsi/ || $conf->{scsihw} eq 'pvscsi') {
$device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
}
$device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
+ # only scsi-hd and scsi-cd support passing vendor and product information
+ if ($devicetype eq 'hd' || $devicetype eq 'cd') {
+ if (my $vendor = $drive->{vendor}) {
+ $device .= ",vendor=$vendor";
+ }
+ if (my $product = $drive->{product}) {
+ $device .= ",product=$product";
+ }
+ }
+
} elsif ($drive->{interface} eq 'ide' || $drive->{interface} eq 'sata') {
my $maxdev = ($drive->{interface} eq 'sata') ? $PVE::QemuServer::Drive::MAX_SATA_DISKS : 2;
my $controller = int($drive->{index} / $maxdev);
if length($ifname) >= 16;
my $vhostparam = '';
- if (is_native($arch)) {
+ if (is_native_arch($arch)) {
$vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio';
}
return $1 || 1;
}
-sub is_native($) {
- my ($arch) = @_;
- return get_host_arch() eq $arch;
-}
-
sub get_vm_arch {
my ($conf) = @_;
return $conf->{arch} // get_host_arch();
};
sub get_command_for_arch($) {
my ($arch) = @_;
- return '/usr/bin/kvm' if is_native($arch);
+ return '/usr/bin/kvm' if is_native_arch($arch);
my $cmd = $Arch2Qemu->{$arch}
or die "don't know how to emulate architecture '$arch'\n";
my $machine_type = get_vm_machine($conf, $forcemachine, $arch, $add_pve_version);
my $machine_version = extract_version($machine_type, $kvmver);
- $kvm //= 1 if is_native($arch);
+ $kvm //= 1 if is_native_arch($arch);
$machine_version =~ m/(\d+)\.(\d+)/;
my ($machine_major, $machine_minor) = ($1, $2);
}
if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
+ die "OVMF (UEFI) BIOS is not supported on 32-bit CPU types\n"
+ if !$forcecpu && get_cpu_bitness($conf->{cpu}, $arch) == 32;
+
my ($code_drive_str, $var_drive_str) =
print_ovmf_drive_commandlines($conf, $storecfg, $vmid, $arch, $q35, $version_guard);
push $cmd->@*, '-drive', $code_drive_str;
if ($hotplug_features->{cpu} && min_version($machine_version, 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);
+ my $cpustr = print_cpu_device($conf, $arch, $i);
push @$cmd, '-device', $cpustr;
}
if scalar(@{$currentrunningvcpus}) != $currentvcpus;
if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
+ my $arch = get_vm_arch($conf);
for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
- my $cpustr = print_cpu_device($conf, $i);
+ my $cpustr = print_cpu_device($conf, $arch, $i);
qemu_deviceadd($vmid, $cpustr);
my $retry = 0;
}
sub qemu_volume_snapshot_delete {
- my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
+ my ($vmid, $storecfg, $volid, $snap) = @_;
my $running = check_running($vmid);
+ my $attached_deviceid;
- if($running) {
-
- $running = undef;
+ if ($running) {
my $conf = PVE::QemuConfig->load_config($vmid);
PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
- $running = 1 if $drive->{file} eq $volid;
+ $attached_deviceid = "drive-$ds" if $drive->{file} eq $volid;
});
}
- if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
- mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync', device => $deviceid, name => $snap);
+ if ($attached_deviceid && do_snapshots_with_qemu($storecfg, $volid, $attached_deviceid)) {
+ mon_cmd(
+ $vmid,
+ 'blockdev-snapshot-delete-internal-sync',
+ device => $attached_deviceid,
+ name => $snap,
+ );
} else {
- PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
+ PVE::Storage::volume_snapshot_delete(
+ $storecfg, $volid, $snap, $attached_deviceid ? 1 : undef);
}
}
safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
PVE::Network::tap_unplug($iface);
+ #set link_down in guest if bridge or vlan change to notify guest (dhcp renew for example)
+ if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
+ safe_num_ne($oldnet->{tag}, $newnet->{tag})) {
+ qemu_set_link_status($vmid, $opt, 0);
+ }
+
if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge})) {
if ($have_sdn) {
PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{macaddr}, $conf->{name});
} else {
PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
}
+
+ #set link_up in guest if bridge or vlan change to notify guest (dhcp renew for example)
+ if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
+ safe_num_ne($oldnet->{tag}, $newnet->{tag})) {
+ qemu_set_link_status($vmid, $opt, 1);
+ }
+
} elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
# Rate can be applied on its own but any change above needs to
# include the rate in tap_plug since OVS resets everything.
if ($hotplug) {
if ($have_sdn) {
PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{name}, $newnet->{macaddr}, $vmid, undef, 1);
+ PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{macaddr}, $vmid, $conf->{name});
}
vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
} else {
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->{product}, $old_drive->{product}) ||
safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
safe_string_ne($drive->{ssd}, $old_drive->{ssd}) ||
+ safe_string_ne($drive->{vendor}, $old_drive->{vendor}) ||
safe_string_ne($drive->{ro}, $old_drive->{ro})) {
die "skip\n";
}
$migrate->{addr} = "[$migrate->{addr}]" if Net::IP::ip_is_ipv6($migrate->{addr});
}
- my $pfamily = PVE::Tools::get_host_address_family($nodename);
- $migrate->{port} = PVE::Tools::next_migrate_port($pfamily);
- $migrate->{uri} = "tcp:$migrate->{addr}:$migrate->{port}";
- push @$cmd, '-incoming', $migrate->{uri};
+ # see #4501: port reservation should be done close to usage - tell QEMU where to listen
+ # via QMP later
+ push @$cmd, '-incoming', 'defer';
push @$cmd, '-S';
} elsif ($statefile eq 'unix') {
eval { PVE::QemuServer::PCI::reserve_pci_usage($pci_reserve_list, $vmid, undef, $pid) };
warn $@ if $@;
- if (defined($res->{migrate})) {
- print "migration listens on $res->{migrate}->{uri}\n";
+ if (defined(my $migrate = $res->{migrate})) {
+ if ($migrate->{proto} eq 'tcp') {
+ my $nodename = nodename();
+ my $pfamily = PVE::Tools::get_host_address_family($nodename);
+ $migrate->{port} = PVE::Tools::next_migrate_port($pfamily);
+ $migrate->{uri} = "tcp:$migrate->{addr}:$migrate->{port}";
+ mon_cmd($vmid, "migrate-incoming", uri => $migrate->{uri});
+ }
+ print "migration listens on $migrate->{uri}\n";
} elsif ($statefile) {
eval { mon_cmd($vmid, "cont"); };
warn $@ if $@;
my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
# some nvidia vgpu driver versions want to clean the mdevs up themselves, and error
- # out when we do it first. so wait for 10 seconds and then try it
- if ($d->{ids}->[0]->[0]->{vendor} =~ m/^(0x)?10de$/) {
- sleep 10;
+ # out when we do it first. so wait for up to 10 seconds and then try it manually
+ if ($d->{ids}->[0]->[0]->{vendor} =~ m/^(0x)?10de$/ && -e $dev_sysfs_dir) {
+ my $count = 0;
+ while (-e $dev_sysfs_dir && $count < 10) {
+ sleep 1;
+ $count++;
+ }
+ print "waited $count seconds for mediated device driver finishing clean up\n";
}
- PVE::SysFSTools::file_write("$dev_sysfs_dir/remove", "1") if -e $dev_sysfs_dir;
+ if (-e $dev_sysfs_dir) {
+ print "actively clean up mediated device with UUID $uuid\n";
+ PVE::SysFSTools::file_write("$dev_sysfs_dir/remove", "1");
+ }
}
}
PVE::QemuServer::PCI::remove_pci_reservation($vmid);