配置 igb_uio
首先,需要确保内核加载了 igb_uio
模块,并将网卡绑定到 igb_uio
驱动。
1. 加载 igb_uio
模块
modprobe uio
insmod /path/to/dpdk/kmod/igb_uio.ko
2. 绑定网卡到 igb_uio
驱动
使用 DPDK 提供的 dpdk-devbind.py
工具将网卡绑定到 igb_uio
驱动。以下示例假设网卡的 PCI 地址是 0000:00:03.0
。
# 查看当前绑定情况
dpdk-devbind.py --status
# 绑定网卡到 igb_uio
dpdk-devbind.py --bind=igb_uio 0000:00:03.0
代码实现轮询模式
确保网卡已经绑定到 igb_uio
后,就可以编写 DPDK 应用程序,使用轮询模式处理数据包。以下是一个完整的示例代码:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <rte_common.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS (4096 - 1)
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
},
};
static inline void
port_init(uint16_t port, struct rte_mempool *mbuf_pool) {
struct rte_eth_conf port_conf = port_conf_default;
struct rte_eth_txconf txconf;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int retval;
uint16_t q;
/* 配置设备 */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0) {
rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n", retval, port);
}
/* 分配和设置 RX 队列 */
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd, rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0) {
rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n", retval, port);
}
}
/* 分配和设置 TX 队列 */
txconf = port_conf_default.txmode;
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd, rte_eth_dev_socket_id(port), &txconf);
if (retval < 0) {
rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n", retval, port);
}
}
/* 启动以太网端口 */
retval = rte_eth_dev_start(port);
if (retval < 0) {
rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n", retval, port);
}
/* 启用 RX 校验和 */
retval = rte_eth_promiscuous_enable(port);
if (retval != 0) {
rte_exit(EXIT_FAILURE, "rte_eth_promiscuous_enable:err=%d, port=%u\n", retval, port);
}
}
static __rte_noreturn void
main_loop(void) {
const uint16_t nb_ports = rte_eth_dev_count_avail();
struct rte_mbuf *bufs[BURST_SIZE];
uint16_t port;
while (1) {
/* 轮询所有端口的 RX 队列 */
for (port = 0; port < nb_ports; port++) {
uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE);
if (nb_rx > 0) {
uint16_t buf;
/* 处理接收到的包 */
for (buf = 0; buf < nb_rx; buf++) {
struct rte_mbuf *mbuf = bufs[buf];
// 在此处进行数据包处理
rte_pktmbuf_free(mbuf);
}
}
}
}
}
int
main(int argc, char **argv) {
struct rte_mempool *mbuf_pool;
uint16_t nb_ports;
uint16_t portid;
/* 初始化 EAL */
int ret = rte_eal_init(argc, argv);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
}
argc -= ret;
argv += ret;
/* 创建 mbuf 内存池 */
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL) {
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
}
nb_ports = rte_eth_dev_count_avail();
if (nb_ports == 0) {
rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
}
/* 初始化所有端口 */
RTE_ETH_FOREACH_DEV(portid) {
port_init(portid, mbuf_pool);
}
/* 进入主循环 */
main_loop();
return 0;
}
说明
- 加载
igb_uio
驱动并绑定设备:通过命令行工具完成,确保网卡资源映射到用户空间。 - 初始化 DPDK 环境:包括创建内存池、配置网卡等。
- 实现轮询模式的主循环:在主循环中,使用
rte_eth_rx_burst
函数轮询 RX 队列以读取数据包,避免使用中断。