【问题标题】:How do I discover the PCIe bus topology and slot numbers on the board?如何发现板上的 PCIe 总线拓扑和插槽号?
【发布时间】:2015-11-11 11:08:30
【问题描述】:

例如,当我使用带有 CUDA C/C++ 和 GPUDirect 2.0 P2P 的多 GPU 系统,并且我使用嵌套的 PCI-Express 开关时,如图所示,那么我必须知道任意两个 GPU 之间的开关数量。 PCI Bus ID,优化数据传输和分配计算。

或者,如果我已经知道带有 PCIe 开关的硬件 PCIe 拓扑,那么我必须知道,任何 GPU 卡都连接到板上的哪个硬件 PCIe 插槽。

据我所知,即使我已经知道带有 PCIe 交换机的硬件 PCIe 拓扑,那么这些标识符也不会硬绑定到板上的 PCIe 插槽,并且这些 ID 可能会随着系统的运行而改变和不同:

  • CUDA device_id
  • nvidia-smi/nvml GPU id
  • PCI 总线 ID

在 Windows 和 Linux 上通过详细的设备树和板上 PCIe 插槽的数量发现 PCIe 总线拓扑的最佳方法是什么?

【问题讨论】:

  • @Robert Crovella 谢谢。但nvidia-smi topo 仅适用于 Linux,不适用于 Windows - 但我需要它。而nvidia-smi topo -m 仅显示任意 2 个 GPU 之间的 4 个值,即显示 PCI 交换机数量:SOC (0 + QPI)、PHB(0)、PXB(1 个或更多)、PIX(1 个内部)。但是如果我的拓扑结构有 2、3 或更多级别的 PCIe-Switch,那么我就无法使用它。
  • 也许你应该和你的系统供应商谈谈。它们可能具有您可以使用的专有硬件抽象层或库。您询问的系统架构类型非常奇特,超出了大多数标准设置和工具的范围。

标签: c++ windows cuda pci pci-e


【解决方案1】:

PCI 设备(端点)具有唯一的地址。这个地址有 3 个部分:

  • 总线ID
  • 设备ID
  • 函数ID

例如,总线 3 上设备 12 的函数 3 是用 BDF 概念编写的:03:0C.3。扩展的 BDF 表示法会添加一个域(主要是 0000)作为前缀:0000:03:0c.3

Linux 在/sys/bus/pci/devices 中列出了这些设备

paebbels@debian8:~$ ll /sys/bus/pci/devices/
drwxr-xr-x 2 root root 0 Aug 19 11:44 .
drwxr-xr-x 5 root root 0 Aug  5 15:14 ..
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:00:01.0 -> ../../../devices/pci0000:00/0000:00:01.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:00:07.0 -> ../../../devices/pci0000:00/0000:00:07.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:00:07.1 -> ../../../devices/pci0000:00/0000:00:07.1
...
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:00:18.6 -> ../../../devices/pci0000:00/0000:00:18.6
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:00:18.7 -> ../../../devices/pci0000:00/0000:00:18.7
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:02:00.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:02:01.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:01.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:02:02.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:02.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:02:03.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0
lrwxrwxrwx 1 root root 0 Aug 19 11:44 0000:03:00.0 -> ../../../devices/pci0000:00/0000:00:15.0/0000:03:00.0

这里可以看到sys-fs列出了bus 02的设备00到03作为连接到bus 00, device 11, function 0

根据这些信息,您可以重建完整的 PCI 总线树。启动后树总是相同的,除非您添加或删除设备。

Windows 设备管理器提供相同的信息。属性对话框显示设备类型、供应商和位置:例如PCI bus 0, device 2, function 0 用于集成 Intel HD 4600 显卡。

目前,我不知道如何在 Windows 环境中通过脚本或编程语言获取这些信息,但 Internet 上有提供这些信息的商业和免费工具。也许有一个 API。

【讨论】:

    【解决方案2】:

    这里是不需要解析注册表的脚本版本。所有信息(此处使用)都在 win32_pnpentity 中可用。

    Function Get-BusFunctionID { 
    
        gwmi -namespace root\cimv2 -class Win32_PnPEntity |% {
    
            if ($_.PNPDeviceID -like "PCI\*") {
    
                $locationInfo = $_.GetDeviceProperties('DEVPKEY_Device_LocationInfo').deviceProperties.Data
    
                if ($locationInfo -match 'PCI bus (\d+), device (\d+), function (\d+)') {
    
                    new-object psobject -property @{ 
                        "Name"       = $_.Name
                        "PnPID"      = $_.PNPDeviceID
                        "BusID"      = $matches[1]
                        "DeviceID"   = $matches[2]
                        "FunctionID" = $matches[3]
                    }
                }
            }
        }
    } 
    

    【讨论】:

      【解决方案3】:

      在 Windows 上,您可以使用例如:以下 Powershell 脚本和来自 Windows 设备驱动程序工具包的 devcon.exe 工具:

      Function Get-BusFunctionID { 
          $Devices = .\devcon.exe find PCI\*
      
          for($i=0; $i -lt $Devices.length; $i++) { 
      
              if(!($Devices[$i] -match "PCI\\*")) {
                  continue
              }
              $DevInfo = $Devices[$i].split(":")
              $deviceId = $DevInfo[0]
              $locationInfo = (get-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Enum\$deviceID" -name locationinformation).locationINformation
      
              $businfo = Resolve-PCIBusInfo -locationInfo $locationinfo 
      
              new-object psobject -property @{ 
                  "Name"        = $DevInfo[1];
                  "PnPID"       = $DevInfo[0]
                  "PCIBusID"      = $businfo.BusID; 
                  "PCIDeviceID"   = $businfo.DeviceID; 
                  "PCIFunctionID" = $businfo.FunctionID 
              } 
          }
      }
      
      Function Resolve-PCIBusInfo { 
      
      param ( 
      [parameter(ValueFromPipeline=$true,Mandatory=$true)] 
      [string] 
      $locationInfo 
      ) 
      PROCESS { 
      [void]($locationInfo -match  "\d+,\d+,\d+")
      $busId,$deviceID,$functionID = $matches[0] -split "," 
      
      new-object psobject -property @{ 
                "BusID" = $busID; 
                "DeviceID" = "$deviceID" 
                "FunctionID" = "$functionID" 
                } 
      }          
      }
      

      使用示例:

      Get-BusFunctionID | Where-Object {$_.PCIBusID -eq 0 -and $_.PCIDeviceID -eq 0} | Format-Table
      Get-BusFunctionID | Sort-Object PCIBusID, PCIDeviceID, PCIFunctionID | Format-Table -GroupBy PCIBusID
      Get-BusFunctionID | Sort-Object PCIBusID, PCIDeviceID, PCIFunctionID | Out-GridView
      

      【讨论】:

        【解决方案4】:

        对于 Windows,准备运行 Powershell 脚本:

        Function Get-BusFunctionID { 
            #gwmi -query "SELECT * FROM Win32_PnPEntity"
            $Devices = get-wmiobject -namespace root\cimv2 -class Win32_PnPEntity
        
            for($i=0; $i -lt $Devices.length; $i++) { 
        
                if(!($Devices[$i].PNPDeviceID -match "PCI\\*")) {
                    continue
                }
                $deviceId = $Devices[$i].PNPDeviceID
                $locationInfo = (get-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Enum\$deviceID" -name locationinformation).locationINformation
        
                $businfo = Resolve-PCIBusInfo -locationInfo $locationinfo 
        
                new-object psobject -property @{ 
                    "Name"        = $Devices[$i].Name;
                    "PnPID"       = $Devices[$i].PNPDeviceID
                    "PCIBusID"      = $businfo.BusID; 
                    "PCIDeviceID"   = $businfo.DeviceID; 
                    "PCIFunctionID" = $businfo.FunctionID 
                } 
            }
        }
        
        Function Resolve-PCIBusInfo { 
        
        param ( 
        [parameter(ValueFromPipeline=$true,Mandatory=$true)] 
        [string] 
        $locationInfo 
        ) 
        PROCESS { 
        [void]($locationInfo -match  "\d+,\d+,\d+")
        $busId,$deviceID,$functionID = $matches[0] -split "," 
        
        new-object psobject -property @{ 
                  "BusID" = $busID; 
                  "DeviceID" = "$deviceID" 
                  "FunctionID" = "$functionID" 
                  } 
        }          
        }
        

        使用示例:

        Get-BusFunctionID | Where-Object {$_.PCIBusID -eq 0 -and $_.PCIDeviceID -eq 0} | Format-Table
        Get-BusFunctionID | Sort-Object PCIBusID, PCIDeviceID, PCIFunctionID | Format-Table -GroupBy
        

        【讨论】:

          【解决方案5】:

          对于网络适配器,从 Windows 8 或 Windows 2012 开始,您可以使用 WMI 类 MSFT_NetAdapterHardwareInfoSettingData

          gwmi -Namespace root\standardcimv2 MSFT_NetAdapterHardwareInfoSettingData | Format-Table Description,BusNumber,DeviceNumber,FunctionNumber
          
          Description                        BusNumber DeviceNumber FunctionNumber
          -----------                        --------- ------------ --------------
          Red Hat VirtIO Ethernet Adapter #6         0           17              0
          Red Hat VirtIO Ethernet Adapter #3         0            9              0
          Red Hat VirtIO Ethernet Adapter #5         0           16              0
          Red Hat VirtIO Ethernet Adapter #2         0            8              0
          Red Hat VirtIO Ethernet Adapter #7         0           18              0
          Red Hat VirtIO Ethernet Adapter #8         0           19              0
          Red Hat VirtIO Ethernet Adapter            0            3              0
          Red Hat VirtIO Ethernet Adapter #4         0           10              0
          

          ...或 PowerShell 命令Get-NetAdapterHardwareInfo:

          Get-NetAdapterHardwareInfo | Format-Table Description,Bus,Device,Function
          
          Description                        Bus Device Function
          -----------                        --- ------ --------
          Red Hat VirtIO Ethernet Adapter #6   0     17        0
          Red Hat VirtIO Ethernet Adapter #3   0      9        0
          Red Hat VirtIO Ethernet Adapter #5   0     16        0
          Red Hat VirtIO Ethernet Adapter #2   0      8        0
          Red Hat VirtIO Ethernet Adapter #7   0     18        0
          Red Hat VirtIO Ethernet Adapter #8   0     19        0
          Red Hat VirtIO Ethernet Adapter      0      3        0
          Red Hat VirtIO Ethernet Adapter #4   0     10        0
          

          【讨论】:

            【解决方案6】:

            Windows 上的设备 id 和功能也编码在PNPDeviceID 的最后部分,比较容易获取。但是,它们的编码方式没有记录。

            例如:PCI\\VEN_8086&DEV_2829&SUBSYS_00000000&REV_02\\3&267A616A&0&68.

            68(十六进制)可能是 设备 编号 13(十进制)向左移动 3 位,而最后 3 位可能是 函数 编号 0 . 由于function 通常为0,您可能会注意到PNPDeviceID 的大多数值以08 结尾。 3位偏移量可能来自CONFIG_ADDRESS PCI register:

            CONFIG_ADDRESS的格式如下:

            0x80000000 |巴士

            另见Intel chipset documentation,第 5 页:

            1. 设备的“PCI 设备号”必须写入位 [15:11] I/O 位置 CF8h

            068 之前可能表示总线 号。

            【讨论】:

              【解决方案7】:

              WMI_PnPDevice 的设备属性中,在注册表项或devcon.exe 的输出中,有Address 属性。它是 32 位整数 (DWORD),其中高 16 位是设备号,低 16 位是功能号:

              PS C:\Users\Administrator> (gwmi win32_pnpEntity).getDeviceProperties('DEVPKEY_Device_Address').deviceProperties | ?{$_.data} | Format-Table DeviceID,keyName,{$_.data -shr 16},{$_.data -band 0xFFFF}
              
              DeviceID                                                               keyName                $_.data -shr 16 $_.data -band 0xFFFF
              --------                                                               -------                --------------- --------------------
              PCI\VEN_8086&DEV_7000&SUBSYS_00000000&REV_00\3&267A616A&0&08           DEVPKEY_Device_Address               1                    0
              STORAGE\VOLUME\{EF420E5C-5744-11E9-92B5-806E6F6E6963}#0000000000100000 DEVPKEY_Device_Address               0                    1
              PCI\VEN_80EE&DEV_CAFE&SUBSYS_00000000&REV_00\3&267A616A&0&20           DEVPKEY_Device_Address               4                    0
              PCI\VEN_8086&DEV_2829&SUBSYS_00000000&REV_02\3&267A616A&0&68           DEVPKEY_Device_Address              13                    0
              SCSI\DISK&VEN_VBOX&PROD_HARDDISK\4&2617AEAE&0&000000                   DEVPKEY_Device_Address               0                65535
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&18           DEVPKEY_Device_Address               3                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&40           DEVPKEY_Device_Address               8                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&48           DEVPKEY_Device_Address               9                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&50           DEVPKEY_Device_Address              10                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&80           DEVPKEY_Device_Address              16                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&88           DEVPKEY_Device_Address              17                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&90           DEVPKEY_Device_Address              18                    0
              PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4&REV_00\3&267A616A&0&98           DEVPKEY_Device_Address              19                    0
              PCI\VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00\3&267A616A&0&10           DEVPKEY_Device_Address               2                    0
              SCSI\CDROM&VEN_VBOX&PROD_CD-ROM\4&2617AEAE&0&010000                    DEVPKEY_Device_Address               1                65535
              

              Question关于地址和answer的含义。 我的answer on related question

              【讨论】:

              • 在我的机器上,这个命令没有显示任何 PCIe 卡。 Win32_PnPEntity 本身会显示所有设备,但是当您添加 deviceProperties 部分时,它会过滤列表,因此它会丢失我所有的 PCIe 卡。
              猜你喜欢
              • 1970-01-01
              • 2019-06-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-04-30
              • 2021-09-09
              相关资源
              最近更新 更多