• 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设备的中断处理函数。

     1 int
     2 rte_eal_intr_init(void)
     3 {
     4     int ret = 0;
     5 
     6     /* init the global interrupt source head */
     7     TAILQ_INIT(&intr_sources);
     8 
     9     /**
    10      * create a pipe which will be waited by epoll and notified to
    11      * rebuild the wait list of epoll.
    12      */
    13     if (pipe(intr_pipe.pipefd) < 0)
    14         return -1;
    15 
    16     /* create the host thread to wait/handle the interrupt */
    17     ret = pthread_create(&intr_thread, NULL,
    18             eal_intr_thread_main, NULL);
    19     if (ret != 0)
    20         RTE_LOG(ERR, EAL,
    21             "Failed to create thread for interrupt handling
    ");
    22 
    23     return -ret;
    24 }

    中断线程执行主体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设备注册的中断处理函数。

     1 static __attribute__((noreturn)) void *
     2 eal_intr_thread_main(__rte_unused void *arg)
     3 {
     4     struct epoll_event ev;
     5 
     6     /* host thread, never break out */
     7     for (;;) {
     8         /* build up the epoll fd with all descriptors we are to
     9          * wait on then pass it to the handle_interrupts function
    10          */
    11         static struct epoll_event pipe_event = {
    12             .events = EPOLLIN | EPOLLPRI,
    13         };
    14         struct rte_intr_source *src;
    15         unsigned numfds = 0;
    16 
    17         /* create epoll fd */
    18         int pfd = epoll_create(1);
    19         if (pfd < 0)
    20             rte_panic("Cannot create epoll instance
    ");
    21 
    22         pipe_event.data.fd = intr_pipe.readfd;
    23         /**
    24          * add pipe fd into wait list, this pipe is used to
    25          * rebuild the wait list.
    26          */
    27         if (epoll_ctl(pfd, EPOLL_CTL_ADD, intr_pipe.readfd,
    28                         &pipe_event) < 0) {
    29             rte_panic("Error adding fd to %d epoll_ctl, %s
    ",
    30                     intr_pipe.readfd, strerror(errno));
    31         }
    32         numfds++;
    33 
    34         rte_spinlock_lock(&intr_lock);
    35 
    36         TAILQ_FOREACH(src, &intr_sources, next) {
    37             if (src->callbacks.tqh_first == NULL)
    38                 continue; /* skip those with no callbacks */
    39             ev.events = EPOLLIN | EPOLLPRI;
    40             ev.data.fd = src->intr_handle.fd;
    41 
    42             /**
    43              * add all the uio device file descriptor
    44              * into wait list.
    45              */
    46             if (epoll_ctl(pfd, EPOLL_CTL_ADD,
    47                     src->intr_handle.fd, &ev) < 0){
    48                 rte_panic("Error adding fd %d epoll_ctl, %s
    ",
    49                     src->intr_handle.fd, strerror(errno));
    50             }
    51             else
    52                 numfds++;
    53         }
    54         rte_spinlock_unlock(&intr_lock);
    55         /* serve the interrupt */
    56         eal_intr_handle_interrupts(pfd, numfds);
    57 
    58         /**
    59          * when we return, we need to rebuild the
    60          * list of fds to monitor.
    61          */
    62         close(pfd);
    63     }
    64 }

     二、中断注册

    以e1000网卡为例说明。在网卡初始化的时候,会调用rte_eth_dev_init()--->eth_igb_dev_init()--->rte_intr_callback_register()注册中断处理函数。

    1 rte_intr_callback_register(&(pci_dev->intr_handle),
    2     eth_igb_interrupt_handler, (void *)eth_dev);

    rte_intr_callback_register()函数,主要工作如下:

    1、首先申请一个struct rte_intr_source变量。

    1 struct rte_intr_source {
    2     TAILQ_ENTRY(rte_intr_source) next;
    3     struct rte_intr_handle intr_handle; /**< interrupt handle */
    4     struct rte_intr_cb_list callbacks;  /**< user callbacks */
    5     uint32_t active;
    6 };

    2、将中断处理函数eth_igb_interrupt_handler,添加到rte_intr_source->callbacks链表中。

    3、再将该rte_intr_source挂到全局intr_sources链表中,方便中断处理线程遍历调用。

    错误之处,欢迎指出。

    转载请标明转自http://www.cnblogs.com/MerlinJ/p/4104039.html 

  • 相关阅读:
    原生sql实现restful接口调用
    Yii框架实现restful 接口调用,增删改查
    http状态码
    封装一个使用cURL以POST方式请求https协议的公众方法
    YII框架实现 RBAC
    nginx开启fileinfo扩展
    微信登录
    Linux系统基础优化及常用命令
    Vim编辑器
    linux基本命令
  • 原文地址:https://www.cnblogs.com/MerlinJ/p/4104039.html
Copyright © 2020-2023  润新知