X-Git-Url: https://git.proxmox.com/?p=qemu-server.git;a=blobdiff_plain;f=PVE%2FQemuServer.pm;h=3b642863fce715d55b66d3393fa48f480e71071b;hp=035d8b7084145d50283b34cb992bdde68a8b7350;hb=b14477e718969468a9a5ef639533b9ff620e84ec;hpb=b4496b9ed5994cb624806db4edf5dbc24576bd9e diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 035d8b7..3b64286 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -34,6 +34,7 @@ use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr); use PVE::QemuServer::Memory; use PVE::QemuServer::USB qw(parse_usb_device); use PVE::QemuServer::Cloudinit; +use PVE::SysFSTools; use PVE::Systemd; use Time::HiRes qw(gettimeofday); use File::Copy qw(copy); @@ -118,8 +119,6 @@ mkdir $var_run_tmpdir; my $lock_dir = "/var/lock/qemu-server"; mkdir $lock_dir; -my $pcisysfs = "/sys/bus/pci"; - my $cpu_vendor_list = { # Intel CPUs 486 => 'GenuineIntel', @@ -187,6 +186,13 @@ my $cpu_fmt = { optional => 1, default => 0 }, + 'hv-vendor-id' => { + type => 'string', + pattern => qr/[a-zA-Z0-9]{1,12}/, + format_description => 'vendor-id', + description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.', + optional => 1, + }, flags => { description => "List of additional CPU flags separated by ';'." . " Use '+FLAG' to enable, '-FLAG' to disable a flag." @@ -238,7 +244,7 @@ my $vga_fmt = { default => 'std', optional => 1, default_key => 1, - enum => [qw(cirrus qxl qxl2 qxl3 qxl4 serial0 serial1 serial2 serial3 std virtio vmware)], + enum => [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)], }, memory => { description => "Sets the VGA memory (in MiB). Has no effect with serial display.", @@ -278,7 +284,7 @@ my $confdesc = { optional => 1, type => 'string', description => "Lock/unlock the VM.", - enum => [qw(migrate backup snapshot rollback)], + enum => [qw(backup clone create migrate rollback snapshot snapshot-delete)], }, cpulimit => { optional => 1, @@ -1203,8 +1209,7 @@ my $usbdesc = { }; PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc); -# NOTE: the match-groups of this regex are used in parse_hostpci -my $PCIRE = qr/([a-f0-9]{2}:[a-f0-9]{2})(?:\.([a-f0-9]))?/; +my $PCIRE = qr/[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/; my $hostpci_fmt = { host => { default_key => 1, @@ -1245,6 +1250,17 @@ EODESCR optional => 1, default => 0, }, + 'mdev' => { + type => 'string', + format_description => 'string', + pattern => '[^/\.:]+', + optional => 1, + description => <{model} eq 'virtio'; + $vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio'; } my $vmname = $conf->{name} || "vm$vmid"; @@ -2014,7 +2032,7 @@ sub print_vga_device { my ($conf, $vga, $arch, $machine, $id, $qxlnum, $bridges) = @_; my $type = $vga_map->{$vga->{type}}; - if ($type eq 'virtio-vga' && $arch eq 'aarch64') { + if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') { $type = 'virtio-gpu'; } my $vgamem_mb = $vga->{memory}; @@ -2102,16 +2120,12 @@ sub parse_hostpci { my @idlist = split(/;/, $res->{host}); delete $res->{host}; foreach my $id (@idlist) { - if ($id =~ /^$PCIRE$/) { - 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"; + if ($id =~ m/\./) { # full id 00:00.1 + push @{$res->{pciid}}, { + id => $id, + }; + } else { # partial id 00:00 + $res->{pciid} = PVE::SysFSTools::lspci($id); } } return $res; @@ -2455,15 +2469,6 @@ sub check_type { } } -sub check_iommu_support{ - #fixme : need to check IOMMU support - #http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM - - my $iommu=1; - return $iommu; - -} - sub touch_config { my ($vmid) = @_; @@ -3333,11 +3338,13 @@ sub get_cpu_options { if ($arch eq 'aarch64') { $cpu = 'cortex-a57'; } + my $hv_vendor_id; 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}; $kvm_off = 1 if $cpuconf->{hidden}; + $hv_vendor_id = $cpuconf->{'hv-vendor-id'}; if (defined(my $flags = $cpuconf->{flags})) { push @$cpuFlags, split(";", $flags); @@ -3359,7 +3366,7 @@ sub get_cpu_options { push @$cpuFlags , '+kvm_pv_eoi' if $kvm; } - add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough) if $kvm; + add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm; push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64'; @@ -3528,7 +3535,13 @@ sub config_to_command { my $pcie = $d->{pcie}; if($pcie){ die "q35 machine model is not enabled" if !$q35; - $pciaddr = print_pcie_addr("hostpci$i"); + # win7 wants to have the pcie devices directly on the pcie bus + # instead of in the root port + if ($winversion == 7) { + $pciaddr = print_pcie_addr("hostpci${i}bus0"); + } else { + $pciaddr = print_pcie_addr("hostpci$i"); + } }else{ $pciaddr = print_pci_addr("hostpci$i", $bridges, $arch, $machine_type); } @@ -3540,7 +3553,7 @@ sub config_to_command { if ($d->{'x-vga'}) { $xvga = ',x-vga=on'; $kvm_off = 1; - $vga->{type} = 'none'; + $vga->{type} = 'none' if !defined($conf->{vga}); $gpu_passthrough = 1; if ($conf->{bios} && $conf->{bios} eq 'ovmf') { @@ -3549,6 +3562,14 @@ sub config_to_command { } my $pcidevices = $d->{pciid}; my $multifunction = 1 if @$pcidevices > 1; + my $sysfspath; + if ($d->{mdev} && scalar(@$pcidevices) == 1) { + my $id = $pcidevices->[0]->{id}; + my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i); + $sysfspath = "/sys/bus/pci/devices/0000:$id/$uuid"; + } elsif ($d->{mdev}) { + warn "ignoring mediated device with multifunction device\n"; + } my $j=0; foreach my $pcidevice (@$pcidevices) { @@ -3557,7 +3578,13 @@ sub config_to_command { $id .= ".$j" if $multifunction; my $addr = $pciaddr; $addr .= ".$j" if $multifunction; - my $devicestr = "vfio-pci,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr"; + my $devicestr = "vfio-pci"; + if ($sysfspath) { + $devicestr .= ",sysfsdev=$sysfspath"; + } else { + $devicestr .= ",host=$pcidevice->{id}"; + } + $devicestr .= ",id=$id$addr"; if($j == 0){ $devicestr .= "$rombar$xvga"; @@ -3873,12 +3900,6 @@ sub config_to_command { } } - # add custom args - if ($conf->{args}) { - my $aa = PVE::Tools::split_args($conf->{args}); - push @$cmd, @$aa; - } - push @$cmd, @$devices; push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags); @@ -3887,6 +3908,12 @@ sub config_to_command { push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags); + # add custom args + if ($conf->{args}) { + my $aa = PVE::Tools::split_args($conf->{args}); + push @$cmd, @$aa; + } + return wantarray ? ($cmd, $vollist, $spice_port) : $cmd; } @@ -5147,13 +5174,21 @@ sub vm_start { next if !$d; my $pcidevices = $d->{pciid}; foreach my $pcidevice (@$pcidevices) { - my $pciid = $pcidevice->{id}.".".$pcidevice->{function}; + my $pciid = $pcidevice->{id}; - my $info = pci_device_info("0000:$pciid"); - die "IOMMU not present\n" if !check_iommu_support(); + my $info = PVE::SysFSTools::pci_device_info("0000:$pciid"); + die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support(); die "no pci device info for device '$pciid'\n" if !$info; - die "can't unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid); - die "can't reset pci device '$pciid'\n" if $info->{has_fl_reset} and !pci_dev_reset($info); + + if ($d->{mdev}) { + my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i); + PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev}); + } else { + die "can't unbind/bind pci group to vfio '$pciid'\n" + if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid); + die "can't reset pci device '$pciid'\n" + if $info->{has_fl_reset} and !PVE::SysFSTools::pci_dev_reset($info); + } } } @@ -5337,10 +5372,21 @@ sub vm_human_monitor_command { } sub vm_commandline { - my ($storecfg, $vmid) = @_; + my ($storecfg, $vmid, $snapname) = @_; my $conf = PVE::QemuConfig->load_config($vmid); + if ($snapname) { + my $snapshot = $conf->{snapshots}->{$snapname}; + die "snapshot '$snapname' does not exist\n" + if !defined($snapshot); + my $digest = $conf->{digest}; + + # we need the digest of the file + $snapshot->{digest} = $conf->{digest}; + $conf = $snapshot; + } + my $defaults = load_defaults(); my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults); @@ -5393,6 +5439,18 @@ sub vm_stop_cleanup { unlink "/var/run/qemu-server/${vmid}.$ext"; } + foreach my $key (keys %$conf) { + next if $key !~ m/^hostpci(\d+)$/; + my $hostpciindex = $1; + my $d = parse_hostpci($conf->{$key}); + my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $hostpciindex); + + foreach my $pci (@{$d->{pciid}}) { + my $pciid = $pci->{id}; + PVE::SysFSTools::pci_cleanup_mdev_device($pciid, $uuid); + } + } + vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes; }; warn $@ if $@; # avoid errors - just warn @@ -5558,123 +5616,6 @@ sub vm_destroy { }); } -# pci helpers - -sub file_write { - my ($filename, $buf) = @_; - - my $fh = IO::File->new($filename, "w"); - return undef if !$fh; - - my $res = print $fh $buf; - - $fh->close(); - - return $res; -} - -sub pci_device_info { - my ($name) = @_; - - my $res; - - return undef if $name !~ m/^([a-f0-9]{4}):([a-f0-9]{2}):([a-f0-9]{2})\.([a-f0-9])$/; - my ($domain, $bus, $slot, $func) = ($1, $2, $3, $4); - - my $irq = file_read_firstline("$pcisysfs/devices/$name/irq"); - return undef if !defined($irq) || $irq !~ m/^\d+$/; - - my $vendor = file_read_firstline("$pcisysfs/devices/$name/vendor"); - return undef if !defined($vendor) || $vendor !~ s/^0x//; - - my $product = file_read_firstline("$pcisysfs/devices/$name/device"); - return undef if !defined($product) || $product !~ s/^0x//; - - $res = { - name => $name, - vendor => $vendor, - product => $product, - domain => $domain, - bus => $bus, - slot => $slot, - func => $func, - irq => $irq, - has_fl_reset => -f "$pcisysfs/devices/$name/reset" || 0, - }; - - return $res; -} - -sub pci_dev_reset { - my ($dev) = @_; - - my $name = $dev->{name}; - - my $fn = "$pcisysfs/devices/$name/reset"; - - return file_write($fn, "1"); -} - -sub pci_dev_bind_to_vfio { - my ($dev) = @_; - - my $name = $dev->{name}; - - my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; - - if (!-d $vfio_basedir) { - system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); - } - die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; - - my $testdir = "$vfio_basedir/$name"; - return 1 if -d $testdir; - - my $data = "$dev->{vendor} $dev->{product}"; - return undef if !file_write("$vfio_basedir/new_id", $data); - - my $fn = "$pcisysfs/devices/$name/driver/unbind"; - if (!file_write($fn, $name)) { - return undef if -f $fn; - } - - $fn = "$vfio_basedir/bind"; - if (! -d $testdir) { - return undef if !file_write($fn, $name); - } - - return -d $testdir; -} - -sub pci_dev_group_bind_to_vfio { - my ($pciid) = @_; - - my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; - - if (!-d $vfio_basedir) { - system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); - } - die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; - - # get IOMMU group devices - opendir(my $D, "$pcisysfs/devices/0000:$pciid/iommu_group/devices/") || die "Cannot open iommu_group: $!\n"; - my @devs = grep /^0000:/, readdir($D); - closedir($D); - - foreach my $pciid (@devs) { - $pciid =~ m/^([:\.\da-f]+)$/ or die "PCI ID $pciid not valid!\n"; - - # pci bridges, switches or root ports are not supported - # they have a pci_bus subdirectory so skip them - next if (-e "$pcisysfs/devices/$pciid/pci_bus"); - - my $info = pci_device_info($1); - pci_dev_bind_to_vfio($info) || die "Cannot bind $pciid to vfio\n"; - } - - return 1; -} - # vzdump restore implementaion sub tar_archive_read_firstfile { @@ -6742,6 +6683,7 @@ sub clone_disk { my $name = undef; if (drive_is_cloudinit($drive)) { $name = "vm-$newvmid-cloudinit"; + $snapname = undef; # cloudinit only supports raw and qcow2 atm: if ($dst_format eq 'qcow2') { $name .= '.qcow2'; @@ -6878,25 +6820,6 @@ sub create_efidisk($$$$$) { return ($volid, $vars_size); } -sub lspci { - - my $devices = {}; - - dir_glob_foreach("$pcisysfs/devices", '[a-f0-9]{4}:([a-f0-9]{2}:[a-f0-9]{2})\.([0-9])', sub { - my (undef, $id, $function) = @_; - my $res = { id => $id, function => $function}; - push @{$devices->{$id}}, $res; - }); - - # Entries should be sorted by functions. - foreach my $id (keys %$devices) { - my $dev = $devices->{$id}; - $devices->{$id} = [ sort { $a->{function} <=> $b->{function} } @$dev ]; - } - - return $devices; -} - sub vm_iothreads_list { my ($vmid) = @_; @@ -6930,12 +6853,15 @@ sub scsihw_infos { } sub add_hyperv_enlightenments { - my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough) = @_; + my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough, $hv_vendor_id) = @_; return if $winversion < 6; return if $bios && $bios eq 'ovmf' && $winversion < 8; - push @$cpuFlags , 'hv_vendor_id=proxmox' if $gpu_passthrough; + if ($gpu_passthrough || defined($hv_vendor_id)) { + $hv_vendor_id //= 'proxmox'; + push @$cpuFlags , "hv_vendor_id=$hv_vendor_id"; + } if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) { push @$cpuFlags , 'hv_spinlocks=0x1fff';