DPDK中断机制简析

 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值