前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Intel E810-iRDMA网卡-Linux内核驱动和用户态源码分析

Intel E810-iRDMA网卡-Linux内核驱动和用户态源码分析

原创
作者头像
ssbandjl
修改2024-04-27 09:52:56
2590
修改2024-04-27 09:52:56
举报
文章被收录于专栏:Linux内核DPULinux内核

简介

术语

虚拟机队列(VMQ) 接口

用户空间直接访问 (UDA) 旨在以通用方式提供用户空间访问队列,但 E810 不支持此功能。 UDA 仅在内核中可用,并且仅限于 iWARP 连接设置和错误处理。 UDA 在用户空间中不可用

主机内存缓存 (HMC)

E810芯片手册

本文档介绍了双端口 100 Gb 以太网 (GbE) 网络接口设备英特尔? 以太网控制器 E810 (E810) 的外部架构(包括设备操作、引脚描述、寄存器、定义等)。 本文档旨在为架构师、逻辑设计人员、固件和软件设备驱动程序开发人员、电路板设计人员、测试工程师或可能需要有关 E810 的特定技术或编程信息的任何其他人提供参考。 E810 是英特尔首款多速 100 GbE 控制器,支持 100 Mb/s 至 100 Gb/s 之间的速率。 E810 的主要目标是实现适用于企业、云和通信服务提供商应用的可扩展以太网控制器。 E810 支持的主要功能包括两个 100 Gigabit 以太网端口、100 Gigabit 吞吐量性能、增强的可编程数据包处理管道、虚拟化(增强的 SR-IOV 支持,最多 256 个 VF 和向后兼容 VF 驱动程序支持)、通信的新功能 市场(细粒度调度程序、传输头丢弃支持、根据不同标头调整信用、增强的 QoS 和增强的突发控制)和 RDMA(iWARP 和 RoCEv2)。 表 1-1 总结了 E810 的功能。 有关支持功能的更多信息,请参阅英特尔? 以太网控制器 E810 功能支持表

Intel E810的PBLE/HMC

E810 的四散列查找和协议引擎使用主机内存作为各种上下文对象的后备存储。 主机内存缓存 (HMC) 负责缓存和管理这些上下文对象。 对于每个 RDMA 连接,协议引擎使用 QP 上下文对象(存储 TCP 序列号等的 TCP/IP 连接上下文)和入站 RDMA 读取队列 (IRRQ) 对象,该对象缓冲入站 RDMA 读取请求,直到其关联的读取响应被响应。 预定传输。 对于每个 RDMA 内存区域,协议引擎使用内存区域表条目 (MRTE) 对象来存储区域边界和访问权限信息,并使用一组物理缓冲区列表条目 (PBLE) 对象来存储虚拟到物理地址转换 这个地区。 这些以及更多协议引擎上下文对象将在第 11 章中详细介绍。第 9 节中提供了有关 HMC 操作和配置的一般信息

E810 使用主机内存作为许多上下文对象的后备存储,这些上下文对象用于跟踪队列状态和 iWARP 对象。 主机内存缓存 (HMC) 是负责管理存储在主机内存中的 iWARP 上下文对象的组件。 HMC 在每个 PCI 功能的基础上管理主机内存,并进一步将每个 PCI 功能的 HMC 内存空间分解为用于管理用于给定 PCI 功能的每个上下文对象的内存。 主机软件负责在访问特定对象之前分配 HMC 使用的主机页面。 此外,可用于特定功能的 HMC 后备存储的内存量由活动资源配置文件决定,而活动资源配置文件由软件驱动程序的操作环境和当前活动的 PCI 功能数量决定。 可以在驱动程序初始化时选择资源配置文件

HMC 需要在主机内存中驻留大量数据结构的后备存储来执行其功能。 表 9-11 提供了数据结构的列表以及需要为每个数据结构分配的内存量。 “HMC 对象位置”列指示 HMC 对象(以及关联的后备存储页面)是否仅位于 PF HMC 对象空间中,还是位于 PF 和 VF HMC 对象空间中。 通常,协议引擎 HMC 对象被分为特定于 PF 和 VF 的 HMC 对象空间。 资源可能稀少。 例如,如果一个函数分配了 512 个 QP,但只使用了 8 个 QP,则只需要分配 4K 内存,而不是为所有 512 个 QP 分配整个内存。 某些 HMC 对象需要在驱动程序初始化时完全填充,例如协议引擎哈希表条目。 有关协议引擎的 HMC 资源分配策略的更多信息,请参阅第 11.5 节

512 字节被保留用于 QP 上下文。 每个设备最多有 256K QP 上下文。 该数字除以所有支持协议引擎的 PCI 功能。 VF 对象的内存由 PF 驱动程序分配并使用 PF 请求者 ID (RID) 进行访问。 对于单功能设备,硬件保留 256K QP 之一供内部使用

每个支持协议引擎的 PCI 功能最多可分配 256M PBLE 条目。 VF 对象的内存由 VF 驱动程序分配并使用 VF 请求者 ID (RID) 进行访问。 任何关联的页面描述符都由 PF 驱动程序分配并使用 PF RID 进行访问

为了访问(并缓存在片上存储器中)表 9-11 中定义的数据结构,HMC 使用私有存储器地址空间的概念。 E810 具有 8GB 私有内存地址空间,可以根据实际上下文使用情况使用主机内存进行稀疏支持。 驱动程序不需要为驱动程序当前未使用的 HMC 对象分配页面。 私有内存地址空间首先按 PCI 功能划分,然后按对象或数据结构类型划分,最后按对象索引划分。 分配给特定 PCI 功能的私有内存地址空间部分称为功能私有内存 (FPM)。 另请注意,VF FPM 不是由 VF 驱动程序直接编程。 PF驱动器使用HMC功能索引来选择要编程的VF FPM。 E810如何提供私有内存和主机物理地址之间的地址映射,如图9-5所示。 图左侧所示的 PM 地址表示 E810 Private Memory 地址从 0 到 8GB-1。 E810内部使用PM地址空间,将其转换为主机物理地址以访问主机内存

图 9-5, 主机内存缓存私有内存地址空间

有关四重哈希缓存的信息,请参阅第 9.3.2 节。 图 9-5 的左侧部分显示了驻留在片上的 HMC 部分。 这部分包括实际的对象缓存,它保留主机内存中的部分数据以提高性能和段描述符 (SD)。 SD 驻留在片上 32 KB RAM 中,称为段描述符表。 段描述符表保存了 4096 个指向主机内存页面的指针(8B * 4096=32KB)。 段描述符表中的连续 SD 的唯一范围被分配给每个活动的 PCI 功能。 段描述符表是E810提供的第一级私有内存地址转换

SD 使用 PFHMC_SDCMD(第 13.2.2.20.35 节)、PFHMC_SDDATALOW(第 13.2.2.20.36 节)和 PFHMC_SDDATAHIGH(第 13.2.2.20.37 节)寄存器进行编程。 协议引擎 CQP 操作也可用于对段描述符进行编程。 图 9-5 中段描述符表右侧的所有内容都驻留在主机内存中。 每个 PCI 功能都有一组寄存器(GLHMC_SDPART[n] 和 GLHMC_VFSDPART[n]),用于定义属于 PCI 功能的 SD 的基数和数量。 GLHMC_SDPART[n] 和 GLHMC_VFSDPART[n] 寄存器从 NVM 编程,也可以在创建控制队列对操作期间由固件编程(第 11.5.2.1 节)。 E810 为每个内部访问提供范围检查,以确保给定的 PCI 功能绝不允许访问其有效 SD 范围之外的内存。 E810 根据资源配置文件在内部管理 SD 基址和编号寄存器,该资源配置文件在 NVM 加载时加载或由第一个 E810 驱动程序选择在创建控制队列对操作期间为设备加载(第 11.5.2.1 节)。 E810 提供的第二级私有内存地址转换是页描述符 (PD)。 每个 SD 指向一个2MB的主机页,该主机页分为 512 个 PD,这些 PD 只是 64 位物理内存地址, 每个PD大小为4KB。 每个 PD 都指向私有内存地址空间的一个后端页。 总共 8 GB(4096个SD * 2MB = 8GB) 专用内存地址空间是使用完全填充的段描述符表导出的,该表指向保存 2M PD 的 4096 个 4KB 主机页。 每个 2M PD 都指向主机内存支持页面,总共 8 GB 地址空间。 如前所述,如果软件未使用该部分专用内存地址空间,则不需要用内存填充所有 SD 或 PD。 主机内存中PD结构的格式如表9-12所示

HMC 支持页物理地址是驱动程序分配的页的地址,该页将保存 HMC 对象上下文。 该地址必须与主机内存中的 4 KB 地址对齐。 PD 有效位允许软件在需要 HMC 上下文对象时根据需要稀疏填充 PD 条目。 软件必须分配保存 PD 打包数组的主机内存页,如图 9-5 所示。 这些 PD 页的物理地址用于通过使用 PFHMC_SDCMD(第 13.2.2.20.35 节)、PFHMC_SDDATALOW(第 13.2.2.20.36 节)和 PFHMC_SDDATAHIGH(第 13.2.2.20.37 节)寄存器来填充 SD 条目。 有关如何使用这些寄存器对 SD 进行编程的更多信息,请参见第 9.3.8 节。 专用存储器进一步分为单独的 PCI 功能专用存储器 (FPM) 地址。 PCI 功能可以是物理功能,也可以是虚拟功能。 前 8 个 FPM 地址空间保留给 NIC PF。 接下来的八个 FPM 地址空间用于支持为 iWARP、RoCEv2 或 UDA 提供加速的协议引擎的 PF。 最后 32 个 FPM 地址空间用于支持协议引擎的 VF,为 iWARP、RoCEv2 或 UDA 提供加速。 图 9-6 显示了如何为每个 PCI 功能划分私有内存地址空间。 可以分配给函数的最小私有内存量为 2 MB (1 SD)。 可以分配给函数的最大值是整个段表,在这种情况下,其他函数不能拥有任何私有内存资源。 请注意,对象缓存使用 HMC 函数编号来寻址 HMC 对象以确定正确的 FPM。 FPM标识属于PCI功能的专用存储器地址空间的范围。 由于每个 SD 代表 2 MB HMC PM 地址空间,因此 FPM 还标识属于 PCI 功能的 SD 范围

图 9-6, 主机内存缓存功能私有内存空间

每个 PCI 功能的私有内存空间进一步分为主机内存中每个对象的单独内存空间。 每个 PCI 函数都有一组寄存器,用于定义 FPM 空间中对象的基地址以及特定对象的边界(或最大条目数)。 图 9-7 描述了驻留在私有内存空间中的一些当前对象(有关对象的完整列表,请参见表 9-11)。 FPM地址是根据对象类型(标识对象基址寄存器)和对象索引(FPM基址已经计算出来)计算出来的。 最终,FPM基地址、对象基地址、对象大小、对象索引都用来确定私有内存地址

图 9-8描述了将私有内存地址解码为主机地址。 此外,图 9-8 描述了 SD 直接对私有内存空间支持页进行寻址,而不是使用第二级(PD)间接寻址。 每个 PCI 功能都可以将其 SD 范围内的任何 SD 设置为指向 PD 或直接指向后端页。 段类型在PFHMC_SDDATALOW.PMSDTYPE 寄存器字段中指定。 直接段方法可用于对 FPM 空间没有大量要求的 PCI 功能,以减少访问 HMC 对象时产生的开销。 如果驱动程序能够分配足够大的物理连续页面范围来容纳支持特定 PCI 功能上加载的驱动程序所需的 FPM 所需的整个 PD 空间,则可以额外使用直接段方法。 如果驱动程序碰巧分配了物理连续的内存块,或者操作系统支持 2 MB 页面,此模式可以防止额外的地址查找并提高性能

9.3.2 对象缓存 RDMA 的主机内存缓存分为两个缓存。 一个缓存包含除四元哈希条目之外的所有 RDMA 对象。 四重哈希条目被放置在不同的缓存中,以便过滤机制可以引用它们。 当添加或删除四元哈希对象时,RDMA 固件负责更新四元哈希缓存中的四元哈希对象。 这两个缓存具有独立的寄存器,但共享相同的基本机制。 由于有两个组件引用四重哈希缓存,因此两个缓存的寄存器必须保持一致。 有关四散列主机内存缓存(包括寄存器和中断)的更多信息,请参阅第 9 节

E810 私有内存地址空间配置分为两步: 1. 将 HMC 相关资源划分为每个 PCI 功能资源。 2. 将生成的函数私有内存 (FPM) 划分为单独的对象。 为了简化资源分配,E810 提供了资源配置文件概念,该概念考虑了 PCI 功能的数量、启用协议引擎的 PCI 功能的数量以及操作系统环境来划分 HMC 专用内存空间和协议引擎 Doorbell 资源。 这些 HMC 资源配置文件用于对 HMC 段描述符表和协议引擎门铃资源进行分区。 软件在每个 PCI 功能的基础上执行 FPM 划分。 表 9-13 中描述了当前定义的 HMC 资源配置文件。 协议引擎门铃资源对 HMC 资源配置文件施加了限制,因为芯片上这些资源的数量是固定的。 有足够的门铃资源用于 256K 协议队列对和 512K 协议引擎完成队列。 这些资源必须在需要协议引擎功能的 PCI 功能之间进行划分。 协议引擎资源将在 11.1 节中进一步讨论

源码分析

模块初始化和probe-E810驱动

代码语言:c
复制
intel irdma/ice/e810 driver:
drivers/infiniband/hw/irdma/main.c
module_init(irdma_init_module); -> 模块初始化
    auxiliary_driver_register i40iw_auxiliary_drv
        .probe = i40iw_probe
    auxiliary_driver_register(&irdma_auxiliary_drv.adrv)
    irdma_register_notifiers
        register_inetaddr_notifier(&irdma_inetaddr_notifier);
        register_inet6addr_notifier(&irdma_inetaddr6_notifier);
        register_netevent_notifier(&irdma_net_notifier);
        register_netdevice_notifier(&irdma_netdevice_notifier);
...		
irdma_probe <- irdma_auxiliary_drv

irdma_ib_register_device
    irdma_init_rdma_device
        irdma_init_roce_device(iwdev)
        or
        irdma_init_iw_device
        ib_set_device_ops(&iwdev->ibdev, &irdma_dev_ops)
    ib_device_set_netdev
    dma_set_max_seg_size
    ib_register_device(&iwdev->ibdev, "irdma%d", iwdev->rf->hw.device)
    irdma_port_ibevent(iwdev)
        event.event = iwdev->iw_status ? IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR
        ib_dispatch_event(&event)


intel e810, drivers/infiniband/hw/irdma/main.c
irdma_probe
    ib_alloc_device -> #define ib_alloc_device(drv_struct, member) -> struct ib_device *_ib_alloc_device(size_t size)
        device = kzalloc(size, GFP_KERNEL)
        rdma_restrack_init
        rdma_init_coredev -> RDMA/core:在net命名空间中实现compat device/sysfs树,实现ib_core的兼容层sysfs条目,以便非init_net net命名空间也可以发现rdma设备。 每个非 init_net 网络命名空间都在其中创建了 ib_core_device。 这样的 ib_core_device sysfs 树类似于 init_net 命名空间中找到的 rdma 设备。 这允许通过 sysfs 条目在多个非 init_net 网络命名空间中发现 rdma 设备,并且对 rdma-core 用户空间很有帮助
            此 BUILD_BUG_ON 旨在捕获 ib_core_device 和 device 联合的布局更改。 dev 必须是第一个元素,因为 ib_core 和提供程序驱动程序使用它。 在 ib_core_device 中的 device 之前添加任何内容都会打破这个假设
            coredev->dev.class = &ib_class
            device_initialize -> device_initialize - 初始化设备结构。 @dev:设备。 * 这通过初始化其字段来准备设备以供其他层使用。 如果由该函数调用,则它是 device_register() 的前半部分,尽管它也可以单独调用,因此可以使用 @dev 的字段。 特别是,调用此函数后,可以使用 get_device()/put_device() 对 @dev 进行引用计数。 * @dev 中的所有字段都必须由调用者初始化为 0,除非那些明确设置为其他值的字段。 最简单的方法是使用 kzalloc() 来分配包含 @dev 的结构。 * 注意:调用此函数后,使用 put_device() 放弃引用,而不是直接释放 @dev
                INIT_LIST_HEAD(&dev->dma_pools)
                device_pm_init(dev)
                swiotlb_dev_init(dev)
                    dev->dma_io_tlb_mem = &io_tlb_default_mem
            INIT_LIST_HEAD(&coredev->port_list) -> RDMA/core:引入 ib_core_device 来保存设备,为了支持 rdma 设备的多个网络命名空间中的 sysfs 条目,引入一个 ib_core_device ,其范围仅限于保存核心设备和每个端口 sysfs 相关条目。 这是准备补丁,以便在后续补丁中可以在每个网络命名空间中创建多个 ib_core_device,这些设备都可以共享 ib_device。 (a) 将 sysfs 特定字段移至 ib_core_device。 (b) 使 sysfs 和设备生命周期相关例程在 ib_core_device 上工作。 (c) 引入并使用 rdma_init_coredev() 帮助程序来初始化 coredev 字段
            write_pnet(&coredev->rdma_net, net)
        INIT_LIST_HEAD(&device->event_handler_list)
        init_rwsem(&device->event_handler_rwsem) -> init_rwsem()的功能是初始化读写信号量,将信号量的count字段设置为0
        xa_init_flags(&device->client_data, XA_FLAGS_ALLOC) -> 初始化数组
        init_completion(&device->unreg_completion) -> RDMA/核心:使用 netlink 命令同步取消注册, -> 初始化动态分配的完成对象 -> 参考: https://zhuanlan.zhihu.com/p/504938832
        device->uverbs_cmd_mask = -> IB_USER_VERBS_CMD_ALLOC_MW... -> 设置命令掩码/比特位
    irdma_fill_device_info -> 填充设备信息,默认配置
        rf->gen_ops.register_qset = irdma_lan_register_qset;
            ice_add_rdma_qset(pf, &qset)
                ice_get_main_vsi
                ice_cfg_vsi_rdma
                    ice_cfg_vsi_qs
                        ice_sched_get_tc_node
                        ice_sched_cfg_vsi
                            ice_get_vsi_ctx
                            ice_sched_get_vsi_node
                            ...
                ice_ena_vsi_rdma_qset
                    ice_sched_get_free_qparent
        rf->gen_ops.unregister_qset = irdma_lan_unregister_qset
        rf->gen_ops.request_reset = irdma_request_reset
        rf->hw.hw_addr = pf->hw.hw_addr
        ...
    irdma_ctrl_init_hw -> 初始化硬件的控制部分
        irdma_setup_init_state
            irdma_save_msix_info
            rf->obj_mem.size = ALIGN(8192, IRDMA_HW_PAGE_SIZE) -> 以4096为上界对齐, 分配8KB
            rf->obj_mem.va = dma_alloc_coherent(rf->hw.device, rf->obj_mem.size, &rf->obj_mem.pa, GFP_KERNEL) -> 申请连续的大块内存(8KB)
            irdma_initialize_dev
                irdma_obj_aligned_mem -> irdma_obj_aligned_mem - 从设备分配的内存中获取对齐的内存,@rf:RDMA PCI函数@memptr:指向内存地址@size:所需的内存大小@mask:对齐内存的掩码获取请求大小的对齐内存并更新memptr 指向新对齐的内存成功则返回0,否则返回无内存错误 -> rf: RDMA PCI function
                    rf->obj_next.pa = memptr->pa + size
                irdma_obj_aligned_mem(rf, &mem, IRDMA_COMMIT_FPM_BUF_SIZE,
                info.bar0 = rf->hw.hw_addr
                irdma_sc_dev_init -> Initialize control part of device
                    dev->hw->hw_addr = info->bar0
                    irdma_sc_init_hw(dev) -> RDMA/irdma:实施硬件管理队列 OP,驱动程序将特权命令发布到硬件管理队列(控制 QP 或 CQP)以请求硬件的管理操作。 实现 CQP 的创建/销毁以及支持函数、数据结构和标头以处理不同的 CQP 命令
                        i40iw_init_hw(dev)
                            dev->hw_regs[i] = (u32 __iomem *)(i40iw_regs[i] + hw_addr)
                            dev->wqe_alloc_db = dev->hw_regs[IRDMA_WQEALLOC]
                            dev->hw_attrs.page_size_cap = SZ_4K | SZ_2M -> RDMA/irdma:不要为 x722 通告 1GB 页面大小,x722 不支持 1GB 页面大小,但 irdma 驱动程序错误地将 x722 设备的 1GB 页面大小支持通告给 ib_core,以计算在此 MR 上使用的最佳页面大小。 这可能会导致 MR 上的硬件计算出不正确的起始偏移量
                            dev->hw_attrs.max_qp_wr = I40IW_MAX_QP_WRS
                        or icrdma_init_hw(dev)
                            dev->hw_attrs.page_size_cap = SZ_4K | SZ_2M | SZ_1G;
                    irdma_wait_pe_ready(dev)
                        do {
                            statuscpu0 = readl(dev->hw_regs[IRDMA_GLPE_CPUSTATUS0])
                            mdelay(1000) -> mdelay是忙等待函数,在延迟过程中无法运行其他任务.这个延迟的时间是准确的.是需要等待多少时间就会真正等待多少时间
                        }
                    val = readl(dev->hw_regs[IRDMA_GLPCI_LBARCTRL])
                    dev->db_addr = dev->hw->hw_addr + (uintptr_t)dev->hw_regs[IRDMA_DB_ADDR_OFFSET]
        irdma_create_cqp(rf)
            cqp->sq.va = dma_alloc_coherent(dev->hw->device, cqp->sq.size,
            irdma_obj_aligned_mem(rf, &mem, sizeof(struct irdma_cqp_ctx),
            irdma_sc_cqp_init(dev->cqp, &cqp_init_info) -> Initialize buffers for a control Queue Pair
                irdma_get_encoded_wqe_size IRDMA_QUEUE_TYPE_CQP
                cqp->rocev2_rto_policy = info->rocev2_rto_policy
                memcpy(&cqp->dcqcn_params, &info->dcqcn_params, sizeof(cqp->dcqcn_params))
                IRDMA_RING_INIT(cqp->sq_ring, cqp->sq_size)
                INIT_LIST_HEAD(&cqp->dev->cqp_cmd_head)
                writel(0, cqp->dev->hw_regs[IRDMA_CQPTAIL]) -> db, register -> fpga, xt not call this db
                writel(0, cqp->dev->hw_regs[IRDMA_CQPDB])
                writel(0, cqp->dev->hw_regs[IRDMA_CCQPSTATUS])
            irdma_sc_cqp_create(dev->cqp, &maj_err, &min_err) -> create cqp during bringup
                cqp->sdbuf.size = ALIGN(IRDMA_UPDATE_SD_BUFF_SIZE * cqp->sq_size, IRDMA_SD_BUF_ALIGNMENT -> 128
                writel(p1, cqp->dev->hw_regs[IRDMA_CCQPHIGH])
                writel(p2, cqp->dev->hw_regs[IRDMA_CCQPLOW])
                udelay(cqp->dev->hw_attrs.max_sleep_count)
                readl(cqp->dev->hw_regs[IRDMA_CCQPSTATUS])
                cqp->process_cqp_sds = irdma_update_sds_noccq
            INIT_LIST_HEAD(&cqp->cqp_avail_reqs)
            INIT_LIST_HEAD(&cqp->cqp_pending_reqs)
            init_waitqueue_head(&cqp->cqp_requests[i].waitq) <- wake_up(&cqp_request->waitq)
        irdma_hmc_setup(rf)
            rf->sd_type = IRDMA_SD_TYPE_DIRECT
            irdma_cfg_fpm_val(&rf->sc_dev, qpcnt) -> 算法
                irdma_sc_init_iw_hmc(dev, dev->hmc_fn_id)
                    irdma_sc_query_fpm_val(dev->cqp, 0, hmc_info->hmc_fn_id, &query_fpm_mem, true, wait_type)
                        wqe = irdma_sc_cqp_get_next_send_wqe(cqp, scratch)
                        irdma_sc_cqp_post_sq(cqp)
                        irdma_cqp_poll_registers(cqp, tail,
                            irdma_get_cqp_reg_info(cqp, &val, &newtail, &error)
                            error = readl(cqp->dev->hw_regs[IRDMA_CQPERRCODES])
                    irdma_sc_parse_fpm_query_buf(dev, query_fpm_mem.va, hmc_info,
                        irdma_sc_decode_fpm_query(buf, 32, obj_info, IRDMA_HMC_IW_HTE)
                        obj_info[IRDMA_HMC_IW_APBVT_ENTRY].size = 8192
                sd_needed = irdma_est_sd(dev, hmc_info) -> 返回 HMC 的 SD 的近似数量
                    size += round_up(hmc_info->hmc_obj[IRDMA_HMC_IW_PBLE].cnt *
                qpwanted = min(qp_count, hmc_info->hmc_obj[IRDMA_HMC_IW_QP].max_cnt)
                while (irdma_q1_cnt(dev, hmc_info, qpwanted) > hmc_info->hmc_obj[IRDMA_HMC_IW_Q1].max_cnt)
                    roundup_pow_of_two
                cfg_fpm_value_gen_1
                or
                cfg_fpm_value_gen_2
                irdma_sc_cfg_iw_fpm(dev, dev->hmc_fn_id) -> warp
                    irdma_sc_parse_fpm_commit_buf
                        irdma_sc_decode_fpm_commit
                            obj_info[rsrc_idx].cnt = (u32)FIELD_GET(IRDMA_COMMIT_FPM_QPCNT, temp) -> 初始化时获取支持的QP数量规格
                hmc_info->sd_table.sd_entry = virt_mem.va
            irdma_create_hmc_objs(rf, true, rf->rdma_ver) -> 初始化时驱动会按照支持QP数量规格把HMC准备好 -> create all hmc objects for the device
                for (i = 0; i < IW_HMC_OBJ_TYPE_NUM; i++)
                    irdma_create_hmc_obj_type(dev, &info) -> irdma_sc_create_hmc_obj
	                    irdma_find_sd_index_limit(info->hmc_info, info->rsrc_type, info->start_idx, info->count, &sd_idx, &sd_lmt) -> 查找段描述符索引限制,@hmc_info:指向 HMC 配置信息结构的指针 @type:我们正在搜索的 HMC 资源类型 @idx:对象的起始索引 @cnt:我们尝试创建的对象数量 @ sd_idx:返回相关段描述符索引的指针 @sd_limit:返回段描述符最大数量的指针 该函数计算 irdma_hmc_rsrc_type 定义的资源的段描述符索引和索引限制
                        irdma_find_pd_index_limit(info->hmc_info, info->rsrc_type, info->start_idx, info->count, &pd_idx, &pd_lmt) -> finds page descriptor index limit
                        for (j = sd_idx; j < sd_lmt; j++)
                            irdma_add_sd_table_entry(dev->hw, info->hmc_info, j, info->entry_type, IRDMA_HMC_DIRECT_BP_SIZE) -> RDMA/irdma:添加 HMC 后备存储设置功能,HW 使用主机内存作为多个协议上下文对象和队列状态跟踪的后备存储。 主机内存缓存 (HMC) 是负责管理存储在主机内存中的这些对象的组件。 添加函数和数据结构来管理 HMC 为各种对象使用的支持页面的分配 -> Adds a segment descriptor to the table
                                allocate a 4K pd page or 2M backing page
                                dma_mem.size = ALIGN(alloc_len, IRDMA_HMC_PD_BP_BUF_ALIGNMENT)
                                dma_mem.va = dma_alloc_coherent(hw->device, dma_mem.size,
                                if (type == IRDMA_SD_TYPE_PAGED)
                                    vmem->size = sizeof(struct irdma_hmc_pd_entry) * 512
                                    vmem->va = kzalloc(vmem->size, GFP_KERNEL)
                                    sd_entry->u.pd_table.pd_entry = vmem->va
                                    memcpy(&sd_entry->u.pd_table.pd_page_addr, &dma_mem, sizeof(sd_entry->u.pd_table.pd_page_addr))
                                else
                                    memcpy(&sd_entry->u.bp.addr, &dma_mem, sizeof(sd_entry->u.bp.addr))
                            irdma_add_pd_table_entry -> Adds page descriptor to the specified table -> 将页面描述符添加到指定的表中,@dev:指向我们的设备结构的指针@hmc_info:指向HMC配置信息结构的指针@pd_index:要操作的页面描述符索引@rsrc_pg:如果不为NULL,则使用预分配的页面而不是分配 新的一个。 该函数: 1. 初始化 pd 条目 2. 在 pd_table 中添加 pd_entry 3. 在 irdma_hmc_pd_entry 结构中标记该条目有效 4. 将 pd_entry 的引用计数初始化为 1 假设: 1. pd 的内存应该固定,物理上连续并且 在 4K 边界上对齐并将内存归零。 2.大小应为4K
                                page->size = ALIGN(IRDMA_HMC_PAGED_BP_SIZE, IRDMA_HMC_PD_BP_BUF_ALIGNMENT)
                                page->va = dma_alloc_coherent(dev->hw->device, page->size, &page->pa,GFP_KERNEL)
                                memcpy(&pd_entry->bp.addr, page, sizeof(pd_entry->bp.addr))
                                pd_entry->bp.entry_type = IRDMA_SD_TYPE_PAGED
                                irdma_invalidate_pf_hmc_pd(dev, sd_idx, rel_pd_idx) -> 使 PF 硬件中的 pd 缓存无效
                                    writel(val, dev->hw_regs[IRDMA_PFHMC_PDINV])
                        irdma_hmc_finish_add_sd_reg(dev, info) -> irdma_hmc_sd_grp -> 为 cqp 设置 sd 条目组
                            for (i = sd_index; i < sd_index + sd_cnt; i++)
                                irdma_set_sd_entry(pa, i, sd_entry->entry_type,
                                or irdma_clr_sd_entry(i, sd_entry->entry_type,
                                ret_code = dev->cqp->process_cqp_sds(dev, &sdinfo)
        irdma_initialize_hw_rsrc -> 初始化硬件资源跟踪数组
            if (rf->rdma_ver != IRDMA_GEN_1)
                rf->allocated_ws_nodes = bitmap_zalloc(IRDMA_MAX_WS_NODES,
                set_bit(0, rf->allocated_ws_nodes)
            rsrc_size = irdma_calc_mem_rsrc_size(rf) -> 计算内存资源大小
                rsrc_size = sizeof(struct irdma_arp_entry) * rf->arp_table_size
                rsrc_size += sizeof(unsigned long) * BITS_TO_LONGS(rf->max_qp)
                ...
            rf->mem_rsrc = vzalloc(rsrc_size) -> 分配虚拟连续内存并用零填充
            rf->arp_table = (struct irdma_arp_entry *)rf->mem_rsrc
            irdma_set_hw_rsrc(rf) -> 按基址偏移和预留空间设置各物理资源(QP,CQ,MR,PD,AH,MCG,ARP,qp_table, cq_table)基地址
            rf->mr_stagmask = ~(((1 << mrdrvbits) - 1) << (32 - mrdrvbits))
        irdma_create_ccq -> 创建用于控制操作的完成队列
            ccq->shadow_area.size = sizeof(struct irdma_cq_shadow_area)
            ccq->mem_cq.va = dma_alloc_coherent
            info.num_elem = IW_CCQ_SIZE -> 2048
            irdma_sc_ccq_init(dev->ccq, &info)
                cq->cq_type = IRDMA_CQ_TYPE_CQP
                IRDMA_RING_INIT(cq->cq_uk.cq_ring, info->num_elem)
                cq->cq_uk.polarity = true -> 极性位/可反转/有效位
            irdma_sc_ccq_create(dev->ccq, 0, true, true)
                irdma_sc_cq_create(ccq, scratch, check_overflow, post_sq) -> 创建完成队列
                     irdma_sc_add_cq_ctx(ceq, cq) -> 为 ceq 添加 cq ctx 跟踪
                        ceq->reg_cq[ceq->reg_cq_size++] = cq
                     ...
                irdma_sc_ccq_create_done -> irdma_sc_poll_for_cqp_op_done(cqp, IRDMA_CQP_OP_CREATE_CQ, NULL) -> 等待 CQP SQ 中最后一次写入完成
                    while (1)
                        irdma_sc_ccq_get_cqe_info
                            polarity = (u8)FIELD_GET(IRDMA_CQ_VALID, temp)
                            IRDMA_RING_MOVE_HEAD ->  move the head for cq
                            IRDMA_RING_MOVE_TAIL ->  update cq tail in cq shadow memory also
                        udelay(cqp->dev->hw_attrs.max_sleep_count)
                ccq->dev->cqp->process_cqp_sds = irdma_cqp_sds_cmd
        irdma_setup_ceq_0 -> 创建CEQ 0及其中断资源 -> 为所有设备完成事件队列分配一个列表,创建ceq 0并配置其msix中断向量返回0,如果设置成功,否则返回错误
            num_ceqs = min(rf->msix_count, rf->sc_dev.hmc_fpm_misc.max_ceqs
            rf->ceqlist = kcalloc(num_ceqs, sizeof(*rf->ceqlist), GFP_KERNEL)
            irdma_create_ceq(rf, iwceq, 0, &rf->default_vsi) -> 创建完成事件队列
                irdma_sc_ceq_init(&iwceq->sc_ceq, &info)
                irdma_cqp_ceq_cmd(&rf->sc_dev, &iwceq->sc_ceq, IRDMA_OP_CEQ_CREATE)
                or irdma_sc_cceq_create(&iwceq->sc_ceq, 0)
            irdma_cfg_ceq_vector(rf, iwceq, 0, msix_vec) -> 为完成事件队列设置中断
                if (rf->msix_shared && !ceq_id)
                    tasklet_setup(&rf->dpc_tasklet, irdma_dpc)
                    request_irq(msix_vec->irq, irdma_irq_handler, 0,
                else
                    tasklet_setup(&iwceq->dpc_tasklet, irdma_ceq_dpc)
                        irdma_process_ceq(rf, iwceq)
                            do cq = irdma_sc_process_ceq(dev, sc_ceq)
                                ceq->polarity ^= 1
                                irdma_sc_cq_ack(cq)
                            queue_work(rf->cqp_cmpl_wq, &rf->cqp_cmpl_work)
                            irdma_puda_ce_handler(rf, cq)
                                do {
                                    irdma_puda_poll_cmpl(dev, cq, &compl_error)
                                        if (info.q_type == IRDMA_CQE_QTYPE_RQ)
                                            irdma_puda_poll_info(cq, &info)
                                                cqe = IRDMA_GET_CURRENT_CQ_ELEM(&cq->cq_uk)
                                                    (_cq)->cq_base[IRDMA_RING_CURRENT_HEAD((_cq)->cq_ring)].buf
                                                get_64bit_val(cqe, 24, &qword3)
                                                info->qp = (struct irdma_qp_uk *)(unsigned long)comp_ctx
                                                ...
                                            dma_sync_single_for_cpu -> 确保DMA缓冲区中的数据与物理内存中的数据同步。如果需要将数据从设备上读取到内存中,则应该使用 dma_sync_single_for_cpu()函数。如果需要将数据从内存中写入到设备上,则应该使用 dma_sync_single_for_device()函数
                                            irdma_puda_get_tcpip_info -> get tcpip info from puda buffer
                                                if (buf->vsi->dev->hw_attrs.uk_attrs.hw_rev == IRDMA_GEN_1)
                                                    irdma_gen1_puda_get_tcpip_info(info, buf)
                                                        if (ethh->h_proto == htons(0x8100))
                                                            buf->vlan_id = ntohs(((struct vlan_ethhdr *)ethh)->h_vlan_TCI) & VLAN_VID_MASK
                                                            ip6h = (struct ipv6hdr *)buf->iph
                                                buf->ipv4 = info->ipv4
                                                buf->seqnum = ntohl(tcph->seq)
                                                ether_addr_copy(buf->smac, info->smac)
                                            rsrc->receive(rsrc->vsi, buf) -> irdma_ieq_receive
                                                qp = irdma_ieq_get_qp(vsi->dev, buf)
                                                irdma_ieq_handle_exception(ieq, qp, buf)
                                                    irdma_ieq_check_first_buf(buf, fps)
                                                    irdma_send_ieq_ack(qp)
                                            irdma_ilq_putback_rcvbuf
                                            or irdma_puda_replenish_rq
                                                irdma_puda_get_bufpool
                                                    irdma_puda_get_listbuf
                                                irdma_puda_post_recvbuf
                                        else
                                            rsrc->xmit_complete(rsrc->vsi, buf -> irdma_ieq_tx_compl
                                                irdma_puda_ret_bufpool
                                                    list_add(&buf->list, &rsrc->bufpool)
                                            irdma_puda_send_buf
                                                irdma_puda_send
                                                    wqe = irdma_puda_get_next_send_wqe(&qp->qp_uk, &wqe_idx)
                                                    irdma_uk_qp_post_wr(&qp->qp_uk)
                                                        writel(qp->qp_id, qp->wqe_alloc_db)
                                    irdma_sc_ccq_arm(cq)
                                        writel(ccq->cq_uk.cq_id, ccq->dev->cq_arm_db)
                                }
                        irdma_ena_intr(&rf->sc_dev, iwceq->msix_idx) -> dev->irq_ops->irdma_en_irq(dev, msix_id) -> icrdma_ena_irq -> 启用中断
                            writel(val, dev->hw_regs[IRDMA_GLINT_DYN_CTL] + idx)
                    request_irq(msix_vec->irq, irdma_ceq_handler, 0,
                        tasklet_schedule(&iwceq->dpc_tasklet)
                cpumask_clear(&msix_vec->mask)
                cpumask_set_cpu(msix_vec->cpu_affinity, &msix_vec->mask)
                irq_update_affinity_hint(msix_vec->irq, &msix_vec->mask)
                rf->sc_dev.irq_ops->irdma_cfg_ceq(&rf->sc_dev, ceq_id, msix_vec->idx, true) -> icrdma_cfg_ceq
                    writel(reg_val, dev->hw_regs[IRDMA_GLINT_CEQCTL] + ceq_id)
            irdma_ena_intr(&rf->sc_dev, msix_vec->idx)
        INIT_WORK(&rf->cqp_cmpl_work, cqp_compl_worker) -> irdma_cqp_ce_handler
            wake_up(&cqp_request->waitq)
            or cqp_request->callback_fcn(cqp_request)
        irdma_sc_ccq_arm
    ice_get_qos_params
    irdma_fill_qos_info
        l2params->vsi_prio_type = qos_info->vport_priority_type
        l2params->tc_info[i].rel_bw = qos_info->tc_info[i].rel_bw
        memcpy(l2params->dscp_map, qos_info->dscp_map, sizeof(l2params->dscp_map))
    irdma_rt_init_hw -> 初始化硬件运行时, ILQ, IEQ, CEQs and PBLEs
        irdma_sc_vsi_init
        irdma_setup_cm_core
        irdma_vsi_stats_init
        do {
            if (!iwdev->roce_mode) -> 非RoCE模式
            irdma_initialize_ilq -> create iwarp local queue for cm
            irdma_initialize_ieq
                irdma_puda_create_rsrc
                    irdma_puda_qp_create
                        irdma_cqp_qp_create_cmd
                            cqp_info->cqp_cmd = IRDMA_OP_QP_CREATE
            irdma_setup_ceqs -> 管理设备 ceq 及其中断资源,@rf:RDMA PCI 函数 @vsi:此 CEQ 的 VSI 结构 为所有设备完成事件队列分配列表 创建 ceq 并配置其 msix 中断向量 如果 ceq 成功
                irdma_create_ceq
                irdma_cfg_ceq_vector
                irdma_ena_intr
            irdma_hmc_init_pble -> Initialize pble resources during module load
                pble_rsrc->fpm_base_addr = hmc_info->hmc_obj[IRDMA_HMC_IW_PBLE].base
                INIT_LIST_HEAD(&pble_rsrc->pinfo.clist)
                add_pble_prm(pble_rsrc)
            irdma_setup_aeq
                irdma_create_aeq
                irdma_cfg_aeq_vector
                irdma_ena_intr
            irdma_alloc_set_mac -> set up a mac address table entry
                irdma_alloc_local_mac_entry
                    cqp_info->cqp_cmd = IRDMA_OP_ALLOC_LOCAL_MAC_ENTRY
                irdma_add_local_mac_entry
                    cqp_info->cqp_cmd = IRDMA_OP_ADD_LOCAL_MAC_ENTRY
            irdma_add_ip -> add ip addresses
                irdma_add_ipv4_addr
                    ip_addr = ntohl(ifa->ifa_address)
                    irdma_manage_arp_cache(iwdev->rf, dev->dev_addr, &ip_addr, true, IRDMA_ARP_ADD) -> manage hw arp cache
                        arp_index = irdma_arp_table(rf, ip_addr, ipv4, mac_addr, action)
                        cqp_request = irdma_alloc_and_get_cqp_request(&rf->cqp, false)
                        cqp_info->cqp_cmd = IRDMA_OP_ADD_ARP_CACHE_ENTRY
                irdma_add_ipv6_addr -> 将IPv6地址添加到硬件ARP表中
                    idev = __in6_dev_get(ip_dev)
                    irdma_copy_ip_ntohl(local_ipaddr6, ifp->addr.in6_u.u6_addr32)
                        *dst++ = ntohl(*src++);
                        ...
            iwdev->cleanup_wq = alloc_workqueue("irdma-cleanup-wq",
            irdma_get_used_rsrc -> 确定内部使用的资源
                iwdev->rf->used_pds
                ...
            init_waitqueue_head
        }
        irdma_rt_deinit_hw(iwdev) -> clean up the irdma device resources -> 根据状态机清理设备资源
            switch (iwdev->init_state)
    irdma_ib_register_device
    ice_rdma_update_vsi_filter -> update main VSI filters for RDMA -> get ice eth api
        vsi = ice_find_vsi(pf, vsi_id)
        ice_cfg_rdma_fltr(&pf->hw, vsi->idx, enable) -> enable/disable RDMA filtering on VSI
            cached_ctx = ice_get_vsi_ctx(hw, vsi_handle)
            ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID)
            ice_update_vsi
                ice_aq_update_vsi -> Ice:扩展 VSI 句柄的使用第 1/2 部分,VSI 句柄只是驱动程序维护的一个数字,用于唯一标识 VSI。 VSI 句柄由硬件中的 VSI 编号支持。 当与硬件交互时,VSI句柄被转换成VSI编号。 在提交 0f9d5027a749(“ice:重构 VSI 分配、删除和重建流程”)中,引入了 VSI 句柄,但仅在创建和删除 VSI 时使用。 该补丁是两个补丁之一,这两个补丁扩展了 VSI 句柄在驱动程序其余部分的使用。 此外,在此补丁中,必须重构代码的某些部分才能正确使用 VSI 句柄
                    ice_fill_dflt_direct_cmd_desc
                    cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID)
                    status = ice_aq_send_cmd(hw, &desc, &vsi_ctx->info, -> send FW Admin Queue command to FW Admin Queue
                        ice_sq_send_cmd_retry
                            ice_sq_send_cmd -> send command to Control Queue (ATQ)
                                desc_on_ring = ICE_CTL_Q_DESC(cq->sq, cq->sq.next_to_use)
                                wr32(hw, cq->sq.tail, cq->sq.next_to_use)
                                ice_flush(hw)
                                udelay(5)
    auxiliary_set_drvdata




static const struct irdma_irq_ops icrdma_irq_ops = {
	.irdma_cfg_aeq = irdma_cfg_aeq,
	.irdma_cfg_ceq = icrdma_cfg_ceq,
	.irdma_dis_irq = icrdma_disable_irq,
	.irdma_en_irq = icrdma_ena_irq,
};
           

分配BAR0硬件地址和注册寄存器

代码语言:javascript
复制
register addr,
static int irdma_probe(
    struct ice_pf *pf = iidc_adev->pf
    irdma_fill_device_info(iwdev, pf, vsi)
        rf->hw.hw_addr = pf->hw.hw_addr; <- info.bar0 = rf->hw.hw_addr;
    irdma_ctrl_init_hw
        irdma_setup_init_state
            static int irdma_initialize_dev
                info.bar0 = rf->hw.hw_addr;
                irdma_sc_dev_init
                    dev->hw->hw_addr = info->bar0
                    irdma_sc_init_hw                    
                        icrdma_init_hw
                            dev->hw_regs[i] = (u32 __iomem *)(hw_addr + icrdma_regs[i]) -> 注册寄存器
?
irdma_sc_cqp_get_next_send_wqe_idx
    IRDMA_ATOMIC_RING_MOVE_HEAD(cqp->sq_ring, *wqe_idx, ret_code)
    wqe = cqp->sq_base[*wqe_idx].elem -> 从DMA的VA基址依次往后填充
    IRDMA_CQP_INIT_WQE(wqe) -> #define IRDMA_CQP_INIT_WQE(wqe) memset(wqe, 0, 64) -> WQE大小为64字节
?
?
?
ring, 环形链表, 从头部取出一个元数后, 将头指针向后移动一位
#define IRDMA_RING_MOVE_HEAD(_ring, _retcode) \
    { \
        register u32 size; \
        size = (_ring).size;  \
        if (!IRDMA_RING_FULL_ERR(_ring)) { \
            (_ring).head = ((_ring).head + 1) % size; \
            (_retcode) = 0; \
        } else { \
            (_retcode) = -ENOMEM; \
        } \
    }
?
分配一段设备和软件共享的DMA内存, 作为发送队列的内存, 起始VA作为发送队列的基址
cqp->sq.va = dma_alloc_coherent(dev->hw->device, cqp->sq.size, &cqp->sq.pa, GFP_KERNEL);

用户态轮询完成队列

代码语言:txt
复制
static inline int ibv_poll_cq -> .poll_cq = irdma_upoll_cq, -> 轮询 CQ 以获取(可能是多个)完成情况。 如果返回值 < 0,则发生错误。 如果返回值 >= 0,则为返回的完成数。 如果返回值非负且严格小于num_entries,则CQ被清空
    ret = __irdma_upoll_cq(iwucq, num_entries, entry)
        list_for_each_safe(&iwucq->resize_list, cq_buf, next, list) -> 浏览之前调整大小的 CQ 缓冲区列表
            while (npolled < num_entries)
                ret = irdma_poll_one(&cq_buf->cq, cur_cqe, entry ? entry + npolled : NULL); -> ret 0 = success
                    int ret = irdma_uk_cq_poll_cmpl(ukcq, cur_cqe)
                        cqe = IRDMA_GET_CURRENT_CQ_ELEM(cq) -> ( (cq)->cq_base[(((cq)->cq_ring).head)].buf ) -> get cqe from cq_ring head
                        get_64bit_val(cqe, 24, &qword3)
                        polarity = (__u8)FIELD_GET(IRDMA_CQ_VALID, qword3)
                        return ENONENT -> providers/irdma:删除 enum irdma_status_code ,- 用 Linux 错误代码替换自定义 irdma 状态代码的使用。 - 删除枚举 irdma_status_code 和定义它的头文件。 - 删除某些函数中多余的“ret”变量。 由于 irdma_status_code 被替换为 int,因此不需要两个变量来保存错误代码。 - 删除不再需要的冗余初始化
                        udma_from_device_barrier(); -> 确保检查有效位后读取 CQE 内容 -> asm volatile("lfence" ::: "memory")
                        ...
                        info->q_type = (__u8)FIELD_GET(IRDMA_CQ_SQ, qword3);
                        info->qp_handle = (irdma_qp_handle)(uintptr_t)qp;
                        info->comp_status = IRDMA_COMPL_STATUS_SUCCESS;
                        ...
                        get_64bit_val(cqe, 0, &qword0);
                        get_64bit_val(cqe, 16, &qword2);
                        get_64bit_val(cqe, 8, &comp_ctx);
                        if (info->q_type == IRDMA_CQE_QTYPE_RQ)
                            info->wr_id = qp->rq_wrid_array[array_idx]
                            IRDMA_RING_SET_TAIL(qp->rq_ring, array_idx + 1) -> (qp->rq_ring).tail = (array_idx + 1) % (qp->rq_ring).size
                        else { /* q_type is IRDMA_CQE_QTYPE_SQ */
                            if (qp->first_sq_wq)
                                irdma_uk_cq_poll_cmpl -> recu
                    irdma_process_cqe_ext
                    or
                    irdma_process_cqe
                        entry->status = IBV_WC_SUCCESS
                        entry->wc_flags |= IBV_WC_WITH_IMM
                        set_ib_wc_op_sq(cur_cqe, entry); -> services/irdma:修复 RQ 完成操作码,RQ CQE 中 HW 写入的操作码是来自接收数据包的 RoCEv2/iWARP 协议操作码,而不是当前假设的 SW 操作码。 通过将 CQE 中的原始操作类型和队列类型返回到 irdma_process_cqe 并添加 2 个助手 set_ib_wc_op_sq set_ib_wc_op_rq 将 IRDMA HW op 类型映射到 IB op 类型来修复此问题。 请注意,对于 iWARP,仅支持立即写入,因此当存在立即数据时,操作码只能是 IB_WC_RECV_RDMA_WITH_IMM
                            case IRDMA_OP_TYPE_RDMA_WRITE
                                entry->opcode = IBV_WC_RDMA_WRITE; -> set wc opcode
                        or
                        set_ib_wc_op_rq(cur_cqe, entry,
        while (npolled < num_entries)
            irdma_poll_one
        irdma_process_resize_list
            list_for_each_safe(&iwucq->resize_list, cq_buf, next, list)
                list_del(&cq_buf->list)
                irdma_free_cq_buf(cq_buf
        irdma_uk_cq_set_resized_cnt
            sw_cq_sel += cq_cnt;
            set_64bit_val(cq->shadow_area, 32, temp_val)

参考

Intel E810 datasheet

Intel E810 RDMA(irdma)实现: https://lore.kernel.org/all/20210520143809.819-7-shiraz.saleem@intel.com/T/

代码语言:c
复制
intel e810,
commit: https://lore.kernel.org/all/20210520143809.819-7-shiraz.saleem@intel.com/T/
以下补丁系列介绍了适用于 X722 iWARP 设备的统一英特尔 RDMA (irdma) 以太网协议驱动程序以及支持 iWARP 和 RoCEv2 的新 E810 设备。 irdma 模块取代了 X722 的旧版 i40iw 模块,并扩展了已为 i40iw 定义的 ABI。 它向后兼容旧版 X722 rdma 核心提供程序 (libi40iw)。 X722 和 E810 是支持 RDMA 的 PCI 网络设备。 该父设备的 RDMA 块通过使用最近为 5.11 内核添加的核心辅助总线基础结构导出到“irdma”的辅助设备来表示。 父 PCI netdev 驱动程序“i40e”和“ice”使用封装的私有数据/操作注册辅助 RDMA 设备,这些数据/操作绑定到在 irdma 模块中注册的辅助驱动程序。 该补丁集最初作为 RFC 提交,我们在其中得到了反馈,提出了 RDMA 驱动程序附加到 netdev PCI 驱动程序 [1] 拥有的 PCI 设备的通用方案。 探索了使用平台总线和 MFD 的解决方案,但被社区拒绝,共识是添加新的总线基础设施来支持这种使用模型。 该系列的进一步修订以及辅助总线已提交[2]。 此时,Greg KH 要求我们将辅助总线审查和修订流程纳入内部邮件列表,并获得受人尊敬的内核贡献者的认可,以及包括 Nvidia 在内的所有主要利益相关者的共识(用于 mlx5 子功能使用) -案例)和英特尔声音驱动程序。 这个过程花了一段时间,并阻碍了该 netdev/irdma 系列的进一步开发/审查。 辅助总线最终在5.11中被合并。 在本次提交的 v1-->v2 和 v4-->v5 之间,IIDC 根据反馈进行了重大重写,我们希望它现在更符合社区的需求。 目前,E810 默认为 RoCEv2。 在未来的补丁中,将通过 devlink 提供对协议切换到 iWARP 的运行时支持。 该系列是针对 5.13-rc1 构建的,目前包含 netdev 补丁以方便查看。 这包括更新“ice”驱动程序以提供 RDMA 支持,并将“i40e”驱动程序转换为使用辅助总线基础设施。 一旦社区确认此提交,就可以提交共享拉取请求。 v5-->v6:*将 aux 设备名称从 <模块>.intel_rdma_<rdma 协议>.<num> 压缩为 <模块>.<rdma 协议>.<num> *修复 alloc_hw_stats 的驱动程序 API,仅导出端口统计信息 sysfs v4-->v5:*导出所有 IIDC 核心操作回调并从 irdma 直接调用。 irdma 依赖于 i40e 和ice。 *删除协议切换的 devlink 运行时选项。 E810 上默认为 RoCEv2。 将通过 [3] 中讨论的社区工作添加切换到 IWARP 的运行时选项 *导出 iidc_auxiliary_dev 中的ice_pf 指针,该指针在 irdma drv.probe() 中可用,并使用它派生 PCI 函数相关子字段。 *使用定义来设置辅助开发名称,而不是 kasprintf。 *删除 IIDC 中的所有未来配置。 删除 IIDC 中的多个辅助驱动程序支持。 *添加核心驱动程序的辅助操作回调以直接在 iidc_auxiliary_drv 对象中使用。 *修复ice中的auxdevice ida资源泄漏,并更新到i40e中最新的ida API。 *删除 IIDC 和 irdma 驱动程序中任何残留的 VF 残渣。 *简化 IIDC API 以添加和删除 RDMA qset。 删除 iidc_res_base 联合使用。 *使用直接调用转换 irdma 中的所有单一实现间接 .ops 回调 *添加 rsvd 仅用于 irdma ABI 中的对齐 *清理 iw_memreg_type enum v3-->v4: * 修复冰补丁中的 W=1 警告 * 修复由 用于创建用户 AH 和多播的 pyverbs * 修复在 v2 提交 v2-->v3 中移植到 FIELD_PREP 期间引入的快速寄存器的描述符集问题: * rebase rdma for-next。 适应核心更改'1fb7f8973f51(“RDMA:支持超过255个rdma端口”)' * irdma Kconfig更新以符合linux编码风格。 * 修复 0-day 构建问题 * 删除 rdma_resource_limits 选择器 devlink 参数。 按照 Parav 的建议提交补丁以使用 devlink 资源。 * Ice idc 中的缩写大写。 例如 'aux' 到 'AUX' v1-->v2: * 删除 IIDC 通道操作 - 打开、关闭、peer_register 和peer_unregister。 及其所有相关的FSM都在ice PCI核心驱动程序中。 * 在发出 IIDC ops 回调时,在ice PCI 核心驱动程序中使用 device_lock。 * 从共享 IIDC 标头中删除peer_* 措辞,并使用 iidc_core*/iidc_auxiliary* 重命名结构和通道操作。 * 在irdma gen2辅助驱动程序中开始时分配ib_device并在drv.probe()结束时注册它。 * 在大多数驱动程序中广泛使用 ibdev_* 打印删除 idev_to_dev、ihw_to_dev 宏,因为新打印方案中不再需要这些宏。 * 不要修改 ABI 版本。 irdma 为 6。 维护 irdma ABI 版本。 5 表示旧版 i40iw 用户-提供商兼容性。 * 在 irdma_alloc_ucontext 中添加边界检查,以防止与 < 4 用户空间提供程序版本的绑定失败。 * 从 irdma 中删除 devlink。 添加 2 个新的 rdma 相关内容devlink 参数添加到ice PCI 核心驱动程序中。 * 在描述符字段的获取/设置上使用FIELD_PREP/FIELD_GET/GENMASK,而不是自行开发的LS_*/RS_*。 * 在 irdma 中绑定 2 个独立的辅助驱动程序 - 一个用于第 1 代,一个用于第 2 代和未来的设备。 * 其他。 irdma 中的驱动程序修复 [1] https://patchwork.kernel.org/project/linux-rdma/patch/20190215171107.6464-2-shiraz.saleem@intel.com/ [2] https://lore.kernel.org/ linux-rdma/20200520070415.3392210-1-jeffrey.t.kirsher@intel.com/ [3] https://lore.kernel.org/linux-rdma/20210407224631.GI282464@nvidia.com/ Dave Ertman (4):iidc :引入iidc.hice:初始化RDMA支持ice:实现iidc操作ice:注册辅助设备以提供RDMA Michael J. Ruhl(1):RDMA/irdma:为CM Mustafa Ismail添加动态跟踪(13):RDMA/irdma: 注册辅助驱动程序并实现专用通道 OP RDMA/irdma:实现设备初始化定义 RDMA/irdma:实现硬件管理队列 OP RDMA/irdma:添加 HMC 后备存储设置函数 RDMA/irdma:添加特权 UDA 队列实现 RDMA/irdma:添加 QoS 定义 RDMA/irdma:添加连接管理器 RDMA/irdma:添加 PBLE 资源管理器 RDMA/irdma:实现设备支持的动词 API RDMA/irdma:添加 RoCEv2 UD OP 支持 RDMA/irdma:添加用户/内核共享库 RDMA/irdma:添加杂项 实用程序定义 RDMA/irdma:添加 ABI 定义 Shiraz Saleem (4):i40e:为 aux 总线转换准备 i40e 标头 i40e:注册辅助设备以提供 RDMA RDMA/irdma:添加 irdma Kconfig/Makefile 并删除 i40iw RDMA/irdma:更新维护者 文件

晓兵(ssbandjl)

博客: /developer/user/5060293/articles | https://logread.cn | https://blog.csdn.net/ssbandjl | https://www.zhihu.com/people/ssbandjl/posts

DPU专栏

/developer/column/101987

技术会友: 欢迎对DPU/智能网卡/卸载/网络,存储加速/安全隔离等技术感兴趣的朋友加入DPU技术交流群

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
    • E810芯片手册
      • Intel E810的PBLE/HMC
      • 源码分析
        • 模块初始化和probe-E810驱动
          • 分配BAR0硬件地址和注册寄存器
            • 用户态轮询完成队列
            • 参考
            • 晓兵(ssbandjl)
              • DPU专栏
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
              http://www.vxiaotou.com