DPDK 启用中断收包

DPDK默认的轮询模式在没有接收到报文的空载状态下也会占用100%的CPU,非常浪费电力。通过在低速率和空载状态下启用中断收包,在高速率下使用轮询收包便能兼顾性能与节能的目标。

以下代码基于DPDK 21.11.3的示例代码,examples/l3fwd-power/main.c,该程序提供了多种CPU、网卡节能的工作模式,以及动态CPU频率调节等节能手段。这里仅关注其中处理中断的部分(指定--legacy运行的模式),并将代码简化为网卡上一个端口一个接收队列的处理。

#define SUSPEND_THRESHOLD 300

static inline void turn_on_off_intr(uint16_t port_id, uint8_t queue_id, bool on) {
	if(on)
		rte_eth_dev_rx_intr_enable(port_id, queue_id);
	else
		rte_eth_dev_rx_intr_disable(port_id, queue_id);
}

// num 为接收队列数量
static int sleep_until_rx_interrupt(int num, int lcore) {
	static unsigned int sleep_count = 0;
	struct rte_epoll_event event[num];

	printf("lcore %u sleeps until interrupt triggers, times: %u\n", 
			lcore, sleep_count);
	sleep_count++;

	// timeout = -1, 代表不超时,在收到报文前一直等待
    // event[i].epdata.data 获取rte_eth_dev_rx_intr_ctl_q()设置的用户数据
	rte_epoll_wait(RTE_EPOLL_PER_THREAD, event, num, -1);

	return 0;
}

int main() {
	int ret;
	int port_id = 0;
	int queue_id = 0;

	struct rte_eth_conf port_conf;
	port_conf.intr_conf.rxq = 1;		// 允许接收中断队列
	rte_eth_dev_configure(portid, nb_rx_queue, n_tx_queue, &port_conf);

	rte_eth_dev_start(port_id);

	// data 参数用于传递用户数据给中断唤醒时处理,可将port和queue id传递过去,这里暂时设为NULL
	ret = rte_eth_dev_rx_intr_ctl_q(port_id, queue_id, 
									RTE_EPOLL_PER_THREAD, RTE_INTR_EVENT_ADD, NULL);
	if(ret < 0) {
		rte_exit(EXIT_FAILURE, "rte_eth_dev_rx_intr_ctl_q failed = %d\n", ret);
	}

	uint16_t nb_rx;
	uint16_t nb_tx;
	int idle = 0;		// 空闲计数
	
	// 收发包流程
	while(1) {
		nb_rx = rte_eth_rx_burst(...);
		nb_tx = rte_eth_tx_burst(...);

		if(nb_rx == 0) {
			if(idle++ < SUSPEND_THRESHOLD) {
				rte_delay_us(1);							// 也可以使用 rte_delay_us_sleep()
			}
			else {
				turn_on_off_intr(port_id, queue_id, 1);		// 开中断
				sleep_unti_rx_interrupt(1, rte_lcore_id()); // 等待收到报文,触发中断
				turn_on_off_intr(port_id, queue_id, 0);		// 关中断

				idle = 0;
			}
		}
	}
}

其他细节请查看DPDK的l3fwd-power示例代码,主要关注

main(), main_legacy_loop() / main_intr_loop(), event_register()函数,以及APP_MODE_LEGACY标志。

你可能感兴趣的:(计算机网络,c/c++,网络,服务器)