一、概述
从下往上,依次是:
1、硬件产生中断信号
2、Eq收到中断信号,触发中断函数。
3、中断函数唤醒阻塞的线程。
4、read读取到数据。
二、驱动侧中断的初始化
流程描述:
1、驱动初始化阶段,mlx5_irq_table_init()创建irq table;
2、mlx5_irq_table_create()根据PF和SF的个数申请MSI-X中断向量个数,根据中断向量个数,初始化PF和SF的个数填充irq table,用于管理中断。
3、eq初始化阶段mlx5_eq_table_init(),创建eq table,用于管理eq;初始化用于中断处理的atomic notifier链表。
4、mlx5_eq_table_create()调用create_async_eqs初始化async eq,使用MSI-X的最后一个中断向量,注册中断,mlx5_eq_async_int()是async eqs的中断处理函数。用于处理command等一些异步事件。
5、mlx5_eq_table_create()调用create_comp_eqs初始化completion eq,批量注册MSI-X中断,完成eq队列的实例化,一个eq对应一个irq,过程中将mlx5_cq_tasklet_cb()函数注册到tasklet上,此函数是软中断的调度处理函数;mlx5_eq_comp_int()是completion eq的硬中断处理函数。
备注:
1、eq使用的通知机制是atomic notifier chains机制实现中断的上半部分,tasklet机制实现中断的下半部分。
2、eq使用的是radix tree管理cqn,即一个eq对应多个cq。
三、用户态的操作
流程描述:
1、调用ibv_create_comp_channel()函数创建completion channel,函数会创建event queue,调用rdma_alloc_begin_uobject()得到一个索引到内核的fd,event queue和此fd相关联。
2、调用ibv_create_cq()函数创建cq,
(1) 将创建的completion channel得到的fd传入内核,根据fd找到对应的event queue;
(2) 初始化completion channel的处理函数ib_uverbs_comp_handler;
(3) 初始化tasklet的处理函数mlx5_ib_cq_comp();
(4) 初始化completion eq的处理函数mlx5_add_cq_to_tasklet();
(5) 调用mlx5_eq_add_cq()函数将cqn插入到radix tree,与eq关联。
3、调用ibv_req_notify_cq()函数在cq中请求一个完成通知。
4、调用ibv_get_cq_event() 阻塞等待completion channel的通知,本质上使用read函数读取创建completion channel得到的fd。
5、等到completion channel事件通知,调用ibv_poll_cq()从cq中查询已完成的WC。
四、驱动中断的交互
1、驱动中断注册,初始化完成后,等待硬件产生中断信号。
2、硬件产生中断信号,irq_int_handler()函数响应中断进行中断处理。
3、如果是最后一个中断向量送上来的信号,获取eqe,根据注册的中断类型,调用atomic_notifier_call_chainhen处理对应类型的中断事件。
4、如果是其他中断向量送上来的信号,atomic_notifier_call_chain会通知到mlx5_eq_comp_int()函数处理中断。
5、mlx5_eq_comp_int()函数,根据入参nb可以得到eq队列指针,读取eqe,从EQE读取cqn,索引到对应的cq队列,调用cq上注册的completion函数mlx5_add_cq_to_tasklet。
6、mlx5_add_cq_to_tasklet()函数将需要进行触发的cq上链到tasklet list,只有当链表为空的时候才能上链,如果链表不为空,表示当前cq已经上链了。
7、调用tasklet_schedule,进入中断的下半部处理,调用到mlx5_cq_tasklet_cb()。
8、mlx5_cq_tasklet_cb()调用在创建cq时注册的mlx5_ib_cq_comp()函数。
9、mlx5_ib_cq_comp()函数调用ib_uverbs_comp_handler()函数。
10、ib_uverbs_comp_handler()函数调用wake_up_interruptible()唤醒对应阻塞的线程,通知用户态处理。