PCIe学习笔记之PCIe设备发现过程

PCIe在调试过程中,经常会出现扫描不到对端EP设备的问题,在问题定位过程中,了解内核中pcie枚举流程至关重要。

PCIe枚举过程一般分为三步:
1.创建根节点
2.扫描根节点下设备
3.为根节点下设备分配资源

那么如何发现设备?

从总线扫描pcie设备的函数pci_scan_child_bus开始分析

unsigned int pci_scan_child_bus(struct pci_bus *bus)
{
    unsigned int devfn, pass, max = bus->busn_res.start;
    struct pci_dev *dev;

    dev_dbg(&bus->dev, "scanning bus\n");

    /* Go find them, Rover! */
    for (devfn = 0; devfn < 0x100; devfn += 8)
        pci_scan_slot(bus, devfn);

    /* Reserve buses for SR-IOV capability. */
    max += pci_iov_bus_range(bus);

    /*
     * After performing arch-dependent fixup of the bus, look behind
     * all PCI-to-PCI bridges on this bus.
     */
    if (!bus->is_added) {
        dev_dbg(&bus->dev, "fixups for bus\n");
        pcibios_fixup_bus(bus);
        bus->is_added = 1;
    }

    for (pass = 0; pass < 2; pass++)
        list_for_each_entry(dev, &bus->devices, bus_list) {
            if (pci_is_bridge(dev))
                max = pci_scan_bridge(bus, dev, max, pass);
        }

    /*
     * Make sure a hotplug bridge has at least the minimum requested
     * number of buses.
     */
    if (bus->self && bus->self->is_hotplug_bridge && pci_hotplug_bus_size) {
        if (max - bus->busn_res.start < pci_hotplug_bus_size - 1)
            max = bus->busn_res.start + pci_hotplug_bus_size - 1;
    }

    /*
     * We've scanned the bus and so we know all about what's on
     * the other side of any bridges that may be on this bus plus
     * any devices.
     *
     * Return how far we've got finding sub-buses.
     */
    dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);
    return max;
}
EXPORT_SYMBOL_GPL(pci_scan_child_bus);

该函数的核心代码为

for (devfn = 0; devfn < 0x100; devfn += 8)
    pci_scan_slot(bus, devfn);

这里的bus变量来源于pci_create_root_bus,也就是创建的根总线的总线号
devfn : 设备和功能号。
这里使用的是穷举法,把所有的dev和function都尝试一次。

pci_scan_slot函数:

int pci_scan_slot(struct pci_bus *bus, int devfn)
{
    unsigned fn, nr = 0;
    struct pci_dev *dev;

    if (only_one_child(bus) && (devfn > 0))
        return 0; /* Already scanned the entire slot */

    dev = pci_scan_single_device(bus, devfn);
    if (!dev)
        return 0;
    if (!dev->is_added)
        nr++;

    for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
        dev = pci_scan_single_device(bus, devfn + fn);
        if (dev) {
            if (!dev->is_added)
                nr++;
            dev->multifunction = 1;
        }
    }

    /* only one slot has pcie device */
    if (bus->self && nr)
        pcie_aspm_init_link_state(bus->self);

    return nr;
}

pci_scan_single_device:

struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;

    dev = pci_get_slot(bus, devfn);
    if (dev) {
        pci_dev_put(dev);
        return dev;
    }

    dev = pci_scan_device(bus, devfn);
    if (!dev)
        return NULL;

    pci_device_add(dev, bus);

    return dev;
}

核心函数为pci_scan_device:

struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;

    dev = pci_get_slot(bus, devfn);
    if (dev) {
        pci_dev_put(dev);
        return dev;
    }

    dev = pci_scan_device(bus, devfn);
    if (!dev)
        return NULL;

    pci_device_add(dev, bus);

    return dev;
}

pci_bus_read_dev_vendor_id会去读设备的devid和venderid,如果读不到,说明设备不存在了,如果读到了,就会创建一个pdev。
所以能不能扫描对端设备,就看能不能读到设备的vendorid。

bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
                int crs_timeout)
{
    int delay = 1;

    if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
        return false;

    /* some broken boards return 0 or ~0 if a slot is empty: */
    if (*l == 0xffffffff || *l == 0x00000000 ||
        *l == 0x0000ffff || *l == 0xffff0000)
        return false;

    /*
     * Configuration Request Retry Status.  Some root ports return the
     * actual device ID instead of the synthetic ID (0xFFFF) required
     * by the PCIe spec.  Ignore the device ID and only check for
     * (vendor id == 1).
     */
    while ((*l & 0xffff) == 0x0001) {
        if (!crs_timeout)
            return false;

        msleep(delay);
        delay *= 2;
        if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
            return false;
        /* Card hasn't responded in 60 seconds?  Must be stuck. */
        if (delay > crs_timeout) {
            printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
                   pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
                   PCI_FUNC(devfn));
            return false;
        }
    }

    return true;
}

while循环里每隔一段时间就会进行一次venderid的配置读写,从上述代码中可以看出,退出循环的原因会有两个
1. config配置读写失败了 (说明链路不通)
2. 超时没有得到响应(预留时间为60s,已经非常长了,第一点失败的可能性更大)

所以扫描不到对端设备时:
1.确认建链是否成功,建链失败,肯定扫描不到对端
2.确认对端的配置空间是否可写(对端的pcie模块是否处于解复位状态)
3.确认type0,type1,iatu等参数配置是否正确,如果正确了,确认配置访问的地址空间大小是否足够。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PCIE-DMA是一种基于PCIe接口的直接内存访问技术,能够实现高速数据传输。在我的学习笔记中,我详细记录了与PCIE-DMA相关的知识和学习心得。 首先,我了解到PCIE-DMA技术的基本原理和作用。PCIE-DMA可以通过PCIe总线直接访问系统内存中的数据,而不需要过多的CPU干预,提高数据传输的速度和效率。这种技术在需要大量数据传输的场景下非常有效,比如高性能计算、数据采集等。 其次,我深入学习PCIE-DMA的工作原理。PCIE-DMA的核心是DMA控制器,它负责管理和控制数据传输的流程。当设备需要读写内存中的数据时,它通过DMA控制器发送请求,然后DMA控制器生成一个事务,将数据直接传输到或从内存中。这样就大大减少了CPU的参与,提高了数据传输的效率。 另外,我还学习PCIE-DMA的配置和编程方法。PCIE-DMA的配置主要包括硬件配置和软件配置两个部分。硬件配置通常涉及到DMA控制器和PCIe接口的初始化和配置,软件配置则需要编写驱动程序来驱动DMA控制器和处理数据传输过程中的事件和异常。这部分内容对于我来说还比较新颖,需要更多的实践和实践。 最后,我总结了PCIE-DMA的应用场景和发展前景。PCIE-DMA在高性能计算、数据采集等领域具有广阔的应用前景。随着数据量的不断增加和传输速度的要求越来越高,PCIE-DMA技术的需求也将越来越大。因此,对于我来说,学习掌握PCIE-DMA技术非常有价值。 通过学习和记录PCIE-DMA的相关知识和经验,我对这项技术有了更深入的理解和掌握。希望将来能通过应用PCIE-DMA技术解决实际问题,为科研和工程项目的顺利进行做出贡献。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值