要在dpdk中使用中断,需要调用rte_eal_intr_init 来进行中断子系统初始化,但是这个函数不用用户调用,会在
环境初始化函数rte_eal_init 中被调用,用户只要调用rte_intr_callback_register 来注册callback即可
rte_eal_intr_init 源码分析如下:
int
rte_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_main
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 */
#新建一个新的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 void
eal_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 int
eal_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函数,下来看看怎么注册中断
int
rte_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 的结构如下,可以看到明显看到包含读和写fd
union intr_pipefds{
struct {
int pipefd[2];
};
struct {
int readfd;
int writefd;
};
};
从fd读数据到buffer的结构体如下:可以看到在dpdk中中断分为四类,分别是uio/vfio/timer/other
union 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 */
};
dpdk 中的中断
最新推荐文章于 2023-12-09 08:53:15 发布