本文共 6717 字,大约阅读时间需要 22 分钟。
要在dpdk中使用中断,需要调用rte_eal_intr_init 来进行中断子系统初始化,但是这个函数不用用户调用,会在环境初始化函数rte_eal_init 中被调用,用户只要调用rte_intr_callback_register 来注册callback即可rte_eal_intr_init 源码分析如下:intrte_eal_intr_init(void){ int ret = 0, ret_1 = 0; char thread_name[RTE_MAX_THREAD_NAME_LEN]; /* init the global interrupt source head */ #初始化全局的中断源head TAILQ_INIT(&intr_sources); /** * create a pipe which will be waited by epoll and notified to * rebuild the wait list of epoll. */ #新建两个pipe分别用于读和写 if (pipe(intr_pipe.pipefd) < 0) { rte_errno = errno; return -1; } /* create the host thread to wait/handle the interrupt */ #建立一个thread来polling中断 ret = pthread_create(&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"); } else { /* Set thread_name for aid in debugging. */ snprintf(thread_name, RTE_MAX_THREAD_NAME_LEN, "eal-intr-thread"); #设置中断的名字为eal-intr-thread,这样就可以通过ps -ef 查询到这个name ret_1 = rte_thread_setname(intr_thread, thread_name); if (ret_1 != 0) RTE_LOG(DEBUG, EAL, "Failed to set thread name for interrupt handling\n"); } return -ret;}中断线程的回调函数为eal_intr_thread_mainstatic __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 */ #新建一个新的epoll 用于轮询中断 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. */ #为epoll添加fd 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); #下面这段code是为uio 驱动添加epoll的fd TAILQ_FOREACH(src, &intr_sources, next) { if (src->callbacks.tqh_first == NULL) continue; /* skip those with no callbacks */ ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP; ev.data.fd = src->intr_handle.fd; /** * add all the uio device file descriptor * 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++; } rte_spinlock_unlock(&intr_lock); /* serve the interrupt */ #fd添加完成后开始等待epoll 返回 eal_intr_handle_interrupts(pfd, numfds); /** * when we return, we need to rebuild the * list of fds to monitor. */ close(pfd); }}static voideal_intr_handle_interrupts(int pfd, unsigned totalfds){ struct epoll_event events[totalfds]; int nfds = 0; for(;;) { #等待epoll返回 nfds = epoll_wait(pfd, events, totalfds, EAL_INTR_EPOLL_WAIT_FOREVER); /* epoll_wait fail */ #小于零说明返回failed了 if (nfds < 0) { if (errno == EINTR) continue; RTE_LOG(ERR, EAL, "epoll_wait returns with fail\n"); return; } #等于零说明是timeout了,继续等待 /* epoll_wait timeout, will never happens here */ else if (nfds == 0) continue; /* epoll_wait has at least one fd ready to read */ #开始处理fd的read if (eal_intr_process_interrupts(events, nfds) < 0) return; }}static inteal_intr_process_interrupts(struct epoll_event *events, int nfds){ bool call = false; int n, bytes_read; struct rte_intr_source *src; struct rte_intr_callback *cb; union rte_intr_read_buffer buf; struct rte_intr_callback active_cb; for (n = 0; n < nfds; n++) { /** * if the pipe fd is ready to read, return out to * rebuild the wait list. */ if (events[n].data.fd == intr_pipe.readfd){ #开始读fd结果保存到buf.charbuf int r = read(intr_pipe.readfd, buf.charbuf, sizeof(buf.charbuf)); RTE_SET_USED(r); return -1; } if (call) { /* Finally, call all callbacks. */ TAILQ_FOREACH(cb, &src->callbacks, next) { /* make a copy and unlock. */ #得到注册中断时注册的callback函数,并传递并调用这个callback函数 active_cb = *cb; rte_spinlock_unlock(&intr_lock); /* call the actual callback */ active_cb.cb_fn(active_cb.cb_arg); /*get the lock back. */ rte_spinlock_lock(&intr_lock); } } } return 0;}原来中断处理就是等待epoll返回,然后调用注册fd时候的callback函数,下来看看怎么注册中断intrte_intr_callback_register(const struct rte_intr_handle *intr_handle, rte_intr_callback_fn cb, void *cb_arg){ int ret, wake_thread; struct rte_intr_source *src; struct rte_intr_callback *callback; wake_thread = 0; /* first do parameter checking */ #这三个参数中任何一个为null,则说明参数不合理,返回error if (intr_handle == NULL || intr_handle->fd < 0 || cb == NULL) { RTE_LOG(ERR, EAL, "Registering with invalid input parameter\n"); return -EINVAL; } #申请一个callback函数并赋值 /* allocate a new interrupt callback entity */ callback = rte_zmalloc("interrupt callback list", sizeof(*callback), 0); if (callback == NULL) { RTE_LOG(ERR, EAL, "Can not allocate memory\n"); return -ENOMEM; } callback->cb_fn = cb; callback->cb_arg = cb_arg; rte_spinlock_lock(&intr_lock); #检查这个handle对应的fd是否已经被注册过,如果已经注册则退出,说明同一个fd #不能被重复注册 /* check if there is at least one callback registered for the fd */ TAILQ_FOREACH(src, &intr_sources, next) { if (src->intr_handle.fd == intr_handle->fd) { /* we had no interrupts for this */ if TAILQ_EMPTY(&src->callbacks) wake_thread = 1; TAILQ_INSERT_TAIL(&(src->callbacks), callback, next); ret = 0; break; } } /* no existing callbacks for this - add new source */ if (src == NULL) { if ((src = rte_zmalloc("interrupt source list", sizeof(*src), 0)) == NULL) { RTE_LOG(ERR, EAL, "Can not allocate memory\n"); rte_free(callback); ret = -ENOMEM; } else { src->intr_handle = *intr_handle; TAILQ_INIT(&src->callbacks); TAILQ_INSERT_TAIL(&(src->callbacks), callback, next); #将callback插入到全局变量intr_sources 中 TAILQ_INSERT_TAIL(&intr_sources, src, next); wake_thread = 1; ret = 0; } } rte_spinlock_unlock(&intr_lock); /** * check if need to notify the pipe fd waited by epoll_wait to * rebuild the wait list. */ #前面成功注册的话,则这里会唤醒处理终端的线程,方法就是给pipe中写一个1 if (wake_thread) if (write(intr_pipe.writefd, "1", 1) < 0) return -EPIPE; return ret;}前面申请intr_pipefds 的结构如下,可以看到明显看到包含读和写fdunion intr_pipefds{ struct { int pipefd[2]; }; struct { int readfd; int writefd; };};从fd读数据到buffer的结构体如下:可以看到在dpdk中中断分为四类,分别是uio/vfio/timer/otherunion rte_intr_read_buffer { int uio_intr_count; /* for uio device */#ifdef VFIO_PRESENT uint64_t vfio_intr_count; /* for vfio device */#endif uint64_t timerfd_num; /* for timerfd */ char charbuf[16]; /* for others */};
转载地址:http://nnnmi.baihongyu.com/