[pve-devel] [PATCH common 3/3] add PVE/HardwareMap and Plugins
Dominik Csapak
d.csapak at proxmox.com
Mon Jun 21 15:55:17 CEST 2021
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 <d.csapak at proxmox.com>
---
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
More information about the pve-devel
mailing list