Linux Soft-RoCE implementation (zz)
Linux Soft-RoCE implementation
首页分类标签留言关于订阅2017-11-08 | 分类 Network | 标签 RDMA RoCE Linux-RDMA
队列初始化
libRXE (user space library)
ibv_create_qp
|--- rxe_create_qp
|--- ibv_cmd_create_qp
- ibv_create_qp
LATEST_SYMVER_FUNC(ibv_create_qp, 1_1, "IBVERBS_1.1",
struct ibv_qp *,
struct ibv_pd *pd,
struct ibv_qp_init_attr *qp_init_attr)
{
struct ibv_qp *qp = pd->context->ops.create_qp(pd, qp_init_attr); ///rxe_ctx_ops
///..
}
- rxe_create_qp
static struct ibv_qp *rxe_create_qp(struct ibv_pd *pd,
struct ibv_qp_init_attr *attr)
{
struct ibv_create_qp cmd;
struct rxe_create_qp_resp resp;
struct rxe_qp *qp;
int ret;
////..
ret = ibv_cmd_create_qp(pd, &qp->ibv_qp, attr, &cmd, sizeof cmd,
&resp.ibv_resp, sizeof resp); /// ibv_create_qp CMD, to kernel
///...
qp->sq.max_sge = attr->cap.max_send_sge;
qp->sq.max_inline = attr->cap.max_inline_data;
qp->sq.queue = mmap(NULL, resp.sq_mi.size, PROT_READ | PROT_WRITE,
MAP_SHARED,
pd->context->cmd_fd, resp.sq_mi.offset); ///mmap,参考rxe_mmap
ibv_context->cmd_fd
指向对应的ibv_device
,由ibv_open_device
返回。
ibv_cmd_create_qp
会通过ibv_context->cmd_fd
给内核发送IB_USER_VERBS_CMD_CREATE_QP
命令,参考libiverbs@ibv_cmd_create_qp.
对应的内核write
函数为ib_uverbs_write
:
///drivers/infiniband/core/uverbs_main.c
static const struct file_operations uverbs_fops = {
.owner = THIS_MODULE,
.write = ib_uverbs_write,
.open = ib_uverbs_open,
.release = ib_uverbs_close,
.llseek = no_llseek,
};
- ibv_open_device
///libibverbs/device.c
LATEST_SYMVER_FUNC(ibv_open_device, 1_1, "IBVERBS_1.1",
struct ibv_context *,
struct ibv_device *device)
{
struct verbs_device *verbs_device = verbs_get_device(device);
char *devpath;
int cmd_fd, ret;
struct ibv_context *context;
struct verbs_context *context_ex;
if (asprintf(&devpath, "/dev/infiniband/%s", device->dev_name) < 0)
return NULL;
/*
* We'll only be doing writes, but we need O_RDWR in case the
* provider needs to mmap() the file.
*/
cmd_fd = open(devpath, O_RDWR | O_CLOEXEC); /// /dev/infiniband/uverbs0
free(devpath);
if (cmd_fd < 0)
return NULL;
if (!verbs_device->ops->init_context) {
context = verbs_device->ops->alloc_context(device, cmd_fd); ///rxe_alloc_context, rxe_dev_ops
if (!context)
goto err;
}
///...
context->device = device;
context->cmd_fd = cmd_fd;
pthread_mutex_init(&context->mutex, NULL);
ibverbs_device_hold(device);
return context;
///...
}
kernel (rdma_rxe module)
- ib_uverbs_create_qp
IB_USER_VERBS_CMD_CREATE_QP
的处理函数为函数ib_uverbs_create_qp.
ib_uverbs_write
|--- ib_uverbs_create_qp
|--- create_qp
|--- ib_device->create_qp
|--- rxe_create_qp
create_qp
调用ib_device->create_qp
,对于RXE, 为函数rxe_create_qp
, 参考rxe_register_device
.
- rxe_create_qp
rxe_create_qp
|--- rxe_qp_from_init
|--- rxe_qp_init_req
rxe_qp_from_init
完成发送队列和接收队列的初始化。
- rxe_qp_init_req
rxe_qp_init_req
主要做以下一些事情:
创建对应的UDP socket
调用
rxe_queue_init
完成发送队列的初始化.初始化对应的tasklet
- rxe_queue_init
rxe_queue_init
给队列分配内存空间:
struct rxe_queue *rxe_queue_init(struct rxe_dev *rxe,
int *num_elem,
unsigned int elem_size)
{
struct rxe_queue *q;
size_t buf_size;
unsigned int num_slots;
///...
buf_size = sizeof(struct rxe_queue_buf) + num_slots * elem_size;
q->buf = vmalloc_user(buf_size);
///...
}
rxe_queue->buf
指向的内存缓冲区,由rxe_mmap
映射到用户空间,队列的element
对应数据结构struct rxe_send_wqe
.
libiverbs API
调用ibv_post_send
时,会将对应的struct rxe_send_wqe
加入到该队列,参考rdma-core@post_one_send
.
- rxe_mmap
/**
* rxe_mmap - create a new mmap region
* @context: the IB user context of the process making the mmap() call
* @vma: the VMA to be initialized
* Return zero if the mmap is OK. Otherwise, return an errno.
*/
int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
{
struct rxe_dev *rxe = to_rdev(context->device);
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
struct rxe_mmap_info *ip, *pp;
///...
found_it:
list_del_init(&ip->pending_mmaps);
spin_unlock_bh(&rxe->pending_lock);
ret = remap_vmalloc_range(vma, ip->obj, 0);
if (ret) {
pr_err("rxe: err %d from remap_vmalloc_range\n", ret);
goto done;
}
vma->vm_ops = &rxe_vm_ops;
vma->vm_private_data = ip;
rxe_vma_open(vma);
///...
}
发送数据
libRXE
rxe_post_send
会将struct ibv_send_wr
转成struct rxe_send_wqe
,并加入到发送队列rxe_qp->rq
,然后通过cmd_fd
给RXE内核模块发送IB_USER_VERBS_CMD_POST_SEND
命令:
///providers/rxe/rxe.c
/* this API does not make a distinction between
restartable and non-restartable errors */
static int rxe_post_send(struct ibv_qp *ibqp,
struct ibv_send_wr *wr_list,
struct ibv_send_wr **bad_wr)
{
int rc = 0;
int err;
struct rxe_qp *qp = to_rqp(ibqp);/// ibv_qp -> rxe_qp
struct rxe_wq *sq = &qp->sq;
if (!bad_wr)
return EINVAL;
*bad_wr = NULL;
if (!sq || !wr_list || !sq->queue)
return EINVAL;
pthread_spin_lock(&sq->lock);
while (wr_list) {
rc = post_one_send(qp, sq, wr_list); /// ibv_send_wr -> rxe_send_wqe, enqueue
if (rc) {
*bad_wr = wr_list;
break;
}
wr_list = wr_list->next;
}
pthread_spin_unlock(&sq->lock);
err = post_send_db(ibqp); /// IB_USER_VERBS_CMD_POST_SEND cmd
return err ? err : rc;
}
kernel
处理的IB_USER_VERBS_CMD_POST_SEND
的函数为ib_uverbs_post_send
:
ib_uverbs_post_send
-> ib_device->post_send
-> rxe_post_send
-> rxe_requester
-> ip_local_out
。
- rxe_post_send
static int rxe_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
struct ib_send_wr **bad_wr)
{
int err = 0;
struct rxe_qp *qp = to_rqp(ibqp); ///ib_qp -> rxe_qp
///...
/*
* Must sched in case of GSI QP because ib_send_mad() hold irq lock,
* and the requester call ip_local_out_sk() that takes spin_lock_bh.
*/
must_sched = (qp_type(qp) == IB_QPT_GSI) ||
(queue_count(qp->sq.queue) > 1);
rxe_run_task(&qp->req.task, must_sched); /// to rxe_requester
return err;
}
- rxe_requester
rxe_requester
从rxe_qp
队列取出rxe_send_wqe
,生成对应的skb_buff
,然后下发给对应的rxe_dev
设备:
///sw/rxe/rxe_req.c
int rxe_requester(void *arg)
{
struct rxe_qp *qp = (struct rxe_qp *)arg;
struct rxe_pkt_info pkt;
struct sk_buff *skb;
struct rxe_send_wqe *wqe;
///...
wqe = req_next_wqe(qp); /// get rxe_send_wqe
///...
/// rxe_send_wqe -> skb
skb = init_req_packet(qp, wqe, opcode, payload, &pkt);
///...
ret = rxe_xmit_packet(to_rdev(qp->ibqp.device), qp, &pkt, skb);
///...
}
static inline int rxe_xmit_packet(struct rxe_dev *rxe, struct rxe_qp *qp,
struct rxe_pkt_info *pkt, struct sk_buff *skb)
{
///...
if (pkt->mask & RXE_LOOPBACK_MASK) {
memcpy(SKB_TO_PKT(skb), pkt, sizeof(*pkt));
err = rxe->ifc_ops->loopback(skb);
} else {
err = rxe->ifc_ops->send(rxe, pkt, skb);/// ifc_ops->send, send
}
///...
}
ifc_ops->send
最后会调用ip_local_out
,从对应的物理NIC发送出去。
Refs
Linux Soft-RoCE implementation (zz)的更多相关文章
- Linux实战教学笔记08:Linux 文件的属性(下半部分)
第八节 Linux 文件的属性(下半部分) 标签(空格分隔): Linux教学笔记 ---更多相关资料请点我查看 第1章 链接的概念 在linux系统中,链接可分为两种:一种为硬链接(Hard Lin ...
- Linux时间子系统之(十三):Tick Device layer综述
专题文档汇总目录 Notes:从概念层次描述了tick-comm.oneshot tick.broadcast tick:重点介绍了tick和tickless概念及其区别,两种tick device: ...
- Linux:进程实例信息(/proc)
https://blog.csdn.net/test1280/article/details/73632333 Linux:进程实例信息(/proc) 问几个问题: 1.怎么知道一个进程对应哪个可执行 ...
- Linux命令之vim(二)
这一章主要介绍vim编辑器的内部使用方法和注意事项 vim编辑器有四种工作模式:正常模式.插入模式.命令模式.可视模式.简单的判断方法就是看底部,什么都没有就是正常模式,光标在编辑器最底下时则是命令模 ...
- Linux常用命令大全(三)
Linux常用命令大全(三) 文件类型 普通文件(文本文件.数据文件.可执行的二进制文件) 目录文件 同上 差别:由成对的"I节点号.文件名"构成的列表 设备文件 (字符设备.块设 ...
- Linux实战教学笔记08:Linux 文件的属性(上半部分)
第八节 Linux 文件的属性(上半部分) 标签(空格分隔):Linux实战教学笔记 第1章 Linux中的文件 1.1 文件属性概述(ls -lhi) linux里一切皆文件 Linux系统中的文件 ...
- Linux.NET学习手记(7)
前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...
- Linux.NET学习手记(8)
上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中.这 ...
- 关于《Linux.NET学习手记(8)》的补充说明
早前的一两天<Linux.NET学习手记(8)>发布了,这一篇主要是讲述OWIN框架与OwinHost之间如何根据OWIN协议进行通信构成一套完整的系统.文中我们还直接学习如何直接操作OW ...
随机推荐
- C++ 相关问题记录
目录 编译链接 使用初始化和使用赋值时,调用的函数不同:使用 auto_ptr() 时可能会出现编译错误 宏定义不受命名空间的约束 Switch-case 中不能定义变量 技巧/注意项 多层继承中基类 ...
- Java连接Jira,创建、修改、删除工单信息
还不了解Jira是什么的同学可以看一下这篇文章:https://www.cnblogs.com/wgblog-code/p/11750767.html 本篇文章主要介绍如何使用Java操作Jira,包 ...
- flutter从入门到精通三
flutter可以通过一套代码运行在多个平台上,包括移动,web,桌面,嵌入式,但是在 Web 平台的支持尚未达到 Beta 阶段,请不要用在生产环节,在阅读文档时候,推荐大家阅读https://fl ...
- 怎样修改element-ui中的样式?
方法一 方法二 使用 /deep/ .homePage /deep/ .el-main { padding: 0; } .homePage为我们要修改组件类名的父级组件样式类名..即使定义一个空的 ...
- MySQL 查询优化 - 关联查询
1. 关联查询执行流程 MySQL执行关联查询的策略很简单,他会从一个表中循环取出单条数据,然后用该条数据到下一个表中寻找匹配的行,然后回溯到上一个表,到所有的数据匹配完成为止.因此也被称为" ...
- SMARTY的知识
smarty的原理: <?php class Smarty { $ldelimiter = "{";//左分隔符 $rdelimiter = "}";// ...
- Java 之 字节流
一.一切皆为字节 一切文件数据(文本.图片.视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此.所以,字节流可以传输任意文件数据.在操作流的时候,我们要时刻明确,无论使 ...
- shopxo代码审计
由于工作原因,分析了很多的cms也都写过文章,不过觉得好像没什么骚操作都是网上的基本操作,所以也就没发表在网站上,都保存在本地.最近突然发现自己博客中实战的东西太少了,决定将以前写的一些文章搬过来,由 ...
- stm32 CAN通信 TJA1040
CAN协议特点 1.多主控制 所有单元都可以发送消息,根据标识符(Identifier简称ID)决定优先级.仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工 ...
- Linux命令——tac、rev
tac和rev命令列到一起,并不是功能相似,而是他们都是将输入内容反置. tac -s:使用指定字符串代替换行作为分隔标志 [root@localhost ~]# echo "1,2&quo ...