DPDK 通过在线程中使用 epoll 模型,监听 UIO 设备的事件,来模拟操作系统的中断处理。
一、中断初始化
在 rte_eal_intr_init() 函数中初始化中断。具体如下:
1、首先初始化 intr_sources 链表。所有 UIO 设备的中断都挂在这个链表上,中断处理线程通过遍历这个链表,来执行设备的中断。
2、创建 intr_pipe 管道,用于 epoll 模型的消息通知。
3、创建线程 intr_thread,线程的执行体是 eal_intr_thread_main() 函数,创建 epoll 模型,遍历 intr_sources 链表,监听已注册的所有 UIO 设备的中断事件,并调用对应 UIO 设备的中断处理函数。
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 byepoll and notified to
* rebuild the wait list of epoll.
*/
if (pipe(intr_pipe.pipefd) < 0)
return -1;
/* create the host thread to wait/handlethe interrupt */
ret = pthread_create(&intr_thread,NULL, eal_intr_thread_main, NULL);
if (ret != 0)
RTE_LOG(ERR, EAL, "Failed to create thread forinterrupt handling\n");
return -ret;
}
中断线程执行主体 eal_intr_thread_main() 函数具体如下:
1、epoll_create() 创建 epoll 模型。
2、将 intr_pipe 管道加入到 epoll 中。
3、遍历 intr_sources 链表,将所有 UIO 设备加入到 epoll 中。
4、在eal_intr_handle_interrupts() 函数中,在一个 for(;;) 死循环中,调用 epoll_wait() 阻塞模式监听事件。如果有事件发生,则调用 eal_intr_process_interrupts() 函数,最终会调用到相应 UIO 设备注册的中断处理函数。
static __attribute__((noreturn)) void *
eal_intr_thread_main(__rte_unused void*arg)
{
struct epoll_event ev;
/* host thread, never break out */
for (;;) {
/* build up the epoll fd with all descriptors we are to
* wait on then pass it to the handle_interrupts function
*/
static struct epoll_event pipe_event = {
.events = EPOLLIN | EPOLLPRI,
};
struct rte_intr_source *src;
unsigned numfds = 0;
/* create epoll fd */
int pfd = epoll_create(1);
if (pfd < 0)
rte_panic("Cannot create epoll instance\n");
pipe_event.data.fd = intr_pipe.readfd;
/*
* add pipe fd into wait list, this pipe is used to
* rebuild the wait list.
*/
if (epoll_ctl(pfd, EPOLL_CTL_ADD, intr_pipe.readfd, &pipe_event) <0) {
rte_panic("Error adding fd to %d epoll_ctl, %s\n", intr_pipe.readfd,strerror(errno));
}
numfds++;
rte_spinlock_lock(&intr_lock);
TAILQ_FOREACH(src, &intr_sources, next) {
if (src->callbacks.tqh_first == NULL)
continue; /* skip those with nocallbacks */
ev.events = EPOLLIN | EPOLLPRI;
ev.data.fd = src->intr_handle.fd;
/*
* add all the uio device filedescriptor
* into wait list.
*/
if (epoll_ctl(pfd, EPOLL_CTL_ADD, src->intr_handle.fd,&ev) < 0) {
rte_panic("Error adding fd%d epoll_ctl, %s\n", src->intr_handle.fd,strerror(errno));
}
else
numfds++;
} // end TAILQ_FOREACH(src, &intr_sources, next)
rte_spinlock_unlock(&intr_lock);
/* serve the interrupt */
eal_intr_handle_interrupts(pfd, numfds);
/*
* when we return, we need to rebuild the
* list of fds to monitor.
*/
close(pfd);
} // end for(;;)
}
二、中断注册
以 e1000 网卡为例说明。在网卡初始化的时候,会调用 rte_eth_dev_init() ---> eth_igb_dev_init() ---> rte_intr_callback_register() 注册中断处理函数。
rte_intr_callback_register(&(pci_dev->intr_handle),
eth_igb_interrupt_handler, (void*)eth_dev);
rte_intr_callback_register() 函数,主要工作如下:
1、首先申请一个 structrte_intr_source 变量。
struct rte_intr_source {
TAILQ_ENTRY(rte_intr_source) next;
struct rte_intr_handle intr_handle; /**<interrupt handle */
struct rte_intr_cb_list callbacks; /**< user callbacks */
uint32_t active;
};
2、将中断处理函数 eth_igb_interrupt_handler,添加到 rte_intr_source->callbacks 链表中。
3、再将该 rte_intr_source 挂到全局 intr_sources 链表中,方便中断处理线程遍历调用。
文章来源
http://www.cnblogs.com/MerlinJ/p/4104039.html