From: Dominik Csapak Date: Tue, 20 Nov 2018 16:13:38 +0000 (+0100) Subject: SysFSTools.pm: improve and extend lspci X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=commitdiff_plain;h=a70972cd0590efb73c13d427324459f4a0952394 SysFSTools.pm: improve and extend lspci this implements following improvements and optimizations for lspci * removes the unecessary split between id and function since everywhere we need that information, we stitch them together anyway. to preserve ordering, simply order by id with string comparison 'cmp' (this is important for the shorthand syntax '00:01' in the config) * returns now a list directly, instead of an hash with lists * returns now always the vendor/device id and class[1] * filter is now either a string (matches the id partially), or a sub, which filters the device out if it returns a falsy value * adds a verbose flag to include more information about the device, such as device/vendor name, iommu-group, mdev support, etc. this will be used for the pci scan api call for the gui 1: https://pci-ids.ucw.cz/read/PD/ Signed-off-by: Dominik Csapak --- diff --git a/src/PVE/SysFSTools.pm b/src/PVE/SysFSTools.pm index b0a025f..2e7d028 100644 --- a/src/PVE/SysFSTools.pm +++ b/src/PVE/SysFSTools.pm @@ -10,25 +10,104 @@ 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 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; }