use PVE::QemuServer::Machine;
use PVE::QemuServer::Memory;
use PVE::QemuServer::Monitor qw(mon_cmd);
-use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port);
+use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
use PVE::QemuServer::USB qw(parse_usb_device);
my $have_sdn;
my $MAX_USB_DEVICES = 5;
my $MAX_NETS = 32;
-my $MAX_HOSTPCI_DEVICES = 16;
my $MAX_SERIAL_PORTS = 4;
my $MAX_PARALLEL_PORTS = 3;
my $MAX_NUMA = 8;
description => 'Whether this interface should be disconnected (like pulling the plug).',
optional => 1,
},
+ mtu => {
+ type => 'integer',
+ minimum => 1, maximum => 65520,
+ description => "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
+ optional => 1,
+ },
};
my $netdesc = {
};
PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
-my $PCIRE = qr/([a-f0-9]{4}:)?[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
-my $hostpci_fmt = {
- host => {
- default_key => 1,
- type => 'string',
- pattern => qr/$PCIRE(;$PCIRE)*/,
- format_description => 'HOSTPCIID[;HOSTPCIID2...]',
- 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,
- },
- romfile => {
- type => 'string',
- pattern => '[^,;]+',
- format_description => 'string',
- description => "Custom pci device rom filename (must be located in /usr/share/kvm/).",
- optional => 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,
- },
- 'mdev' => {
- type => 'string',
- format_description => 'string',
- pattern => '[^/\.:]+',
- optional => 1,
- description => <<EODESCR
-The type of mediated device to use.
-An instance of this type will be created on startup of the VM and
-will be cleaned up when the VM stops.
-EODESCR
- }
-};
-PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
-
-my $hostpcidesc = {
- optional => 1,
- type => 'string', format => 'pve-qm-hostpci',
- description => "Map host PCI devices into guest.",
- verbose_description => <<EODESCR,
-Map host PCI devices into guest.
-
-NOTE: This option allows direct access to host hardware. So it is no longer
-possible to migrate such machines - use with special care.
-
-CAUTION: Experimental! User reported problems with this option.
-EODESCR
-};
-PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc);
-
my $serialdesc = {
optional => 1,
type => 'string',
$confdesc->{"serial$i"} = $serialdesc;
}
-for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
- $confdesc->{"hostpci$i"} = $hostpcidesc;
+for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
+ $confdesc->{"hostpci$i"} = $PVE::QemuServer::PCI::hostpcidesc;
}
for my $key (keys %{$PVE::QemuServer::Drive::drivedesc_hash}) {
}
$tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
+ if (my $mtu = $net->{mtu}) {
+ if ($net->{model} eq 'virtio' && $net->{bridge}) {
+ my $bridge_mtu = PVE::Network::read_bridge_mtu($net->{bridge});
+ if ($mtu == 1) {
+ $mtu = $bridge_mtu;
+ } elsif ($mtu < 576) {
+ die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
+ } elsif ($mtu > $bridge_mtu) {
+ die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
+ }
+ $tmpstr .= ",host_mtu=$mtu";
+ } else {
+ warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
+ }
+ }
+
if ($use_old_bios_files) {
my $romfile;
if ($device eq 'virtio-net-pci') {
return $res;
}
-sub parse_hostpci {
- my ($value) = @_;
-
- return undef if !$value;
-
- my $res = PVE::JSONSchema::parse_property_string($hostpci_fmt, $value);
-
- my @idlist = split(/;/, $res->{host});
- delete $res->{host};
- foreach my $id (@idlist) {
- my $devs = PVE::SysFSTools::lspci($id);
- die "no PCI device found for '$id'\n" if !scalar(@$devs);
- push @{$res->{pciid}}, @$devs;
- }
- return $res;
-}
-
# netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
sub parse_net {
my ($data) = @_;
}
sub audio_devs {
- my ($audio, $audiopciaddr) = @_;
+ my ($audio, $audiopciaddr, $machine_version) = @_;
my $devs = [];
my $id = $audio->{dev_id};
- my $audiodev = "audiodev=$audio->{backend_id}";
+ my $audiodev = "";
+ if (min_version($machine_version, 4, 2)) {
+ $audiodev = ",audiodev=$audio->{backend_id}";
+ }
if ($audio->{dev} eq 'AC97') {
- push @$devs, '-device', "AC97,id=${id}${audiopciaddr},$audiodev";
+ push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
} elsif ($audio->{dev} =~ /intel\-hda$/) {
push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
- push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0,$audiodev";
- push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1,$audiodev";
+ push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
+ push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
} else {
die "unkown audio device '$audio->{dev}', implement me!";
}
die "uefi base image '$ovmf_code' not found\n" if ! -f $ovmf_code;
my ($path, $format);
- if (my $d = parse_drive('efidisk0', $conf->{efidisk0})) {
+ if (my $efidisk = $conf->{efidisk0}) {
+ my $d = parse_drive('efidisk0', $efidisk);
my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
$format = $d->{format};
if ($storeid) {
push @$devices, '-device', $kbd if defined($kbd);
}
- my $kvm_off = 0;
- my $gpu_passthrough;
-
- # host pci devices
- for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
- my $id = "hostpci$i";
- my $d = parse_hostpci($conf->{$id});
- next if !$d;
-
- if (my $pcie = $d->{pcie}) {
- die "q35 machine model is not enabled" if !$q35;
- # 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("${id}bus0");
- } else {
- # add more root ports if needed, 4 are present by default
- # by pve-q35 cfgs, rest added here on demand.
- if ($i > 3) {
- push @$devices, '-device', print_pcie_root_port($i);
- }
- $pciaddr = print_pcie_addr($id);
- }
- } else {
- $pciaddr = print_pci_addr($id, $bridges, $arch, $machine_type);
- }
-
- my $xvga = '';
- if ($d->{'x-vga'}) {
- $xvga = ',x-vga=on' if !($conf->{bios} && $conf->{bios} eq 'ovmf');
- $kvm_off = 1;
- $vga->{type} = 'none' if !defined($conf->{vga});
- $gpu_passthrough = 1;
- }
-
- my $pcidevices = $d->{pciid};
- my $multifunction = 1 if @$pcidevices > 1;
-
- my $sysfspath;
- if ($d->{mdev} && scalar(@$pcidevices) == 1) {
- my $pci_id = $pcidevices->[0]->{id};
- my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
- $sysfspath = "/sys/bus/pci/devices/$pci_id/$uuid";
- } elsif ($d->{mdev}) {
- warn "ignoring mediated device '$id' with multifunction device\n";
- }
-
- my $j=0;
- foreach my $pcidevice (@$pcidevices) {
- my $devicestr = "vfio-pci";
-
- if ($sysfspath) {
- $devicestr .= ",sysfsdev=$sysfspath";
- } else {
- $devicestr .= ",host=$pcidevice->{id}";
- }
-
- my $mf_addr = $multifunction ? ".$j" : '';
- $devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
-
- if ($j == 0) {
- $devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
- $devicestr .= "$xvga";
- $devicestr .= ",multifunction=on" if $multifunction;
- $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile};
- }
-
- push @$devices, '-device', $devicestr;
- $j++;
- }
- }
+ # host pci device passthrough
+ my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE::QemuServer::PCI::print_hostpci_devices(
+ $conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type);
# usb devices
my $usb_dev_features = {};
if (min_version($machine_version, 4, 0) && (my $audio = conf_has_audio($conf))) {
my $audiopciaddr = print_pci_addr("audio0", $bridges, $arch, $machine_type);
- my $audio_devs = audio_devs($audio, $audiopciaddr);
+ my $audio_devs = audio_devs($audio, $audiopciaddr, $machine_version);
push @$devices, @$audio_devs;
}
my $rng = parse_rng($conf->{rng0}) if $conf->{rng0};
if ($rng && &$version_guard(4, 1, 2)) {
+ check_rng_source($rng->{source});
+
my $max_bytes = $rng->{max_bytes} // $rng_fmt->{max_bytes}->{default};
my $period = $rng->{period} // $rng_fmt->{period}->{default};
-
my $limiter_str = "";
if ($max_bytes) {
$limiter_str = ",max-bytes=$max_bytes,period=$period";
}
- # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
- die "cannot create VirtIO RNG device: source file '$rng->{source}' doesn't exist\n"
- if ! -e $rng->{source};
-
my $rng_addr = print_pci_addr("rng0", $bridges, $arch, $machine_type);
-
push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
}
for my $k (sort {$b cmp $a} keys %$bridges) {
next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
- $pciaddr = print_pci_addr("pci.$k", undef, $arch, $machine_type);
+
+ my $k_name = $k;
+ if ($k == 2 && $legacy_igd) {
+ $k_name = "$k-igd";
+ }
+ $pciaddr = print_pci_addr("pci.$k_name", undef, $arch, $machine_type);
+
my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
if ($q35) {
# add after -readconfig pve-q35.cfg
return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
}
+sub check_rng_source {
+ my ($source) = @_;
+
+ # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
+ die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
+ if ! -e $source;
+
+ my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
+ if ($source eq '/dev/hwrng' && file_read_firstline($rng_current) eq 'none') {
+ # Needs to abort, otherwise QEMU crashes on first rng access.
+ # Note that rng_current cannot be changed to 'none' manually, so
+ # once the VM is past this point, it is no longer an issue.
+ die "Cannot start VM with passed-through RNG device: '/dev/hwrng'"
+ . " exists, but '$rng_current' is set to 'none'. Ensure that"
+ . " a compatible hardware-RNG is attached to the host.\n";
+ }
+}
+
sub spice_port {
my ($vmid) = @_;
my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
my %options = split(/[=,]/, $netdev);
+ if (defined(my $vhost = $options{vhost})) {
+ $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
+ }
+
+ if (defined(my $queues = $options{queues})) {
+ $options{queues} = $queues + 0;
+ }
+
mon_cmd($vmid, "netdev_add", %options);
return 1;
}
}
# host pci devices
- for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
+ for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
my $d = parse_hostpci($conf->{"hostpci$i"});
next if !$d;
my $pcidevices = $d->{pciid};
sub restore_file_archive {
my ($archive, $vmid, $user, $opts) = @_;
+ return restore_vma_archive($archive, $vmid, $user, $opts)
+ if $archive eq '-';
+
my $info = PVE::Storage::archive_info($archive);
my $format = $opts->{format} // $info->{format};
my $comp = $info->{compression};
my ($vmid, $conf, $volid_hash) = @_;
my $changes;
- my $prefix = "VM $vmid:";
+ my $prefix = "VM $vmid";
# used and unused disks
my $referenced = {};
return if drive_is_cdrom($drive);
return if !$volid_hash->{$volid};
- my ($updated, $old_size, $new_size) = PVE::QemuServer::Drive::update_disksize($drive, $volid_hash);
+ my ($updated, $msg) = PVE::QemuServer::Drive::update_disksize($drive, $volid_hash->{$volid}->{size});
if (defined($updated)) {
$changes = 1;
$conf->{$opt} = print_drive($updated);
- print "$prefix size of disk '$volid' ($opt) updated from $old_size to $new_size\n";
+ print "$prefix ($opt): $msg\n";
}
});
my $fingerprint = $scfg->{fingerprint};
my $repo = "$username\@$server:$datastore";
+
+ # This is only used for `pbs-restore`!
my $password = PVE::Storage::PBSPlugin::pbs_get_password($scfg, $storeid);
local $ENV{PBS_PASSWORD} = $password;
local $ENV{PBS_FINGERPRINT} = $fingerprint if defined($fingerprint);
my $path = PVE::Storage::path($storecfg, $volid);
+ # This is the ONLY user of the PBS_ env vars set on top of this function!
my $pbs_restore_cmd = [
'/usr/bin/pbs-restore',
'--repository', $repo,
'--verbose',
];
+ push @$pbs_restore_cmd, '--format', $d->{format} if $d->{format};
+
if (PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $volid)) {
push @$pbs_restore_cmd, '--skip-zero';
}