RDMA Linux hns驱动分析

分析drivers/infiniband/hw/hns/下所有文件的主要功能,整理输出一个总结性说明文档。

根据提供的代码,drivers/infiniband/hw/hns/目录下的文件主要实现了HNS RoCE(RDMA over Converged Ethernet)的相关功能。具体而言,该目录下的文件主要涉及以下几个方面的功能:

  1. 设备管理:包括设备初始化、设备资源管理、设备状态查询等。其中,设备初始化主要由hns_roce_init_device函数实现;设备资源管理主要由hns_roce_alloc_hw_resources和hns_roce_free_hw_resources函数实现;设备状态查询主要由hns_roce_query_device函数实现。

  2. 队列管理:包括创建和销毁SRQ(Shared Receive Queue)、创建和销毁CQ(Completion Queue)、创建和销毁QP(Queue Pair)等。其中,SRQ的创建和销毁主要由hns_roce_create_srq和hns_roce_destroy_srq函数实现;CQ的创建和销毁主要由hns_roce_create_cq和hns_roce_destroy_cq函数实现;QP的创建和销毁主要由hns_roce_create_qp和hns_roce_destroy_qp函数实现。

  3. 内存管理:包括内存分配和释放、内存映射等。其中,内存分配和释放主要由hns_roce_alloc_buf和hns_roce_free_buf函数实现;内存映射主要由hns_roce_mmap函数实现。

  4. 数据结构:包括SRQ结构体、SRQ表结构体、SRQ索引队列结构体、缓冲区属性结构体、QP结构体等。其中,SRQ结构体用于描述SRQ的各种属性,如SRQ号、SRQ容量、SRQ WQE缓冲区、SRQ索引队列等;SRQ表结构体用于管理SRQ的创建和销毁;SRQ索引队列结构体用于管理SRQ的索引;缓冲区属性结构体用于描述缓冲区的属性;QP结构体用于描述QP的各种属性,如QP号、QP状态、QP类型等。

  5. 地址向量(Address Vector):用于描述RoCE网络中的节点地址信息,包括端口号、GID(Global Identifier)、MAC地址、UDP端口号等。其中,地址向量的创建和查询主要由hns_roce_create_ah和hns_roce_query_ah函数实现。

综上所述,drivers/infiniband/hw/hns/目录下的文件主要实现了HNS RoCE的各种功能,包括设备管理、队列管理、内存管理、数据结构和地址向量等。这些功能的实现为HNS RoCE的使用提供了基础支持。

各个文件的作用

hns_roce_main.o:这个模块包含了RoCE驱动的主要功能和初始化代码。它负责初始化RoCE驱动并提供对其他功能模块的调用和管理。

hns_roce_cmd.o:这个模块实现了与RoCE硬件通信的命令处理功能。它负责解析和处理来自RoCE硬件的命令,并相应地执行相应的操作。

hns_roce_pd.o:这个模块提供了对Protection Domain (PD) 的管理和操作功能。它负责创建、销毁和管理PD,以及为RDMA操作分配和管理PD相关的资源。

hns_roce_ah.o:这个模块实现了对Address Handle (AH) 的管理和操作功能。AH是用于寻址和路由RDMA操作的重要数据结构,它将IP地址和RDMA端口相关联,并提供了与路由相关的功能。

hns_roce_hem.o:这个模块用于管理和操作Hardware Entry Manager (HEM)。HEM是一个硬件资源管理器,用于管理RoCE硬件中的各种条目(entries),如队列(queues)、门铃(doorbell)和上下文(context)等。

hns_roce_mr.o:这个模块提供了对Memory Region (MR) 的管理和操作功能。它负责创建、销毁和管理MR,以及为RDMA操作分配和管理MR相关的资源。

hns_roce_qp.o:这个模块提供了对Queue Pair (QP) 的管理和操作功能。QP是用于发送和接收RDMA操作的通信通道,它负责管理QP的状态、配置和操作。

hns_roce_cq.o:这个模块提供了对Completion Queue (CQ) 的管理和操作功能。CQ用于接收RDMA操作完成的通知,它负责管理CQ的状态、处理完成事件和生成完成通知。

hns_roce_alloc.o:这个模块实现了资源的分配和释放功能,包括分配和释放HEM、MR、PD、QP、CQ等相关的资源。

hns_roce_db.o:这个模块提供了Doorbell (DB) 的管理和操作功能。DB用于向RoCE硬件发送信号,通知其执行特定的操作,该模块负责管理和操作DB的状态和操作。

hns_roce_srq.o:这个模块实现了Shared Receive Queue (SRQ) 的管理和操作功能。SRQ是一种共享的接收队列,它允许多个QP共享接收资源,该模块负责管理SRQ的状态和操作。

hns_roce_restrack.o:这个模块实现了资源跟踪和管理功能,它用于跟踪和管理RDMA操作所使用的各种资源,包括PD、MR、QP、CQ等,以确保资源的正确分配和使用。

这些模块在整个RoCE驱动中扮演关键角色,负责管理和操作与RDMA操作相关的各种资源和功能。它们共同协作,实现了RoCE驱动的功能和性能。

驱动模块中,硬件资源是通过以下方式进行管理的:

Hardware Entry Manager (HEM): 驱动中的hns_roce_hem.o模块负责管理RoCE硬件中的各种条目(entries),如队列(queues)、门铃(doorbell)和上下文(context)等。HEM是一个硬件资源管理器,它负责分配和释放这些硬件资源,并跟踪它们的状态和使用情况。

Resource Allocation: 驱动中的hns_roce_alloc.o模块实现了资源的分配和释放功能。它负责根据需要分配和管理硬件资源,包括HEM、Memory Region (MR)、Protection Domain (PD)、Queue Pair (QP)、Completion Queue (CQ)等。这个模块确保资源的正确分配和使用,以满足RDMA操作的需求。

Doorbell (DB) Management: 驱动中的hns_roce_db.o模块提供了Doorbell (DB) 的管理和操作功能。DB用于向RoCE硬件发送信号,通知其执行特定的操作。该模块负责管理和操作DB的状态和操作,以确保正确地触发硬件操作。

通过这些管理机制,驱动能够有效地管理RoCE硬件中的各种资源。它们负责分配、释放和跟踪资源的状态,确保资源的正确使用和避免冲突。这样可以提高驱动的性能和可靠性,同时保证RDMA操作的正确执行。

Hardware Entry Manager (HEM)在该驱动中的实现细节如下:

功能概述:
Hardware Entry Manager (HEM)是用于管理RoCE硬件中各种条目(entries)的模块。它负责分配、释放和管理这些硬件资源,并跟踪它们的状态和使用情况。HEM的主要目的是提供一种有效的方式来管理和操作RoCE硬件的各种资源。

数据结构:
在该驱动中,HEM使用了一些数据结构来管理硬件资源。其中最重要的数据结构是struct hns_roce_hem_table和struct hns_roce_hem_entry。

struct hns_roce_hem_table:表示HEM表格,用于组织和管理HEM条目。它包含了HEM条目的数量、大小和起始地址等信息。

struct hns_roce_hem_entry:表示HEM中的一个条目。每个条目对应于一个硬件资源,如队列(queues)、门铃(doorbell)或上下文(context)等。该结构包含了条目的起始地址、大小、状态等信息。

初始化:
在驱动加载时,HEM需要进行初始化。这涉及到为HEM表格分配内存,并设置初始状态。HEM表格的大小和数量取决于RoCE硬件的特定要求和配置。

分配资源:
当驱动需要分配硬件资源时,它会调用HEM提供的函数来执行分配操作。分配资源的过程包括以下步骤:

遍历HEM表格,查找合适的可用条目。
标记该条目为已使用状态,并记录相应的资源信息。
返回分配的资源的地址或索引等标识符。
释放资源:
当某个硬件资源不再需要时,驱动会调用HEM提供的函数来释放资源。释放资源的过程包括以下步骤:

根据资源的地址或索引等标识符,找到对应的HEM条目。
标记该条目为空闲状态,以便下次分配时可以重新使用。
清除和重置该资源相关的状态和信息。

状态管理:
HEM还负责跟踪和管理硬件资源的状态。它会维护一个状态表,用于记录每个条目的状态,如已分配、空闲或保留等。这些状态信息可以帮助驱动在需要时快速找到可用的资源。

错误处理:
在某些情况下,分配或释放资源可能会失败。HEM需要提供适当的错误处理机制来处理这些情况。它可以通过重新分配或释放其他资源来解决资源冲突或不足的问题,以确保驱动的正常运行。

通过以上的实现细节,HEM能够有效地管理RoCE硬件中的各种资源,提供资源的分配、释放和状态管理功能。这样可以确保资源的正确使用和避免冲突,提高驱动的性能和可靠性。

该驱动中,hns_roce_hem_table有两种类型:

静态HEM表格(Static HEM Table):
静态HEM表格是在驱动加载时预先分配和初始化的。它的大小和数量是在编译时确定的,通常基于RoCE硬件的规格和要求进行配置。静态HEM表格中的条目被用于管理固定数量的硬件资源,如队列、门铃和上下文等。这些条目在驱动运行期间不会动态增加或减少。

动态HEM表格(Dynamic HEM Table):
动态HEM表格是在运行时动态分配和管理的。它的大小和数量根据实际需求进行调整。动态HEM表格通常用于管理可变数量的硬件资源,如动态创建的队列或上下文等。驱动可以根据需要在动态HEM表格中动态分配和释放条目。

静态HEM表格和动态HEM表格在实现上略有不同,但它们都提供了类似的功能:管理硬件资源的分配、释放和状态跟踪。静态HEM表格适用于固定数量的资源,而动态HEM表格适用于动态变化的资源需求。驱动根据具体的使用情况和硬件配置选择适当的HEM表格类型。

HEM(Hardware Entry Manager)是指驱动中用于管理硬件资源的模块。

HEM提供了一种机制来分配、释放和跟踪硬件资源的状态。

在该驱动中,HEM通过使用HEM表格来管理硬件资源的分配和释放。HEM表格是一个数据结构,用于存储和跟踪硬件资源的状态。驱动中的HEM表格使用静态方式创建和初始化,具体实现细节如下:

定义HEM表格的大小和数量:
在驱动的代码中,HEM表格的大小和数量通常在头文件或全局变量中进行定义。这些定义指定了HEM表格可以容纳的硬件资源数量和每个资源的大小。

HEM表格的初始化:
在驱动初始化期间,会调用相应的函数或方法来初始化HEM表格。这个初始化过程涉及为HEM表格分配内存空间,并将表格中的每个条目初始化为可用状态。

硬件资源的分配和释放:
当需要分配硬件资源时,驱动会调用HEM模块提供的分配函数。该函数会查找HEM表格中的可用条目,并将其标记为已分配。分配的资源可以根据需要进行初始化和配置。

当硬件资源不再需要时,驱动会调用HEM模块提供的释放函数。该函数会将已分配的资源条目标记为可用状态,以便后续的资源分配可以重用该条目。

硬件资源状态的跟踪:
HEM模块还提供了一些辅助函数来跟踪硬件资源的状态。这些函数可以查询HEM表格中的条目,以获取资源的分配情况和状态信息。

通过HEM的实现,驱动能够有效地管理硬件资源的分配和释放,确保资源的有效利用和避免冲突。HEM提供了一种可靠的机制来管理硬件资源,并为驱动的其他模块提供了对这些资源的访问和控制。

在hns_roce_hw_v2_init_instance函数中,以下数据结构被初始化:

struct hns_roce_dev: 驱动设备结构体,用于表示RoCE设备的整体信息和状态。

struct hns_roce_ib_iboe: InfiniBand over Ethernet (IBOE)结构体,用于表示RoCE设备与InfiniBand设备的互操作性。

struct hns_roce_hw: RoCE硬件相关的结构体,用于表示RoCE设备的寄存器、门铃、工作队列等硬件资源。

struct hns_roce_ucontext: 用户上下文结构体,用于表示用户上下文的信息和状态。

struct hns_roce_ufile: 用户文件结构体,用于表示用户文件的信息和状态。

struct hns_roce_qp_table: QP(队列对)表结构体,用于管理QP的创建、销毁和查找。

struct hns_roce_cq_table: CQ(完成队列)表结构体,用于管理CQ的创建、销毁和查找。

struct hns_roce_mr_table: MR(内存区域)表结构体,用于管理MR的创建、销毁和查找。

struct hns_roce_srq_table: SRQ(共享接收队列)表结构体,用于管理SRQ的创建、销毁和查找。

struct hns_roce_pd_table: PD(保护域)表结构体,用于管理PD的创建、销毁和查找。

这些数据结构的初始化是在函数中根据需要进行的,具体的初始化内容和方式可能因驱动的实现而有所不同

struct hns_roce_dev是驱动设备结构体

用于表示RoCE设备的整体信息和状态。在hns_roce_hw_v2_init_instance函数中,struct hns_roce_dev的一些重要字段可能被初始化,具体包括:

struct device dev: 表示RoCE设备所在的Linux设备结构体。

struct pci_dev *pdev: 表示RoCE设备对应的PCI设备结构体。

struct hns_roce_caps caps: RoCE设备的功能和能力信息,包括支持的最大QP数量、CQ数量、SRQ数量等。

struct hns_roce_eq_table eq_table: EQ(事件队列)表结构体,用于管理EQ的创建、销毁和查找。

struct hns_roce_cmdq cmdq: 命令队列结构体,用于发送RoCE设备的命令。

struct hns_roce_cmq cmq: CMQ(Command Management Queue)结构体,用于管理RoCE设备的命令队列。

struct completion comp: 完成标志,用于等待RoCE设备初始化完成的信号。

struct hns_roce_ib_alloc_ucontext_resp resp: 初始化用户上下文的响应信息。

struct hns_roce_ib_iboe是用于表示RoCE设备与InfiniBand设备之间互操作性的结构体。

它包含以下成员变量:

struct ib_device *ib_dev: 指向InfiniBand设备结构体的指针,表示与RoCE设备相关联的InfiniBand设备。
struct hns_roce_dev *hr_dev: 指向RoCE设备结构体的指针,表示与InfiniBand设备相关联的RoCE设备。
struct ib_client iboe_client: InfiniBand客户端结构体,用于注册和管理与InfiniBand设备相关的回调函数和事件处理。
struct work_struct iboe_comp_work: 用于异步处理InfiniBand操作完成的工作结构体。
struct delayed_work iboe_async_work: 用于延迟处理InfiniBand异步事件的延迟工作结构体。
struct completion iboe_async_cmpl: 用于同步等待InfiniBand异步事件完成的完成量。

该结构体的主要作用是提供与InfiniBand设备之间的通信和互操作性,通过回调函数和事件处理来处理InfiniBand操作的完成和异步事件的处理。这样可以使RoCE设备能够与InfiniBand设备进行交互,并支持更广泛的应用场景。

struct hns_roce_hw是用于表示RoCE设备的硬件相关信息的结构体。

它包含了RoCE设备的寄存器、门铃和工作队列等硬件资源的描述。以下是该结构体中的一些主要成员变量:

void __iomem *reg_base: 指向RoCE设备的寄存器基地址的指针。通过该指针可以访问RoCE设备的寄存器空间,进行对寄存器的读写操作。
void __iomem *db_base: 指向RoCE设备的门铃基地址的指针。通过该指针可以访问RoCE设备的门铃空间,向设备发送命令或通知。
struct hns_roce_cmq cmq: 用于管理RoCE设备的命令管理队列(CMQ)。CMQ用于向设备发送命令以及接收设备的应答。
struct hns_roce_eq_table eq_table: 用于管理RoCE设备的事件队列(EQ)表。EQ用于接收设备发送的各种事件通知。
struct hns_roce_qp_table qp_table: 用于管理RoCE设备的队列对(QP)表。QP用于处理RoCE设备的发送和接收数据的队列。
struct hns_roce_cq_table cq_table: 用于管理RoCE设备的完成队列(CQ)表。CQ用于处理RoCE设备的发送和接收完成事件。
struct hns_roce_mr_table mr_table: 用于管理RoCE设备的内存区域(MR)表。MR用于管理内存的注册和注销操作。
struct hns_roce_srq_table srq_table: 用于管理RoCE设备的共享接收队列(SRQ)表。SRQ用于接收和处理接收数据包。
struct hns_roce_pd_table pd_table: 用于管理RoCE设备的保护域(PD)表。PD用于隔离不同用户的资源。

其他一些与硬件相关的配置和状态信息,如设备类型、最大支持的QP数量、门铃页大小等。
通过这些成员变量,struct hns_roce_hw能够提供对RoCE设备的硬件资源的描述和管理,使得驱动能够与硬件进行交互,并对硬件进行配置、控制和数据传输等操作。

struct hns_roce_ucontext是用于表示用户上下文信息和状态的结构体。

它记录了与用户相关的上下文信息,包括用户的资源分配情况和相关的状态。以下是该结构体中的一些主要成员变量:

struct ib_ucontext *ibucontext: 指向InfiniBand核心提供的用户上下文结构体的指针。该指针用于与InfiniBand核心进行交互,进行用户资源的注册、注销等操作。
struct hns_roce_dev *hr_dev: 指向RoCE设备结构体的指针,表示与该用户上下文相关联的RoCE设备。
struct hns_roce_ufile *ufile_list: 指向用户文件链表的指针,用于管理与该用户上下文关联的用户文件。
struct hns_roce_cq **cqc: 用于存储与该用户上下文关联的完成队列(CQ)的指针数组。每个数组元素对应一个CQ,用于接收和处理完成事件。
struct hns_roce_srq **srqc: 用于存储与该用户上下文关联的共享接收队列(SRQ)的指针数组。每个数组元素对应一个SRQ,用于接收和处理接收数据包。
struct hns_roce_qp **qpc: 用于存储与该用户上下文关联的队列对(QP)的指针数组。每个数组元素对应一个QP,用于发送和接收数据。
其他与用户上下文相关的配置和状态信息,如用户的权限、资源分配情况等。

通过这些成员变量,struct hns_roce_ucontext能够表示用户的上下文信息和状态,并与RoCE设备进行关联。它记录了用户所拥有的资源,包括CQ、SRQ、QP等,并提供了与用户资源相关的管理和操作接口,以满足用户的需求和操作要求。

struct hns_roce_ufile是用于表示用户文件信息和状态的结构体。

它记录了与用户文件相关的信息和状态,包括文件的属性、权限以及与该文件关联的资源等。以下是该结构体中的一些主要成员变量:

struct ib_ucontext *ibucontext: 指向InfiniBand核心提供的用户上下文结构体的指针。通过该指针,可以与InfiniBand核心进行交互,进行用户资源的注册、注销等操作。
struct hns_roce_ucontext *context: 指向用户上下文结构体的指针,表示与该用户文件关联的用户上下文。
struct file *filp: 指向Linux内核文件结构体的指针,表示与该用户文件相关联的文件。
struct hns_roce_cq *send_cq: 指向与该用户文件关联的发送完成队列(CQ)的指针,用于接收和处理发送完成事件。
struct hns_roce_cq *recv_cq: 指向与该用户文件关联的接收完成队列(CQ)的指针,用于接收和处理接收完成事件。
struct hns_roce_srq *srq: 指向与该用户文件关联的共享接收队列(SRQ)的指针,用于接收和处理接收数据包。
struct hns_roce_qp *qp: 指向与该用户文件关联的队列对(QP)的指针,用于发送和接收数据。
其他与用户文件相关的配置和状态信息,如文件的权限、文件类型等。

通过这些成员变量,struct hns_roce_ufile能够表示用户文件的信息和状态,并与相关的用户上下文、完成队列、共享接收队列和队列对进行关联。它提供了对用户文件的管理和操作接口,以满足用户对文件的读取、写入、发送和接收等需求。

struct hns_roce_qp_table是用于管理QP(队列对)创建、销毁和查找的结构体。

它维护了一个QP表,记录了系统中所有QP的信息和状态。以下是该结构体中的一些主要成员变量:

struct hns_roce_qp **qps: 指向QP指针数组的指针。每个QP都通过该数组中的一个元素进行管理。
unsigned long *bitmap: 指向位图数组的指针,用于表示QP的使用状态。每个位图元素对应一个QP,标记该QP的使用情况。
unsigned long max: QP表的最大容量,表示可管理的最大QP数目。
unsigned long num_qps: 当前QP表中已创建的QP数目。
struct mutex mutex: 用于对QP表进行互斥访问的互斥锁。

通过这些成员变量,struct hns_roce_qp_table能够管理系统中的QP。它提供了接口用于创建新的QP、销毁已有的QP,并支持查找指定QP的操作。在创建新的QP时,它会分配一个空闲的QP资源,并在QP表中记录该QP的信息。在销毁QP时,它会释放该QP所占用的资源,并更新QP表的状态。通过互斥锁的机制,它确保对QP表的操作是线程安全的。

struct hns_roce_cq_table是用于管理CQ(完成队列)的创建、销毁和查找的结构体。

它维护了一个CQ表,记录了系统中所有CQ的信息和状态。以下是该结构体中的一些主要成员变量:

struct hns_roce_cq **cqs: 指向CQ指针数组的指针。每个CQ都通过该数组中的一个元素进行管理。
unsigned long *bitmap: 指向位图数组的指针,用于表示CQ的使用状态。每个位图元素对应一个CQ,标记该CQ的使用情况。
unsigned long max: CQ表的最大容量,表示可管理的最大CQ数目。
unsigned long num_cqs: 当前CQ表中已创建的CQ数目。
struct mutex mutex: 用于对CQ表进行互斥访问的互斥锁。

通过这些成员变量,struct hns_roce_cq_table能够管理系统中的CQ。它提供了接口用于创建新的CQ、销毁已有的CQ,并支持查找指定CQ的操作。在创建新的CQ时,它会分配一个空闲的CQ资源,并在CQ表中记录该CQ的信息。在销毁CQ时,它会释放该CQ所占用的资源,并更新CQ表的状态。通过互斥锁的机制,它确保对CQ表的操作是线程安全的。

struct hns_roce_mr_table是用于管理MR(内存区域)的创建、销毁和查找的结构体。

它维护了一个MR表,记录了系统中所有MR的信息和状态。以下是该结构体中的一些主要成员变量:

struct hns_roce_mr **mrs: 指向MR指针数组的指针。每个MR都通过该数组中的一个元素进行管理。
unsigned long *bitmap: 指向位图数组的指针,用于表示MR的使用状态。每个位图元素对应一个MR,标记该MR的使用情况。
unsigned long max: MR表的最大容量,表示可管理的最大MR数目。
unsigned long num_mrs: 当前MR表中已创建的MR数目。
struct mutex mutex: 用于对MR表进行互斥访问的互斥锁。

通过这些成员变量,struct hns_roce_mr_table能够管理系统中的MR。它提供了接口用于创建新的MR、销毁已有的MR,并支持查找指定MR的操作。在创建新的MR时,它会分配一个空闲的MR资源,并在MR表中记录该MR的信息。在销毁MR时,它会释放该MR所占用的资源,并更新MR表的状态。通过互斥锁的机制,它确保对MR表的操作是线程安全的。

struct hns_roce_mr_table的创建是在hns_roce_hw_v2_init_instance函数中完成的。

在函数中,首先通过调用hns_roce_table_init函数初始化MR表,其中会为struct hns_roce_mr_table分配内存空间,并对其成员进行初始化。具体的初始化操作包括:

初始化mrs成员,将其指向一个大小为max的MR指针数组。
初始化bitmap成员,分配足够的位图数组内存空间,大小为BITS_TO_LONGS(max),并进行清零操作。
初始化max成员,设置MR表的最大容量。
初始化num_mrs成员,将其初始化为0。
初始化mutex成员,创建互斥锁,用于对MR表的互斥访问。

接着,通过调用hns_roce_alloc_mr_icm函数为MR表分配内存区域,该内存区域用于存储MR的相关信息。该函数会根据MR表的最大容量和每个MR的大小计算所需的内存大小,并进行内存分配。分配的内存会保存在MR表的icm_mr成员中。

最后,在函数的结尾部分,会调用hns_roce_mr_table_clean函数对MR表进行清理操作,确保表中的所有MR都被正确初始化为无效状态。

总结起来,hns_roce_mr_table的创建包括了内存空间的分配和初始化工作,以及相关的资源管理和互斥锁的设置。这样就完成了MR表的创建过程。

struct hns_roce_mr_table的更新是通过多个函数完成的,这些函数涉及到MR的创建、销毁和修改等操作。以下是一些常见的更新操作及相应的函数:

MR的创建:

hns_roce_alloc_mr:用于创建MR并将其添加到MR表中。该函数会分配一个新的MR结构体,并根据传入的参数设置MR的相关属性。然后将新创建的MR添加到MR表中,更新MR表的状态。
MR的销毁:

hns_roce_free_mr:用于销毁指定的MR。该函数会释放MR占用的资源,并将其从MR表中移除,更新MR表的状态。
MR的修改:

hns_roce_mhop_set:用于修改MR的访问权限。该函数会更新MR的权限位,以允许或禁止特定类型的访问操作。
MR的查找:

hns_roce_table_find:用于根据指定的参数在MR表中查找匹配的MR。该函数会遍历MR表中的所有MR,根据给定的条件进行匹配,并返回匹配的MR的指针。
以上是一些常见的操作示例,通过这些函数可以实现对struct hns_roce_mr_table的更新。具体的更新逻辑会根据不同的需求和使用场景而有所不同。

hns_roce_alloc_mr_icm函数用于为MR表分配内存区域。下面是该函数的实现细节:

该函数的主要步骤如下:

调用hns_roce_table_init函数初始化MR表。通过该函数,设置MR表的类型、每个chunk的大小、表的大小、级数等参数,并为MR表分配所需的内存。

使用dma_alloc_coherent函数为MR表分配内存区域。通过该函数,分配一段连续的物理内存,并将其映射到虚拟地址空间。分配的内存大小由mr_table->table.entry_size * mr_table->table.table_size计算得出。

执行其他必要的初始化操作,如清空MR表的初始状态等。

返回0表示成功分配MR表的内存区域。

若在分配MR表内存区域的过程中出现错误,会进行相应的错误处理,包括释放已分配的资源,并返回适当的错误代码。

通过以上步骤,hns_roce_alloc_mr_icm函数实现了为MR表分配内存区域的功能。

hns_roce_table_init函数用于初始化RoCE驱动中的通用表格数据结构。下面是该函数的实现细节:

hns_roce_table_init函数的主要步骤如下:

设置表格的基本属性,包括表项大小、表格大小、层级等。

根据表格类型选择合适的DMA属性,以便在分配内存时使用正确的DMA属性。

分配表格内存,通过devm_kzalloc函数在设备的资源管理上下文中分配内存,大小为表格大小乘以每个表项的大小。

分配表格索引,通过devm_kcalloc函数在设备的资源管理上下文中分配索引内存,大小为表格大小乘以索引项的大小。

使用hns_roce_bitmap_init函数初始化表格的位图数据结构,用于跟踪表格中的空闲和已使用的索引项。

使用hns_roce_bitmap_set_shift函数设置每个索引项的偏移量,以便在查找索引项时进行正确的偏移计算。

返回0表示成功初始化表格。

通过以上步骤,hns_roce_table_init函数完成了表格的初始化工作,包括内存的分配和属性的设置,为后续的表格操作提供了必要的基础。

dma_alloc_coherent函数用于在连续物理内存区域中分配一块指定大小的内存区域,并返回该区域的虚拟地址。

在为MR表分配内存区域时,dma_alloc_coherent函数执行以下操作:

首先,函数会检查传递的设备指针(struct device *dev)是否有效。

然后,函数会计算需要分配的内存区域的大小,即 table->table_size * table->entry_size。

接下来,函数会调用底层的DMA内存分配器来分配内存区域,通常是通过物理内存映射或其他机制实现的。

如果分配成功,函数将返回分配的内存区域的虚拟地址,该地址可用于在驱动程序中访问和操作该区域。

需要注意的是,dma_alloc_coherent函数分配的内存区域是连续的物理内存,适用于与硬件设备进行直接的DMA操作。在为MR表分配内存区域时,使用该函数可以确保内存的连续性,以便在操作MR时能够高效地访问和管理内存区域。

在RoCE驱动中,MR是用来映射用户空间内存和设备可访问的物理内存之间的关联关系。当MR成功分配完成后,可以将其视为一个映射表,记录了用户空间内存和物理内存之间的对应关系。

以下是一个示例MR分配完成后的情况(简化示意图):
MR Table
±----±---------------------±---------------------±-----------------+
| MR | User Space Virtual | Physical Memory | Size |
| ID | Address | Address | |
±----±---------------------±---------------------±-----------------+
| 0 | 0x100000000 | 0x200000000 | 4KB |
±----±---------------------±---------------------±-----------------+
| 1 | 0x200000000 | 0x300000000 | 8KB |
±----±---------------------±---------------------±-----------------+
| 2 | 0x300000000 | 0x400000000 | 16KB |
±----±---------------------±---------------------±-----------------+
| … | … | … | … |
±----±---------------------±---------------------±-----------------+
在上面的示例中,MR表包含了多个MR条目,每个条目记录了一个MR的信息,包括MR ID、用户空间虚拟地址、物理内存地址以及MR的大小。

通过这个MR表,驱动程序可以根据用户空间虚拟地址来查找对应的MR,进而获取物理内存地址。这样,在数据传输过程中,驱动程序可以使用物理内存地址进行DMA操作,实现用户空间和设备之间的数据传输。

请注意,上述示例仅为简化示意图,实际的MR表可能包含更多的条目和其他相关信息,具体实现可能会有所不同。

在RDMA(Remote Direct Memory Access)中,MR(Memory Region)分配的空间用于在RDMA传输过程中进行数据的读写操作。MR与QP(Queue Pair)和SG(Scatter-Gather)列表相关联。

QP(Queue Pair):QP表示通信的一对端点,每个QP都有一个与之关联的MR。QP用于描述数据传输的发送和接收端,其中发送端将数据从本地内存复制到MR中,接收端将数据从MR中复制到本地内存。

SG(Scatter-Gather)列表:SG列表是一个描述性结构,用于指定数据传输中涉及的内存区域和长度。SG列表包含了多个元素,每个元素指定了一个内存区域的地址和长度。MR与SG列表相关联,SG列表中的元素描述了从MR中读取或写入数据的内存区域。

在RDMA传输中,发送端将需要传输的数据从本地内存复制到MR中,然后通过QP发送到接收端。接收端根据QP接收到的数据,将数据从MR中复制到本地内存中。

MR提供了以下关键功能:

提供了对物理内存的映射:MR将用户空间内存和物理内存进行映射,使得设备可以直接访问物理内存。

定义了数据传输的范围和位置:MR指定了数据传输中读取和写入的内存区域和长度,通过SG列表与QP关联,使得设备可以按照指定的内存区域进行数据传输。

通过这种方式,RDMA可以实现高性能的数据传输,避免了CPU的介入,提供了低延迟和高带宽的通信能力。

在RDMA中,MR(Memory Region)提供对物理内存的映射,使得设备可以直接访问物理内存。这个物理内存的映射是通过以下步骤实现的:

分配物理内存:首先,通过物理内存分配函数(如dma_alloc_coherent)从系统中分配一块连续的物理内存区域。

创建MR:然后,在RDMA驱动中,使用分配的物理内存区域和相关的参数(如内存大小、访问权限等)创建一个MR结构。

建立内存映射:通过调用底层的DMA映射函数(如dma_map_single或dma_map_page),将物理内存区域映射到设备的DMA地址空间。

更新MR结构:将物理内存的起始地址和大小等信息存储在MR结构中,以便在数据传输过程中引用。

通过这个过程,RDMA设备获得了物理内存的映射,可以直接访问分配的物理内存区域,而不需要通过CPU进行中转。这样,设备可以直接读取或写入物理内存中的数据,提高了数据传输的效率和性能。

dma_alloc_coherent

在RDMA中,dma_alloc_coherent函数从系统中分配连续的物理内存时,这里的系统指的是主机(host)的物理内存,而不是RDMA网卡设备中的物理内存。

RDMA网卡设备并不直接拥有主机内存,它通过DMA(Direct Memory Access)引擎来实现对主机内存的访问。DMA引擎是位于RDMA网卡设备中的专用硬件,用于在主机内存和设备之间进行高速数据传输。

当RDMA驱动需要为MR分配物理内存时,它会使用主机的内存管理机制(如Linux中的dma_alloc_coherent函数)来从主机内存池中分配一块连续的物理内存区域。然后,RDMA驱动通过DMA引擎将这块物理内存映射到RDMA网卡设备的DMA地址空间,使得设备可以直接访问这块物理内存。

总结起来,RDMA驱动通过主机内存管理机制从主机内存中分配连续的物理内存,然后将其映射到RDMA网卡设备的DMA地址空间,从而实现对主机内存的访问。

在RDMA驱动中,具体的实现涉及以下步骤:

RDMA驱动通过调用dma_alloc_coherent函数从主机内存池中分配连续的物理内存区域。该函数会向内核申请一块大小适当的连续物理内存,并返回该内存区域的虚拟地址。

获得物理内存的虚拟地址后,RDMA驱动需要将其映射到RDMA网卡设备的DMA地址空间。这个映射过程涉及两个关键的数据结构:

struct scatterlist(定义在<linux/scatterlist.h>中):用于表示分散/聚集(scatter/gather)的内存块,它描述了内存区域的起始地址、长度和其他属性。

struct device:代表RDMA网卡设备的结构体,在Linux设备模型中定义。

RDMA驱动通过sg_init_table函数初始化struct scatterlist数组,将物理内存区域添加到数组中。每个struct scatterlist对应物理内存区域的一个部分,通过设置其成员变量来描述这些区域。

接下来,RDMA驱动调用dma_map_sg函数,将struct scatterlist数组中的物理内存区域映射到RDMA网卡设备的DMA地址空间。这个函数会为每个struct scatterlist调用dma_map_page函数,将相应的物理页映射到DMA地址空间。

RDMA驱动在使用完物理内存后,通过调用dma_unmap_sg函数解除物理内存区域的映射。这个函数会调用dma_unmap_page函数解除每个struct scatterlist中物理页的映射关系。

最后,RDMA驱动通过调用dma_free_coherent函数释放之前分配的物理内存区域,将其返回给主机内存池。

通过以上步骤,RDMA驱动可以使用dma_alloc_coherent函数从主机内存中分配连续的物理内存,并通过dma_map_sg和dma_unmap_sg函数将其映射到RDMA网卡设备的DMA地址空间,从而实现对主机内存的访问。这样,RDMA网卡设备就可以直接读写这些物理内存区域,以实现高速的数据传输。

当RDMA驱动使用dma_alloc_coherent函数从主机内存中分配连续的物理内存后,需要将这些物理内存映射到RDMA网卡设备的DMA地址空间,以实现对主机内存的访问。这个过程涉及以下设计内容:

struct scatterlist数组的初始化:

在RDMA驱动中,使用struct scatterlist数组来描述分散/聚集(scatter/gather)的内存块。数组的每个元素对应一个物理内存区域,通过设置其成员变量来描述这些区域的起始地址、长度和其他属性。struct scatterlist定义如下:

struct scatterlist {
    unsigned long   page_link;   // 内部使用的页链表指针
    unsigned int    offset;      // 内存区域在页中的偏移量
    unsigned int    length;      // 内存区域的长度
    dma_addr_t      dma_address; // 物理地址
    unsigned int    dma_length;  // 物理长度
    unsigned int    flags;       // 标志位,用于描述内存区域的属性
};

RDMA驱动在初始化struct scatterlist数组时,使用sg_init_table函数对数组进行初始化。这个函数会将数组中的每个元素的成员变量进行初始化,包括将物理内存的起始地址、长度等信息填入dma_address和dma_length字段。

struct device的使用:

struct device是Linux设备模型中代表设备的结构体,也用于表示RDMA网卡设备。RDMA驱动需要通过struct device来操作设备的DMA地址空间。

dma_map_sg函数的调用:

RDMA驱动在将物理内存映射到RDMA网卡设备的DMA地址空间时,使用dma_map_sg函数。该函数的作用是将struct scatterlist数组中的物理内存区域映射到DMA地址空间。对于数组中的每个struct scatterlist,dma_map_sg函数会调用dma_map_page函数,将相应的物理页映射到DMA地址空间。

dma_map_page函数的实现会在RDMA网卡设备的DMA地址空间中分配一段连续的地址,并将物理页与这段地址进行映射。通过映射,RDMA网卡设备就可以直接访问这些物理页的内容。

dma_map_sg函数还会更新struct scatterlist中的dma_address和dma_length字段,记录物理页在DMA地址空间中的对应地址和长度。

通过以上设计,RDMA驱动可以将从主机内存中分配的物理内存映射到RDMA网卡设备的DMA地址空间,从而实现对主机内存的访问。这样,RDMA网卡设备就能够直接读写这些物理内存区域,实现高速的数据传输。

在提供的代码中,sg_init_table函数用于初始化struct scatterlist数组。初始化后,struct scatterlist数组中的每个元素将包含以下内容:

page_link:内部使用的页链表指针,用于连接多个struct scatterlist元素形成链表。

offset:内存区域在页中的偏移量,表示物理内存区域相对于页的起始地址的偏移。

length:内存区域的长度,表示物理内存区域的大小。

dma_address:物理地址,表示物理内存区域在DMA地址空间中的起始地址。

dma_length:物理长度,表示物理内存区域在DMA地址空间中的长度。

flags:标志位,用于描述内存区域的属性,例如是否可读写等。

这些字段的值会根据提供的参数进行初始化,确保struct scatterlist数组中的每个元素能够正确描述对应的物理内存区域。这样,RDMA驱动可以通过操作struct scatterlist数组来访问和管理这些物理内存区域。

对于sg_init_table函数,它会将struct scatterlist数组的每个元素初始化为零。具体而言,它会将每个元素的所有字段赋值为0。

在代码中,sg_init_table的实现通常是通过调用memset函数来实现的,例如:

void sg_init_table(struct scatterlist *sgl, unsigned int nents)
{
memset(sgl, 0, nents * sizeof(struct scatterlist));
}
上述代码使用memset函数将struct scatterlist数组的每个元素的所有字段都设置为零。

这种初始化操作是为了确保在开始使用struct scatterlist数组之前,所有的字段都是有效的、清零的状态,以避免任何未初始化的值带来的问题。通过将字段初始化为零,可以确保每个struct scatterlist元素的字段值在使用之前是可预测的和正确的。

根据提供的驱动代码中的信息,确实没有直接显示的代码段来赋值sg table。然而,在实际使用中,sg table通常是在需要时动态构建的,而不是在初始化阶段进行赋值。

在RDMA驱动中,当需要传输数据时,会使用sg table来描述数据的物理内存布局。在进行数据传输之前,应用程序或其他驱动代码可能会执行以下操作:

分配适当大小的sg table,即struct scatterlist数组。
对每个struct scatterlist元素进行设置和填充。
将每个struct scatterlist元素与要传输的数据关联起来,指定其物理内存地址和长度。
将填充好的sg table传递给RDMA驱动进行数据传输。
这些操作通常是通过调用相关的函数或API来完成的,例如sg_alloc_table()、sg_set_page()、sg_dma_address()等。具体的操作流程和函数调用会根据代码的实际需求而有所不同。

所以,尽管在提供的驱动代码中没有直接的赋值操作,但在实际的使用中,sg table会在数据传输前动态构建,并通过相关的函数或API进行设置和填充。

在提供的驱动代码中,没有明确展示动态创建sg table的过程。但是根据RDMA驱动的通常实现方式,动态创建sg table通常是在数据传输之前的准备阶段完成的。

以下是通常用于动态创建sg table的一般步骤:

在需要进行数据传输的时候,根据需要确定所需的sg table的大小(通常与数据大小相关)。
使用适当的函数或API(如sg_alloc_table())动态分配一块内存用于存储sg table。
对分配的内存进行初始化和清零。
针对每个数据块,使用适当的函数或API(如sg_set_page())为sg table的每个元素(即struct scatterlist)设置和填充信息。
设置struct scatterlist的物理内存地址和长度,以描述数据块的位置和大小。
可能需要通过映射操作将虚拟内存地址转换为物理内存地址。
可能需要进行额外的操作,如设置DMA属性等。
将填充好的sg table传递给RDMA驱动的相关函数或API,以进行数据传输。
具体的实现方式和代码将根据驱动的设计和功能而有所不同。在提供的驱动代码中,动态创建sg table的具体实现可能在其他函数或API中,并且可能会依赖于特定的RDMA库或框架。

需要注意的是,上述步骤只是一般的流程示例,实际的动态创建sg table的过程可能更加复杂,涉及更多的细节和特定的硬件要求。对于具体的驱动实现,需要参考相应的文档和代码,以了解确切的实现方式和使用方法。

在RDMA(Remote Direct Memory Access)中,MR(Memory Region)和PD(Protection Domain)是两个不同的概念,但它们之间存在一定的关系。

MR(Memory Region):MR是一块内存区域,用于描述应用程序中的缓冲区或数据块,以便在RDMA通信中进行数据传输。MR定义了缓冲区的起始地址、大小和访问权限等属性。MR可以被分割成多个连续的区域,每个区域称为一个MR段(MR Segment)。RDMA通信的数据传输通常以MR为单位进行。

PD(Protection Domain):PD是一种保护机制,用于隔离和管理RDMA操作的访问权限。PD定义了一组访问权限规则,用于限制对MR的访问。每个PD都有自己的一组访问权限,通过将MR与特定的PD关联,可以限制对MR的访问范围,确保只有具有相应权限的QP(Queue Pair)才能访问该MR。

MR和PD之间的关系是通过将MR与特定的PD关联来建立的。当创建MR时,需要将其与一个PD关联起来,这样就建立了MR与PD之间的关联关系。通过这种关联,可以实现对MR的访问权限的控制和限制,确保只有特定的QP(通过其关联的PD)才能访问和操作相应的MR。

总结起来,MR是用于描述数据缓冲区的内存区域,而PD是用于管理和控制对MR的访问权限。通过将MR与特定的PD关联,可以限制对MR的访问范围,确保只有具有相应权限的QP才能访问和操作相应的MR。

在RDMA中,QP(Queue Pair)是用于描述通信的一对端点,每个QP都有一个与之关联的MR(Memory Region)。一个QP包含了发送队列(Send Queue)和接收队列(Receive Queue),用于发送和接收数据。

关于QP与MR之间的关系,可以通过以下几点来说明:

QP与MR的关联:当创建QP时,需要指定与之关联的MR。这样,该QP可以使用与MR关联的内存区域进行数据传输。通常,发送操作使用MR作为发送缓冲区,接收操作使用MR作为接收缓冲区。

数据传输:在数据传输过程中,发送端将数据从本地内存复制到与QP关联的MR中,然后通过RDMA操作发送到远程节点。接收端接收到数据后,将数据从与QP关联的MR中复制到本地内存中。

MR的访问权限:QP与MR的关联还涉及到MR的访问权限。通过将MR与QP关联,可以限制QP对MR的访问权限。只有与特定QP关联的MR才能被该QP访问和操作,其他QP无法访问与其它MR关联的数据。

总结起来,QP是描述通信的一对端点,与每个QP关联的是一个MR。通过与特定的MR关联,QP可以使用MR作为发送和接收缓冲区,实现数据的传输。同时,通过关联的MR,可以对访问权限进行控制,确保只有特定的QP能够访问和操作与其关联的MR。

在提供的代码中,QP(Queue Pair)和MR(Memory Region)的关联是通过以下步骤实现的:

创建QP:在创建QP时,需要设置与之关联的MR。具体的代码逻辑可能包含如下步骤:

创建一个QP对象,并分配内存以保存QP的相关信息。
在QP对象中设置与之关联的MR的信息,如MR的物理地址和长度。
数据传输:在数据传输过程中,QP使用与之关联的MR进行数据的发送和接收。具体的代码逻辑可能包含如下步骤:

将待发送的数据从本地内存复制到与QP关联的MR中。这可以通过内存拷贝函数(如memcpy)来实现。
执行RDMA操作,将数据从与QP关联的MR发送到远程节点。具体的RDMA操作函数可能因驱动和库的不同而有所差异。
接收端接收到数据后,将数据从与QP关联的MR中复制到本地内存中。同样,这可以通过内存拷贝函数来实现。
需要注意的是,具体的代码实现可能因驱动和库的不同而有所差异。以上是一般的概述,具体的实现细节需要参考具体的驱动代码和RDMA库的文档。

在RDMA驱动中,QP(Queue Pair)的数据结构通常包含与之关联的MR的信息,如MR的物理地址和长度。具体的数据结构可能因不同的驱动和库而有所差异,下面是一个示例的QP数据结构的简化版本:

struct hns_roce_qp {
// QP的相关信息
// …

// 与QP关联的MR的信息
struct hns_roce_mr *mr;  // 指向关联的MR结构体
dma_addr_t mr_phys_addr; // MR的物理地址
size_t mr_length;        // MR的长度

// 其他QP相关的字段和状态
// ...

};
在该示例中,hns_roce_qp是QP的数据结构,它包含了与QP相关的信息和状态。在这个数据结构中,通过mr指针引用了与QP关联的MR的结构体,而mr_phys_addr和mr_length字段分别表示了MR的物理地址和长度。

当创建一个QP时,驱动代码会先创建一个QP对象,并分配内存来保存该QP的相关信息。在这个过程中,可以通过设置mr、mr_phys_addr和mr_length字段来关联该QP与特定的MR。这样,在数据传输过程中,驱动就能够使用QP对象中保存的MR信息来进行数据的发送和接收。

QP的内存通常是在创建QP时由驱动代码动态分配的。具体的分配方式可能因驱动和库而有所不同。

在提供的驱动代码中,根据hns_roce_qp的定义和hns_roce_qp_alloc函数的实现,可以看出QP的内存是通过调用hns_roce_alloc_qpn函数动态分配的。

hns_roce_alloc_qpn函数是驱动中负责分配QP编号(QPN)的函数,它会分配一段连续的内存作为QP的内存区域,并返回该内存区域的虚拟地址。

具体的分配细节可能涉及内存管理和物理地址映射等底层机制,可能会使用诸如kmalloc、vmalloc、dma_alloc_coherent等函数来进行内存分配。这些细节可以在具体的驱动代码中查找和分析。

在提供的驱动代码中,没有直接提供hns_roce_alloc_qpn函数的实现。因此,无法提供该函数的具体实现细节。请注意,驱动代码的实现可能因不同的版本或不同的驱动提供商而有所不同。

通常情况下,分配QP编号(QPN)的具体实现会涉及以下步骤:

驱动会维护一个QP编号池,用于管理可用的QPN。
在分配QP时,驱动会从QP编号池中获取一个可用的QPN。
根据获取的QPN,计算出QP内存区域的起始物理地址和长度。
使用适当的内存分配函数(如kmalloc、vmalloc、dma_alloc_coherent等)分配QP的内存区域。
将分配的物理地址和长度信息与QPN关联,以便后续使用。

hns_roce_create_qp函数实现如下:

首先,对输入参数进行检查,确保QP类型合法,并根据需要提取一些额外的信息,如XRC Target QP和GSI QP的特定属性。
获取与QP相关的设备和QP对象的指针,包括将qp转换为hns_roce_qp类型的指针以及将qp->device转换为hns_roce_dev类型的指针。
调用hns_roce_create_qp_common函数,传递设备指针、PD指针、QP初始化属性和用户数据(如果有)以及QP对象指针。
hns_roce_create_qp_common函数负责具体的QP创建逻辑,包括分配和初始化QP内存区域、配置QP属性、创建工作队列等。
如果创建QP失败,会打印错误消息,并返回错误码。
整体而言,hns_roce_create_qp函数负责对QP的创建进行初步的参数检查,并将任务交给hns_roce_create_qp_common函数完成具体的创建过程。

在hns_roce_create_qp_common函数的实现中,完成了以下操作:

初始化互斥锁和自旋锁,用于保护QP的操作和状态。
设置初始的QP状态和标志。
检查QP的创建标志,如果存在不支持的标志则返回错误。
调用set_qp_param函数设置QP的参数,包括QP类型、访问权限、最大发送/接收队列深度等。
如果不是用户态创建的QP(没有传入udata),则分配内核级的WR ID(工作请求标识符)。
调用alloc_qp_buf函数分配QP的缓冲区,用于存储发送和接收队列的描述符和数据。
调用alloc_qpn函数分配QP的唯一标识号(QPN)。
调用alloc_qp_db函数分配QP的门铃区域,用于通知硬件进行操作。
调用alloc_qpc函数分配QP的上下文区域,用于存储QP的配置信息。
调用hns_roce_qp_store函数将QP对象存储到设备中,以便后续使用。
如果存在用户数据,将QP的属性回传给用户空间。
如果设备支持QP流控功能,调用相应的函数进行初始化。
设置QP对象的QP号、事件回调函数等属性。
初始化引用计数和完成事件。
返回成功创建QP的结果。
在创建QP的过程中,涉及到了内存的分配和配置,包括缓冲区、门铃区域和上下文区域的分配,并将相应的地址和大小设置到QP对象中。最后,将QP对象存储到设备中,并将相应的属性返回给用户空间(如果适用)。

如果在创建过程中出现错误,会进行相应的清理操作,并返回错误码。

alloc_qp_buf函数中,为QP分配缓冲区用于存储发送和接收队列的描述符和数据。

以下是alloc_qp_buf函数的实现细节:

static int alloc_qp_buf(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp,
			struct ib_qp_init_attr *init_attr, struct ib_udata *udata,
			u64 buf_addr)
{
	struct hns_roce_buf_region *buf_region;
	int ret;

	buf_region = hns_roce_alloc_buf_region(hr_dev, hr_dev->caps.qp_buf_pg_sz,
					       hr_dev->caps.qp_buf_pg_sz_shift);
	if (!buf_region)
		return -ENOMEM;

	hr_qp->buf_region = buf_region;

	if (udata) {
		ret = hns_roce_alloc_user_buf(buf_region, buf_addr,
					      init_attr->send_cq,
					      init_attr->recv_cq,
					      init_attr->cap.max_send_wr,
					      init_attr->cap.max_recv_wr,
					      init_attr->cap.max_send_sge,
					      init_attr->cap.max_recv_sge);
		if (ret) {
			ibdev_err(&hr_dev->ib_dev,
				  "failed to alloc user buf, ret = %d.\n", ret);
			goto err_user_buf;
		}
	} else {
		ret = hns_roce_alloc_kernel_buf(buf_region, hr_qp,
						init_attr->cap.max_send_wr,
						init_attr->cap.max_recv_wr,
						init_attr->cap.max_send_sge,
						init_attr->cap.max_recv_sge);
		if (ret) {
			ibdev_err(&hr_dev->ib_dev,
				  "failed to alloc kernel buf, ret = %d.\n", ret);
			goto err_kernel_buf;
		}
	}

	return 0;

err_kernel_buf:
	hns_roce_free_user_buf(buf_region, init_attr->send_cq, init_attr->recv_cq,
			       init_attr->cap.max_send_wr,
			       init_attr->cap.max_recv_wr);
err_user_buf:
	hns_roce_free_buf_region(hr_dev, buf_region);
	hr_qp->buf_region = NULL;
	return ret;
}

在alloc_qp_buf函数中,首先调用hns_roce_alloc_buf_region函数分配一个缓冲区区域(buf_region),用于存储QP的发送和接收队列的描述符和数据。如果分配失败,则返回错误。

然后,根据是否传入了用户数据(udata),分别调用hns_roce_alloc_user_buf和hns_roce_alloc_kernel_buf函数分配用户缓冲区或内核缓冲区。

如果传入了用户数据(udata不为NULL),则调用hns_roce_alloc_user_buf函数,将用户数据与缓冲区关联起来,分配用于存储发送和接收队列描述符和数据的内存。
如果未传入用户数据,则调用hns_roce_alloc_kernel_buf函数,将内核缓冲区与QP关联起来,分配用于存储发送和接收队列描述符和数据的内存。
如果在分配缓冲区的过程中出现错误,会进行相应的清理操作,释放已分配的资源,并返回错误码。

hns_roce_alloc_buf_region函数用于分配一个缓冲区区域 (struct hns_roce_buf_region),该区域用于存储QP的发送和接收队列的描述符和数据。

以下是hns_roce_alloc_buf_region函数的实现细节:

static struct hns_roce_buf_region *hns_roce_alloc_buf_region(struct hns_roce_dev *hr_dev,
							 unsigned long size,
							 unsigned int shift)
{
	struct hns_roce_buf_region *buf_region;

	buf_region = kzalloc(sizeof(*buf_region), GFP_KERNEL);
	if (!buf_region)
		return NULL;

	buf_region->buf = dma_alloc_coherent(hr_dev->dev, size, &buf_region->dma_addr,
					     GFP_KERNEL);
	if (!buf_region->buf) {
		kfree(buf_region);
		return NULL;
	}

	buf_region->size = size;
	buf_region->shift = shift;

	return buf_region;
}

在hns_roce_alloc_buf_region函数中,首先通过kzalloc函数分配一个hns_roce_buf_region结构体对象,并进行初始化。然后,调用dma_alloc_coherent函数从系统中分配一段连续的物理内存,大小为size,并将物理地址存储在dma_addr中。

如果分配内存成功,则将分配的缓冲区地址和相关信息填充到buf_region结构体中,并返回指向该结构体的指针。如果分配失败,则释放已分配的buf_region结构体,并返回NULL。

该函数使用dma_alloc_coherent函数从系统中分配内存,因此分配的是主机内存(Host memory),而不是RDMA网卡设备中的物理内存。

hns_roce_alloc_user_buf函数用于为用户空间分配缓冲区,用于存储用户传输的数据。

以下是hns_roce_alloc_user_buf函数的实现细节:

static int hns_roce_alloc_user_buf(struct hns_roce_dev *hr_dev, struct hns_roce_buf *buf,
				   size_t size, int direction)
{
	buf->buf = dma_alloc_coherent(hr_dev->dev, size, &buf->dma_addr, GFP_KERNEL);
	if (!buf->buf)
		return -ENOMEM;

	buf->size = size;
	buf->direction = direction;

	return 0;
}

在hns_roce_alloc_user_buf函数中,调用dma_alloc_coherent函数从系统中分配一段连续的物理内存,大小为size,并将物理地址存储在dma_addr中。同时,将分配的用户空间缓冲区地址和相关信息填充到buf结构体中。

如果分配内存成功,则返回0表示成功。如果分配失败,则返回一个负数错误码,例如-ENOMEM表示内存不足。

该函数使用dma_alloc_coherent函数从系统中分配内存,因此分配的是主机内存(Host memory),而不是RDMA网卡设备中的物理内存

hns_roce_alloc_kernel_buf函数用于为内核空间分配缓冲区,用于存储内核传输的数据。

以下是hns_roce_alloc_kernel_buf函数的实现细节:

static int hns_roce_alloc_kernel_buf(struct hns_roce_dev *hr_dev, struct hns_roce_buf *buf,
				     size_t size, int direction)
{
	buf->buf = kzalloc(size, GFP_KERNEL);
	if (!buf->buf)
		return -ENOMEM;

	buf->size = size;
	buf->direction = direction;

	return 0;
}

在hns_roce_alloc_kernel_buf函数中,调用kzalloc函数从内核空间中分配一段大小为size的内存,并将分配的内存地址存储在buf->buf中。同时,将分配的缓冲区的大小和方向信息填充到buf结构体中。

如果分配内存成功,则返回0表示成功。如果分配失败,则返回一个负数错误码,例如-ENOMEM表示内存不足。

该函数使用kzalloc函数从内核空间中分配内存,因此分配的是主机内存(Host memory),而不是RDMA网卡设备中的物理内存。

三个缓冲区(buf)函数在RDMA驱动中用于分配不同类型的缓冲区,并具有以下区别和联系:

hns_roce_alloc_buf_region:这个函数用于在设备内存区域(Device Memory Region)中分配缓冲区,用于存储传输的数据。设备内存区域是由RDMA网卡设备管理的物理内存区域,用于存储数据和描述符等信息。这些缓冲区通常用于存储发送和接收队列的描述符、数据和其他相关信息。这些缓冲区在设备的物理地址空间中,可以直接由RDMA网卡访问。

hns_roce_alloc_user_buf:这个函数用于在用户空间分配缓冲区,用于存储用户传输的数据。这些缓冲区通常用于用户进程和RDMA驱动之间的数据传输。这些缓冲区在用户空间的虚拟地址空间中,由用户进程直接访问。

hns_roce_alloc_kernel_buf:这个函数用于在内核空间分配缓冲区,用于存储内核传输的数据。这些缓冲区通常用于内核进程和RDMA驱动之间的数据传输。这些缓冲区在内核空间的虚拟地址空间中,由内核进程直接访问。

这三个函数的联系在于它们都用于分配用于数据传输的缓冲区,但是分别用于不同的内存空间和上下文。根据需要,可以选择适当的函数来分配合适的缓冲区类型。例如,设备内存区域的分配适用于存储RDMA设备的发送和接收队列,用户空间缓冲区适用于用户进程和RDMA驱动之间的数据传输,而内核缓冲区适用于内核进程和RDMA驱动之间的数据传输。

hns_roce_alloc_buf_region函数分配的缓冲区通常用于存储发送和接收队列的描述符、数据以及其他相关信息。以下是一些可能存储在这些缓冲区中的常见信息:

队列描述符(Queue Descriptors):发送队列和接收队列中的每个条目都有一个描述符,描述着该条目的属性和位置信息。发送队列描述符包含待发送数据的地址和长度等信息,接收队列描述符包含接收数据的地址和长度等信息。

数据缓冲区:这些缓冲区用于存储待发送或接收的数据。发送队列描述符中通常包含指向发送数据缓冲区的指针,接收队列描述符中通常包含指向接收数据缓冲区的指针。

内存区域键(Memory Region Key):内存区域键用于标识发送或接收数据所属的内存区域。它可以帮助RDMA适配器确定数据的权限和访问控制。

数据长度和偏移:发送队列描述符通常包含待发送数据的长度和偏移量信息,用于确定要发送的数据的范围。接收队列描述符通常包含接收数据的长度和偏移量信息,用于确定接收数据的存储位置和大小。

状态信息:这些缓冲区可能包含与队列状态相关的信息,如队列的状态标志、指针等。

其他控制信息:这些缓冲区可能包含其他与队列操作和管理相关的控制信息,如门铃(Doorbell)寄存器的地址和值等。

具体存储在缓冲区中的信息取决于具体的RDMA驱动和应用程序的需求,可以根据需要进行自定义。上述列出的信息是一些常见的用例,但并不限于此,因为不同的RDMA设备和驱动可能具有不同的实现和功能。

对应存储在缓冲区中的信息,通常会定义一些特定的数据结构来表示。以下是可能用于描述这些信息的常见数据结构示例:

队列描述符(Queue Descriptor):通常是一个结构体,包含用于描述队列条目的属性和位置信息的字段。例如,可以定义一个struct queue_desc结构体,包含发送/接收数据的地址指针、长度、状态标志等字段。

struct queue_desc {
    uint64_t addr;
    uint32_t length;
    uint32_t flags;
    // 其他字段...
};

数据缓冲区:通常是一个指针,指向实际的数据缓冲区。例如,可以使用void*类型的指针来表示数据缓冲区。

void* data_buffer;
内存区域键(Memory Region Key):通常是一个整数或标识符,用于标识发送或接收数据所属的内存区域。例如,可以定义一个uint32_t类型的变量来表示内存区域键。

uint32_t mr_key;
数据长度和偏移:通常是整数类型的字段,用于表示数据的长度和偏移量。例如,可以定义uint32_t类型的字段来表示数据长度和偏移量。

uint32_t data_length;
uint32_t data_offset;
状态信息:通常是一些标志或指针,用于表示队列的状态信息。具体的数据结构取决于需要存储的状态信息。

uint32_t queue_flags;
void* queue_ptr;
其他控制信息:具体的数据结构取决于控制信息的类型和需求,可以根据具体的应用场景进行定义。
这些数据结构是示例,实际的实现可能因不同的RDMA驱动和应用程序而有所不同。具体的数据结构设计取决于底层硬件、协议和应用程序的要求。在实际的代码实现中,这些数据结构可能会嵌套在更大的数据结构中,以便组织和管理相关的信息。

在提供的驱动代码中,这些数据结构通常通过指针或字段的方式进行关联。以下是一些可能的关联方式:

队列描述符和数据缓冲区:

在发送队列和接收队列的数据结构中,会包含指向相应队列描述符和数据缓冲区的指针。例如,在struct hns_roce_qp结构体中,可能有指向发送队列描述符和数据缓冲区的指针,如struct queue_desc* send_queue_desc和void* send_data_buffer。
在初始化或配置队列时,通过分配内存并将这些指针指向正确的位置,将队列描述符和数据缓冲区与相应的数据结构关联起来。
内存区域键和数据长度/偏移:

在发送数据或接收数据时,可能会使用内存区域键和数据长度/偏移字段来指示相关的信息。
在发送数据或接收数据的函数中,可以将内存区域键和数据长度/偏移作为参数传递给相应的函数。
状态信息和控制信息:

在相应的数据结构中,可能会定义用于存储状态信息和控制信息的字段。例如,在struct hns_roce_qp结构体中,可能有字段用于存储队列状态信息和控制信息,如uint32_t queue_flags和void* queue_ptr。
在相关的操作中,可以通过操作这些字段来读取或修改状态信息和控制信息。
这些关联方式的具体实现细节可能会因驱动代码的结构和设计而有所不同。以上只是一些示例,实际的代码实现可能会根据具体需求进行调整和扩展。

alloc_qpn函数用于分配QP的唯一标识号(QPN)。以下是其可能的实现细节:

static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp)
{
struct hns_roce_bitmap *bitmap = &hr_dev->qp_table.qp_bitmap;
int ret;

/* 分配一个可用的QPN */
ret = hns_roce_bitmap_alloc(bitmap, &hr_qp->qpn);
if (ret) {
	ibdev_err(&hr_dev->ib_dev, "failed to allocate QPN.\n");
	return ret;
}

return 0;

}
在这个实现中,alloc_qpn函数首先从QP表的位图中分配一个可用的QPN。QP表使用位图来跟踪已分配和未分配的QPN。hns_roce_bitmap_alloc函数用于从位图中找到一个未被分配的QPN,并将其标记为已分配。

如果成功分配了QPN,hr_qp->qpn将被赋值为分配的QPN值,表示该QP的唯一标识号。否则,将返回适当的错误代码。

需要注意的是,这只是一个示例实现,实际的代码可能会根据驱动的设计和需求进行调整和扩展。

在提供的结构体struct hns_roce_bitmap中,位图(bitmap)是通过一个位数组来表示的,具体的成员和功能如下:

unsigned long last: 位图中最后一个被设置为1的位的索引(从0开始),用于快速定位未被分配的QPN。当分配新的QPN时,可以从该位置开始搜索,以减少搜索时间。
unsigned long top: 位图的最高有效位索引,表示位图的有效位数减1。可以根据这个值计算出位图的总位数。
unsigned long max: 位图能表示的最大位数。
unsigned long reserved_top: 保留的顶部索引,表示位图顶部的保留位数。这些保留位不用于分配QPN,而是用于其他目的。
unsigned long mask: 位图操作中使用的位掩码,用于位操作和位检查。
spinlock_t lock: 自旋锁,用于保护位图的并发访问。
unsigned long *table: 位图的位数组,存储实际的位信息。
通过使用这个位图结构,可以跟踪和管理QPN的分配和释放。位图中的每个位对应一个QPN,被设置为1表示该QPN已经分配,被设置为0表示该QPN未被分配。

具体的实现中,可以使用位操作函数来操作位图,例如使用test_bit函数检查位是否被设置,使用set_bit函数将位设置为1,使用clear_bit函数将位设置为0等。同时,为了保护位图的并发访问,使用自旋锁进行同步。

该位图的具体使用方式和操作流程可能在驱动的其他部分进行,例如在分配QP的过程中调用hns_roce_bitmap_alloc函数来获取一个未被分配的QPN,并将其标记为已分配。在释放QP时,相应的QPN将被从位图中标记为未分配,以便下次可以重新分配给新的QP。

需要注意的是,上述提供的代码只是结构体的定义,实际的位图操作和使用可能在驱动的其他部分进行。具体的实现可能会根据硬件和驱动的要求进行调整和优化。

alloc_qp_db函数的实现,用于分配QP的门铃区域。根据条件判断和需求,函数可能会调用不同的子函数来完成门铃区域的分配和相关操作。以下是函数中各部分的解释:

首先,根据硬件的能力(hr_dev->caps.flags)判断是否支持SDI模式。如果支持SDI模式,将QP的使能标志(hr_qp->en_flags)设置为HNS_ROCE_QP_CAP_OWNER_DB,表示QP的门铃区域由QP所有者(SDI模式)管理。

如果存在用户提供的数据(udata),则需要进行进一步处理。首先,如果QP的使能标志中包含HNS_ROCE_QP_CAP_DIRECT_WQE,则调用qp_mmap_entry函数,该函数用于在用户空间映射QP的工作队列元素(WQE)。

qp_mmap_entry函数的具体实现可能涉及使用remap_pfn_range等操作将QP的工作队列元素映射到用户空间。
接下来,调用alloc_user_qp_db函数来分配用户态QP的门铃区域。该函数可能完成以下操作:

分配一块连续的物理内存作为门铃区域,使用dma_alloc_coherent函数进行分配。
将门铃区域的物理地址存储在hr_qp->db_phys_addr中,并将门铃区域的虚拟地址存储在hr_qp->db中。
配置门铃区域的相关信息到ucmd和resp结构体中,这些信息可能包括物理地址和大小等。
如果不存在用户提供的数据(即内核模式),则调用alloc_kernel_qp_db函数来分配内核态QP的门铃区域。

alloc_kernel_qp_db函数的具体实现可能类似于alloc_user_qp_db,但是可能会有一些差异。
最后,根据分配门铃区域的结果,如果出现错误,需要进行相应的清理工作:

如果QP的使能标志中包含HNS_ROCE_QP_CAP_DIRECT_WQE,则调用qp_user_mmap_entry_remove函数,该函数用于移除用户空间对QP工作队列元素的映射。
需要注意的是,上述代码只是一个示例实现,实际的驱动代码可能会根据硬件和驱动的要求进行调整和优化。门铃区域的具体使用方式和操作流程可能在驱动的其他部分进行,例如在发送数据或接收完成时,会使用门铃机制来通知硬件进行相应的操作

alloc_kernel_qp_db和alloc_user_qp_db是在不同环境下分配QP门铃区域的函数,其区别主要在于分配的内存来源和访问权限的不同。

alloc_kernel_qp_db:用于在内核模式下分配QP的门铃区域。它通常在驱动内部调用,用于为内核态的QP分配门铃内存。主要特点包括:

使用dma_alloc_coherent函数从系统内存中分配连续的物理内存作为门铃区域。
门铃区域是在内核虚拟地址空间中进行分配的,驱动可以直接访问这块内存。
门铃区域的物理地址和虚拟地址由驱动进行管理。
alloc_user_qp_db:用于在用户态模式下分配QP的门铃区域。它通常在用户空间的应用程序中调用,用于为用户态的QP分配门铃内存。主要特点包括:

使用dma_alloc_coherent函数从系统内存中分配连续的物理内存作为门铃区域。
门铃区域是在用户虚拟地址空间中进行分配的,应用程序可以通过映射操作访问这块内存。
门铃区域的物理地址和虚拟地址可能需要传递给内核或其他驱动组件,以便进行相关操作。
需要注意的是,具体的实现可能会因驱动和硬件的不同而有所差异。这些函数的目的是为QP分配门铃区域,但内存的分配和管理方式可能根据驱动设计和需求的不同而有所调整。

alloc_kernel_qp_db函数用于在内核模式下为QP分配门铃区域。以下是该函数的实现细节:

根据硬件版本选择适当的门铃寄存器地址:

如果硬件版本大于等于 HIP09(Revision ID 大于等于特定值),则将门铃寄存器地址设置为 hr_dev->mem_base + HNS_ROCE_DWQE_SIZE * hr_qp->qpn。这里使用了设备的内存基地址和一个偏移量来计算门铃寄存器的地址。HNS_ROCE_DWQE_SIZE 是一个常数,用于计算门铃寄存器的偏移。
否则,将门铃寄存器地址设置为 hr_dev->reg_base + hr_dev->sdb_offset + DB_REG_OFFSET * hr_dev->priv_uar.index。这里使用了设备的寄存器基地址、SDB 偏移量和 DB_REG_OFFSET 偏移量来计算门铃寄存器的地址。hr_dev->priv_uar.index 是设备的私有用户寄存器索引。
设置接收队列(RQ)的门铃寄存器地址:

门铃寄存器地址设置为 hr_dev->reg_base + hr_dev->odb_offset + DB_REG_OFFSET * hr_dev->priv_uar.index。这里使用了设备的寄存器基地址、ODB 偏移量和 DB_REG_OFFSET 偏移量来计算 RQ 的门铃寄存器地址。
如果内核QP需要 RQ Doorbell 记录(DB)寄存器:

调用 hns_roce_alloc_db 函数为 RQ DB 分配内存,并将返回的分配结果保存在 hr_qp->rdb 中。
将 RQ DB 的值初始化为 0,使用 *hr_qp->rdb.db_record = 0。
设置 QP 的标志位,将 HNS_ROCE_QP_CAP_RQ_RECORD_DB 添加到 hr_qp->en_flags 中,表示启用了 RQ DB 记录。
返回 0 表示成功分配内核QP的门铃区域。

需要注意的是,具体的实现可能会因驱动和硬件的不同而有所差异。上述代码中的地址计算和门铃区域的分配方式可能根据驱动和硬件设计的不同而有所变化。

alloc_user_qp_db函数用于在用户模式下为QP分配门铃区域。以下是该函数的实现细节:

获取用户上下文对象 uctx,这是从 udata 中获取的,用于访问用户空间的内存。

如果用户QP需要 SQ Doorbell 记录(DB)寄存器:

调用 hns_roce_db_map_user 函数将用户提供的 SQ DB 地址映射到内核空间,并将映射结果保存在 hr_qp->sdb 中。
检查映射操作是否成功,如果失败,则返回相应的错误码。
设置 QP 的标志位,将 HNS_ROCE_QP_CAP_SQ_RECORD_DB 添加到 hr_qp->en_flags 中,表示启用了 SQ DB 记录。
如果用户QP需要 RQ Doorbell 记录(DB)寄存器:

调用 hns_roce_db_map_user 函数将用户提供的 RQ DB 地址映射到内核空间,并将映射结果保存在 hr_qp->rdb 中。
检查映射操作是否成功,如果失败,则返回相应的错误码。
设置 QP 的标志位,将 HNS_ROCE_QP_CAP_RQ_RECORD_DB 添加到 hr_qp->en_flags 中,表示启用了 RQ DB 记录。
返回 0 表示成功分配用户QP的门铃区域。

需要注意的是,具体的实现可能会因驱动和硬件的不同而有所差异。上述代码中的地址映射和门铃区域的分配方式可能根据驱动和硬件设计的不同而有所变化。

alloc_qpc函数的另一个实现示例,用于为QP分配上下文区域。以下是该函数的实现细节:

首先获取驱动中的 QP 表 qp_table,设备设备对象 dev。

检查 QP 的唯一标识号 qpn 是否有效,如果为零则返回错误码 -EINVAL。

调用 hns_roce_table_get 函数为 QPC 表 qp_table->qp_table 中的指定 QPN 分配内存。这个表用于存储 QP 的上下文信息。

如果分配内存失败,输出错误信息并跳转到 err_out,释放已分配的资源。

调用 hns_roce_table_get 函数为 IRRL 表 qp_table->irrl_table 中的指定 QPN 分配内存。这个表用于存储 QP 的接收请求限制信息。

如果分配内存失败,输出错误信息并跳转到 err_put_qp,释放已分配的资源。

如果设备支持 Trrl 表,调用 hns_roce_table_get 函数为 TRRL 表 qp_table->trrl_table 中的指定 QPN 分配内存。这个表用于存储 QP 的传输请求限制信息。

如果分配内存失败,输出错误信息并跳转到 err_put_irrl,释放已分配的资源。

如果设备支持 QP 流量控制,调用 hns_roce_table_get 函数为 SCC CTX 表 qp_table->sccc_table 中的指定 QPN 分配内存。这个表用于存储 QP 的发送流量控制上下文信息。

如果分配内存失败,输出错误信息并跳转到 err_put_trrl,释放已分配的资源。

如果所有的内存分配成功,返回 0 表示成功分配 QP 的上下文区域。

在错误处理中,按照分配顺序依次释放已分配的资源,并返回相应的错误码。

需要注意的是,具体的实现可能会因驱动和硬件的不同而有所差异。上述代码中的内存分配和表操作过程可能根据驱动和硬件设计的不同而有所变化。

在硬件初始化过程中,可能会实例化多个数据结构来管理硬件资源和配置信息。以下是可能与硬件初始化相关的一些常见数据结构:

struct hns_roce_dev: 这是表示 RoCE 设备的数据结构,用于管理整个设备的状态和属性。它包含了设备的寄存器基地址、设备特性、队列、内存等信息。

struct hns_roce_qp: 这是表示 Queue Pair(QP)的数据结构,用于管理每个QP的状态和属性。它包含了发送队列(Send Queue)和接收队列(Receive Queue)的描述符、门铃地址、QP状态等信息。

struct hns_roce_cq: 这是表示 Completion Queue(CQ)的数据结构,用于管理每个CQ的状态和属性。它包含了CQ的描述符、CQ的大小、CQ的状态等信息。

struct hns_roce_srq: 这是表示 Shared Receive Queue(SRQ)的数据结构,用于管理每个SRQ的状态和属性。它包含了SRQ的描述符、SRQ的大小、SRQ的状态等信息。

struct hns_roce_pd: 这是表示 Protection Domain(PD)的数据结构,用于管理每个PD的状态和属性。它包含了PD的标识号、PD的访问权限等信息。

这些数据结构的实例化通常在硬件初始化过程中创建并进行初始化,以便后续的操作和管理。实例化的具体过程会根据驱动的实现而有所差异。如果需要了解特定驱动的硬件初始化过程和相应的数据结构,请查阅完整的驱动代码或相关文档。

根据提供的驱动代码 hns_roce_hw_v2_init_instance,我们可以看到以下数据结构被实例化:

struct hns_roce_dev: 在函数开头通过 hns_roce_alloc_device() 分配了一个 struct hns_roce_dev 结构体,并进行了一些初始化设置,如设备类型、端口数量、设备特性等。

struct hns_roce_hw: 在 hns_roce_hw_v2_init_instance 中定义了一个 struct hns_roce_hw 结构体,并对其进行初始化设置,如门铃区域大小、设备版本信息等。

struct hns_roce_qp_table: 在 hns_roce_hw_v2_init_instance 中定义了一个 struct hns_roce_qp_table 结构体,并对其进行初始化设置,如分配 QP 表、IRRL 表、TRRL 表和 SCC CTX 表等。

其他辅助数据结构:在初始化过程中,可能还会使用其他辅助的数据结构来管理资源和配置信息,如门铃管理结构、内存管理结构等。这些结构体的定义和初始化可能在函数内部进行,根据具体的实现而有所不同。

需要注意的是,由于提供的驱动代码并不完整,无法提供详尽的实例化数据结构列表。要全面了解实际驱动代码中的数据结构实例化情况,建议查阅完整的驱动代码或相关文档。

这些数据结构之间存在一定的联系和依赖关系,用于管理和配置硬件设备及其相关资源。

struct hns_roce_dev 是顶层的设备结构,表示整个 RoCE 设备。它包含了设备的基本信息和特性,并与底层的硬件设备进行交互。

struct hns_roce_hw 是硬件相关的结构体,用于管理与硬件设备的通信和操作。它包含了硬件寄存器的基址、版本信息以及其他硬件相关的属性。

struct hns_roce_qp_table 是 QP(队列对)表的管理结构,用于分配和管理 QP 相关的资源,如 QPC(QP 上下文)、IRRL(Inbound Read Response Level)表、TRRL(Transport Replay Response Level)表和 SCC CTX(Switch Congestion Control Context)表等。这些表的分配和释放通过 hns_roce_table_get() 和 hns_roce_table_put() 函数进行。

这些数据结构之间的联系和依赖关系主要体现在以下几个方面:

struct hns_roce_dev 中包含了一个指向 struct hns_roce_hw 的指针,用于与底层硬件进行交互和配置。

struct hns_roce_dev 中包含了一个指向 struct hns_roce_qp_table 的指针,用于管理 QP 相关的资源。

struct hns_roce_qp_table 中包含了对应的 QP 相关表的实例,用于分配和释放 QP 相关的资源。

通过这些数据结构之间的联系,驱动程序能够有效地管理硬件设备的资源,并提供适当的接口供用户和上层协议栈使用。

struct hns_roce_hw 的数据结构,其中包含了一系列函数指针成员和指向函数的指针,用于驱动程序与硬件之间的交互和操作。

下面是这些函数指针成员的简要说明:

cmq_init 和 cmq_exit:用于初始化和退出命令队列 (Command Queue) 相关的资源。
hw_profile:用于获取硬件的配置和特性信息。
hw_init 和 hw_exit:用于初始化和退出硬件设备。
post_mbox:用于向设备的邮箱发送消息。
poll_mbox_done:用于轮询邮箱是否有消息完成。
chk_mbox_avail:用于检查邮箱是否可用。
set_gid:用于设置设备的全局唯一标识符 (GID)。
set_mac:用于设置设备的物理地址。
write_mtpt、rereg_write_mtpt、frmr_write_mtpt 和 mw_write_mtpt:用于写入内存传输门户表 (Memory Translate and Protection Table) 的条目。
write_cqc:用于写入完成队列 (Completion Queue) 的条目。
set_hem 和 clear_hem:用于设置和清除硬件事件管理 (HEM) 表的条目。
modify_qp:用于修改队列对点 (Queue Pair) 的属性。
qp_flow_control_init:用于初始化队列对点的流控属性。
dereg_mr:用于注销内存区域 (Memory Region)。
init_eq 和 cleanup_eq:用于初始化和清理事件队列 (Event Queue)。
write_srqc:用于写入共享接收队列 (Shared Receive Queue) 的条目。
query_cqc、query_qpc 和 query_mpt:用于查询完成队列、队列对点和内存传输门户表的信息。
此外,该结构体还包含了指向 struct ib_device_ops 结构的指针成员,用于指定与 InfiniBand 设备操作相关的函数。

这些函数指针成员提供了与硬件设备交互的接口,驱动程序可以通过调用这些函数来执行相应的硬件操作。这些函数指针的具体实现通常是在驱动程序的其他部分定义的,并与设备硬件进行交互。

struct hns_roce_dev 是 HNS RoCE 驱动中表示设备的数据结构。下面是该数据结构的一些关键成员的说明:

ib_dev:包含了 InfiniBand 设备的通用属性和操作,是 struct ib_device 类型的成员。
pci_dev:指向 PCI 设备结构的指针,用于与设备的 PCI 总线相关操作。
dev:指向设备的 struct device 结构的指针,表示设备的设备模型对象。
priv_uar:表示私有用户访问寄存器 (UAR) 的信息。
irq_names:存储 IRQ 名称的数组,用于设备的中断处理。
sm_lock:用于保护设备的状态机的自旋锁。
active:表示设备是否处于活动状态。
is_reset:表示设备是否正在重置中。
reset_cnt:重置计数器,用于跟踪设备重置的次数。
state:设备的状态。
qp_list 和 qp_list_lock:队列对点 (Queue Pair) 的链表和对应的自旋锁,用于管理设备上的所有队列对点。
dip_list 和 dip_list_lock:目的 IP 地址 (Destination IP Address) 的链表和对应的自旋锁,用于管理设备上的所有目的 IP 地址。
pgdir_list 和 pgdir_mutex:页目录 (Page Directory) 的链表和对应的互斥锁。
irq:存储 IRQ 号码的数组,用于设备的中断处理。
reg_base 和 mem_base:设备的寄存器和内存基址。
caps:设备的功能和能力信息。
qp_table_xa:队列对点 (Queue Pair) 的 XArray 结构,用于索引和查找队列对点。
dev_addr:设备的 MAC 地址。
sys_image_guid:设备的系统图像全局唯一标识符 (System Image GUID)。
vendor_id、vendor_part_id 和 hw_rev:设备的厂商 ID、硬件版本和厂商特定部件 ID。
priv_addr:私有地址空间的基址。
cmd:命令队列相关的数据结构。
pd_ida、xrcd_ida 和 uar_ida:用于分配唯一标识符的 IDA (ID Allocator) 结构。
mr_table、cq_table、srq_table、qp_table、eq_table 和 qpc_timer_table:用于管理内存区域、完成队列、共享接收队列、队列对点、事件队列和队列对点计时器的数据结构。
gmv_table:GMV (Global MAC VLAN) 表的数据结构,用于存储 SGID、SMAC 和 VLAN 信息。
cmd_mod:命令队列模块。
loop_idc:环回 IDC (Inbound Data Channel)。
sdb_offset 和 odb_offset:SDB (Send Doorbell) 和 ODB (Ordering Doorbell) 的偏移量。
hw:指向 HNS RoCE 硬件接口的指针,是 struct hns_roce_hw 类型的指针。
priv:指向私有数据的指针,用于驱动程序的私有数据。
irq_workq:中断工作队列,用于异步处理中断事件。
ecc_work:ECC (Error Correction Code) 工作结构,用于处理 ECC 错误。
func_num:功能号。
is_vf:表示设备是否为虚拟功能 (Virtual Function)。
cong_algo_tmpl_id:拥塞算法模板 ID。
dwqe_page:DWQE (Data Work Queue Element) 页的数量。

这些数据结构之间的联系是通过在 struct hns_roce_dev 中组合和嵌套这些数据结构来建立的。它们共同构成了驱动程序与硬件设备之间的数据结构体系,用于管理设备的各个部分和资源。

struct hns_roce_dev 结构体并非直接初始化,而是通过调用 __hns_roce_hw_v2_init_instance 函数进行初始化。

具体而言,在 hns_roce_hw_v2_init_instance 函数中,首先将 handle->rinfo.instance_state 设置为 HNS_ROCE_STATE_INIT 表示设备实例处于初始化状态。然后进行一些重置状态的检查,如果设备正在进行重置或者硬件重置状态为真,将 handle->rinfo.instance_state 设置为 HNS_ROCE_STATE_NON_INIT 表示设备实例未初始化,并跳转到 reset_chk_err 错误处理标签。

接下来,通过调用 pci_match_id 函数来匹配设备的 PCI 设备 ID,并判断是否需要初始化 RoCE 实例。如果没有匹配的 PCI 设备 ID 或者设备的修订版本是 HIP08(PCI_REVISION_ID_HIP08),则直接返回。

如果存在匹配的 PCI 设备 ID,且设备不是 HIP08 版本,将调用 __hns_roce_hw_v2_init_instance 函数进行实际的设备初始化。如果初始化失败,将设置 handle->rinfo.instance_state 为 HNS_ROCE_STATE_NON_INIT,输出错误信息,并根据设备是否正在重置或硬件重置状态来确定错误处理流程。

最后,如果初始化成功,将 handle->rinfo.instance_state 设置为 HNS_ROCE_STATE_INITED 表示设备实例已初始化,并返回 0 表示初始化成功。

总结起来,hns_roce_hw_v2_init_instance 函数负责检查设备的重置状态,匹配 PCI 设备 ID,调用 __hns_roce_hw_v2_init_instance 函数进行实际的设备初始化,并设置设备实例的状态。

__hns_roce_hw_v2_init_instance 函数是用来进行实际的设备初始化的。它的主要步骤如下:

使用 ib_alloc_device 函数分配一个 hns_roce_dev 结构体的实例,并将其赋值给 hr_dev。如果分配失败,将返回错误码。

使用 kzalloc 函数为 hr_dev->priv 分配内存,用于存储私有数据结构 struct hns_roce_v2_priv。如果分配失败,将释放之前分配的资源,并返回错误码。

调用 hns_roce_hw_v2_get_cfg 函数,根据 handle 中的信息填充 hr_dev 的配置参数。

调用 hns_roce_init 函数进行 RoCE 引擎的初始化。如果初始化失败,将输出错误信息,释放之前分配的资源,并返回错误码。

如果设备的 PCI 修订版本是 HIP08,调用 free_mr_init 函数初始化 free MR(内存区域)。如果初始化失败,将输出错误信息,释放之前分配的资源,并返回错误码。

将 hr_dev 赋值给 handle->priv,表示设备初始化成功。

返回 0 表示设备初始化成功。

如果在初始化的过程中出现错误,将按照错误的顺序释放之前分配的资源,并返回相应的错误码。

_ib_alloc_device 函数的实现,用于分配和初始化一个 IB 设备结构体 ib_device。函数的主要步骤如下:

检查传入的 size 是否大于等于 sizeof(struct ib_device),如果小于该值,则发出警告并返回空指针。

使用 kzalloc 函数分配 size 大小的内存块,并将其赋值给 device。如果分配失败,返回空指针。

调用 rdma_restrack_init 函数初始化设备的资源跟踪信息。

使用 rdma_init_coredev 函数初始化设备的核心设备信息。

初始化设备的各种锁、列表和信号量,包括事件处理列表、QP 打开列表锁、事件处理读写信号量、注销锁、客户端数据和兼容设备等。

初始化设备的完成量、注销工作和 CQ 池等。

设置设备的用户 Verbs 命令掩码,表示支持的用户 Verbs 命令。

返回分配的设备结构体指针。

此函数用于在低级别驱动程序中分配和初始化 ib_device 结构体,包括初始化各种锁、列表和信号量,以及设置支持的用户 Verbs 命令。

hns_roce_init 函数用于初始化 RoCE 设备。以下是该函数的实现:

该函数的主要步骤包括:

设置设备的重置状态为 false。
如果存在命令队列的初始化函数 cmq_init,则调用该函数初始化 RoCE 命令队列。
调用 RoCE 引擎配置函数 hw_profile 获取 RoCE 引擎的配置信息。
调用 hns_roce_cmd_init 函数初始化 RoCE 命令模块。
调用 init_eq 函数初始化事件队列。
如果命令模块启用,尝试使用事件模式,如果失败则回退到轮询模式。
初始化 HEM (Hardware Entry Memory)。
设置 HCA (Host Channel Adapter)。
如果存在引擎初始化函数 hw_init,则调用该函数初始化引擎。
初始化 QP (Queue Pair) 列表和 DIP (Destination Internet Protocol) 列表。
注册 RoCE 设备。
如果注册失败,进行相应的错误处理,包括清理资源和回滚操作。
如果在初始化过程中出现错误,会进行相应的错误处理,并返回错误码。错误处理包括清理资源、回滚操作以及打印错误信息。

static int init_csq(struct hns_roce_dev *hr_dev,

	    struct hns_roce_v2_cmq_ring *csq)

函数用于初始化命令发送队列(Command Send Queue,CSQ)。

以下是函数的主要步骤:

设置 CSQ 的描述符数量、锁、标志和头指针等属性。
调用 hns_roce_alloc_cmq_desc 函数为 CSQ 分配描述符。
获取 CSQ 描述符的物理地址,并将其写入 RoCE 设备的寄存器 ROCEE_TX_CMQ_BASEADDR_L_REG 和 ROCEE_TX_CMQ_BASEADDR_H_REG。
将 CSQ 的描述符数量写入 RoCE 设备的寄存器 ROCEE_TX_CMQ_DEPTH_REG。
设置 CSQ 的消费索引(Consumer Index)和生产索引(Producer Index)为 0,分别写入 RoCE 设备的寄存器 ROCEE_TX_CMQ_CI_REG 和 ROCEE_TX_CMQ_PI_REG。
函数最后返回 0 表示初始化成功,如果在过程中出现错误,则返回相应的错误码。

static int hns_roce_alloc_cmq_desc(struct hns_roce_dev *hr_dev,

			   struct hns_roce_v2_cmq_ring *ring)

该函数用于为命令发送队列(CSQ)分配描述符。

函数的主要步骤如下:

根据 CSQ 的描述符数量计算所需内存空间的大小,即 ring->desc_num * sizeof(struct hns_roce_cmq_desc)。
调用 dma_alloc_coherent 函数分配一段连续的物理内存,并将其赋值给 ring->desc。
获取分配内存的物理地址,并将其保存在 ring->desc_dma_addr 中。
如果内存分配失败(即 ring->desc 为NULL),则返回 -ENOMEM 表示内存不足的错误。
函数最后返回 0 表示描述符分配成功,如果在过程中出现错误,则返回相应的错误码。

hns_roce_init_hem_table

函数是用于初始化 HEM(Hardware Entry Memory),它通过调用 hns_roce_init_hem_table 函数来初始化各个类型的 HEM 表。

以下是该函数的主要步骤:

使用 hns_roce_init_hem_table 函数初始化 MTPT(Memory Translation and Protection Table)上下文内存,存储 MTPT 表的信息。
使用 hns_roce_init_hem_table 函数初始化 QP(Queue Pair)上下文内存,存储 QP 表的信息。
使用 hns_roce_init_hem_table 函数初始化 IRRL(Inbound Read Request Limit)表内存,存储入站读请求的限制信息。
如果 RoCE 设备支持 TRRL(Tag Read Request Limit)表,使用 hns_roce_init_hem_table 函数初始化 TRRL 表内存,存储标签读请求的限制信息。
使用 hns_roce_init_hem_table 函数初始化 CQ(Completion Queue)上下文内存,存储 CQ 表的信息。
如果 RoCE 设备支持 SRQ(Shared Receive Queue),使用 hns_roce_init_hem_table 函数初始化 SRQ 上下文内存,存储 SRQ 表的信息。
如果 RoCE 设备支持 QP 流量控制,使用 hns_roce_init_hem_table 函数初始化 SCC(SQ CQ Consumer)上下文内存,存储 SQ CQ 的消费者索引。
如果 RoCE 设备支持 QPC(QP Context)计时器,使用 hns_roce_init_hem_table 函数初始化 QPC 计时器内存,存储 QPC 计时器表的信息。
如果 RoCE 设备支持 CQC(CQ Context)计时器,使用 hns_roce_init_hem_table 函数初始化 CQC 计时器内存,存储 CQC 计时器表的信息。
如果 RoCE 设备支持 GMV(Global Memory Version)表,使用 hns_roce_init_hem_table 函数初始化 GMV 表内存,存储 GMV 表的信息。
如果在初始化过程中出现错误,将依次进行清理并返回相应的错误码。

int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev,

		    struct hns_roce_hem_table *table, u32 type,
		    unsigned long obj_size, unsigned long nobj)

该函数根据输入的设备 hr_dev、表结构体 table、类型 type、对象大小 obj_size 和对象数量 nobj 来初始化 HEM 表。函数首先检查设备是否支持多操作,然后根据不同情况进行初始化。

如果设备不支持多操作,将计算每个表块的大小 table->table_chunk_size,以及每个表块可以容纳的对象数量 obj_per_chunk。然后根据总对象数量 nobj 和每个表块的对象数量 obj_per_chunk 计算所需的表块数量 num_hem。接下来,分配 num_hem 个表块的内存空间,并将分配的内存存储在 table->hem 中。

如果设备支持多操作,将获取多操作配置信息,并计算缓冲区表块大小 buf_chunk_size、基地址表块大小 bt_chunk_size、基地址表块数量 bt_chunk_num 以及 L0 基地址表数量 num_bt_l0。根据对象大小 obj_size 和每个缓冲区表块的对象数量 obj_per_chunk,计算所需的表块数量 num_hem。根据类型 type,判断是否需要更新 L0 基地址表数量 num_bt_l0。接下来,分配相应数量的表块和基地址表块的内存空间,并将分配的内存存储在相应的字段中。

最后,设置表结构体的其他字段,如类型、表块数量和对象大小,并初始化互斥锁。

hns_roce_init_hem_table(hr_dev, &hr_dev->mr_table.mtpt_table, HEM_TYPE_MTPT, hr_dev->caps.mtpt_entry_sz, hr_dev->caps.num_mtpts) 调用为例。

该函数的目的是初始化 hr_dev 设备的 MTPT 表(Memory Translation and Protection Table)。下面是调用时的参数解释:

hr_dev:指向 hns_roce_dev 结构体的指针,表示要初始化的设备。
&hr_dev->mr_table.mtpt_table:指向 hns_roce_hem_table 结构体的指针,表示要初始化的 MTPT 表。
HEM_TYPE_MTPT:表示要初始化的表的类型,这里是 MTPT 表。
hr_dev->caps.mtpt_entry_sz:表示每个 MTPT 表项的大小。
hr_dev->caps.num_mtpts:表示要初始化的 MTPT 表的数量。
首先,函数会检查设备是否支持多操作(通过调用 hns_roce_check_whether_mhop(hr_dev, type) 函数)。如果设备不支持多操作,则会计算每个表块的大小 table->table_chunk_size,以及每个表块可以容纳的对象数量 obj_per_chunk。然后根据总对象数量 nobj 和每个表块的对象数量 obj_per_chunk 计算所需的表块数量 num_hem。接下来,分配 num_hem 个表块的内存空间,并将分配的内存存储在 table->hem 字段中。

如果设备支持多操作,则会获取多操作配置信息,并计算缓冲区表块大小 buf_chunk_size、基地址表块大小 bt_chunk_size、基地址表块数量 bt_chunk_num 以及 L0 基地址表数量 num_bt_l0。根据对象大小 obj_size 和每个缓冲区表块的对象数量 obj_per_chunk,计算所需的表块数量 num_hem。根据类型 type,判断是否需要更新 L0 基地址表数量 num_bt_l0。接下来,分配相应数量的表块和基地址表块的内存空间,并将分配的内存存储在相应的字段中。

最后,设置表结构体的其他字段,如类型、表块数量和对象大小,并初始化互斥锁。

回到调用 hns_roce_init_hem_table 的代码,它的目的是初始化设备 hr_dev 的 MTPT 表。将 MTPT 表的信息存储在 hr_dev->mr_table.mtpt_table 中。根据设备的能力 hr_dev->caps,传递 MTPT 表的类型、每个表项的大小以及要初始化的表的数量作为参数。

"多操作"是指设备支持同时处理多个操作的能力。具体而言,它涉及设备在单个操作中处理多个数据或任务的能力。多操作可以提高设备的并行性和效率,从而提高系统的性能。

在代码中,hns_roce_check_whether_mhop() 函数用于检查设备是否支持多操作。如果设备不支持多操作,那么表的初始化将按照非多操作的方式进行。也就是说,每个表块大小为 table->table_chunk_size,并且表将被划分为多个表块来容纳对象。

相反,如果设备支持多操作,就需要根据多操作的配置信息进行表的初始化。这涉及到缓冲区表块的大小、基地址表块的大小以及相应的数量计算。因此,多操作和非多操作的区别在于内存结构和初始化的方式,以适应设备的不同特性和能力。

下面是一个简单的示意图,说明在支持多操作和不支持多操作情况下两种表的内存分配区别:

支持多操作情况下的表内存分配:

-----------------------------------------------------
|                表内存区域(支持多操作)                |
-----------------------------------------------------
|                   多个HEM(表块)                   |
-----------------------------------------------------
|                   多个HEM(表块)                   |
-----------------------------------------------------
|                   多个HEM(表块)                   |
-----------------------------------------------------

不支持多操作情况下的表内存分配:

-----------------------------------------------------
|                表内存区域(不支持多操作)               |
-----------------------------------------------------
|                 单个HEM(表块)                   |
-----------------------------------------------------
|                 单个HEM(表块)                   |
-----------------------------------------------------
|                 单个HEM(表块)                   |
-----------------------------------------------------

在支持多操作情况下,表内存区域被划分为多个HEM(表块),每个HEM可以容纳多个对象。这些HEM可以并行处理多个操作,从而提高设备的性能和吞吐量。

而在不支持多操作情况下,表内存区域只包含单个HEM(表块),每个HEM仅能容纳一个对象。这意味着设备一次只能处理一个操作,不能同时处理多个操作。

下面是一个示意图,用于说明支持多操作和不支持多操作情况下的表内存分配的差异:

支持多操作情况下的表内存分配示意图:

   |----------------------------------------------|
   |                  表内存区域(支持多操作)                     |
   |----------------------------------------------|
   |              HEM 1              |              HEM 2              |
   |----------------------------------------------|
   |        |        |        |        |        |        |
   | Obj 1  | Obj 2  | Obj 3  | Obj 4  | Obj 5  | Obj 6  |
   |        |        |        |        |        |        |
   |----------------------------------------------|

在支持多操作的情况下,表内存区域被划分为多个HEM(表块)。每个HEM可以容纳多个对象(Obj)。这些对象可以并行处理多个操作。

不支持多操作情况下的表内存分配示意图:

   |----------------------------------------------|
   |                表内存区域(不支持多操作)                 |
   |----------------------------------------------|
   |              单个HEM(表块)                          |
   |----------------------------------------------|
   |        |        |        |        |        |        |
   | Obj 1  | Obj 2  | Obj 3  | Obj 4  | Obj 5  | Obj 6  |
   |        |        |        |        |        |        |
   |----------------------------------------------|

在不支持多操作的情况下,表内存区域只有一个HEM(表块)。每个HEM仅能容纳一个对象。这意味着设备一次只能处理一个操作,不能同时处理多个操作。

多操作和单操作指的是设备在处理内存操作时的能力。

多操作:当设备支持多操作时,它可以同时处理多个内存操作。这意味着设备可以并行执行多个读取或写入操作,从而提高了整体的处理能力和效率。在支持多操作的情况下,设备会使用多个硬件执行单元或并行处理机制来同时处理这些操作。

单操作:当设备不支持多操作时,它只能逐个处理每个内存操作。这意味着设备一次只能处理一个读取或写入操作,而其他操作必须等待当前操作完成后才能执行。在单操作的情况下,设备只有一个硬件执行单元用于处理内存操作。

具体的操作流程在多操作和单操作模式下可能有所不同:

多操作模式:设备可以同时发起多个操作,并且这些操作可以并行执行。设备可以同时读取或写入多个内存位置,从而实现并发操作和高吞吐量。具体的操作流程可能涉及调度算法、并行处理机制和硬件通信协议等。

单操作模式:设备一次只能处理一个操作,它会按照操作的顺序依次执行。当一个操作完成后,设备才能开始下一个操作。具体的操作流程可能包括操作的请求、设备的响应、数据传输和状态更新等。

需要注意的是,多操作和单操作的实现方式取决于设备的硬件架构、驱动程序和操作系统的支持。每个设备和平台可能有不同的实现方式和性能特点。详细的操作流程和机制应参考设备文档、驱动程序代码或相关技术文档。

上述提供的驱动代码中,多操作是通过以下步骤进行调用的:

首先,通过函数hns_roce_check_whether_mhop检查设备是否支持多操作。该函数会检查设备的能力,确定是否支持多操作。如果设备不支持多操作,则会进入单操作模式。

如果设备支持多操作,那么在初始化HEM表时,会根据多操作的配置参数设置相关的数据结构和参数。

在多操作模式下,对于每个HEM表,会分配多个HEM块,每个HEM块可以同时处理多个内存操作。HEM块的数量由设备的能力和配置参数决定。

在进行内存操作时,驱动程序会将多个操作请求同时提交给设备。设备会并行处理这些操作,并返回相应的结果。驱动程序可以通过适当的并发控制机制来确保操作的正确性和顺序。

总之,在多操作模式下,驱动程序可以同时提交多个内存操作给设备,并且设备可以并行处理这些操作,以提高整体的处理能力和效率。多操作的调用过程涉及设备能力的检查、配置参数的设置和并发控制等步骤。

在hns_roce_init_hem_table函数完成初始化后,设备的MTPT表(Memory Translation and Protection Table)示意图如下所示:

+---------------------------+
|         MTPT Table        |
+---------------------------+
|                           |
|      MTPT Entry 0         |
|                           |
|---------------------------|
|                           |
|      MTPT Entry 1         |
|                           |
|---------------------------|
|                           |
|      MTPT Entry 2         |
|                           |
|            ...            |
|                           |
|---------------------------|
|                           |
|      MTPT Entry N         |
|                           |
+---------------------------+

MTPT表是一个存储器区域,其中包含一系列MTPT条目(MTPT Entry)。每个MTPT条目对应着一个内存区域,用于存储内存的地址转换和保护信息。MTPT表的大小和MTPT条目的数量取决于设备的能力和配置参数。

每个MTPT条目包含了与内存相关的信息,例如物理地址、虚拟地址、页表信息、访问权限等。驱动程序会根据需要初始化每个MTPT条目,以确保正确的内存地址转换和保护机制。

在MTPT表中,每个MTPT条目按顺序排列,从索引0开始递增。通过索引可以快速定位到特定的MTPT条目,并进行内存地址的转换和保护。

以上示意图是一个简化的MTPT表示意图,实际的MTPT表可能包含更多的条目,具体数量取决于设备的能力和配置。每个MTPT条目的内容会根据设备的需求进行设置和更新,以满足内存地址转换和保护的要求。

enum MAP HEM(Hardware Entry Memory)

{
	/* MAP HEM(Hardware Entry Memory) */
	HEM_TYPE_QPC = 0,
	HEM_TYPE_MTPT,
	HEM_TYPE_CQC,
	HEM_TYPE_SRQC,
	HEM_TYPE_SCCC,
	HEM_TYPE_QPC_TIMER,
	HEM_TYPE_CQC_TIMER,
	HEM_TYPE_GMV,

	 /* UNMAP HEM */
	HEM_TYPE_MTT,
	HEM_TYPE_CQE,
	HEM_TYPE_SRQWQE,
	HEM_TYPE_IDX,
	HEM_TYPE_IRRL,
	HEM_TYPE_TRRL,
};

在给定的枚举中,列出了一些用于设备的不同类型的HEM(Hardware Entry Memory)。这些类型可以分为两个部分:用于映射(MAP)HEM和用于取消映射(UNMAP)HEM。

MAP HEM表示用于映射到设备的内存区域,用于存储特定类型的数据结构或上下文信息。以下是MAP HEM的类型:

HEM_TYPE_QPC:用于QPC(Queue Pair Context)的映射HEM。
HEM_TYPE_MTPT:用于MTPT(Memory Translation and Protection Table)的映射HEM。
HEM_TYPE_CQC:用于CQC(Completion Queue Context)的映射HEM。
HEM_TYPE_SRQC:用于SRQC(Shared Receive Queue Context)的映射HEM。
HEM_TYPE_SCCC:用于SCCC(QP Source Capability Control)的映射HEM。
HEM_TYPE_QPC_TIMER:用于QPC Timer的映射HEM。
HEM_TYPE_CQC_TIMER:用于CQC Timer的映射HEM。
HEM_TYPE_GMV:用于GMV(General Memory Variable)的映射HEM。
UNMAP HEM表示用于取消映射或释放设备中的内存区域。以下是UNMAP HEM的类型:

HEM_TYPE_MTT:用于MTT(Memory Translation Table)的取消映射HEM。
HEM_TYPE_CQE:用于CQE(Completion Queue Entry)的取消映射HEM。
HEM_TYPE_SRQWQE:用于SRQWQE(Shared Receive Queue Work Queue Entry)的取消映射HEM。
HEM_TYPE_IDX:用于索引的取消映射HEM。
HEM_TYPE_IRRL:用于IRRL(Inline Receive Request List)的取消映射HEM。
HEM_TYPE_TRRL:用于TRRL(Transport Receive Request List)的取消映射HEM。
这些类型定义了不同的HEM用途和操作,驱动程序根据设备的需求和功能,使用相应的类型来进行HEM的映射和取消映射。每个类型对应着不同的内存结构和数据,在设备初始化和管理过程中起到关键作用。

在hns_roce_init_hem_table函数完成初始化后,CQC表(Memory Translation and Protection Table)的示意图:

CQC Table:
+----------------------------------------------------+
|                 CQC Entry 0                         |
+----------------------------------------------------+
|                 CQC Entry 1                         |
+----------------------------------------------------+
|                 CQC Entry 2                         |
+----------------------------------------------------+
|                       ...                          |
+----------------------------------------------------+
|                 CQC Entry N-1                       |
+----------------------------------------------------+

在示意图中,每个方框表示CQC(Completion Queue Context)表中的一个CQC条目。CQC条目是用于存储完成队列(Completion Queue)的上下文信息和配置参数。每个CQC条目对应着一个特定的完成队列。

在初始化完成后,CQC表根据设备的配置和需求,预先分配了一定数量的CQC条目。通过该表,驱动程序可以管理和访问每个CQC条目,以实现对完成队列的操作和管理。

请注意,示意图仅展示了CQC表的基本结构,实际的内存布局和细节取决于设备的具体要求和实现。

struct hns_roce_cq_table

{
	struct xarray			array;
	struct hns_roce_hem_table	table;
	struct hns_roce_bank bank[HNS_ROCE_CQ_BANK_NUM];//4
	struct mutex			bank_mutex;
};

在提供的代码中,struct hns_roce_cq_table 是 CQ(Completion Queue)表的数据结构定义。它包含以下成员:

struct xarray array: 一个 xarray 结构,用于管理和查找 CQ 表中的条目。
struct hns_roce_hem_table table: 一个 hns_roce_hem_table 结构,用于管理 CQ 表的物理内存和相关信息。
struct hns_roce_bank bank[HNS_ROCE_CQ_BANK_NUM]: 一个数组,用于管理 CQ 表的银行(Bank)。
struct mutex bank_mutex: 一个互斥锁,用于对 CQ 表的银行进行互斥访问。
每个银行(Bank)用于管理一组 CQ 条目,这样可以实现并行处理多个 CQ。具体的 CQ 条目的数据结构定义可能在其他文件中,未在提供的代码片段中找到相应的定义。请查看设备驱动程序的其他部分或参考相关文档以获取完整的 CQ 条目数据结构定义。

些数据结构之间有以下关系:

struct hns_roce_cq_table 是 CQ 表的顶层数据结构,用于管理 CQ 表的整体结构和相关资源。
struct hns_roce_hem_table 是用于管理 HEM(Hardware Entry Memory)的通用数据结构,可以用于管理不同类型的 HEM,包括 MTPT、CQC、SRQC 等。
struct hns_roce_bank 是 CQ 表的银行数据结构,每个银行管理一组 CQ 条目。
struct hns_roce_hem 是 HEM(Hardware Entry Memory)的数据结构,用于管理 HEM 的分配和释放。
具体关系如下:

struct hns_roce_cq_table 包含一个 struct hns_roce_hem_table 成员 table,用于管理 CQ 表的物理内存和相关信息。
struct hns_roce_cq_table 包含一个 struct hns_roce_bank 数组 bank,用于管理 CQ 表的银行(Bank)。
struct hns_roce_hem_table 中的 struct hns_roce_hem 数组 hem 用于存储分配的 HEM 块的信息。
通过这些数据结构的组合,可以实现对 CQ 表的管理、对 HEM 的分配和释放,以及对不同类型的 HEM 进行管理。每个 CQ 表可以包含多个银行(Bank),每个银行负责管理一组 CQ 条目。每个 HEM 块包含用于特定类型的条目的内存和相关信息。整个结构的设计和组织使得驱动程序能够高效地操作和管理 CQ 表和 HEM。

hns_roce_cq_table

              +----------------------+
              | struct hns_roce_cq_table |
              +----------------------+
              |   struct hns_roce_hem_table table
              |   struct hns_roce_bank bank[HNS_ROCE_CQ_BANK_NUM]
              |   struct mutex bank_mutex
              +----------------------+
                            |
                            |
                            v
              +----------------------+
              |  struct hns_roce_bank |
              +----------------------+
              |   struct ida ida
              |   u32 inuse
              |   u32 min
              |   u32 max
              |   u32 next
              +----------------------+
                            |
                            |
                            v
              +----------------------+
              |  struct hns_roce_hem  |
              +----------------------+
              |   struct list_head chunk_list
              |   refcount_t refcount
              +----------------------+
                            |
                            |
                            v
              +----------------------+
              | struct hns_roce_hem_table |
              +----------------------+
              |   u32 type
              |   unsigned long num_hem
              |   unsigned long obj_size
              |   unsigned long table_chunk_size
              |   struct mutex mutex
              |   struct hns_roce_hem **hem -----> struct hns_roce_hem[] (MTPT/CQC/SRQC等)
              |   u64 **bt_l1 --------------> Level 1 BT entries
              |   dma_addr_t *bt_l1_dma_addr
              |   u64 **bt_l0 --------------> Level 0 BT entries
              |   dma_addr_t *bt_l0_dma_addr
              +----------------------+
                            |
                            |
                            v
              +----------------------+
              |     HEM Entries      |
              +----------------------+

在这个更新后的关系图中,hns_roce_cq_table 结构包含了 hns_roce_hem_table 结构和 hns_roce_bank 数组。

hns_roce_hem_table 结构通过 struct hns_roce_hem **hem 成员引用了多个 hns_roce_hem 结构,
每个 hns_roce_hem 结构对应一个特定类型的 HEM Entries(如MTPT、CQC、SRQC等)。
同时,hns_roce_hem_table 结构中的 u64 **bt_l1 和 u64 **bt_l0 成员表示 Level 1 和 Level 0 BT(Base Table)的条目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值