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是不正确,会产生数据截断的错误。