roce初始化1

本文详细解析了ROCE驱动的初始化过程,包括平台驱动结构、设备兼容性检查、内存映射基址获取、中断名称及编号配置等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

roce驱动的路径在drivers/infiniband/hw/hns其入口函数在hns_roce_main.c 中
static struct platform_driver hns_roce_driver = {
    .probe = hns_roce_probe,
    .remove = hns_roce_remove,
    .driver = {
        .name = DRV_NAME,
        .of_match_table = hns_roce_of_match,
        .acpi_match_table = ACPI_PTR(hns_roce_acpi_match),
    },
};

module_platform_driver(hns_roce_driver);
其调用module_platform_driver 来初始化roce,以ACPI 为例,如果匹配hns_roce_acpi_match 中的id,则调用hns_roce_probe
static const struct acpi_device_id hns_roce_acpi_match[] = {
    { "HISI00D1", (kernel_ulong_t)&hns_roce_hw_v1 },
    {},
};

static int hns_roce_probe(struct platform_device *pdev)
{
    int ret;
    struct hns_roce_dev *hr_dev;
    struct device *dev = &pdev->dev;
//一个hns_roce_dev *hr_dev;结构并做基本的初始化,最后调用platform_set_drvdata(pdev, hr_dev);将hr_dev 设置为对应platform_device的drvdata.
    hr_dev = (struct hns_roce_dev *)ib_alloc_device(sizeof(*hr_dev));
    if (!hr_dev)
        return -ENOMEM;
//清零hr_dev中除了ib_device的部分,因为这部分在调用ib_alloc_device 申请的时候已经初始化了
    memset((u8 *)hr_dev + sizeof(struct ib_device), 0,
        sizeof(struct hns_roce_dev) - sizeof(struct ib_device));

    hr_dev->pdev = pdev;
    platform_set_drvdata(pdev, hr_dev);
//调用dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64ULL)) 设定dma_mask,这里其实没有必有加ULL,直接写64 就可以了
    if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64ULL)) &&
        dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32ULL))) {
        dev_err(dev, "Not usable DMA addressing mode\n");
        ret = -EIO;
        goto error_failed_get_cfg;
    }
//得到acpi_device_id中的driver_data
    ret = hns_roce_get_cfg(hr_dev);
    if (ret) {
        dev_err(dev, "Get Configuration failed!\n");
        goto error_failed_get_cfg;
    }

}

static int hns_roce_get_cfg(struct hns_roce_dev *hr_dev)
{
    int i;
    int ret;
    u8 phy_port;
    int port_cnt = 0;
    struct device *dev = &hr_dev->pdev->dev;
    struct device_node *net_node;
    struct net_device *netdev = NULL;
    struct platform_device *pdev = NULL;
    struct resource *res;

    /* check if we are compatible with the underlying SoC */
//判断是通过dt还是acpi启动,如果dev_of_node返回非null,则是通过dt启动,反之如果is_acpi_device_node 返回非null则是ACPI启动.
    if (dev_of_node(dev)) {
        const struct of_device_id *of_id;

        of_id = of_match_node(hns_roce_of_match, dev->of_node);
        if (!of_id) {
            dev_err(dev, "device is not compatible!\n");
            return -ENXIO;
        }
        hr_dev->hw = (struct hns_roce_hw *)of_id->data;
        if (!hr_dev->hw) {
            dev_err(dev, "couldn't get H/W specific DT data!\n");
            return -ENXIO;
        }
    } else if (is_acpi_device_node(dev->fwnode)) {
        const struct acpi_device_id *acpi_id;
//根据匹配的id拿到driver_data
        acpi_id = acpi_match_device(hns_roce_acpi_match, dev);
        if (!acpi_id) {
            dev_err(dev, "device is not compatible!\n");
            return -ENXIO;
        }
//将hr_dev->hw 指向acpi_id->driver_data。本例中的driver_data 就是hns_roce_hw_v1
        hr_dev->hw = (struct hns_roce_hw *) acpi_id->driver_data;
        if (!hr_dev->hw) {
            dev_err(dev, "couldn't get H/W specific ACPI data!\n");
            return -ENXIO;
        }
    } else {
        dev_err(dev, "can't read compatibility data from DT or ACPI\n");
        return -ENXIO;
    }

    /* get the mapped register base address */
//得到要map的io mem的基地址
    res = platform_get_resource(hr_dev->pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(dev, "memory resource not found!\n");
        return -EINVAL;
    }
//map成虚拟地址
    hr_dev->reg_base = devm_ioremap_resource(dev, res);
    if (IS_ERR(hr_dev->reg_base))
        return PTR_ERR(hr_dev->reg_base);

    /* read the node_guid of IB device from the DT or ACPI */
//通过device_property_read_u8_array 的到node-guid,并把结果保存在hr_dev->ib_dev.node_guid,这个函数对dt和acpi都适用
    ret = device_property_read_u8_array(dev, "node-guid",
                        (u8 *)&hr_dev->ib_dev.node_guid,
                        GUID_LEN);
    if (ret) {
        dev_err(dev, "couldn't get node_guid from DT or ACPI!\n");
        return ret;
    }

    /* get the RoCE associated ethernet ports or netdevices */
    for (i = 0; i < HNS_ROCE_MAX_PORTS; i++) {
        if (dev_of_node(dev)) {
            net_node = of_parse_phandle(dev->of_node, "eth-handle",
                            i);
            if (!net_node)
                continue;
            pdev = of_find_device_by_node(net_node);
        } else if (is_acpi_device_node(dev->fwnode)) {
            struct acpi_reference_args args;
            struct fwnode_handle *fwnode;
//得到eth-handle,其类型是struct acpi_reference_args args;

            ret = acpi_node_get_property_reference(dev->fwnode,
                                   "eth-handle",
                                   i, &args);
            if (ret)
                continue;
            fwnode = acpi_fwnode_handle(args.adev);
//找到net_device 对应的platform_device
            pdev = hns_roce_find_pdev(fwnode);
        } else {
            dev_err(dev, "cannot read data from DT or ACPI\n");
            return -ENXIO;
        }

        if (pdev) {
//可见net_device 是最为platform_device的drvdata存在的
            netdev = platform_get_drvdata(pdev);
            phy_port = (u8)i;
//net_device 不为null的话,在hr_dev->iboe 中分别保存netdev和phy_port
            if (netdev) {
                hr_dev->iboe.netdevs[port_cnt] = netdev;
                hr_dev->iboe.phy_port[port_cnt] = phy_port;
            } else {
                dev_err(dev, "no netdev found with pdev %s\n",
                    pdev->name);
                return -ENODEV;
            }
            port_cnt++;
        }
    }

    if (port_cnt == 0) {
        dev_err(dev, "unable to get eth-handle for available ports!\n");
        return -EINVAL;
    }

    hr_dev->caps.num_ports = port_cnt;

    /* cmd issue mode: 0 is poll, 1 is event */
    hr_dev->cmd_mod = 1;
    hr_dev->loop_idc = 0;

    /* read the interrupt names from the DT or ACPI */
    ret = device_property_read_string_array(dev, "interrupt-names",
                        hr_dev->irq_names,
                        HNS_ROCE_MAX_IRQ_NUM);
    if (ret < 0) {
        dev_err(dev, "couldn't get interrupt names from DT or ACPI!\n");
        return ret;
    }
//得到irq number
    /* fetch the interrupt numbers */
    for (i = 0; i < HNS_ROCE_MAX_IRQ_NUM; i++) {
        hr_dev->irq[i] = platform_get_irq(hr_dev->pdev, i);
        if (hr_dev->irq[i] <= 0) {
            dev_err(dev, "platform get of irq[=%d] failed!\n", i);
            return -EINVAL;
        }
    }

    return 0;
}

我们看看device_property_read_u8_array的实现
    /* read the node_guid of IB device from the DT or ACPI */
    ret = device_property_read_u8_array(dev, "node-guid",
                        (u8 *)&hr_dev->ib_dev.node_guid,
                        GUID_LEN);
int device_property_read_u8_array(struct device *dev, const char *propname,
                  u8 *val, size_t nval)
{
    return fwnode_property_read_u8_array(dev_fwnode(dev), propname, val, nval);
}

可见device_property_read_u8_array 对dt和acpi都适用,因为这里会调用dev_fwnode(dev)
static inline struct fwnode_handle *dev_fwnode(struct device *dev)
{
    return IS_ENABLED(CONFIG_OF) && dev->of_node ?
        &dev->of_node->fwnode : dev->fwnode;
}
可以看到无论如何都会返回正确的fwnode
总的来说本例中device_property_read_u8_array 就是读取"node-guid"的值
linux-81uf:/sys/class/infiniband/hns_0 # cat node_guid
009a:cd00:0001:0203


<think>嗯,用户这次的问题聚焦在NvLINK、RoCE和PCIe Switch这三种互连技术对PCIe Root Complex(RC)的依赖程度上。从用户引用的资料来看,ta应该是在设计高性能计算或AI集群的网络架构,特别是参考了H100服务器的部署方案(引用[2])和RDMA通信细节(引用[3]),可能需要优化跨设备通信路径。 用户的核心诉求其实是想厘清三种技术的本质差异:哪些事务能绕过RC直接点对点传输,哪些必须经过RC。这关系到系统延迟和吞吐量的优化方向。比如引用[1]提到的NUMA绑定和PCIe带宽分配,说明用户对资源争用很敏感。 具体分析三种技术: 1)NvLINK最特殊,它本质是NVidia的私有协议,物理层和链路层都独立于PCIe。虽然早期版本需要RC初始化,但数据传输时GPU间直接通过NVSwitch交换数据,完全绕过RC。用户引用[2]里H100服务器的部署没有提到NvLINK,但实际NVSwitch拓扑正是NvLINK的典型应用场景。 2)RoCE虽然跑在以太网上层,但网卡本身是PCIe设备。引用[3]明确展示了DMA操作必须通过RC访问主机内存,即使用户数据最终走网络传输,但内存准备阶段必然受RC制约。这也是为什么云计算场景(用户问题3)无法摆脱PCIe依赖。 3)PCIe Switch作为扩展器,所有事务都要经过RC路由。即使用户在Switch下挂接多个设备,设备间通信(比如GPU到SSD)仍然需要RC参与地址解析和协议转换,这点和引用[1]强调的通道隔离问题直接相关。 用户可能隐含的深层需求是:如何减少RC瓶颈。比如NvLINK适合GPU间密集型通信,而RoCE更适合跨节点场景。在回复时需要用技术细节支撑这些选择依据,比如指出NvLINK的延迟优势(200ns级)来自RC旁路。</think>以下是NvLINK、RoCE和PCIe Switch在涉及PCIe Root Complex(RC)事务处理方面的对比分析,结合技术原理和引用内容: --- ### **事务处理与PCIe RC的交互关系** | **技术** | **需要PCIe RC处理的事务** | **绕过PCIe RC的场景** | **关键特征** | |-------------------|--------------------------------------------------|-----------------------------------------|------------------------------------------| | **NvLINK** | **初始化配置**:设备发现、地址映射建立[^1]<br>**管理操作**:驱动加载、中断配置 | **数据传输**:GPU间通过NVSwitch直接通信(P2P)<br>**原子操作**:GPU间共享内存同步 | 物理层独立协议,数据路径不依赖PCIe RC[^1] | | **RoCE** | **所有DMA操作**:主机内存↔网卡数据搬运[^3]<br>**队列管理**:WQE/CQE提交与轮询<br>**中断处理**:完成事件通知 | **网络传输**:报文封装/解封装在网卡内部完成<br>**端到端协议**:RoCEv2路由由以太网交换机处理 | 完全依赖RC实现主机内存访问(引用[3]流程) | | **PCIe Switch** | **所有下游设备事务**:地址解析、协议转换<br>**非透明桥接**:跨RC域通信<br>**热插拔事件**:设备动态配置 | **Switch内部交换**:同RC域下端点设备间P2P(需RC初始化但无需持续干预) | 纯扩展器,不改变事务对RC的依赖本质[^1] | --- ### **技术差异深度解析** 1. **NvLINK的RC规避机制** - GPU间通过**NVSwitch**建立直接链路,数据传输使用专用协议栈 - 仅需RC初始化地址映射: $$ \text{RC配置} \rightarrow \text{GPU BAR空间映射} \rightarrow \text{NVSwitch路由表建立} $$ - 运行时数据路径: $$ \text{GPU A} \xrightarrow{\text{NVLINK}} \text{NVSwitch} \xrightarrow{\text{NVLINK}} \text{GPU B} $$ 全程无需RC参与(引用[1] NUMA优化思想的硬件实现) 2. **RoCE对RC的强依赖** - 核心瓶颈在**DMA双阶段路径**(引用[3]流程): - **发送端**: ```mermaid graph LR A[App下发WQE] --> B[RNIC DMA读内存] --> C[RC路由] --> D[数据封装发送] ``` - **接收端**: ```mermaid graph LR E[报文到达] --> F[RNIC校验] --> G[RC路由DMA写内存] --> H[生成CQE] ``` - 即使使用SR-IOV,VF的DMA仍受RC监管(云计算场景无法规避[^2]) 3. **PCIe Switch的定位限制** - 作为**透明桥接器**,所有事务需符合PCIe规范: - 端点设备通信路径: $$ \text{EP1} \rightarrow \text{Switch} \rightarrow \text{RC} \rightarrow \text{Switch} \rightarrow \text{EP2} $$ - 仅当RC启用**P2P使能**时: $$ \text{EP1} \xrightarrow{\text{Switch直通}} \text{EP2} \quad (\text{RC仅初始路由}) $$ - 跨RC域通信(如多主机)强制经过RC(引用[2]中TOR Switch的物理隔离类似) --- ### **性能影响对比** | **指标** | NvLINK | RoCE | PCIe Switch | |----------------|-------------------------|--------------------------|--------------------------| | **延迟** | 200~300ns(绕过RC) | 1~2μs(RC+DMA开销)[^3] | 500ns~1μs(RC路由延迟) | | **吞吐瓶颈** | NVSwitch交换容量 | PCIe链路带宽(如Gen5 x16=128GB/s) | PCIe Switch芯片带宽 | | **扩展性限制** | NVLink域内GPU数量 | RC处理能力(IOMMU效率) | PCIe域最大负载设备数 | > 注:RoCE在引用[2]的400Gbps网卡场景中,实际带宽受限于PCIe Gen4 x16(64GB/s)与RC处理效率[^1][^2] --- ### **设计启示** 1. **NvLINK适用场景** - GPU密集型计算(如AI训练):需规避RC瓶颈时优先选用 - 拓扑优化:通过NVSwitch构建全连接,避免跨NUMA(引用[1]原则) 2. **RoCE部署要点** - 强制优化RC:启用ATS(Address Translation Services)、调整IOMMU映射粒度 - 避免通道争用:为网卡分配独立PCIe通道(引用[1] PCIe带宽分配策略) 3. **PCIe Switch使用禁忌** - 禁止将高吞吐设备(GPU/NVMe)与RoCE网卡置于同Switch域 - 跨RC通信需改用InfiniBand或专用互联方案 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值