以下是DPDK 20.11版本初始化RSS,并将报文轮询放到不同接收队列的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sys/queue.h>
#include <rte_common.h>
#include <rte_ethdev.h>
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_RX_QUEUES 2
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = ETHER_MAX_LEN,
.split_hdr_size = 0,
.header_split = 0, /**< Header Split disabled */
.hw_ip_checksum = 1, /**< IP checksum offload enabled */
.hw_vlan_filter = 0, /**< VLAN filtering disabled */
.jumbo_frame = 0, /**< Jumbo Frame Support disabled */
.hw_strip_crc = 1, /**< CRC stripped by hardware */
},
.txmode = {
.mq_mode = ETH_MQ_TX_NONE,
},
};
int main(int argc, char **argv)
{
int ret;
uint16_t nb_ports;
uint16_t portid;
// 初始化EAL环境
ret = rte_eal_init(argc, argv);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error: Failed to initialize EAL (%s)\n", strerror(-ret));
}
// 获取可用端口数
nb_ports = rte_eth_dev_count_avail();
if (nb_ports == 0) {
rte_exit(EXIT_FAILURE, "Error: No available network ports\n");
}
// 配置每个端口
for (portid = 0; portid < nb_ports; portid++) {
struct rte_eth_conf port_conf;
struct rte_eth_rxconf rxq_conf[NUM_RX_QUEUES];
struct rte_eth_txconf txq_conf;
struct rte_eth_dev_info dev_info;
// 获取端口设备信息
memset(&dev_info, 0, sizeof(dev_info));
rte_eth_dev_info_get(portid, &dev_info);
// 设置rx和tx队列配置
memset(&rxq_conf, 0, sizeof(rxq_conf));
// 配置每个接收队列的rss功能
for (int i = 0; i < NUM_RX_QUEUES && dev_info.reta_size > RTE_ETH_RSS_RETA_SIZE_64; i++) {
uint8_t rss_key[] = {52,102,121,161,177};
uint8_t rss_hash_func =
ETH_RSS_IP | ETH_RSS_UDP | ETH_RSS_TCP |
ETH_RSS_SCTP | ETH_RSS_L2_PAYLOAD |
ETH_RSS_IPV6_EX | ETH_RSS_IPV6_TCP_EX |
ETH_RSS_IPV6_UDP_EX | ETH_RSS_IPV6_EX_L2_PAYLOAD |
ETH_RSS_NONFRAG_IPV4_OTHER |
ETH_RSS_NONFRAG_IPV4_UDP |
ETH_RSS_NONFRAG_IPV4_TCP |
ETH_RSS_NONFRAG_IPV6_OTHER |
ETH_RSS_NONFRAG_IPV6_UDP |
ETH_RSS_NONFRAG_IPV6_TCP;
ret = rte_eth_dev_rss_hash_update(portid,
rss_key,
sizeof(rss_key),
rss_hash_func);
if (ret < 0) {
rte_exit(EXIT_FAILURE,
"Error: Failed to update RSS hash functions for port %u and queue %d (%s)\n",
portid, i, strerror(-ret));
}
uint8_t rss_key_len = sizeof(rss_key);
uint16_t reta_size = dev_info.reta_size;
uint8_t reta[reta_size];
memset(reta, 0, sizeof(reta));
for (int j = 0; j < reta_size; j++) {
reta[j] = i % dev_info.nb_rx_queues;
}
ret = rte_eth_dev_rss_reta_update(portid,
reta,
reta_size);
if (ret < 0) {
rte_exit(EXIT_FAILURE,
"Error: Failed to update RSS redirect table for port %u (%s)\n",
portid, strerror(-ret));
}
// 配置rx队列
rxq_conf[i].rx_thresh.pthresh = 8;
rxq_conf[i].rx_thresh.hthresh = 8;
rxq_conf[i].rx_thresh.wthresh = 4;
rxq_conf[i].rx_free_thresh = 32;
}
memset(&txq_conf, 0, sizeof(txq_conf));
txq_conf.tx_rs_thresh = 32;
txq_conf.tx_free_thresh = 0;
txq_conf.tx_thresh.pthresh = 36;
txq_conf.tx_thresh.hthresh = 0;
txq_conf.tx_thresh.wthresh = 0;
// 初始化port configuration structure with default values
port_conf = port_conf_default;
// 配置端口并启动
ret = rte_eth_dev_configure(portid, NUM_RX_QUEUES, 1, &port_conf);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error: Failed to configure port %u (%s)\n", portid, strerror(-ret));
}
// 初始化rx和tx队列
for (int i = 0; i < NUM_RX_QUEUES; i++) {
ret = rte_eth_rx_queue_setup(portid, i, RX_RING_SIZE,
rte_eth_dev_socket_id(portid),
&rxq_conf[i],
rte_eth_rx_burst);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error: Failed to setup rx queue %d for port %u (%s)\n",
i, portid, strerror(-ret));
}
}
ret = rte_eth_tx_queue_setup(portid, 0, TX_RING_SIZE,
rte_eth_dev_socket_id(portid),
&txq_conf);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error: Failed to setup tx queue for port %u (%s)\n", portid,strerror(-ret));
}
// 启动端口
ret = rte_eth_dev_start(portid);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error: Failed to start port %u (%s)\n", portid,strerror(-ret));
}
}
}
此示例演示了如何初始化DPDK以使用RSS功能,并将报文轮询放到不同的接收队列中。在主函数中,我们首先调用rte_eal_init()来初始化EAL环境,然后获取可用端口数,并遍历每个端口以配置其rx和tx队列。
对于支持更大的RETAsize的设备,我们为每个接收队列启用RSS哈希功能,并将哈希键、哈希函数等参数传递给rte_eth_dev_rss_hash_update()函数。然后,我们使用rte_eth_dev_rss_reta_update()函数将每个接收队列的重定向表设置为其所在的队列编号。
对于每个接收队列,我们都有自己的rxq_conf结构体变量,并调用rte_eth_rx_queue_setup()来初始化它们。最后,我们启动端口并开始处理数据包。