siw是内核中实现的RDMA设备驱动模块。与其他RDMA设备驱动不同的是,这个模块没有对应的硬件设备,而是通过软件方式模拟了一个使用iWARP协议的RDMA设备,通过内核的socket接口完成tcp报文的收发。本文将基于5.9.11内核中的siw模块代码分析一下这个虚拟设备的实现逻辑。

初始化

  1. 创建一个名为siw_cm_wq的workqueue,用于处理一些需要异步或延迟处理的cm相关操作,例如MPA协商消息超时处理等。为了保证操作的有序性,这个workqueue是单线程处理的,不使用cmwq技术并发处理。workqueue的处理函数为siw_cm_work_handler。
  2. 为每个cpu创建一个名为siw_tx/x的kthread,这些kthread用于处理sq中的sqe消息。在线程主函数siw_run_sq中,会持续等待tx_task队列中出现待处理的qp,出现qp后调用siw_sq_resume->siw_qp_sq_process处理qp中sq的消息。
  3. 注册网卡事件处理函数,register_netdevice_notifier(&siw_netdev_nb)。因为siw是基于普通网卡的纯软件模拟设备,因此当网卡状态变化时siw需要相应的处理,这里的状态变化包括网卡启动、停用、更改地址等。
  4. 注册netlink接口,用于让用户态工具(rdma)能够让内核创建siw设备,实现函数为siw_newlink。siw_newlink会调用siw_device_create、siw_device_register创建和注册siw设备。

控制面命令处理

在开始数据面交互前和完成数据面交互后,都需要执行控制面命令来准备和销毁rdma相关的数据结构。siw支持的控制面命令如下:
    .alloc_ucontext = siw_alloc_ucontext,
    .create_cq = siw_create_cq,
    .create_qp = siw_create_qp,
    .create_srq = siw_create_srq,
    .dealloc_driver = siw_device_cleanup,
    .dealloc_ucontext = siw_dealloc_ucontext,
    .dereg_mr = siw_dereg_mr,
    .destroy_cq = siw_destroy_cq,
    .destroy_qp = siw_destroy_qp,
    .destroy_srq = siw_destroy_srq,
    .get_dma_mr = siw_get_dma_mr,
    .get_port_immutable = siw_get_port_immutable,
    .iw_accept = siw_accept,
    .iw_add_ref = siw_qp_get_ref,
    .iw_connect = siw_connect,
    .iw_create_listen = siw_create_listen,
    .iw_destroy_listen = siw_destroy_listen,
    .iw_get_qp = siw_get_base_qp,
    .iw_reject = siw_reject,
    .iw_rem_ref = siw_qp_put_ref,
    .map_mr_sg = siw_map_mr_sg,
    .mmap = siw_mmap,
    .mmap_free = siw_mmap_free,
    .modify_qp = siw_verbs_modify_qp,
    .modify_srq = siw_modify_srq,
    .poll_cq = siw_poll_cq,
    .post_recv = siw_post_receive,
    .post_send = siw_post_send,
    .post_srq_recv = siw_post_srq_recv,
    .query_device = siw_query_device,
    .query_gid = siw_query_gid,
    .query_port = siw_query_port,
    .query_qp = siw_query_qp,
    .query_srq = siw_query_srq,
    .req_notify_cq = siw_req_notify_cq,
    .reg_user_mr = siw_reg_user_mr,

数据面处理

siw需要支持的数据面处理主要有以下几块:

  1. sq处理:调用ibv_post_send/ib_post_send后,siw需要处理qp中的sqe。根据sqe中的操作信息和数据内容构造iwarp协议报文,再调用内核socket接口将报文发出。
  2. rx报文处理&rq处理:调用ibv_post_recv/ib_post_recv后,rqe只会保存在siw_qp.recvq中,需要等协议栈收到并处理对端的send消息后才会填充rqe并构造cqe。除了send消息外的其他rdma操作消息(例如read、write等)不会填充rqe和生成cqe。siw通过在sock上注册回调函数的方式,让内核协议栈在处理iwarp连接上的报文时会调用siw函数进行解析处理。

sq处理流程

由于post_send操作在用户态和内核态都能调用,因此sq有两种添加sqe的方式。这两种不同的方式有对应的处理入口函数,但后续对sqe的处理逻辑和函数是相同的。主要的处理流程和函数如下:

  1. siw_post_send=>siw_sq_start=>(唤醒)siw_run_sq => siw_sq_resume => siw_qp_sq_process,或者siw_post_send => siw_qp_sq_process。分别是用户态调用ibv_post_send后内核的事件处理逻辑和内核态直接调用ib_post_send后的同步调用。最后都要通过siw_qp_sq_process来处理sq中的sqe。
    值得注意的是不管是用户态还是内核态的post_send,最终都要调用siw_post_send。用户态的ibv_post_send在调用到siw_post_send中会再调用siw_db(qp)发送doorbell,这个doorbell是通过写siw设备对应的sysfs文件实现的,这时写入的是IB_USER_VERBS_CMD_POST_SEND消息,内核处理这个消息会最终调用到siw内核模块的siw_post_send。因此ib_device_ops.post_send(siw_post_send)这个函数指针不止是内核的ib_post_send会调用,用户态的ibv_post_send的调用链也会调用到的,只是这时传入给内核的wr是NULL,调用只是为了通知siw去处理sq。
  2. siw_qp_sq_process,逐个处理sq中的每个sqe。根据sqe操作类型的不同,需要向远端发送数据的操作(例如send、read、write等)调用siw_qp_sq_proc_tx处理,只是本地操作(例如reg_mr、invalidate_stag等)调用siw_qp_sq_proc_local。处理完成后,对于本地操作和不需要等待响应的操作(例如send、write),调用siw_sqe_complete生成cqe。如果处理失败,也生成相应的cqe通知给应用。
  3. siw_qp_sq_proc_tx,将sqe构造成报文通过socket接口发送。首先通过siw_check_sgl_tx检查sqe中要求访问的本地内存地址是否有LOCAL_READ权限。之后调用siw_qp_prepare_tx构造报文首部。
  4. siw_qp_prepare_tx,构造报文首部。并调用siw_try_1seg,如果sqe中携带的数据段较短,就将数据段复制到首部后面,整个sqe可以用一段buffer表示和发送。这里的实现是因为报文首部是在一段预分配的首部内存中构造的,这段内存构造完首部后可能还有一小段空间,将短数据段复制进去可以提高后续的处理性能。如果能构造成一段数据,则调用siw_tx_ctrl将报文最终构造完成并调用kernel_sendmsg发出。
  5. 如果siw_qp_prepare_tx后不能在首部buffer中包含所有sqe数据,则调用siw_prepare_fpdu构造分段报文信息,更新首部。之后调用siw_tx_hdt发出一段数据。之后反复调用siw_prepare_fpdu和siw_tx_hdt将sqe中所有数据段发出。这里siw_prepare_fpdu会根据mss计算一段fpdu的最大长度,siw_tx_hdt中会根据这个长度发送数据。这是为了在一个报文中包含完整的fpdu,从而支持对端能够乱序接收fpdu,而不需要做缓存和排序。但是如果接收端也是siw的话这个逻辑意义不大,因为siw基于socket接口,是不支持segment和fpdu乱序接收的。但是在发送的数据较大时也有一定的意义,至少可以在收到每个顺序报文时可以直接处理掉,不需要因为有不完整的fpdu而缓存重组。

rx报文处理&rq处理

和post_send一样,post_recv也能在用户态和内核态调用。和post_send不同,post_recv只负责将rqe添加到rq中,而并不会对rqe做进一步的处理。rq和rqe的处理流程主要在网卡接收到iwarp报文并处理的过程中。内核的rx报文处理过程中,会找到rx报文对应的socket,并调用socket注册的事件回调函数来实现对这个socket和连接的特殊处理逻辑。siw就是通过在iwarp连接对应的socket上注册回调函数,来实现对iwarp报文处理逻辑的调用和rqe/cqe的生成。

socket的回调函数在sock结构中定义。sock主要有5个回调函数:sk_state_change、sk_data_ready、sk_write_space、sk_error_report、sk_backlog_rcv

siw中注册的函数有:

sk_state_change:siw_cm_llp_state_change

sk_data_ready:siw_cm_llp_data_ready、siw_qp_llp_data_ready、siw_rtr_data_ready

sk_write_space:siw_cm_llp_write_space、siw_qp_llp_write_space

sk_error_report:siw_cm_llp_error_report

可以看到有的回调函数有多种实现,分别用于iwarp连接的不同阶段和状态。

siw_cm_llp_state_change:用于使用cm监听和建连的socket的建连过程。这个函数会在socket的tcp连接状态改变时被调用。函数中,当连接状态变成ESTABLISHED时会触发siw_accept_newconn任务,accept新建立的连接并等待处理mpa请求。这里有点奇怪的是只处理了listen socket的状态却没有处理connect socket的状态,原因是为了简化实现siw在实现connect时使用了blocking connect,因此connect socket不需要用回调函数来处理连接建立事件,而是在connect返回后立即处理。

siw_cm_llp_data_ready:tcp连接建立后,mpa请求/响应处理逻辑。

siw_rtr_data_ready&siw_qp_llp_data_ready:mpa协商后,主动建连一侧后续调用siw_qp_llp_data_ready处理收到的tcp报文数据。但被动建连一侧根据收到的mpa req不同,可能进入不同状态,如果对端没有进入RTS状态(这里需要看协议细节确定)会先调用一次siw_rtr_data_ready,收到一次数据后再改成siw_qp_llp_data_ready。这两个函数对报文处理的逻辑基本是一样的,只是siw_rtr_data_ready有一个生成IW_CM_EVENT_ESTABLISHED事件并切换data_ready回调函数的逻辑,来和直接调用siw_qp_llp_data_ready的情况达到相同状态。这两个函数都通过tcp_read_sock(sk, &rd_desc, siw_tcp_rx_data)来逐个处理收到的skb,真正的处理逻辑在siw_tcp_rx_data中。

siw_tcp_rx_data函数会解析skb中的数据内容,根据解出的RDMAP协议头判断当前消息是哪种操作,并调用对应的iwarp_pktinfo[opcode].rx_data注册的函数继续解析消息内容,例如RDMAP_SEND消息对应的处理函数是siw_proc_send填充rqe。处理完一条完整的消息后,会调用siw_rdmap_complete完成相应的处理操作,例如send消息会调用siw_rqe_complete生成相应的cqe。

siw_cm_llp_write_space:没有具体逻辑,仅用于debug。

siw_qp_llp_write_space:在sq处理流程中,siw_qp_sq_process流程可能由于socket的sendbuf耗尽而中断。当socket又有sendbuf时就会调用siw_qp_llp_write_space,将qp加入siw_tx_task_g队列并唤醒siw_tx线程再次尝试处理qp上的sqe。

siw_cm_llp_error_report:没有具体逻辑,仅用于debug。

从上面的分析可以看出,接收端的处理逻辑大致为:

  1. 网卡驱动和tcp/ip协议栈处理iwarp连接的tcp报文,并找到对应的socket
  2. 调用siw注册的socket回调函数,主要是siw_qp_llp_data_ready。这个函数会调用siw_tcp_rx_data处理socket上当前收到的skb数据,根据其中的RDMAP协议头中指定的操作类型调用不同的消息处理函数,例如siw_proc_send
  3. siw_proc_send,将消息中的数据处理后放入rqe指定的内存空间,并生成cqe。

内核RDMA模块(siw)代码分析相关推荐

  1. linux内核笔记之SMMU代码分析

    2020/06/10: first version, 主要介绍smmu驱动的初始化流程 在前一篇博文ARM SMMU学习笔记中, 介绍了SMMU的一些基本概念以及SMMU地址转换的基本流程,本文主要分 ...

  2. linux内核自旋锁的代码分析

    前面看操作系统导论这本书,其中锁的历史变化有点感悟,现在追一下linux内核锁的代码. 一.自旋锁 1.锁的结构体 系统自旋锁结构体如下: typedef struct spinlock {union ...

  3. 西门子PLC模拟量转换scale模块公式代码分析

    1 模拟量应用中的参数 模拟量输入值:通过AI模块采集会来的电流或电压信号: 模拟量上限:采集回来的模拟量最大值,一般为27648: 模拟量下限:采集回来的模拟量最小智,一般为0或5530(5530对 ...

  4. Linux内核2:中断代码分析

    总体来说,中断相关的汇编代码有两个,asm.s,systemcall.s ,其中定义了中断发生前的相关参数入栈,调用的C函数入口地址入栈,中断发生后的恢复.而各种中断的C函数代码分布在不同的C文件中. ...

  5. 模块加载过程代码分析1

    一.概述 模块是作为ELF对象文件存放在文件系统中的,并通过执行insmod程序链接到内核中.对于每个模块,系统都要分配一个包含以下数据结构的内存区. 一个module对象,表示模块名的一个以null ...

  6. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  7. 基于windows PE文件的恶意代码分析;使用SystemInternal工具与内核调试器研究windows用户空间与内核空间...

    基于windows PE文件的恶意代码分析:使用SystemInternal工具与内核调试器研究windows用户空间与内核空间 ******************** 既然本篇的主角是PE文件,那 ...

  8. 输入子系统代码内核代码分析

    本篇博客里将会对输入子系统进行比较深入的分析,第一部分将基于内核2.6.2版本,第二部分将基于内核的3.14版本,同时我会为两个版本都提供一个示例代码 我会尽可能深入的分析代码,如果你看完了这篇博客, ...

  9. Linux内核分析2:一个简单的时间片轮转多道程序内核代码分析

    Lab2:一个简单的时间片轮转多道程序内核代码 席金玉   <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 ...

最新文章

  1. 快起床刷题去,别人把你offer拿走啦
  2. java 中向文本写入和读取hashmap_就靠这一篇HashMap的讲解,我与头条面试官聊了一个小时。...
  3. Java 8 CompletableFuture 教程
  4. python-基础知识
  5. sql两个时间计算时间差_Java 8时间类,越用越香
  6. [图神经网络] 图节点Node表示---GraphSAGE与PinSAGE
  7. response.end后抛了异常_(七)异常处理
  8. 遗传算法(Genetic Algorithm)
  9. 云计算教程学习入门视频:云计算的模型都有哪些
  10. linux查看tongweb端口,东方通tongweb linux安装
  11. 将路由器作为AP组建局域网的方法
  12. 一次用Python制作电影字幕的尝试
  13. 数据库修改用友U8账套
  14. java awt canvas_java.awt 类 Canvas - Java 中文参考手册
  15. 给boss直聘的搜索结果加上hr活跃状态,少看点半年活跃的岗位,有书签版,油猴版
  16. 浏览器刷新、关闭页面与统计在线人数
  17. linux之shell脚本
  18. 大基金支持下 晋江能否在存储器领域杀出一条“血路”?
  19. 不再依赖血液 气相色谱呼气测试发现早期疾病
  20. 从市场洞察数字化找到被浪费一半的广告费!

热门文章

  1. 学会这27种编程语言,你还怕找不到女朋友?!那是要几个有几个!
  2. elasticsearch6.8.4-docker部署升级方式以及安全加密
  3. java两两组合(不重复)
  4. 【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务快应用卡片接入指南(下)
  5. vim 常用配置整理
  6. [TI TDA4 J721E] TDA4平台 相关技术文章 汇总
  7. 抛砖引玉系列:Android简易实现录屏软件。
  8. 关于ReportingService配置邮件发送报表到其他邮箱的配置
  9. awk、ruby计算总分排名,相同总分显示同一名次
  10. HTML5网页设计阶梯教程(3)——编辑图片