lspci 如何找到 PCI(E) 设备的物理插槽号?

问题描述 投票:0回答:3

lspci
能够在详细演示中显示物理插槽编号:

我想知道它是如何做到的。我将在我想要修改的驱动程序中应用此方法,因此它将枚举设备(具有相同的 ID)并根据物理插槽消除设备文件的歧义。就像 /dev/device_physslot 。该驱动程序将在 Ubuntu 18 上运行

我尝试挖掘源代码。我在https://github.com/pciutils/pciutils/blob/master/lspci.c中找到了相关的第775行:

if (p->phy_slot)
    printf("\tPhysical Slot: %s\n", p->phy_slot);

p 是

struct pci_dev
。这非常令人困惑,因为标准
linux/pci.h
没有字段
phy_slot
,直到我发现那是 他们自己的(重新)定义

结构由函数填充

int
pci_fill_info_v38(struct pci_dev *d, int flags)
{
  unsigned int uflags = flags;
  if (uflags & PCI_FILL_RESCAN)
    {
      uflags &= ~PCI_FILL_RESCAN;
      pci_reset_properties(d);
    }
  if (uflags & ~d->known_fields)
    d->methods->fill_info(d, uflags);
  return d->known_fields;
}

fill_info
是在https://github.com/pciutils/pciutils/blob/master/lib/internal.h(第44行)

中定义的函数指针

那就是我迷失方向的地方。

c linux-kernel
3个回答
1
投票

如果运行 dmidecode,它将显示存储的平台信息,这将告诉您物理插槽到 PCIe 地址的映射。例如:

Handle 0x001D, DMI type 9, 17 bytes
System Slot Information
    Designation: J6B1
    Type: x1 PCI Express
    Current Usage: In Use
    Length: Short
    ID: 1                      <== SLOT (starting at 0)
    Characteristics:
        3.3 V is provided
        Opening is shared
        PME signal is supported
    Bus Address: 0000:00:1c.3    <== PCI BUS ADDRESS

插槽的编程列表:

sudo dmidecode -t 9 |awk '/ID:/ {id=$2} /Bus Address/ {print "Slot",id+1,"PCIe",$3}'
Slot 1 PCIe 0000:00:01.0
Slot 2 PCIe 0000:00:1c.3
Slot 3 PCIe 0000:00:1c.4
Slot 4 PCIe 0000:00:1c.5
Slot 5 PCIe 0000:00:1c.6

1
投票

深入挖掘源代码并成功在调试器中运行 lspci(感谢 Netbeans)后,我发现 lspci 使用 sysfs 来收集信息。 特别是 /sys/bus/pci/slots/slot_num/address 文件包含插槽的总线地址。这就是 lspci 在函数

sysfs_fill_slots
(在 sysfs.c 中)

中用来将插槽归属于总线地址的方法。

不幸的是,这种方法不适合我的目的,因为无法从内核模块执行文件 I/O。


0
投票

由于这个问题已经有两年多了,我认为不再相关,但我偶然发现了同样的问题,并且确实找到了解决方案。也许对你有帮助。

如今解析为探测函数的

struct pci_dev
包含
struct pci_slot
:

/* The pci_dev structure describes PCI devices */
struct pci_dev {
    struct list_head bus_list;  /* Node in per-bus list */
    struct pci_bus  *bus;       /* Bus this device is on */
    struct pci_bus  *subordinate;   /* Bus this device bridges to */

    void        *sysdata;   /* Hook for sys-specific extension */
    struct proc_dir_entry *procent; /* Device entry in /proc/bus/pci */
    struct pci_slot *slot;      /* Physical slot this device is in */

这个

struct pci_slot
包含一个槽号:

/* pci_slot represents a physical slot */
struct pci_slot {
    struct pci_bus      *bus;       /* Bus this slot is on */
    struct list_head    list;       /* Node in list of slots */
    struct hotplug_slot *hotplug;   /* Hotplug info (move here) */
    unsigned char       number;     /* PCI_SLOT(pci_dev->devfn) */
    struct kobject      kobj;
};

不幸的是,在我的测试系统上,struct

pci_slot *slot
结果是
NULL
。它似乎是由 ACPI 填写的,但我没有详细诊断,因为更改内核配置并重建它不是这个项目的选项,因为它需要在标准发行版上工作。

因此,我决定使用

dmi_find_device()
来查找它,以迭代所有可用的插槽编号:

const struct dmi_device  *dmi = NULL;

for (;;) {
    struct dmi_dev_onboard  *dev_onboard;

    if (!(dmi = dmi_find_device(DMI_DEV_TYPE_DEV_SLOT, NULL, dmi))) {
        dev_err(&pdev->dev, "PCIe physical slot number not found");
        return -ENODEV;
    }

    dev_onboard = (struct dmi_dev_onboard *)dmi->device_data;

    if (dev_onboard->segment == pci_domain_nr(pdev->bus) &&   /* TODO: Is this test correct? */
        dev_onboard->bus     == pdev->bus->parent->number &&  /* This test is correct */
        dev_onboard->devfn   == pdev->devfn)                  /* TODO: Is this test correct? */
        break;
}

正如评论中所写,我现在不知道第一个测试是否正确。我确信第二个测试是正确的,因为正在报告的是parent's公交车号码。我相当确定第三次测试也是正确的。

脱离

for
循环后,
dmi->name
包含格式为
"Slot %u"
的字符串,其中
%u
包含插槽编号。我使用 sscanf 来隔离和转换它,但是比较文本部分、前进指针并在其余部分使用
strltoul()
可能是一个更值得内核使用的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.