在DPDK的初始化函数rte_eal_init中,会调用函数rte_eal_intr_init来做中断的初始化,在驱动中可以调用rte_intr_callback_register来注册中断。
本文围绕此中断的使用做简要介绍。
函数rte_eal_intr_init的分析
此函数
int
rte_eal_intr_init(void)
{
int ret = 0;
/* init the global interrupt source head */
TAILQ_INIT(&intr_sources);
/**
* create a pipe which will be waited by epoll and notified to
* rebuild the wait list of epoll.
*/
if (pipe(intr_pipe.pipefd) < 0) {
rte_errno = errno;
return -1;
}
/* create the host thread to wait/handle the interrupt */
ret = rte_ctrl_thread_create(&intr_thread, "eal-intr-thread", NULL,
eal_intr_thread_main, NULL);
if (ret != 0) {
rte_errno = -ret;
RTE_LOG(ERR, EAL,
"Failed to create thread for interrupt handling\n");
}
return ret;
}
函数依次完成三部分的工作:
- 初始化中断源的队列头
- 打开一个pipe,pipe分两头,写者来自于中断请求者,读者来自于步骤三的线程
- 中断处理主线程 eal_intr_thread_main
在中断处理主线程中,分为两重无限循环。
- 外层无限循环创建一个epoll句柄,并对pipe的readfd进行监听;同时对所有中断源的fd进行监听,将所有fd都添加进epoll之后,进入内层循环。
- 内层循环调用epoll_wait监听所有fd状态,当某fd可用后,调用eal_intr_process_interrupts来处理该fd。该函数首先查看是否是pipe的readfd可用,如果是,说明中断源发生的变化,需要重新构造终端监听的fd列表,因此跳出内层循环,从新进入外层循环。如果是其他fd可用,就调用其它fd的handler进行处理。
中断注册函数
函数rte_intr_callback_register是终端注册函数,它将终端添加到中断源队列中。然后对pipe的writefd写1,通知中断处理线程有新的中断源需要被监听。rte_intr_callback_unregister是对应的反注册函数。
注意
一般来说,驱动只注册了一些异常或不频繁的中断到这个中断回掉中,并且中断处理要尽可能的快,因为这个线程系统中只有一个,由很多任务共用,包括alarm线程也使用它。如果是收发包之类的需要占用大量CPU的中断,需要使用单独的线程处理。