RDMA ibverbs_API功能说明
设备管理
获取当前活动网卡
返回当前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)
使用方法
- 打开网卡获取ibv_context上下文
- 申请PD保护域
- 注册内存区域
- 创建完成通道
- 设置完成队列(可添加完成事件通知)
- 建链(cm建链和Socket建链)
- 申请QP
- 设置发送工作请求(发数据)
- 设置接收工作请求 (收数据)
- 获取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年中
相关文章:
RDMA ibverbs_API功能说明
设备管理 获取当前活动网卡 返回当前rdma设备列表 struct ibv_device **ibv_get_device_list(int *num_devices);//使用 struct ibv_device **dev_list ibv_get_device_list(NULL);获取网卡名 返回网卡名字字符串:如"mlx5_0",一般通过网卡…...
第15届 蓝桥杯 C++编程青少组中/高级选拔赛 202401 真题答案及解析
第 1 题 【 单选题 】 表达式117 % 16 的结果是( )。 A:0 B:5 C:7 D:10 解析: % 是取模运算符,用于计算两个数相除后的余数。 计算 117 / 16,结果是 7,余数是 5。因此,117 % 16 = 5。答案: B 第 2 题 【 单选题 】 下列选项中,字符数组定义正确的是( …...
【R语言】绘图
一、散点图 散点图也叫X-Y图,它将所有的数据以点的形式展现在坐标系上,用来显示变量之间的相互影响程度。 ggplot2包中用来绘制散点图的函数是geom_point(),但在绘制前需要先用ggplot()函数指定数据集和变量。 下面用mtcars数据集做演示&a…...
Linux基本指令(三)+ 权限
文章目录 基本指令grep打包和压缩zip/unzipLinux和windows压缩包互传tar(重要)Linux和Linux压缩包互传 bcuname -r常用的热键关机外壳程序 知识点打包和压缩 Linux中的权限用户权限 基本指令 grep 1. grep可以过滤文本行 done用于标记循环的结束&#x…...
容器化部署tomcat
容器化部署tomcat 需求在docker容器中部署tomcat,并通过外部机器访问tomcat部署的项目 容器化部署要先装好docker容器(docker安装配置) 实现步骤: 拉取tomcat docker pull tomcat用于列出本地Docker主机上存储的所有镜像 docker images在root目录里面创建tomc…...
vscode软件中引入vant组件
一、vant简介 Vant 是一个轻量、可靠的移动端组件库,于 2017 年开源。 目前 Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本,并由社区团队维护 React 版本和支付宝小程序版本。 官网:介绍 - Vant Weapp 里面的快速上手的教程&a…...
DeepSeek vs ChatGPT:AI 领域的华山论剑,谁主沉浮?
一、引言 在当今科技飞速发展的时代,人工智能(AI)已然成为推动各领域变革的核心力量。而在人工智能的众多分支中,自然语言处理(NLP)因其与人类日常交流和信息处理的紧密联系,成为了最受瞩目的领…...
Ubuntu 22.04 Install deepseek
前言 deepseekAI助手。它具有聊天机器人功能,可以与用户进行自然语言交互,回答问题、提供建议和帮助解决问题。DeepSeek 的特点包括: 强大的语言理解能力:能够理解和生成自然语言,与用户进行流畅的对话。多领域知识&…...
如何将公钥正确添加到服务器的 authorized_keys 文件中以实现免密码 SSH 登录
1. 下载密钥文件 2. RSA 解析 将 id_ed25519 类型的私钥转换为 RSA 类型,要将 ED25519 私钥转换为 RSA 私钥,需要重新生成一个新的 RSA 密钥对。 步骤: 生成新的 RSA 密钥对 使用 ssh-keygen 来生成一个新的 RSA 密钥对。比如,执…...
深入理解设计模式之解释器模式
深入理解设计模式之解释器模式 在软件开发的复杂世界中,我们常常会遇到需要处理特定领域语言的情况。比如在开发一个计算器程序时,需要解析和计算数学表达式;在实现正则表达式功能时,要解析用户输入的正则表达式来匹配文本。这些场景都涉及到对特定语言的解释和执行,而解…...
【WebGL】attribute方式实例化绘制
背景 一般有attribute和uniform两种方式进行实例化绘制 attribute方式实例化 这里需要注意 bufferData和bufferSubData方式的用法顺序和参数 gl.bufferData(target, sizeOrData, usage); sizeOrData(实例化配合bufferSubData 更新数据一般使用这种先)…...
线代[8]|北大丘维声教授《怎样学习线性代数?》(红色字体为博主注释)
文章目录 说明一、线性代数的内容简介二、学习线性代数的用处三、线性代数的特点四、学习线性代数的方法五、更新时间记录 说明 文章中红色字体为博主敲录完丘教授这篇文章后所加,刷到这篇文章的读者在首次阅读应当跳过红色字体,先通读一读文章全文&…...
光明谷推出AT指令版本的蓝牙音箱SOC 开启便捷智能音频开发新体验
前言 在蓝牙音箱市场竞争日益激烈的当下,开发一款性能卓越且易于上手的蓝牙音箱,成为众多厂商追求的目标。而光明谷科技有限公司推出的 AT 指令版本的蓝牙音箱 SOC,无疑为行业带来了全新的解决方案,以其诸多独特卖点,迅…...
C#从入门到精通(34)—如何防止winform程序被同时打开多次
前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在开发上位机软件的过程中,评判一个人软件写的好不好,有一…...
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(input是random input). 文章的framew…...
如何在 ubuntu 上使用 Clash 与 docker 开启代理拉起
如何在 ubuntu 上使用 Clash https://github.com/doreamon-design/clash/releases上面是clash 的地址 clash_2.0.24_linux_386.tar.gz 下载 386 的 如果你的电脑是inter tar -xzvf clash_2.0.24_linux_386.tar.gz 启动 ./clash 然后会在电脑上生成一个config的文件 /home/xxx/…...
MFC开发:如何创建第一个MFC应用程序
文章目录 一、概述二、MFC 的主要组件三、创建一个MFC窗口四、控件绑定消息函数 一、概述 MFC 是微软提供的一个 C 类库,用于简化 Windows 应用程序的开发。它封装了 Windows API,提供面向对象的接口,帮助开发者更高效地创建图形用户界面&am…...
react hook useReducer
useReducer useReducer 是 React 中用于状态管理的 Hook,与 useState 不同,它更适合处理复杂的状态逻辑. const [state, dispatch] useReducer(reducer, initialArg, init?) reducer 是一个处理函数,用于更新状态, reducer 里面包含了两个…...
Java与C语言中取模运算符%的区别对比
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: Java 文章目录 💯前言💯C语言中的取模运算符 %基本行为示例 注意事项示例:负数取模 💯Java中的取模运算符 %基本行为示例 对浮点数的支持示例:浮点数取模 符…...
Zabbix 7.2实操指南:基于OpenEuler系统安装Zabbix 7.2
原文出处:乐维社区 部署环境 openEuler 22.03 LTS PHP 8.0 Apache Mysql 8.0 MySQL数据库 6.0 以上版本需要安装mysql8.0以上版本的数据库(以mysql为例子)。 欧拉系统自带 mysql8.0 的源,无需要安装额外的源。 安装mysql …...
Springboot的简单推荐实现
以springboot 推荐社团招新为例子 使用 Spring Boot 构建社团招新推荐系统,用户注册后选择兴趣,系统根据兴趣推荐社团。 实现包括用户注册、兴趣选择和基于标签匹配的推荐算法。 系统使用 JPA 管理数据库,Spring Security 确保安全࿰…...
如何使用Python快速开发一个带管理系统界面的网站-解析方案
如果你想用 Python 开发一个 管理系统界面 的网站,并且希望界面美观,可以考虑以下几个框架和库: 1. Streamlit(快速、简洁) 适合:数据分析、仪表盘、内部管理系统特点: 写法简单,类…...
深入剖析抽象工厂模式:设计模式中的架构利器
深入剖析抽象工厂模式:设计模式中的架构利器 在软件开发领域,设计模式是解决常见问题的通用方案,而抽象工厂模式作为创建型设计模式的重要一员,在构建复杂软件系统时发挥着关键作用。它为创建一系列相关或相互依赖的对象提供了一…...
面试基础-如何设计一个短链接系统
设计一个每秒处理 100 万个请求(WQPS)的短链系统需要综合考虑性能、可用性和可扩展性。以下是设计方案: 1. 系统架构设计 采用微服务架构,将功能模块化,便于水平扩展和故障隔离。 核心组件: 短链生成服务…...
Win11 24h2 不能正常使用ensp的问题(已解决)
因为Win11 24h2的内核大小更改,目前virtualbox在7.1.4中更新解决了。所以Win11 24H2系统版本无法使用 5.x.xx的virtualbox版本,virtualbox对于这个5.x.xx版本早已停止维护,所以这个以后不会有调整。 对应的报错代码是 virtualbox错误代码&…...
蓝桥杯——按键
一:按键得原理图 二:按键的代码配置 step1 按键原理图对应引脚配置为输入状态 step2 在GPIO中将对应引脚设置为上拉模式 step3 在fun.c中写按键扫描函数 写完后的扫描函数需放在主函数中不断扫描 扫描函数主要通过两个定义变量的值来判断…...
Linux环境基础开发工具的使用(三)
五、Linux项目自动化构建工具-make/Makefile make:是一条指令。 makefile:是一个当前目录下的文件。 第一行:依赖关系。 第二行:依赖方法。 clean是空依赖关系。 编译文件清理 背景 会不会写makefile,从一个侧面说…...
vue中将el-table导出为excel文件
在 Vue Element UI 中,el-table 数据导出 Excel 文件,可以使用 xlsx(SheetJS)库进行处理。以下是详细的实现方法,包括安装依赖、代码示例和优化建议。 1. 安装依赖 首先,安装 xlsx 库: 复制…...
electron提升软件运行权限,以管理员权限运行
大家有任何想法,都可以联系博主沟通。 本系列为实战文章,最终实现的桌面工具软件,获取方式:百度网盘地址:https://pan.baidu.com/s/1yrl0jYpti7QCn8CHBRT2lw?pwd1234 正文开始 前言一、提升electron运行权限的三种方…...
力扣LeetCode: 2506 统计相似字符串对的数目
题目: 给你一个下标从 0 开始的字符串数组 words 。 如果两个字符串由相同的字符组成,则认为这两个字符串 相似 。 例如,"abca" 和 "cba" 相似,因为它们都由字符 a、b、c 组成。然而,"aba…...
