cfg2cmd: hostpci: move code to PCI.pm

To avoid further cluttering config_to_command with subsequent changes.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
Stefan Reiter 2020-06-18 16:36:53 +02:00 committed by Thomas Lamprecht
parent 5a92276e2d
commit 74c17b7a23
2 changed files with 177 additions and 163 deletions

View File

@ -48,7 +48,7 @@ use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdr
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;
@ -768,7 +768,6 @@ while (my ($k, $v) = each %$confdesc) {
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;
@ -1011,76 +1010,6 @@ my $usbdesc = {
};
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',
@ -1119,8 +1048,8 @@ for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
$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}) {
@ -1756,23 +1685,6 @@ sub parse_numa {
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) = @_;
@ -3189,77 +3101,9 @@ sub config_to_command {
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) = PVE::QemuServer::PCI::print_hostpci_devices(
$conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type);
# usb devices
my $usb_dev_features = {};
@ -5092,7 +4936,7 @@ sub vm_start_nolock {
}
# 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};

View File

@ -1,13 +1,89 @@
package PVE::QemuServer::PCI;
use PVE::JSONSchema;
use PVE::SysFSTools;
use base 'Exporter';
our @EXPORT_OK = qw(
print_pci_addr
print_pcie_addr
print_pcie_root_port
parse_hostpci
);
our $MAX_HOSTPCI_DEVICES = 16;
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);
our $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 $pci_addr_map;
sub get_pci_addr_map {
$pci_addr_map = {
@ -253,4 +329,98 @@ sub print_pcie_root_port {
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;
}
sub print_hostpci_devices {
my ($conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type) = @_;
my $kvm_off = 0;
my $gpu_passthrough = 0;
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++;
}
}
return ($kvm_off, $gpu_passthrough);
}
1;