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 …...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...