[pve-devel] [PATCH qemu-server 2/3] cfg2cmd: hostpci: move code to PCI.pm
Stefan Reiter
s.reiter at proxmox.com
Thu Jun 18 16:36:53 CEST 2020
To avoid further cluttering config_to_command with subsequent changes.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
PVE/QemuServer.pm | 170 ++----------------------------------------
PVE/QemuServer/PCI.pm | 170 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 177 insertions(+), 163 deletions(-)
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index fa71b25..1c08222 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -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};
diff --git a/PVE/QemuServer/PCI.pm b/PVE/QemuServer/PCI.pm
index 4d9ce26..39f9970 100644
--- a/PVE/QemuServer/PCI.pm
+++ b/PVE/QemuServer/PCI.pm
@@ -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;
--
2.20.1
More information about the pve-devel
mailing list