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",一般通过网卡…...
【C++语言】string 类
一、为什么要学习 string 类 C语言中,字符串是以 “\0” 结尾的一些字符的集合,为了操作方便,C标准库中提供了一些 str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合 OOP 的思想,而且底层空间需…...
快速上手gdb/cgdb
Linux调试器-gdb使用 1.背景2.调试原理、技巧命令2.1指令2.2 本质2.3 技巧 1.背景 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g…...
《养生》(二)
一、基础生活调整 1.作息规律 固定每天7-8小时睡眠,尽量22:30前入睡,晨起后拉开窗帘晒太阳5分钟,调节生物钟 2.饮食优化 三餐定时,每餐细嚼慢咽20次以上,优先吃蔬菜和蛋白质(如鸡蛋、豆腐&#x…...
JAVA:集成 Drools 业务规则引擎的技术指南
1、简述 Drools 是一个强大的业务规则引擎,适用于需要动态决策或规则管理的场景。它允许开发人员将业务逻辑与应用代码分离,使得业务人员可以通过规则文件维护和更新规则,而无需修改应用代码。本文将介绍 Drools 的基本概念、配置方式&#…...
GeoHD - 一种用于智慧城市热点探测的Python工具箱
GeoHD - 一种用于智慧城市热点探测的Python工具箱 详细原理请参考:Yan, Y., Quan, W., Wang, H., 2024. A data‐driven adaptive geospatial hotspot detection approach in smart cities. Trans. GIS tgis.13137. 代码下载:下载 1. 简介 在城市数据…...
记一次Ngnix配置
记一次Ngnix配置 配置Ngnix配置防火墙 假设一个服务器中有一个公网IP、一个内网IP,另外已经部署好后台服务的接口地址为http://内网ip:8088。 配置Ngnix 找到Ngnix的配置文件,通过在Ngnix的安装路径下的 \conf\nginx.conf 文件。 worker_processes 1;…...
2024年国赛高教杯数学建模C题农作物的种植策略解题全过程文档及程序
2024年国赛高教杯数学建模 C题 农作物的种植策略 原题再现 根据乡村的实际情况,充分利用有限的耕地资源,因地制宜,发展有机种植产业,对乡村经济的可持续发展具有重要的现实意义。选择适宜的农作物,优化种植策略&…...
java基础语知识(8)
类之间的关系 在类之间,最常见的关系有: 依赖(“uses-a”);聚合(“has-a”);继承(“is-a”)。 依赖:一种使用关系,即一个类的实现需要另一个类的协助&#x…...
室内定位精度方案对比
室内定位精度方案对比:成本、开发难度与精度的权衡 索引 引言 Wi-Fi 定位方案 定位原理 成本分析 开发难度 定位精度 蓝牙定位方案 定位原理 成本分析 开发难度 定位精度 超宽带(UWB)定位方案 定位原理 成本分析 开发难度 定…...
Pytorch深度学习教程_5_编写第一个神经网络
欢迎来到《pytorch深度学习教程》系列的第五篇!在前面的四篇中,我们已经介绍了Python、numpy及pytorch的基本使用,并在上一个教程中介绍了梯度。今天,我们将探索神经网络,对于神经网络进行概述并进行简单的实践学习 欢…...
ImportError: cannot import name ‘FixtureDef‘ from ‘pytest‘
错误信息表明 pytest 在尝试导入 FixtureDef 时出现了问题。通常是由于 pytest 版本不兼容 或 插件版本冲突 引起的。以下是详细的排查步骤和解决方案: 1. 检查 pytest 版本 首先,确认当前安装的 pytest 版本。某些插件可能需要特定版本的 pytest 才能…...
改BUG:Mock测试的时候,when失效
问题再现: 这里我写了一测试用户注册接口的测试类,并通过when模拟下层的服务,但实际上when并没有奏效,还是走了真实的service层的逻辑。 package cn.ac.evo.review.test;import cn.ac.evo.review.user.UserMainApplication; imp…...
【自动化脚本工具】AutoHotkey (Windows)
目录 1. 介绍AutoHotkey2. 功能脚本集锦2.1 桌面键盘显示 1. 介绍AutoHotkey 支持Windows安装使用,下载地址为:https://www.autohotkey.com/ 2. 功能脚本集锦 2.1 桌面键盘显示 便于练习键盘盲打 脚本地址:https://blog.csdn.net/weixin_6…...
专题--Linux体系
Linux体系结构相关| ProcessOn免费在线作图,在线流程图,在线思维导图 ProcessOn是一个在线协作绘图平台,为用户提供强大、易用的作图工具!支持在线创作流程图、思维导图、组织结构图、网络拓扑图、BPMN、UML图、UI界面原型设计、iOS界面原型设计等。同时…...
【DeepSeek】Mac m1电脑部署DeepSeek
一、电脑配置 个人电脑配置 二、安装ollama 简介:Ollama 是一个强大的开源框架,是一个为本地运行大型语言模型而设计的工具,它帮助用户快速在本地运行大模型,通过简单的安装指令,可以让用户执行一条命令就在本地运…...
Spring AI + Ollama 实现调用DeepSeek-R1模型API
一、前言 随着人工智能技术的飞速发展,大语言模型(LLM)在各个领域的应用越来越广泛。DeepSeek 作为一款备受瞩目的国产大语言模型,凭借其强大的自然语言处理能力和丰富的知识储备,迅速成为业界关注的焦点。无论是文本生…...
如何在本地和服务器新建Redis用户和密码
文章目录 一. Redis安装二. 新建Redis用户,测试连接2.1 本地数据库2.2 线上数据库2.2.1 安装和配置2.2.2 测试连接 三. 配置四. 分布式 一. Redis安装 Redis安装 可以设置开机自动启动,也可以在去查看系统服务,按[win R],输入命…...
jmeter接口测试(一)
一、什么是接口测试?为什么要做接口测试? 接口测试:就是测试项目和项目之间,模块和模块之间,组件和组件之间的数据交互和权限鉴定(鉴权)。 前后端分离:前后端联调。mock模拟&#x…...
Java-11
淘天集团2025届春季校园招聘在线笔试-研发 1。设有一个顺序共享栈storageArray[70],其中栈X的栈顶指针top1的初值为-1,栈Y的栈顶指针top2的初值为70,通过不断进行入栈操作,直到storageArray数组已满,此时top1 top2 …...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
