searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

dpdk中断机制简介

2025-03-26 05:39:36
7
0

dpdk的中断是用户态的中断,实现方式是通过vfio或uio模块将内核的中断传递到用户态,并且dpdk实现的中断机制属于控制中断,用来实现一些控制操作,例如uio中断用来设置一些网卡的状态之类。网卡收发包过程,还是使用轮询的方式从网卡接收报文。

中断初始化

中断初始化主要在rte_eal_intr_init中完成

首先初始化intr_sources链表。所有设备的中断都挂在这个链表上,中断处理线程通过遍历这个链表,来执行设备的中断。

然后 创建intr_pipe管道,用于epoll模型的消息通知。

再创建线程intr_thread,线程的执行体是eal_intr_thread_main()函数,创建epoll模型,遍历intr_sources链表,监听已注册的所有设备的中断事件,并调用对应设备的中断处理函数。中断控制线程对应的例程为eal_intr_thread_main(),其使用了 epoll_create() 创建了一个epoll对象,然后使用 epoll_ctl() 将intr_pipe的读端的fd,以及中断源list中的对应的fd加入到epoll对象的fd interesting list中。然后使用 epoll_wait() 监听epoll对象的fd interesting list

static __rte_noreturn void *
eal_intr_thread_main(__rte_unused void *arg)
{
	/* 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) {
			struct epoll_event ev;

			if (src->callbacks.tqh_first == NULL)
				continue; /* skip those with no callbacks */
			memset(&ev, 0, sizeof(ev));
			ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
			ev.data.fd = rte_intr_fd_get(src->intr_handle);

			/**
			 * add all the uio device file descriptor
			 * into wait list.
			 */
			if (epoll_ctl(pfd, EPOLL_CTL_ADD,
					rte_intr_fd_get(src->intr_handle), &ev) < 0) {
				rte_panic("Error adding fd %d epoll_ctl, %s\n",
					rte_intr_fd_get(src->intr_handle),
					strerror(errno));
			}
			else
				numfds++;
		}
		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);
	}
}

中断注册和注销

可以通过 rte_intr_callback_register() 注册一个中断源以及对应的callback,注册完成后,中断控制线程会对其信号进行监听,内部会将中断源链表中的所有中断源描述符都加入到epoll实现的红黑树中, 当相应中断源有事件发生时,epoll会调用这些中断源注册的回调函数。
还可以通过 rte_intr_callback_unregister() 注销一个中断源的callback, 此时中断控制线程会停止对中断信号进行监听。相关示例代码如下

int
mlx4_intr_install(struct mlx4_priv *priv)
{
	const struct rte_eth_intr_conf *const intr_conf =
		&ETH_DEV(priv)->data->dev_conf.intr_conf;
	int rc;

	mlx4_intr_uninstall(priv);
	if (intr_conf->lsc | intr_conf->rmv) {
		if (rte_intr_fd_set(priv->intr_handle, priv->ctx->async_fd))
			return -rte_errno;

		rc = rte_intr_callback_register(priv->intr_handle,
						(void (*)(void *))
						mlx4_interrupt_handler,
						priv);
		if (rc < 0) {
			rte_errno = -rc;
			goto error;
		}
	}
	return 0;
error:
	mlx4_intr_uninstall(priv);
	return -rte_errno;
}

int
mlx4_intr_uninstall(struct mlx4_priv *priv)
{
	int err = rte_errno; /* Make sure rte_errno remains unchanged. */

	if (rte_intr_fd_get(priv->intr_handle) != -1) {
		rte_intr_callback_unregister(priv->intr_handle,
					     (void (*)(void *))
					     mlx4_interrupt_handler,
					     priv);
		if (rte_intr_fd_set(priv->intr_handle, -1))
			return -rte_errno;
	}
	rte_eal_alarm_cancel((void (*)(void *))mlx4_link_status_alarm, priv);
	priv->intr_alarm = 0;
	mlx4_rxq_intr_disable(priv);
	rte_errno = err;
	return 0;
}

相关函数调用图如下

dpdk_interrupt.png

0条评论
0 / 1000
刘****珂
5文章数
0粉丝数
刘****珂
5 文章 | 0 粉丝
刘****珂
5文章数
0粉丝数
刘****珂
5 文章 | 0 粉丝
原创

dpdk中断机制简介

2025-03-26 05:39:36
7
0

dpdk的中断是用户态的中断,实现方式是通过vfio或uio模块将内核的中断传递到用户态,并且dpdk实现的中断机制属于控制中断,用来实现一些控制操作,例如uio中断用来设置一些网卡的状态之类。网卡收发包过程,还是使用轮询的方式从网卡接收报文。

中断初始化

中断初始化主要在rte_eal_intr_init中完成

首先初始化intr_sources链表。所有设备的中断都挂在这个链表上,中断处理线程通过遍历这个链表,来执行设备的中断。

然后 创建intr_pipe管道,用于epoll模型的消息通知。

再创建线程intr_thread,线程的执行体是eal_intr_thread_main()函数,创建epoll模型,遍历intr_sources链表,监听已注册的所有设备的中断事件,并调用对应设备的中断处理函数。中断控制线程对应的例程为eal_intr_thread_main(),其使用了 epoll_create() 创建了一个epoll对象,然后使用 epoll_ctl() 将intr_pipe的读端的fd,以及中断源list中的对应的fd加入到epoll对象的fd interesting list中。然后使用 epoll_wait() 监听epoll对象的fd interesting list

static __rte_noreturn void *
eal_intr_thread_main(__rte_unused void *arg)
{
	/* 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) {
			struct epoll_event ev;

			if (src->callbacks.tqh_first == NULL)
				continue; /* skip those with no callbacks */
			memset(&ev, 0, sizeof(ev));
			ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
			ev.data.fd = rte_intr_fd_get(src->intr_handle);

			/**
			 * add all the uio device file descriptor
			 * into wait list.
			 */
			if (epoll_ctl(pfd, EPOLL_CTL_ADD,
					rte_intr_fd_get(src->intr_handle), &ev) < 0) {
				rte_panic("Error adding fd %d epoll_ctl, %s\n",
					rte_intr_fd_get(src->intr_handle),
					strerror(errno));
			}
			else
				numfds++;
		}
		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);
	}
}

中断注册和注销

可以通过 rte_intr_callback_register() 注册一个中断源以及对应的callback,注册完成后,中断控制线程会对其信号进行监听,内部会将中断源链表中的所有中断源描述符都加入到epoll实现的红黑树中, 当相应中断源有事件发生时,epoll会调用这些中断源注册的回调函数。
还可以通过 rte_intr_callback_unregister() 注销一个中断源的callback, 此时中断控制线程会停止对中断信号进行监听。相关示例代码如下

int
mlx4_intr_install(struct mlx4_priv *priv)
{
	const struct rte_eth_intr_conf *const intr_conf =
		&ETH_DEV(priv)->data->dev_conf.intr_conf;
	int rc;

	mlx4_intr_uninstall(priv);
	if (intr_conf->lsc | intr_conf->rmv) {
		if (rte_intr_fd_set(priv->intr_handle, priv->ctx->async_fd))
			return -rte_errno;

		rc = rte_intr_callback_register(priv->intr_handle,
						(void (*)(void *))
						mlx4_interrupt_handler,
						priv);
		if (rc < 0) {
			rte_errno = -rc;
			goto error;
		}
	}
	return 0;
error:
	mlx4_intr_uninstall(priv);
	return -rte_errno;
}

int
mlx4_intr_uninstall(struct mlx4_priv *priv)
{
	int err = rte_errno; /* Make sure rte_errno remains unchanged. */

	if (rte_intr_fd_get(priv->intr_handle) != -1) {
		rte_intr_callback_unregister(priv->intr_handle,
					     (void (*)(void *))
					     mlx4_interrupt_handler,
					     priv);
		if (rte_intr_fd_set(priv->intr_handle, -1))
			return -rte_errno;
	}
	rte_eal_alarm_cancel((void (*)(void *))mlx4_link_status_alarm, priv);
	priv->intr_alarm = 0;
	mlx4_rxq_intr_disable(priv);
	rte_errno = err;
	return 0;
}

相关函数调用图如下

dpdk_interrupt.png

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0