内核RDMA模块(siw)代码分析

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。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值