[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