RDMA ibverbs_API功能说明

news2025/2/23 20:55:05

设备管理

获取当前活动网卡

返回当前rdma设备列表

struct ibv_device **ibv_get_device_list(int *num_devices);

//使用
struct ibv_device **dev_list = ibv_get_device_list(NULL);

获取网卡名

返回网卡名字字符串:如"mlx5_0",一般通过网卡名字确定将要使用的网卡

const char *ibv_get_device_name(struct ibv_device *device);

打开网卡

初始化一个网卡,返回该网卡的ibv_context上下文,后续对于该网卡的操作都基于这个上下文。

//初始化使用网卡
struct ibv_context *ibv_open_device(struct ibv_device *device);

//释放网卡
int ibv_close_device(struct ibv_context *context);

资源初始化

所有基于网卡的配置,都是以该网卡的ibv_context上下文进行操作

申请PD保护域

PD保护域用来注册内存区域(MR)、创建队列等

struct ibv_pd *ibv_alloc_pd(struct ibv_context *context);

注册内存区域

注册可以本地/远程读写的内存区域

这个内存区域归属于PD保护域

struct ibv_mr *ibv_reg_mr(struct ibv_pd *pd, void *addr, size_t length, int access);

enum ibv_access_flags {
	IBV_ACCESS_LOCAL_WRITE		= 1,
	IBV_ACCESS_REMOTE_WRITE		= (1<<1),
	IBV_ACCESS_REMOTE_READ		= (1<<2),
	IBV_ACCESS_REMOTE_ATOMIC	= (1<<3),
	IBV_ACCESS_MW_BIND		= (1<<4),
	IBV_ACCESS_ZERO_BASED		= (1<<5),
	IBV_ACCESS_ON_DEMAND		= (1<<6),
	IBV_ACCESS_HUGETLB		= (1<<7),
	IBV_ACCESS_RELAXED_ORDERING	= IBV_ACCESS_OPTIONAL_FIRST,
};

参数addr, 本地申请好的内存地址, length空间大小, access内存读写flag

创建完成通道

完成通道用于存放完成队列,I/O可由event事件驱动

struct ibv_comp_channel *ibv_create_comp_channel(struct ibv_context *context);

创建完成队列

完成队列绑定到完成通道,

参数cqe是队列大小

/**
 * ibv_create_cq - Create a completion queue
 * @context - Context CQ will be attached to
 * @cqe - Minimum number of entries required for CQ
 * @cq_context - Consumer-supplied context returned for completion events
 * @channel - Completion channel where completion events will be queued.
 *     May be NULL if completion events will not be used.
 * @comp_vector - Completion vector used to signal completion events.
 *     Must be >= 0 and < context->num_comp_vectors.
 */
struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,
			     void *cq_context,
			     struct ibv_comp_channel *channel,
			     int comp_vector);

// 一般用法
struct ibv_cq *cq = ibv_create_cq(context, num, NULL, channel, 0);

//还有ibv_create_cq_ex扩展完成队列,具备更多功能 To Do.

创建QueuePair

根据qp_init_attr参数设置QP,返回ibv_qp指针。

struct ibv_qp *ibv_create_qp(struct ibv_pd *pd,
			     struct ibv_qp_init_attr *qp_init_attr);

// 查询QP属性,创建后可用其查询确认相关属性
int ibv_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,
		 int attr_mask,
		 struct ibv_qp_init_attr *init_attr);

qp_init_attr属性

其中发送/接收完成队列提前设置为已创建的完成队列。

struct ibv_qp_init_attr {
	void		       *qp_context;  
	struct ibv_cq	       *send_cq;  // 发送完成队列
	struct ibv_cq	       *recv_cq;  // 接收完成队列
	struct ibv_srq	       *srq;      // 共享接收队列, 多个的QP共享一个队列
	struct ibv_qp_cap	cap;		  // QP容量属性
	enum ibv_qp_type	qp_type;      // QP类型,可靠RC,不可靠UC,UD等
	int			sq_sig_all;
};

struct ibv_qp_cap {
	uint32_t		max_send_wr;   //最大发送工作请求数量
	uint32_t		max_recv_wr;   //最大接收工作请求数量
	uint32_t		max_send_sge;  //一次发送最大的数据块数量
	uint32_t		max_recv_sge;  //一次接收最大的数据块数量
	uint32_t		max_inline_data; //内联数据大小,小数据和控制信令使用
};

收发API

建链

CM建链,使用CM VERBS,也是基于ibverbs 特殊的控制QP实现。

Socket建链,使用TCP/IP Socket建立链接

建链的本质是交换彼此的 LID, QPN,PSN,GID。

这个可以单独开一篇文档描述(To Do).

修改QueuePair

修改QP的相关状态属性

int ibv_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,
		  int attr_mask);

在创建QP以后,通过该方法初始化QP

qp_state设置为INIT, pkey_index,access_flags设为0

struct ibv_qp_attr attr = {
    .qp_state        = IBV_QPS_INIT,
    .pkey_index      = 0,
    .port_num        = 1,
    .qp_access_flags = 0
};

if (ibv_modify_qp(ctx->qp, &attr,
                  IBV_QP_STATE              |
                  IBV_QP_PKEY_INDEX         |
                  IBV_QP_PORT               |
                  IBV_QP_ACCESS_FLAGS)) 

在建链交互之后,获取到对端QPN, PSN,LID, GID,通过该函数设置这些参数,建立QP连接。

第一步先设置远端参数,进入RTR状态

RTR状态以后可以接收数据

	struct ibv_qp_attr attr = {
		.qp_state		= IBV_QPS_RTR,
		.path_mtu		= mtu,
		.dest_qp_num		= dest_qpn,
		.rq_psn			= dest_psn,
		.max_dest_rd_atomic	= 1,
		.min_rnr_timer		= 12,
		.ah_attr		= {
			.is_global	= 0,
			.dlid		= dest_lid,
			.sl		= sl,
			.src_path_bits	= 0,
			.port_num	= port
		}
	};

	if (dest->gid.global.interface_id) {
		attr.ah_attr.is_global = 1;
		attr.ah_attr.grh.hop_limit = 1;
		attr.ah_attr.grh.dgid = dest->gid;
		attr.ah_attr.grh.sgid_index = sgid_idx;
	}
	if (ibv_modify_qp(ctx->qp, &attr,
			  IBV_QP_STATE              |
			  IBV_QP_AV                 |
			  IBV_QP_PATH_MTU           |
			  IBV_QP_DEST_QPN           |
			  IBV_QP_RQ_PSN             |
			  IBV_QP_MAX_DEST_RD_ATOMIC |
			  IBV_QP_MIN_RNR_TIMER)) {
		fprintf(stderr, "Failed to modify QP to RTR\n");
		return 1;
	}

第二步设置RTS状态

RTS状态后可以发送数据

	attr.qp_state	    = IBV_QPS_RTS;
	attr.timeout	    = 14;
	attr.retry_cnt	    = 7;
	attr.rnr_retry	    = 7;
	attr.sq_psn	    = my_psn;
	attr.max_rd_atomic  = 1;
	if (ibv_modify_qp(ctx->qp, &attr,
			  IBV_QP_STATE              |
			  IBV_QP_TIMEOUT            |
			  IBV_QP_RETRY_CNT          |
			  IBV_QP_RNR_RETRY          |
			  IBV_QP_SQ_PSN             |
			  IBV_QP_MAX_QP_RD_ATOMIC)) {
		fprintf(stderr, "Failed to modify QP to RTS\n");
		return 1;
	}

设置接收工作请求

将工作请求放入接收队列,用于接收数据

参数 wr是工作请求链表的头,每个工作请求包含接收内存区域,数据长度等

参数bad_wr用于返回发生错误的工作请求

/**
 * ibv_post_recv - Post a list of work requests to a receive queue.
 */
static inline int ibv_post_recv(struct ibv_qp *qp, struct ibv_recv_wr *wr,
				struct ibv_recv_wr **bad_wr)
{
	return qp->context->ops.post_recv(qp, wr, bad_wr);
}

用法示例:

// 数据块列表
struct ibv_sge list = {
    .addr	= buf,      // 接收数据内存地址
    .length = size,     // 数据大小
    .lkey	= mr->lkey  // 内存注册区域的lkey
};
// 接收工作请求实例
struct ibv_recv_wr wr = {
    .wr_id	    = 1,     //工作请求id
    .sg_list    = &list, //使用数据块列表
    .num_sge    = 1,     //列表中数据块个数
};
struct ibv_recv_wr *bad_wr; // 接收错误请求

// 一般可以重复执行ibv_post_recv,从而塞入一列工作请求
ibv_post_recv(qp, &wr, &bad_wr);

设置发送工作请求

设置一组工作请求wr,工作请求中包含传输数据块

/**
 * ibv_post_send - Post a list of work requests to a send queue.
 *
 * If IBV_SEND_INLINE flag is set, the data buffers can be reused
 * immediately after the call returns.
 */
static inline int ibv_post_send(struct ibv_qp *qp, struct ibv_send_wr *wr,
				struct ibv_send_wr **bad_wr)
{
	return qp->context->ops.post_send(qp, wr, bad_wr);
}

发送示例

struct ibv_sge list = {
    .addr	= buf,
    .length = size,
    .lkey	= lkey
};
struct ibv_send_wr wr = {
    .wr_id	    = 2,
    .sg_list    = &list,
    .num_sge    = 1,
    .opcode     = IBV_WR_SEND,
    .send_flags = ctx->send_flags,
};
struct ibv_send_wr *bad_wr;
ibv_post_send(ctx->qp, &wr, &bad_wr);

设置CQ通知

设置完成队列请求通知,当有完成消息到达完成队列,会触发event事件。

/**
 * ibv_req_notify_cq - Request completion notification on a CQ.  An
 *   event will be added to the completion channel associated with the
 *   CQ when an entry is added to the CQ.
 * @cq: The completion queue to request notification for.
 * @solicited_only: If non-zero, an event will be generated only for
 *   the next solicited CQ entry.  If zero, any CQ entry, solicited or
 *   not, will generate an event.
 */
static inline int ibv_req_notify_cq(struct ibv_cq *cq, int solicited_only)
{
	return cq->context->ops.req_notify_cq(cq, solicited_only);
}

获取CQ事件

获取完成通道内的完成事件,后2参数返回CQ和CQ上下文

该函数是阻塞的,直到有完成事件才能调用成功

/**
 * ibv_get_cq_event - Read next CQ event
 * @channel: Channel to get next event from.
 * @cq: Used to return pointer to CQ.
 * @cq_context: Used to return consumer-supplied CQ context.
 *
 * All completion events returned by ibv_get_cq_event() must
 * eventually be acknowledged with ibv_ack_cq_events().
 */
int ibv_get_cq_event(struct ibv_comp_channel *channel,
		     struct ibv_cq **cq, void **cq_context);

获取接口属性

获取接口属性,判断接口工作状态

/**
 * ibv_query_port - Get port properties
 */
int ibv_query_port(struct ibv_context *context, uint8_t port_num,
		   struct _compat_ibv_port_attr *port_attr);

//获取的属性
struct ibv_port_attr {
	enum ibv_port_state	state;
	enum ibv_mtu		max_mtu;
	enum ibv_mtu		active_mtu;
	int			gid_tbl_len;
	uint32_t		port_cap_flags;
	uint32_t		max_msg_sz;
	uint32_t		bad_pkey_cntr;
	uint32_t		qkey_viol_cntr;
	uint16_t		pkey_tbl_len;
	uint16_t		lid;
	uint16_t		sm_lid;
	uint8_t			lmc;
	uint8_t			max_vl_num;
	uint8_t			sm_sl;
	uint8_t			subnet_timeout;
	uint8_t			init_type_reply;
	uint8_t			active_width;
	uint8_t			active_speed;
	uint8_t			phys_state;
	uint8_t			link_layer;
	uint8_t			flags;
	uint16_t		port_cap_flags2;
};

获取接口GID

获取的是接口的gid表项,其中index可以为0,1,2

对于CX5网卡

index 0 时, GID为 本地IPv6地址

index 1 时, GID为本地IPv4地址构成

具体可因网卡有所不同,如Soft-Roce网卡,index 2 对应的是IPv4地址构成的GID

/**
 * ibv_query_gid - Get a GID table entry
 */
int ibv_query_gid(struct ibv_context *context, uint8_t port_num,
		  int index, union ibv_gid *gid);

获取CQ消息

在获得CQ事件通知之后,执行该函数获取完成消息

/**
 * ibv_poll_cq - Poll a CQ for work completions
 * @cq:the CQ being polled
 * @num_entries:maximum number of completions to return
 * @wc:array of at least @num_entries of &struct ibv_wc where completions
 *   will be returned
 *
 * Poll a CQ for (possibly multiple) completions.  If the return value
 * is < 0, an error occurred.  If the return value is >= 0, it is the
 * number of completions returned.  If the return value is
 * non-negative and strictly less than num_entries, then the CQ was
 * emptied.
 */
static inline int ibv_poll_cq(struct ibv_cq *cq, int num_entries, struct ibv_wc *wc)
{
	return cq->context->ops.poll_cq(cq, num_entries, wc);
}


根据返回的wc中的信息可以根据wr_id关联到设置发送/接收的工作请求


struct ibv_wc {
	uint64_t		wr_id;
	enum ibv_wc_status	status;
	enum ibv_wc_opcode	opcode;
	uint32_t		vendor_err;
	uint32_t		byte_len;
	/* When (wc_flags & IBV_WC_WITH_IMM): Immediate data in network byte order.
	 * When (wc_flags & IBV_WC_WITH_INV): Stores the invalidated rkey.
	 */
	union {
		__be32		imm_data;
		uint32_t	invalidated_rkey;
	};
	uint32_t		qp_num;
	uint32_t		src_qp;
	unsigned int		wc_flags;
	uint16_t		pkey_index;
	uint16_t		slid;
	uint8_t			sl;
	uint8_t			dlid_path_bits;
};

概念与关系

一个接口可以申请一个ibv_context

一个ibv_context上有多个PD保护域

一个ibv_context上有多个完成通道

一个完成通道只能绑定一个完成队列

一个PD保护域上可以有多个MR(memory region)内存区域

一个PD保护域上可以有多个QP(Queue Pair)

使用方法

  1. 打开网卡获取ibv_context上下文
  2. 申请PD保护域
  3. 注册内存区域
  4. 创建完成通道
  5. 设置完成队列(可添加完成事件通知)
  6. 建链(cm建链和Socket建链)
  7. 申请QP
  8. 设置发送工作请求(发数据)
  9. 设置接收工作请求 (收数据)
  10. 获取CQ消息 (发送数据的结果 和 接收到的数据)

发送数据

内存注册区域是可以用来设置发送和接收数据的缓冲区,内存区域(MR)有一个lkey字段指明该区域。

在设置发送工作请求时,使用的ibv_sge通过.lkey指定MR, 其addr字段指向MR内的地址空间。

待发送的数据都是先放入MR中的一段内存,ibv_sge.addr指向其中的内存地址,将ibv_sge封装进工作请求ibv_send_wr,塞入发送队列。

接收数据

在设置接收工作请求时,使用的ibv_sge通过.lkey指定MR, 其addr字段指向MR内的地址空间,该空间用于接收数据。

准备接收数据时,讲ibv_sge封装进工作请求ibv_recv_wr, 塞入接收队列

获取CQ消息,接收到数据后,可以根据返回的ibv_wc中的wr_id找到具体的ibv_recv_wr,进而获取ibv_sge指向地址空间,获得该数据。

参考代码

https://github.com/linux-rdma/rdma-core/blob/master/libibverbs/examples/rc_pingpong.c

— 此文写于2023年中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2304077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【R语言】绘图

一、散点图 散点图也叫X-Y图&#xff0c;它将所有的数据以点的形式展现在坐标系上&#xff0c;用来显示变量之间的相互影响程度。 ggplot2包中用来绘制散点图的函数是geom_point()&#xff0c;但在绘制前需要先用ggplot()函数指定数据集和变量。 下面用mtcars数据集做演示&a…

Linux基本指令(三)+ 权限

文章目录 基本指令grep打包和压缩zip/unzipLinux和windows压缩包互传tar&#xff08;重要&#xff09;Linux和Linux压缩包互传 bcuname -r常用的热键关机外壳程序 知识点打包和压缩 Linux中的权限用户权限 基本指令 grep 1. grep可以过滤文本行 done用于标记循环的结束&#x…

容器化部署tomcat

容器化部署tomcat 需求在docker容器中部署tomcat,并通过外部机器访问tomcat部署的项目 容器化部署要先装好docker容器(docker安装配置) 实现步骤&#xff1a; 拉取tomcat docker pull tomcat用于列出本地Docker主机上存储的所有镜像 docker images在root目录里面创建tomc…

vscode软件中引入vant组件

一、vant简介 Vant 是一个轻量、可靠的移动端组件库&#xff0c;于 2017 年开源。 目前 Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本&#xff0c;并由社区团队维护 React 版本和支付宝小程序版本。 官网&#xff1a;介绍 - Vant Weapp 里面的快速上手的教程&a…

DeepSeek vs ChatGPT:AI 领域的华山论剑,谁主沉浮?

一、引言 在当今科技飞速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;已然成为推动各领域变革的核心力量。而在人工智能的众多分支中&#xff0c;自然语言处理&#xff08;NLP&#xff09;因其与人类日常交流和信息处理的紧密联系&#xff0c;成为了最受瞩目的领…

Ubuntu 22.04 Install deepseek

前言 deepseekAI助手。它具有聊天机器人功能&#xff0c;可以与用户进行自然语言交互&#xff0c;回答问题、提供建议和帮助解决问题。DeepSeek 的特点包括&#xff1a; 强大的语言理解能力&#xff1a;能够理解和生成自然语言&#xff0c;与用户进行流畅的对话。多领域知识&…

如何将公钥正确添加到服务器的 authorized_keys 文件中以实现免密码 SSH 登录

1. 下载密钥文件 2. RSA 解析 将 id_ed25519 类型的私钥转换为 RSA 类型&#xff0c;要将 ED25519 私钥转换为 RSA 私钥&#xff0c;需要重新生成一个新的 RSA 密钥对。 步骤&#xff1a; 生成新的 RSA 密钥对 使用 ssh-keygen 来生成一个新的 RSA 密钥对。比如&#xff0c;执…

光明谷推出AT指令版本的蓝牙音箱SOC 开启便捷智能音频开发新体验

前言 在蓝牙音箱市场竞争日益激烈的当下&#xff0c;开发一款性能卓越且易于上手的蓝牙音箱&#xff0c;成为众多厂商追求的目标。而光明谷科技有限公司推出的 AT 指令版本的蓝牙音箱 SOC&#xff0c;无疑为行业带来了全新的解决方案&#xff0c;以其诸多独特卖点&#xff0c;迅…

TIP: Flex-DLD

Article: Flex-DLD: Deep Low-Rank Decomposition Model With Flexible Priors for Hyperspectral Image Denoising and Restoration, 2024 TIP. 文章的主要思想是用network来学low-rank decomposition的两个matrix&#xff08;input是random input&#xff09;. 文章的framew…

MFC开发:如何创建第一个MFC应用程序

文章目录 一、概述二、MFC 的主要组件三、创建一个MFC窗口四、控件绑定消息函数 一、概述 MFC 是微软提供的一个 C 类库&#xff0c;用于简化 Windows 应用程序的开发。它封装了 Windows API&#xff0c;提供面向对象的接口&#xff0c;帮助开发者更高效地创建图形用户界面&am…

Java与C语言中取模运算符%的区别对比

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: Java 文章目录 &#x1f4af;前言&#x1f4af;C语言中的取模运算符 %基本行为示例 注意事项示例&#xff1a;负数取模 &#x1f4af;Java中的取模运算符 %基本行为示例 对浮点数的支持示例&#xff1a;浮点数取模 符…

Zabbix 7.2实操指南:基于OpenEuler系统安装Zabbix 7.2

原文出处&#xff1a;乐维社区 部署环境 openEuler 22.03 LTS PHP 8.0 Apache Mysql 8.0 MySQL数据库 6.0 以上版本需要安装mysql8.0以上版本的数据库&#xff08;以mysql为例子&#xff09;。 欧拉系统自带 mysql8.0 的源&#xff0c;无需要安装额外的源。 安装mysql …

Win11 24h2 不能正常使用ensp的问题(已解决)

因为Win11 24h2的内核大小更改&#xff0c;目前virtualbox在7.1.4中更新解决了。所以Win11 24H2系统版本无法使用 5.x.xx的virtualbox版本&#xff0c;virtualbox对于这个5.x.xx版本早已停止维护&#xff0c;所以这个以后不会有调整。 对应的报错代码是 virtualbox错误代码&…

蓝桥杯——按键

一&#xff1a;按键得原理图 二&#xff1a;按键的代码配置 step1 按键原理图对应引脚配置为输入状态 step2 在GPIO中将对应引脚设置为上拉模式 step3 在fun.c中写按键扫描函数 写完后的扫描函数需放在主函数中不断扫描 扫描函数主要通过两个定义变量的值来判断&#xf…

Linux环境基础开发工具的使用(三)

五、Linux项目自动化构建工具-make/Makefile make&#xff1a;是一条指令。 makefile&#xff1a;是一个当前目录下的文件。 第一行&#xff1a;依赖关系。 第二行&#xff1a;依赖方法。 clean是空依赖关系。 编译文件清理 背景 会不会写makefile&#xff0c;从一个侧面说…

electron提升软件运行权限,以管理员权限运行

大家有任何想法&#xff0c;都可以联系博主沟通。 本系列为实战文章&#xff0c;最终实现的桌面工具软件&#xff0c;获取方式&#xff1a;百度网盘地址&#xff1a;https://pan.baidu.com/s/1yrl0jYpti7QCn8CHBRT2lw?pwd1234 正文开始 前言一、提升electron运行权限的三种方…

安科瑞能源物联网平台助力企业实现绿色低碳转型

安科瑞顾强 随着全球能源结构的转型和“双碳”目标的推进&#xff0c;能源管理正朝着智能化、数字化的方向快速发展。安科瑞电气股份有限公司推出的微电网智慧能源管理平台&#xff08;EMS 3.0&#xff09;&#xff0c;正是这一趋势下的创新解决方案。该平台集成了物联网&…

Spring Boot 中使用 @Transactional 注解配置事务管理

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务&#xff1b;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污…

动态链接器(九):.init和.init_array

ELF文件中的.init和.init_array段是程序初始化阶段的重要组成部分&#xff0c;用于在main函数执行前完成必要的初始化操作。 1 .init段和.init_array 段 1.1 作用 .init段包含编译器生成的初始化代码&#xff0c;通常由运行时环境&#xff08;如C标准库的启动例程&#xff0…

RT-Thread+STM32L475VET6——TF 卡文件系统

文章目录 前言一、板载资源二、具体步骤1.打开CubeMX进行USB配置1.1 使用外部高速时钟&#xff0c;并修改时钟树1.2 打开SPI1&#xff0c;参数默认即可(SPI根据自己需求调整&#xff09;1.3 打开串口&#xff0c;参数默认1.4 生成工程 2.配置SPI2.1 打开SPI驱动2.2 声明使用SPI…