]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/SysFSTools.pm
fix #2303: detect IPs of p2p interfaces
[pve-common.git] / src / PVE / SysFSTools.pm
index b0a025f28ea051847b41938776fcdc53c5327b16..2da3a38894268cb33acd2ad3b4414fb16888cb39 100644 (file)
@@ -10,29 +10,141 @@ use PVE::Tools qw(file_read_firstline dir_glob_foreach);
 my $pcisysfs = "/sys/bus/pci";
 my $pciregex = "([a-f0-9]{4}):([a-f0-9]{2}):([a-f0-9]{2})\.([a-f0-9])";
 
+my $parse_pci_ids = sub {
+    my $ids = {};
+
+    open(my $fh, '<', "/usr/share/misc/pci.ids")
+       or return $ids;
+
+    my $curvendor;
+    my $curdevice;
+    while (my $line = <$fh>) {
+       if ($line =~ m/^([0-9a-fA-F]{4})\s+(.*)$/) {
+           $curvendor = ($ids->{"0x$1"} = {});
+           $curvendor->{name} = $2;
+       } elsif ($line =~ m/^\t([0-9a-fA-F]{4})\s+(.*)$/) {
+           $curdevice = ($curvendor->{devices}->{"0x$1"} = {});
+           $curdevice->{name} = $2;
+       } elsif ($line =~ m/^\t\t([0-9a-fA-F]{4}) ([0-9a-fA-F]{4})\s+(.*)$/) {
+           $curdevice->{subs}->{"0x$1"}->{"0x$2"} = $3;
+       }
+    }
+
+    return $ids;
+};
+
+# returns a list of pci devices
+#
+# filter is either a string (then it tries to match to the id)
+# or a sub ref (then it adds the device if the sub returns truthy)
+#
+# verbose also returns iommu groups, subvendor/device and the
+# human readable names from /usr/share/misc/pci.ids
+#
+# return format:
+# [
+#     {
+#         id => '00:00.0',
+#         vendor => '0xabab',
+#         device => '0xefef',
+#         class => '0x012345',
+#
+#         # optional
+#         iommugroup => '14',
+#         mdev => 1,
+#         vendor_name => 'Foo Inc.',
+#         device_name => 'Bar 9000AF',
+#         subsystem_vendor => '0xacac',
+#         subsystem_device => '0xfefe',
+#         subsystem_vendor_name => 'Foo Europe GmbH',
+#         subsystem_device_name => 'Bar 9001AF OC',
+#     },
+#     ...
+# ]
+#
 sub lspci {
-    my ($id_filter) = @_;
+    my ($filter, $verbose) = @_;
 
-    my $devices = {};
+    my $devices = [];
+    my $ids = {};
+    if ($verbose) {
+       $ids = $parse_pci_ids->();
+    }
 
     dir_glob_foreach("$pcisysfs/devices", $pciregex, sub {
-            my (undef, undef, $bus, $slot, $function) = @_;
+       my ($fullid, $domain, $bus, $slot, $function) = @_;
+       my $id = "$bus:$slot.$function";
 
-           my $id = "$bus:$slot";
-           return if defined($id_filter) && $id_filter ne $id;
+       if (defined($filter) && !ref($filter) && $id !~ m/^\Q$filter\E/) {
+           return; # filter ids early
+       }
+
+       my $devdir = "$pcisysfs/devices/$fullid";
+
+       my $vendor = file_read_firstline("$devdir/vendor");
+       my $device = file_read_firstline("$devdir/device");
+       my $class = file_read_firstline("$devdir/class");
+
+       my $res = {
+           id => $id,
+           vendor => $vendor,
+           device => $device,
+           class => $class,
+       };
 
-           push @{$devices->{$id}}, { id => $id, function => $function };
+       if (defined($filter) && ref($filter) eq 'CODE' && !$filter->($res)) {
+           return;
+       }
+
+       if ($verbose) {
+           $res->{iommugroup} = -1;
+           if (-e "$devdir/iommu_group") {
+               my ($iommugroup) = (readlink("$devdir/iommu_group") =~ m/\/(\d+)$/);
+               $res->{iommugroup} = int($iommugroup);
+           }
+
+           if (-d "$devdir/mdev_supported_types") {
+               $res->{mdev} = 1;
+           }
+
+           my $device_hash = $ids->{$vendor}->{devices}->{$device} // {};
+
+           my $sub_vendor = file_read_firstline("$devdir/subsystem_vendor");
+           my $sub_device = file_read_firstline("$devdir/subsystem_device");
+
+           my $vendor_name = $ids->{$vendor}->{name};
+           my $device_name = $device_hash->{name};
+           my $sub_vendor_name = $ids->{$sub_vendor}->{name};
+           my $sub_device_name = $device_hash->{subs}->{$sub_vendor}->{$sub_device};
+
+           $res->{vendor_name} = $vendor_name if defined($vendor_name);
+           $res->{device_name} = $device_name if defined($device_name);
+           $res->{subsystem_vendor} = $sub_vendor if defined($sub_vendor);
+           $res->{subsystem_device} = $sub_device if defined($sub_device);
+           $res->{subsystem_vendor_name} = $sub_vendor_name if defined($sub_vendor_name);
+           $res->{subsystem_device_name} = $sub_device_name if defined($sub_device_name);
+       }
+
+       push @$devices, $res;
     });
 
-    # Entries should be sorted by functions.
-    foreach my $id (keys %$devices) {
-       my $dev = $devices->{$id};
-       $devices->{$id} = [ sort { $a->{function} <=> $b->{function} } @$dev ];
-    }
+    # Entries should be sorted by ids
+    $devices = [ sort { $a->{id} cmp $b->{id} } @$devices ];
 
     return $devices;
 }
 
+#
+# return format:
+# [
+#     {
+#         type => 'FooType_1',
+#         description => "a longer description with custom format\nand newlines",
+#         available => 5,
+#     },
+#     ...
+# ]
+#
 sub get_mdev_types {
     my ($id) = @_;