zoukankan      html  css  js  c++  java
  • intel dpdk api interrupt module 中断模块介绍

    声明:此文档只做学习交流使用,请勿用作其他商业用途

    author:朝阳_tony
    E-mail : linzhaolover@gmail.com
    Create Date: 2013-7-12 11:46:21  Friday
    Last Change: 2013-7-12 16:35:36 Friday

    转载请注明出处:http://blog.csdn.net/linzhaolove

    此文中源码可以去http://dpdk.org/dev 网页中下载;更多官方文档请访问http://dpdk.org

    1、intel dpdk interrupt 模块的初始化

    首先调用rte_eal_intr_init()函数去初始化中断;

    1)、rte_eal_intr_init()函数详解

    [cpp] view plain copy
     
     print?
    1. /* init the global interrupt source head */  
    2.    TAILQ_INIT(&intr_sources);  

    首先初始化一个队列链表,intr_sources 是一个static struct rte_intr_source_list结构体,经过TAILQ_HEAD(rte_intr_source_list, rte_intr_source)初始化后的结构体

    [cpp] view plain copy
     
     print?
    1. TAILQ_HEAD(rte_intr_cb_list, rte_intr_callback);  
    2. TAILQ_HEAD(rte_intr_source_list, rte_intr_source);  
    3.   
    4. struct rte_intr_callback {  
    5.     TAILQ_ENTRY(rte_intr_callback) next;  
    6.     rte_intr_callback_fn cb_fn;  /**< callback address */  
    7.     void *cb_arg;                /**< parameter for callback */  
    8. };  
    9.   
    10. struct rte_intr_source {  
    11.     TAILQ_ENTRY(rte_intr_source) next;  
    12.     struct rte_intr_handle intr_handle; /**< interrupt handle */  
    13.     struct rte_intr_cb_list callbacks;  /**< user callbacks */  
    14. };  

    struct rte_intr_source 里面 包含一中断句柄,和一个中断回调函数链表;

    [cpp] view plain copy
     
     print?
    1. /** 
    2.  * create a pipe which will be waited by epoll and notified to 
    3.  * rebuild the wait list of epoll. 
    4.  */  
    5. if (pipe(intr_pipe.pipefd) < 0)  

    去创建一个管道,用于epoll的消息通知;

    [cpp] view plain copy
     
     print?
    1. /* create the host thread to wait/handle the interrupt */  
    2. ret = pthread_create(&intr_thread, NULL,  
    3.         eal_intr_thread_main, NULL);  

    创建中断管理线程,去等待响应处理中断;

    2)、中断线程管理函数详解

    eal_intr_thread_main()作为中断线程的管理函数;

    [cpp] view plain copy
     
     print?
    1. /* create epoll fd */  
    2. int pfd = epoll_create(1);  

    创建epoll功能文件描述符;dpdk是通过epoll消息机制进行通信,然后再从pipe管道中读取消息,完成dpdk中断机制的中断注册;

    [cpp] view plain copy
     
     print?
    1. pipe_event.data.fd = intr_pipe.readfd;  
    2. /** 
    3.  * add pipe fd into wait list, this pipe is used to 
    4.  * rebuild the wait list. 
    5.  */  
    6. if (epoll_ctl(pfd, EPOLL_CTL_ADD, intr_pipe.readfd,  
    7.                 &pipe_event) < 0) {  
    8.     rte_panic("Error adding fd to %d epoll_ctl, %s ",  
    9.             intr_pipe.readfd, strerror(errno));  
    10. }  

    将管道描述符赋值给epoll的结构体中;通过epoll_ctl去设置监控管道的读描述符intr_pipe.readfd;



    [cpp] view plain copy
     
     print?
    1. rte_spinlock_lock(&intr_lock);  
    2.   
    3. TAILQ_FOREACH(src, &intr_sources, next) {  
    4.     if (src->callbacks.tqh_first == NULL)  
    5.         continue; /* skip those with no callbacks */  
    6.   
    7.     ev.events = EPOLLIN | EPOLLPRI;  
    8.     ev.data.fd = src->intr_handle.fd;  
    9.   
    10.     /** 
    11.      * add all the uio device file descriptor 
    12.      * into wait list. 
    13.      */  
    14.     if (epoll_ctl(pfd, EPOLL_CTL_ADD,  
    15.             src->intr_handle.fd, &ev) < 0){  
    16.         rte_panic("Error adding fd %d epoll_ctl, %s ",  
    17.             src->intr_handle.fd, strerror(errno));  
    18.     }  
    19.     else  
    20.         numfds++;  
    21. }  
    22. rte_spinlock_unlock(&intr_lock);  

    这一段code应该是在使用uio设备时候才会运行;需要uio去注册中断函数;也会将uio设备文件描述符添加进去;

    uio设备是支持用户态驱动的一种linux内核机制,在采用uio后会在/dev目录下产生几个设备文件;

    [plain] view plain copy
     
     print?
    1. # ls /dev/uio*  
    2. /dev/uio0  /dev/uio1  /dev/uio2  /dev/uio3  

    dpdk 拥有自己的自旋锁

    [cpp] view plain copy
     
     print?
    1. rte_spinlock_lock(&intr_lock);  
    2. rte_spinlock_unlock(&intr_lock);  
    [cpp] view plain copy
     
     print?
    1. eal_intr_handle_interrupts(pfd, numfds);  

    通过上面这个函数去等待消息,读取中断注册信息;而 numfds这个参数是指,去等待几个描述符的消息 ;

    eal_intr_handle_interrupts定义在dpdk/lib/librte_eal/linuxapp/eal/eal_interrupts.c文件中;

    我们去看一下这函数的实现;

    [cpp] view plain copy
     
     print?
    1. nfds = epoll_wait(pfd, events, totalfds,  
    2.     EAL_INTR_EPOLL_WAIT_FOREVER);  

    此函数会调用epoll_wait去阻塞住,等待消息的到来;nfds是指消息到来的多少个;消息的内容是存储在events这个结构体数组中 ;

    [cpp] view plain copy
     
     print?
    1. /* epoll_wait has at least one fd ready to read */  
    2. if (eal_intr_process_interrupts(events, nfds) < 0)  
    3.     return;  

    在eal_intr_handle_interrupts再去调用eal_intr_process_interrupts去出处理真正的消息;

    介绍一下这个函数eal_intr_process_interrupts

    [cpp] view plain copy
     
     print?
    1. /** 
    2.  * if the pipe fd is ready to read, return out to 
    3.  * rebuild the wait list. 
    4.  */  
    5. if (events[n].data.fd == intr_pipe.readfd){  


    她会判断是否是管道传过来的消息,如果是管道消息,要去返回重建链表;重建的是   epoll的wait 的等待链表;

    一开始还没有中断注册,所以就直接注册了一个管道的epoll wait,后来了新的中断描述符注册,就需要重新建立一个epoll的等待列表;

    [cpp] view plain copy
     
     print?
    1. TAILQ_FOREACH(src, &intr_sources, next)  
    2.     if (src->intr_handle.fd ==  
    3.             events[n].data.fd)  
    4.         break;  

    判断是不是中断描述符中的消息;

    如果是中断描述符的信息,接下来,将调用的回调函数,赋值给中断链表;

    [cpp] view plain copy
     
     print?
    1. /* for this source, make a copy of all the callbacks, 
    2.  * then unlock the lock, so the callbacks can 
    3.  * themselves manipulate the list for future 
    4.  * instances. 
    5.  */  
    6. active_cb = 0;  
    7. memset(active_cbs, 0, sizeof(active_cbs));  
    8. TAILQ_FOREACH(cb, &src->callbacks, next)  
    9.     active_cbs[active_cb++] = *cb;  
    [cpp] view plain copy
     
     print?
    1. /** 
    2.  * Finally, call all callbacks from the copy 
    3.  * we made earlier. 
    4.  */  
    5. for (i = 0; i < active_cb; i++) {  
    6.     if (active_cbs[i].cb_fn == NULL)  
    7.         continue;  
    8.     active_cbs[i].cb_fn(&src->intr_handle,  
    9.             active_cbs[i].cb_arg);  
    10. }  


    最后调用回调函数去进行中断处理;

    2、中断模块的应用

    1、首先应该初始中断模块

    dpdk一般会在rte_eal_init()函数中调用rte_eal_intr_init()初始中断模块;

    2、注册回调函数

    [cpp] view plain copy
     
     print?
    1. int rte_intr_callback_register(struct rte_intr_handle *intr_handle, rte_intr_callback_fn cb, void *cb_arg)  

    struct rte_intr_handle *intr_handle 为中断函数句柄;

    rte_intr_callback_fn cb  这个参数为中断回调函数指针;

    void *cb_arg 这个参数是给回调函数传递参数的指针;

    在./lib/librte_eal/linuxapp/eal/eal_alarm.c 文件的第160行,运用了这个函数

    注册函数是在int rte_eal_alarm_set(uint64_t us, rte_eal_alarm_callback cb_fn, void *cb_arg) 函数中调用的;

    [cpp] view plain copy
     
     print?
    1. if (!handler_registered) {  
    2.     ret |= rte_intr_callback_register(&intr_handle,  
    3.             eal_alarm_callback, NULL);  
    4.     handler_registered = (ret == 0) ? 1 : 0;  
    5. }  
    [cpp] view plain copy
     
     print?
    1. static struct rte_intr_handle intr_handle = {.fd = -1 };  

    intr_handle 为alarm 在初始化时,定义初始化的一个全局的时钟中断句柄;

    eal_alarm_callback 为alarm 定义的回调函数指针;

    最后一个参数传递的为NULL ,应该是不传递任何参数;看了一下eal_alarm_callback 函数实现,的确没有用传递的参数,我们自己写程序时也要注意留接口,虽然当时不用,但可以为以后扩展使用;

  • 相关阅读:
    JDBC 基本语法总结
    MySQL 基本语法
    mysql学习第三天练习(日期和时间函数)
    mysql学习第三天练习(流程控制函数)
    mysql学习第三天笔记
    mysql学习第二天函数
    mysql学习第一天select
    mysql学习第一天
    Java石头剪刀布小游戏
    Java万年历
  • 原文地址:https://www.cnblogs.com/allcloud/p/5477606.html
Copyright © 2011-2022 走看看