From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 9C0CA74521 for ; Mon, 21 Jun 2021 15:56:37 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 07BF81C489 for ; Mon, 21 Jun 2021 15:55:46 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 9445E1C307 for ; Mon, 21 Jun 2021 15:55:36 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 6ECEA43074 for ; Mon, 21 Jun 2021 15:55:36 +0200 (CEST) From: Dominik Csapak To: pve-devel@lists.proxmox.com Date: Mon, 21 Jun 2021 15:55:17 +0200 Message-Id: <20210621135534.14807-5-d.csapak@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210621135534.14807-1-d.csapak@proxmox.com> References: <20210621135534.14807-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.801 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH common 3/3] add PVE/HardwareMap and Plugins X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jun 2021 13:56:37 -0000 adds the Top level package PVE::HardwareMap that registers the Plugins for the config, as well as provides some convenience methods (find_device_on_current_node, lock/write/get config) The Plugins themselves are usual SectionConfigs plugins with (for now) two types: usb, pci each type gets an 'assert_device_valid' method that checks the local node for validity of the device Signed-off-by: Dominik Csapak --- src/Makefile | 4 ++ src/PVE/HardwareMap.pm | 54 ++++++++++++++++++++ src/PVE/HardwareMap/PCIPlugin.pm | 87 ++++++++++++++++++++++++++++++++ src/PVE/HardwareMap/Plugin.pm | 82 ++++++++++++++++++++++++++++++ src/PVE/HardwareMap/USBPlugin.pm | 69 +++++++++++++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 src/PVE/HardwareMap.pm create mode 100644 src/PVE/HardwareMap/PCIPlugin.pm create mode 100644 src/PVE/HardwareMap/Plugin.pm create mode 100644 src/PVE/HardwareMap/USBPlugin.pm diff --git a/src/Makefile b/src/Makefile index 13de6c6..7cd20d5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,6 +17,10 @@ LIB_SOURCES = \ Daemon.pm \ Exception.pm \ Format.pm \ + HardwareMap.pm \ + HardwareMap/Plugin.pm \ + HardwareMap/PCIPlugin.pm \ + HardwareMap/USBPlugin.pm \ INotify.pm \ JSONSchema.pm \ LDAP.pm \ diff --git a/src/PVE/HardwareMap.pm b/src/PVE/HardwareMap.pm new file mode 100644 index 0000000..a0f4a9f --- /dev/null +++ b/src/PVE/HardwareMap.pm @@ -0,0 +1,54 @@ +package PVE::HardwareMap; + +use strict; +use warnings; + +use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file); +use PVE::HardwareMap::Plugin; +use PVE::HardwareMap::PCIPlugin; +use PVE::HardwareMap::USBPlugin; +use PVE::INotify; + +use base qw(Exporter); + +our @EXPORT_OK = qw(find_device_on_current_node); + +PVE::HardwareMap::PCIPlugin->register(); +PVE::HardwareMap::USBPlugin->register(); + +PVE::HardwareMap::Plugin->init(); + +sub find_device_on_current_node { + my ($type, $id) = @_; + + my $data = cfs_read_file($PVE::HardwareMap::Plugin::FILENAME); + + my $res = {}; + + my $node = PVE::INotify::nodename(); + my $sectionid = "$node:$id"; + + return $data->{ids}->{$sectionid}; +} + +sub config { + return cfs_read_file($PVE::HardwareMap::Plugin::FILENAME); +} + +sub lock_config { + my ($code, $errmsg) = @_; + + cfs_lock_file($PVE::HardwareMap::Plugin::FILENAME, undef, $code); + if (my $err = $@) { + $errmsg ? die "$errmsg: $err" : die $err; + } +} + +sub write_config { + my ($cfg) = @_; + + cfs_write_file($PVE::HardwareMap::Plugin::FILENAME, $cfg); +} + + +1; diff --git a/src/PVE/HardwareMap/PCIPlugin.pm b/src/PVE/HardwareMap/PCIPlugin.pm new file mode 100644 index 0000000..eb6d090 --- /dev/null +++ b/src/PVE/HardwareMap/PCIPlugin.pm @@ -0,0 +1,87 @@ +package PVE::HardwareMap::PCIPlugin; + +use strict; +use warnings; + +use PVE::HardwareMap::Plugin; +use PVE::SysFSTools; + +use base qw(PVE::HardwareMap::Plugin); + +sub type { + return 'pci'; +} + +sub properties { + return { + pcipath => { + description => "The path to the device. If the function is omitted, the whole device is mapped. In that case use the attrubes of the first device.", + type => 'string', + pattern => qr/^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}(:?.[0-9A-Fa-f])?$/, + }, + mdev => { + description => "The Device supports mediated devices.", + type => 'boolean', + }, + iommugroup => { + type => 'integer', + description => "The IOMMU group in which the device is in.", + } + }; +} + +sub options { + return { + node => { fixed => 1 }, + name => { fixed => 1 }, + pcipath => { }, + vendor => { }, + device => { }, + iommugroup => { }, + subsystem_vendor => { optional => 1 }, + subsystem_device => { optional => 1 }, + mdev => { optional => 1 }, + }; +} + +sub assert_device_valid { + my ($class, $cfg) = @_; + + my $path = $cfg->{pcipath}; + + if ($path !~ m/\.[a-f0-9]/i) { + # whole device, add .0 (must exist) + $path = "$path.0"; + } + + my $info = PVE::SysFSTools::pci_device_info($path, 1); + die "pci device '$path' not found\n" if !defined($info); + + my $props = { + vendor => $cfg->{vendor}, + device => $cfg->{device}, + subsystem_vendor => $cfg->{subsystem_vendor}, + subsystem_device => $cfg->{subsystem_device}, + iommugroup => $cfg->{iommugroup}, + mdev => $cfg->{mdev}, + }; + + for my $prop (keys %$props) { + next if !defined($info->{$prop}); + die "no '$prop' for device '$path'\n" + if defined($info->{$prop}) && !$props->{$prop}; + + + my $correct_prop = $info->{$prop}; + $correct_prop =~ s/^0x//; + my $configured_prop = $props->{$prop}; + $configured_prop =~ s/^0x//; + + die "'$prop' does not match for '$cfg->{name}' ($correct_prop != $configured_prop)\n" + if $correct_prop ne $configured_prop; + } + + return 1; +} + +1; diff --git a/src/PVE/HardwareMap/Plugin.pm b/src/PVE/HardwareMap/Plugin.pm new file mode 100644 index 0000000..2d33785 --- /dev/null +++ b/src/PVE/HardwareMap/Plugin.pm @@ -0,0 +1,82 @@ +package PVE::HardwareMap::Plugin; + +use strict; +use warnings; + +use PVE::Cluster qw(cfs_register_file cfs_read_file); +use PVE::JSONSchema qw(get_standard_option); + +use base qw(PVE::SectionConfig); + +our $FILENAME = "nodes/hardware-map.conf"; +cfs_register_file($FILENAME, + sub { PVE::HardwareMap::Plugin->parse_config(@_); }, + sub { PVE::HardwareMap::Plugin->write_config(@_); }); + +my $defaultData = { + propertyList => { + type => { description => "Hardware Type", }, + name => { + description => "The custom name for the device", + type => 'string', + format => 'pve-configid', + }, + node => get_standard_option('pve-node'), + vendor => { + description => "The vendor ID", + type => 'string', + pattern => qr/^(:?0x)?[0-9A-Fa-f]{4}$/, + }, + device => { + description => "The device ID", + type => 'string', + pattern => qr/^(:?0x)?[0-9A-Fa-f]{4}$/, + }, + subsystem_vendor => { + description => "The subsystem vendor ID", + type => 'string', + pattern => qr/^(:?0x)?[0-9A-Fa-f]{4}$/, + optional => 1, + }, + subsystem_device => { + description => "The subsystem device ID", + type => 'string', + pattern => qr/^(:?0x)?[0-9A-Fa-f]{4}$/, + optional => 1, + }, + }, +}; + +sub private { + return $defaultData; +} + +sub parse_section_header { + my ($class, $line) = @_; + + if ($line =~ m/^(\S+):\s*(\S+):(\S+)\s*$/) { + my ($type, $node, $name) = ($1, $2, $3); + # TODO verify properties + my $errmsg = undef; + my $config = { + node => $node, + name => $name, + }; + return ($type, "$node:$name", $errmsg, $config); + } + return undef; +} + +sub format_section_header { + my ($class, $type, $sectionId, $scfg, $done_hash) = @_; + $done_hash->{name} = 1; + $done_hash->{node} = 1; + return $class->SUPER::format_section_header($type, $sectionId, $scfg, $done_hash); +} + +sub assert_device_valid { + my ($cfg) = @_; + die "implement me"; +} + +1; diff --git a/src/PVE/HardwareMap/USBPlugin.pm b/src/PVE/HardwareMap/USBPlugin.pm new file mode 100644 index 0000000..a1fff77 --- /dev/null +++ b/src/PVE/HardwareMap/USBPlugin.pm @@ -0,0 +1,69 @@ +package PVE::HardwareMap::USBPlugin; + +use strict; +use warnings; + +use PVE::HardwareMap::Plugin; + +use base qw(PVE::HardwareMap::Plugin); + +sub type { + return 'usb'; +} + +sub properties { + return { + usbpath => { + description => "The path to the usb device.", + type => 'string', + pattern => qr/^(\d+)\-(\d+(\.\d+)*)$/, + }, + }; +} + +sub options { + return { + node => { fixed => 1 }, + name => { fixed => 1 }, + vendor => { }, + device => { }, + usbpath => { optional => 1 }, + }; +} + +sub assert_device_valid { + my ($class, $cfg) = @_; + + my $name = $cfg->{name}; + my $vendor = $cfg->{vendor}; + my $device = $cfg->{device}; + + my $usb_list = PVE::SysFSTools::scan_usb(); + + my $info; + if (my $path = $cfg->{usbpath}) { + for my $dev (@$usb_list) { + next if !$dev->{usbpath} || !$dev->{busnum}; + my $usbpath = "$dev->{busnum}-$dev->{usbpath}"; + next if $usbpath ne $path; + $info = $dev; + } + die "usb device '$path' not found\n" if !defined($info); + + die "'vendor' does not match for '$name'\n" + if $info->{vendid} ne $cfg->{vendor}; + die "'device' does not match for '$name'\n" + if $info->{prodid} ne $cfg->{device}; + } else { + for my $dev (@$usb_list) { + next if $dev->{vendid} ne $vendor; + next if $dev->{prodid} ne $device; + $info = $dev; + } + die "usb device '$vendor:$device' not found\n" if !defined($info); + } + + return 1; +} + +1; -- 2.20.1