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行)中定义的函数指针
那就是我迷失方向的地方。
如果运行 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
深入挖掘源代码并成功在调试器中运行 lspci(感谢 Netbeans)后,我发现 lspci 使用 sysfs 来收集信息。 特别是 /sys/bus/pci/slots/slot_num/address 文件包含插槽的总线地址。这就是 lspci 在函数
sysfs_fill_slots
(在 sysfs.c 中) 中用来将插槽归属于总线地址的方法。
不幸的是,这种方法不适合我的目的,因为无法从内核模块执行文件 I/O。
由于这个问题已经有两年多了,我认为不再相关,但我偶然发现了同样的问题,并且确实找到了解决方案。也许对你有帮助。
如今解析为探测函数的
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()
可能是一个更值得内核使用的解决方案。