searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

RDMA async event 分析

2024-05-31 09:20:28
71
0

1、接口描述

1.1用户态应用

提供两个接口ibv_get_async_event() 用于获取异步事件和ibv_ack_async_event() 用于异步事件处理完后,确认异步事件,接口原型如下

(1)int ibv_get_async_event(struct ibv_context *context, struct ibv_async_event *event);   

参数说明如下:

context

入参,设备上下文,之前调用ibv_open_device返回

event

出参,获取到的异常事件

返回值说明如下:

0

获取成功

-1

获取失败

(2)void ibv_ack_async_event(struct ibv_async_event *event);  

参数说明如下:

event

入参:ibv_get_async_event的出参

返回值说明如下:

 

1.2内核态应用

提供接口ib_register_event_handler()和ib_unregister_event_handler()注册和解注册处理异步事件,ib_dispatch_event()分发异步事件,接口原型如下:

(1)void ib_register_event_handler(struct ib_event_handler *event_handler);

参数说明如下:

event

入参,event事件处理函数

返回值说明如下:

 

(2)void ib_unregister_event_handler(struct ib_event_handler *event_handler);

参数说明如下:

event

入参,event事件处理函数

返回值说明如下:

 

(3)void ib_dispatch_event(const struct ib_event *event);

参数说明如下:

event

入参,待分发的异常事件

返回值说明如下:

 

提供接口ib_create_qp(),创建QP的同时,声明QP异步事件处理函数,接口原型如下:

static inline struct ib_qp *ib_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *init_attr)

参数说明如下:

pd

所属PD

qp_init_attr

初始化属性。包含event_handle(struct ib_event *, void *);

返回值说明如下:

ib_qp指针

创建成功。

NULL

创建失败。

提供接口ib_create_cq(),创建CP的同时,声明CQ异步事件处理函数,接口原型如下:

#define ib_create_cq(device, cmp_hndlr, evt_hndlr, cq_ctxt, cq_attr) 

参数说明如下:

device

IB device

cmp_hndlr

CQ完成事件回调。

evt_hndlr

异常事件回调

cq_ctxt

完成、异常事件的回调参数。

cq_attr

创建CQ的属性

返回值说明如下:

ib_cq指针

创建成功

NULL

创建失败。

提供接口ibv_create_srq(), 创建SRQ的同时,声明srQ异步事件处理函数,接口原型如下:

struct ib_srq *ibv_create_srq(struct ibv_pd *pd, struct ibv_srq_init_attr *srq_init_attr);

参数说明如下:

pd

所属PD

srq_init_attr

初始化属性。包含event_handle(struct ib_event *, void *);

返回值说明如下:

ib_srq指针

创建成功。

NULL

创建失败。

2、代码分析

2.1结构体说明

(1)异步事件结构体(用户态和内核态是一样的)

struct ib_event {
	struct ib_device	*device;
	union {
		struct ib_cq	*cq;         /*event是CQ相关事件填充*/
		struct ib_qp	*qp;        /*event是QP相关事件填充*/
		struct ib_srq	*srq;	       /*event是SRQ相关事件填充*/
		struct ib_wq	*wq;	       /*event是WQ相关事件填充*/
		u32		port_num;      /*event是其他事件填充*/
	} element;
	enum ib_event_type	event;
};

(2)事件类型

enum ib_event_type{}

IB_EVENT_CQ_ERR

CQ事件

IB_EVENT_QP_FATAL

QP事件

IB_EVENT_QP_REQ_ERR

IB_EVENT_QP_ACCESS_ERR

IB_EVENT_COMM_EST

IB_EVENT_SQ_DRAINED

IB_EVENT_PATH_MIG

IB_EVENT_QP_LAST_WQE_REACHED

IB_EVENT_PATH_MIG_ERR

IB_EVENT_DEVICE_FATAL

其他事件

IB_EVENT_PORT_ACTIVE

IB_EVENT_PORT_ERR

IB_EVENT_LID_CHANGE

IB_EVENT_PKEY_CHANGE

IB_EVENT_SM_CHANGE

IB_EVENT_CLIENT_REREGISTER

IB_EVENT_GID_CHANGE

IB_EVENT_SRQ_ERR

SRQ事件

IB_EVENT_SRQ_LIMIT_REACHED

IB_EVENT_WQ_FATAL

WQ事件

2.2事件处理流程

2.2.1初始化过程

(1)内核驱动初始化加载时调用ib_alloc_device()

ib_alloc_device()接口
     --INIT_LIST_HEAD(&device->event_handler_list);  //异步事件链表
     --init_rwsem(&device->event_handler_rwsem);  //保护链表的信号量

(2)用户态业务

(1)业务在使用RDMA时,使用ibv_open_device()接口,获取到context->async_fd
ibv_open_device()接口
       --device.ops.alloc_context
              --IB_USER_VERBS_CMD_GET_CONTEXT(陷入内核态)
                     --ib_uverbs_get_context
ib_uverbs_get_context
       -- uobj_alloc(UVERBS_OBJECT_ASYNC_EVENT, attrs, &ib_dev); 
              --obj->type_class->alloc_begin()    //申请uobject
                     --alloc_begin_fd_uobject()
                            --get_unused_fd_flags()   //申请一个未使用的fd作为async_fd
                            --anon_inode_getfile()  //创建匿名文件,实现file_operations
       --ib_uverbs_init_async_event_file()
              --ib_uverbs_init_event_queue(&async_file->ev_queue);   //初始化event queue
              --smp_store_release(&uverbs_file->default_async_file, async_file);   //初始化default_async_file
       --ib_register_event_handler(&async_file->event_handler);   //注册异步事件处理函数ib_uverbs_event_handler()
       --rdma_alloc_commit_uobject()
              --uobj->uapi_object->type_class->alloc_commit()
                     --alloc_commit_fd_uobject()
                            --fd_install(fd, filp);   //async_fd和file_operations关联。

(2)使用ibv_create_qp()接口创建QP。
ibv_create_qp() 
       --device.ops.create_qp
              --IB_USER_VERBS_CMD_CREATE_QP(陷入内核态)
                     --ib_uverbs_create_qp
ib_uverbs_create_qp()
       --create_qp()
              --obj->uevent.uobject.user_handle = cmd->user_handle;  //用户态ibv_qp的地址
              --attr.event_handler = ib_uverbs_qp_event_handler; //异步事件回调
              --ib_create_qp_user()
                     --create_qp()
                            --qp->event_handler = attr->event_handler; //异步事件回调
                            --qp->qp_context = attr->qp_context; // 用户态传递的context
              --obj->uevent.uobject.object = qp;	
              --obj->uevent.event_file = READ_ONCE(attrs->ufile->default_async_file);

(3)使用ibv_create_cq()接口创建CQ
ibv_create_cq()
       --device.ops.create_cq
              --IB_USER_VERBS_CMD_CREATE_CQ(陷入内核态)
                     --ib_uverbs_create_cq
ib_uverbs_create_cq()
       --create_cq()
              --obj->uevent.uobject.user_handle = cmd->user_handle; //用户态ibv_cq的地址
              --cq->event_handler = ib_uverbs_cq_event_handler; //异步事件回调
              --obj->uevent.uobject.object = cq;
              --obj->uevent.event_file = READ_ONCE(attrs->ufile->default_async_file);

(4)使用ibv_create_srq()接口创建srq
ibv_create_srq()
       --device.ops.create_srq
              --IB_USER_VERBS_CMD_CREATE_SRQ(陷入内核态)
                     --ib_uverbs_create_srq
ib_uverbs_create_srq()
       --__uverbs_create_xsrq()
	       --attr.event_handler  = ib_uverbs_srq_event_handler;   //异步事件回调
	       --INIT_LIST_HEAD(&obj->uevent.event_list);
	       --obj->uevent.uobject.user_handle = cmd->user_handle; ///用户态ibv_srq的地址
              --ib_create_srq_user
                     --srq->event_handler = srq_init_attr->event_handler;  //异步事件回调
                     --srq->srq_context = srq_init_attr->srq_context;

(3)内核态业务

(1)注册事件
ib_register_event_handler()
     --list_add_tail(ib_device->device->event_handler_list) ;   //添加event handle到链表上

(2)解注册事件
ib_unregister_event_handler()
     --list_del()    //从event handle链表上删除

(3)QP、CQ、SRQ都是接口指定event_handle,流程和用户态陷入内核态后的基本一致。

2.2.2事件处理

(1)用户态业务

(1)获取事件
ibv_get_async_event(context, event)
      --read(context->async_fd, &ev, sizeof ev);     // read ibv_get_context得到的async_fd
            --ib_uverbs_async_event_read()(陷入内核态)
                  --ib_uverbs_event_read()
                        --event = list_entry(ev_queue->event_list.next, struct ib_uverbs_event, list);   //从event链表上获取event
                        --copy_to_user(buf, event, eventsz)
--将ev转换ibv_async_event event

(2)确认事件
ibv_ack_async_event()
      --记录异步事件的次数

(3)所有事件上报的公共函数
void ib_uverbs_async_handler(struct ib_uverbs_async_event_file *async_file,
                                                __u64 element,(这个参数需要注意,不同事件,含义不一样, 指向struct ib_event->element)
                                                __u64 event,  (事件)
                                                struct list_head *obj_list,
                                                u32 *counter)
      --entry->desc.async.element = element;	//填充entry,准备放在event list上
      --entry->desc.async.event_type = event;
      --entry->desc.async.reserved = 0;
      --entry->counter = counter;
      --list_add_tail(&entry->list, &async_file->ev_queue.event_list);   //添加到event list链表上
      --wake_up_interruptible(&async_file->ev_queue.poll_wait);   //唤醒fd,产生了event

(4)QP事件
struct ib_qp->event_handle(struct ib_event *, void *);
      --ibqp->event_handler(&event, ibqp->qp_context); 	//此时的qp_context为NULL
            --ib_uverbs_qp_event_handler()
                  --ib_uverbs_async_handler()(eobj->event_file,	//event queue
                                              eobj->uobject.user_handle,  //指向用户态的ibv_qp
                                              event->event,			//QP事件
                                              &eobj->event_list,
                                              &eobj->events_reported)
(5)CQ事件
struct ib_cq->event_handle()
      --ibcq->event_handler(&event, ibcq->cq_context);
            --ib_uverbs_cq_event_handler()
                  --ib_uverbs_async_handler()(eobj->event_file,	//event queue
                                              eobj->uobject.user_handle,	//指向用户态的ibv_cq
                                              event->event,				//CQ事件
                                              &eobj->event_list,
                                              &eobj->events_reported)
(6)SRQ事件
struct ib_cq->event_handle(struct ib_event *, void *);
      --ibsrq->event_handler(&event, ibsrq->srq_context);
            --ib_uverbs_srq_event_handler()
                  --ib_uverbs_async_handler()(eobj->event_file,	//event queue
                                              eobj->uobject.user_handle,	//指向用户态的ibv_cq
                                              event->event,				//SRQ事件
                                              &eobj->event_list,
                                              &eobj->events_reported)

(6)其他事件
ib_dispatch_event()
      --ib_dispatch_event_clients()
            --list_for_each_entry(event_handler_list)
                  --ib_uverbs_event_handler()   //ibv_get_context时陷入内核后注册的函数
                        --ib_uverbs_async_handler(
                                                container_of(handler, struct ib_uverbs_async_event_file,event_handler), //event queue
                                                event->element.port_num,	//数据 
                                                event->event, 			//事件
                                                NULL,
                                                NULL)

(2)内核态业务

(1)QP相关事件使用
struct ib_qp->event_handle(struct ib_event *, void *);
     --ibqp->event_handler(&event, ibqp->qp_context);
          --业务模块创建QP注册的事件处理函数
(2)CQ相关事件使用
struct ib_cq->event_handle()
     --ibcq->event_handler(&event, ibcq->cq_context);
          --业务模块创建CQ时注册的事件处理函数
(3)SRQ相关事件使用
struct ib_srq->event_handle(struct ib_event *, void *);
     --ibsrq->event_handler(&event, ibsrq->srq_context);
          --业务模块创建SRQ时注册的事件处理函数
(4)WQ略
(5)其他事件使用
ib_dispatch_event()
     --ib_dispatch_event_clients()
          --list_for_each_entry(event_handler_list)  //遍历evnet handle链表
               --业务模块注册的事件处理函数

3、注意

在开发过程中,需要注意不同事件对应的不同的处理接口,不能一概使用其中一个接口,比如所有事件都使用ib_dispatch_event是不正确,会产生数据截断的错误。

0条评论
0 / 1000
z****n
8文章数
0粉丝数
z****n
8 文章 | 0 粉丝
原创

RDMA async event 分析

2024-05-31 09:20:28
71
0

1、接口描述

1.1用户态应用

提供两个接口ibv_get_async_event() 用于获取异步事件和ibv_ack_async_event() 用于异步事件处理完后,确认异步事件,接口原型如下

(1)int ibv_get_async_event(struct ibv_context *context, struct ibv_async_event *event);   

参数说明如下:

context

入参,设备上下文,之前调用ibv_open_device返回

event

出参,获取到的异常事件

返回值说明如下:

0

获取成功

-1

获取失败

(2)void ibv_ack_async_event(struct ibv_async_event *event);  

参数说明如下:

event

入参:ibv_get_async_event的出参

返回值说明如下:

 

1.2内核态应用

提供接口ib_register_event_handler()和ib_unregister_event_handler()注册和解注册处理异步事件,ib_dispatch_event()分发异步事件,接口原型如下:

(1)void ib_register_event_handler(struct ib_event_handler *event_handler);

参数说明如下:

event

入参,event事件处理函数

返回值说明如下:

 

(2)void ib_unregister_event_handler(struct ib_event_handler *event_handler);

参数说明如下:

event

入参,event事件处理函数

返回值说明如下:

 

(3)void ib_dispatch_event(const struct ib_event *event);

参数说明如下:

event

入参,待分发的异常事件

返回值说明如下:

 

提供接口ib_create_qp(),创建QP的同时,声明QP异步事件处理函数,接口原型如下:

static inline struct ib_qp *ib_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *init_attr)

参数说明如下:

pd

所属PD

qp_init_attr

初始化属性。包含event_handle(struct ib_event *, void *);

返回值说明如下:

ib_qp指针

创建成功。

NULL

创建失败。

提供接口ib_create_cq(),创建CP的同时,声明CQ异步事件处理函数,接口原型如下:

#define ib_create_cq(device, cmp_hndlr, evt_hndlr, cq_ctxt, cq_attr) 

参数说明如下:

device

IB device

cmp_hndlr

CQ完成事件回调。

evt_hndlr

异常事件回调

cq_ctxt

完成、异常事件的回调参数。

cq_attr

创建CQ的属性

返回值说明如下:

ib_cq指针

创建成功

NULL

创建失败。

提供接口ibv_create_srq(), 创建SRQ的同时,声明srQ异步事件处理函数,接口原型如下:

struct ib_srq *ibv_create_srq(struct ibv_pd *pd, struct ibv_srq_init_attr *srq_init_attr);

参数说明如下:

pd

所属PD

srq_init_attr

初始化属性。包含event_handle(struct ib_event *, void *);

返回值说明如下:

ib_srq指针

创建成功。

NULL

创建失败。

2、代码分析

2.1结构体说明

(1)异步事件结构体(用户态和内核态是一样的)

struct ib_event {
	struct ib_device	*device;
	union {
		struct ib_cq	*cq;         /*event是CQ相关事件填充*/
		struct ib_qp	*qp;        /*event是QP相关事件填充*/
		struct ib_srq	*srq;	       /*event是SRQ相关事件填充*/
		struct ib_wq	*wq;	       /*event是WQ相关事件填充*/
		u32		port_num;      /*event是其他事件填充*/
	} element;
	enum ib_event_type	event;
};

(2)事件类型

enum ib_event_type{}

IB_EVENT_CQ_ERR

CQ事件

IB_EVENT_QP_FATAL

QP事件

IB_EVENT_QP_REQ_ERR

IB_EVENT_QP_ACCESS_ERR

IB_EVENT_COMM_EST

IB_EVENT_SQ_DRAINED

IB_EVENT_PATH_MIG

IB_EVENT_QP_LAST_WQE_REACHED

IB_EVENT_PATH_MIG_ERR

IB_EVENT_DEVICE_FATAL

其他事件

IB_EVENT_PORT_ACTIVE

IB_EVENT_PORT_ERR

IB_EVENT_LID_CHANGE

IB_EVENT_PKEY_CHANGE

IB_EVENT_SM_CHANGE

IB_EVENT_CLIENT_REREGISTER

IB_EVENT_GID_CHANGE

IB_EVENT_SRQ_ERR

SRQ事件

IB_EVENT_SRQ_LIMIT_REACHED

IB_EVENT_WQ_FATAL

WQ事件

2.2事件处理流程

2.2.1初始化过程

(1)内核驱动初始化加载时调用ib_alloc_device()

ib_alloc_device()接口
     --INIT_LIST_HEAD(&device->event_handler_list);  //异步事件链表
     --init_rwsem(&device->event_handler_rwsem);  //保护链表的信号量

(2)用户态业务

(1)业务在使用RDMA时,使用ibv_open_device()接口,获取到context->async_fd
ibv_open_device()接口
       --device.ops.alloc_context
              --IB_USER_VERBS_CMD_GET_CONTEXT(陷入内核态)
                     --ib_uverbs_get_context
ib_uverbs_get_context
       -- uobj_alloc(UVERBS_OBJECT_ASYNC_EVENT, attrs, &ib_dev); 
              --obj->type_class->alloc_begin()    //申请uobject
                     --alloc_begin_fd_uobject()
                            --get_unused_fd_flags()   //申请一个未使用的fd作为async_fd
                            --anon_inode_getfile()  //创建匿名文件,实现file_operations
       --ib_uverbs_init_async_event_file()
              --ib_uverbs_init_event_queue(&async_file->ev_queue);   //初始化event queue
              --smp_store_release(&uverbs_file->default_async_file, async_file);   //初始化default_async_file
       --ib_register_event_handler(&async_file->event_handler);   //注册异步事件处理函数ib_uverbs_event_handler()
       --rdma_alloc_commit_uobject()
              --uobj->uapi_object->type_class->alloc_commit()
                     --alloc_commit_fd_uobject()
                            --fd_install(fd, filp);   //async_fd和file_operations关联。

(2)使用ibv_create_qp()接口创建QP。
ibv_create_qp() 
       --device.ops.create_qp
              --IB_USER_VERBS_CMD_CREATE_QP(陷入内核态)
                     --ib_uverbs_create_qp
ib_uverbs_create_qp()
       --create_qp()
              --obj->uevent.uobject.user_handle = cmd->user_handle;  //用户态ibv_qp的地址
              --attr.event_handler = ib_uverbs_qp_event_handler; //异步事件回调
              --ib_create_qp_user()
                     --create_qp()
                            --qp->event_handler = attr->event_handler; //异步事件回调
                            --qp->qp_context = attr->qp_context; // 用户态传递的context
              --obj->uevent.uobject.object = qp;	
              --obj->uevent.event_file = READ_ONCE(attrs->ufile->default_async_file);

(3)使用ibv_create_cq()接口创建CQ
ibv_create_cq()
       --device.ops.create_cq
              --IB_USER_VERBS_CMD_CREATE_CQ(陷入内核态)
                     --ib_uverbs_create_cq
ib_uverbs_create_cq()
       --create_cq()
              --obj->uevent.uobject.user_handle = cmd->user_handle; //用户态ibv_cq的地址
              --cq->event_handler = ib_uverbs_cq_event_handler; //异步事件回调
              --obj->uevent.uobject.object = cq;
              --obj->uevent.event_file = READ_ONCE(attrs->ufile->default_async_file);

(4)使用ibv_create_srq()接口创建srq
ibv_create_srq()
       --device.ops.create_srq
              --IB_USER_VERBS_CMD_CREATE_SRQ(陷入内核态)
                     --ib_uverbs_create_srq
ib_uverbs_create_srq()
       --__uverbs_create_xsrq()
	       --attr.event_handler  = ib_uverbs_srq_event_handler;   //异步事件回调
	       --INIT_LIST_HEAD(&obj->uevent.event_list);
	       --obj->uevent.uobject.user_handle = cmd->user_handle; ///用户态ibv_srq的地址
              --ib_create_srq_user
                     --srq->event_handler = srq_init_attr->event_handler;  //异步事件回调
                     --srq->srq_context = srq_init_attr->srq_context;

(3)内核态业务

(1)注册事件
ib_register_event_handler()
     --list_add_tail(ib_device->device->event_handler_list) ;   //添加event handle到链表上

(2)解注册事件
ib_unregister_event_handler()
     --list_del()    //从event handle链表上删除

(3)QP、CQ、SRQ都是接口指定event_handle,流程和用户态陷入内核态后的基本一致。

2.2.2事件处理

(1)用户态业务

(1)获取事件
ibv_get_async_event(context, event)
      --read(context->async_fd, &ev, sizeof ev);     // read ibv_get_context得到的async_fd
            --ib_uverbs_async_event_read()(陷入内核态)
                  --ib_uverbs_event_read()
                        --event = list_entry(ev_queue->event_list.next, struct ib_uverbs_event, list);   //从event链表上获取event
                        --copy_to_user(buf, event, eventsz)
--将ev转换ibv_async_event event

(2)确认事件
ibv_ack_async_event()
      --记录异步事件的次数

(3)所有事件上报的公共函数
void ib_uverbs_async_handler(struct ib_uverbs_async_event_file *async_file,
                                                __u64 element,(这个参数需要注意,不同事件,含义不一样, 指向struct ib_event->element)
                                                __u64 event,  (事件)
                                                struct list_head *obj_list,
                                                u32 *counter)
      --entry->desc.async.element = element;	//填充entry,准备放在event list上
      --entry->desc.async.event_type = event;
      --entry->desc.async.reserved = 0;
      --entry->counter = counter;
      --list_add_tail(&entry->list, &async_file->ev_queue.event_list);   //添加到event list链表上
      --wake_up_interruptible(&async_file->ev_queue.poll_wait);   //唤醒fd,产生了event

(4)QP事件
struct ib_qp->event_handle(struct ib_event *, void *);
      --ibqp->event_handler(&event, ibqp->qp_context); 	//此时的qp_context为NULL
            --ib_uverbs_qp_event_handler()
                  --ib_uverbs_async_handler()(eobj->event_file,	//event queue
                                              eobj->uobject.user_handle,  //指向用户态的ibv_qp
                                              event->event,			//QP事件
                                              &eobj->event_list,
                                              &eobj->events_reported)
(5)CQ事件
struct ib_cq->event_handle()
      --ibcq->event_handler(&event, ibcq->cq_context);
            --ib_uverbs_cq_event_handler()
                  --ib_uverbs_async_handler()(eobj->event_file,	//event queue
                                              eobj->uobject.user_handle,	//指向用户态的ibv_cq
                                              event->event,				//CQ事件
                                              &eobj->event_list,
                                              &eobj->events_reported)
(6)SRQ事件
struct ib_cq->event_handle(struct ib_event *, void *);
      --ibsrq->event_handler(&event, ibsrq->srq_context);
            --ib_uverbs_srq_event_handler()
                  --ib_uverbs_async_handler()(eobj->event_file,	//event queue
                                              eobj->uobject.user_handle,	//指向用户态的ibv_cq
                                              event->event,				//SRQ事件
                                              &eobj->event_list,
                                              &eobj->events_reported)

(6)其他事件
ib_dispatch_event()
      --ib_dispatch_event_clients()
            --list_for_each_entry(event_handler_list)
                  --ib_uverbs_event_handler()   //ibv_get_context时陷入内核后注册的函数
                        --ib_uverbs_async_handler(
                                                container_of(handler, struct ib_uverbs_async_event_file,event_handler), //event queue
                                                event->element.port_num,	//数据 
                                                event->event, 			//事件
                                                NULL,
                                                NULL)

(2)内核态业务

(1)QP相关事件使用
struct ib_qp->event_handle(struct ib_event *, void *);
     --ibqp->event_handler(&event, ibqp->qp_context);
          --业务模块创建QP注册的事件处理函数
(2)CQ相关事件使用
struct ib_cq->event_handle()
     --ibcq->event_handler(&event, ibcq->cq_context);
          --业务模块创建CQ时注册的事件处理函数
(3)SRQ相关事件使用
struct ib_srq->event_handle(struct ib_event *, void *);
     --ibsrq->event_handler(&event, ibsrq->srq_context);
          --业务模块创建SRQ时注册的事件处理函数
(4)WQ略
(5)其他事件使用
ib_dispatch_event()
     --ib_dispatch_event_clients()
          --list_for_each_entry(event_handler_list)  //遍历evnet handle链表
               --业务模块注册的事件处理函数

3、注意

在开发过程中,需要注意不同事件对应的不同的处理接口,不能一概使用其中一个接口,比如所有事件都使用ib_dispatch_event是不正确,会产生数据截断的错误。

文章来自个人专栏
RDMA杂谈
8 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0