【DPDK】dpdk样例源码解析之五:dpdk-rss

news2024/12/24 9:31:58

本篇文章介绍DPDK-RSS相关的功能,RSS是网卡提供的分流机制,简单讲就是一个HASH值,如果使用DPDK收包,开启RSS后,会根据配置项将数据包分流到不同的收包队列,用来是实现负载均衡。

通过DPDK-L3FWD样例,添加打印参数进行详细说明,大致分为以下流程:

1、DPDK如何开启RSS(开启和关闭位置,以及开启关闭影响参数);

2、如何查看网卡所支持的RSS选项;

3、DPDK如何配置不同的RSS选项(包括RSS对称算法,根据IP/UDP/TCP进行分流等)获取不同的RSS值;

4、如何通过DPDK携带的API接口获取RSS值(通过将IP五元组信息传入到API中获取到RSS值);

操作系统版本CentOS 8.4

DPDK版本dpdk-20.11.3

1、开启RSS

1.1、RSS开关

借助DPDK-L3FWD样例,在收包API调用之后,打印每个struct rte_mbuf,因为DPDK如果启用RSS时,struct rte_mbuf *m结构中有一个参数会附上数值,那就是rss,如下图所示:

在这里插入图片描述

通过打印m->hash.rss即可获取对应数据包的RSS

DPDK初始化网卡端口是会传入port_conf结构体,包含的有RSS相关内容,DPDK-L3FWD样例中,初始化网卡端口API如下:

在这里插入图片描述

查看port_conf结构:

在这里插入图片描述

结构体中各个参数解释如下:

static struct rte_eth_conf port_conf = {
	.rxmode = {	// rx收包相关
		.mq_mode = ETH_MQ_RX_RSS, 	//启用RSS, 注释掉该项则关闭RSS
		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
		.split_hdr_size = 0,
		.offloads = DEV_RX_OFFLOAD_CHECKSUM,
	},
	.rx_adv_conf = {
		.rss_conf = {		// RSS配置相关
			.rss_key = NULL,		// 如果不为空,则配置对称算法KEY
			.rss_hf = ETH_RSS_IP,	//根据IP进行hash
		},
	},
	.txmode = {	// tx发包相关
		.mq_mode = ETH_MQ_TX_NONE,
	},
};

DPDK-L3FWD默认是启用RSS的。

1.2、RSS打印

添加当接收到数据包时,打印该数据包的RSS值代码如下:

在这里插入图片描述

编译生成可执行文件,启动DPDK-L3FWD程序,对端重放数据包,数据包内如如下:

在这里插入图片描述

通过打印双向的数据包,查看双向获取到的RSS值是否一致:

在这里插入图片描述

这里可以发现,获取到的RSS值不同。后面会介绍为什么,以及怎么配置可以使获取到的RSS值相同

1.3、RSS关闭

DPDK-L3FWD关闭RSS,代码改动如下:

在这里插入图片描述

重现编译运行样例程序,对端重发同样的ICMP数据包,结果如下:

在这里插入图片描述

可以看到,获取到的RSS和预期的一样全为0。至此,DPDKRSS开关位置以及简单的配置已经介绍完毕,后面章节会详细一点介绍RSS相关的参数以及如何灵活配置达到自己的需求。

2、配置RSS

2.1、RSS结构体

struct rte_eth_conf结构体用于存放网卡RSS相关参数,其它详细信息可参考源代码中注释,写的很清楚,这里只介绍RSS相关:

mq_mode = ETH_MQ_RX_RSS用来标识开启RSS,开启RSS后,可以根据rss_conf结构参数进行HASH计算,包含3个参数,信息如下:

/**
 * A structure used to configure the Receive Side Scaling (RSS) feature
 * of an Ethernet port.
 * If not NULL, the *rss_key* pointer of the *rss_conf* structure points
 * to an array holding the RSS key to use for hashing specific header
 * fields of received packets. The length of this array should be indicated
 * by *rss_key_len* below. Otherwise, a default random hash key is used by
 * the device driver.
 *
 * The *rss_key_len* field of the *rss_conf* structure indicates the length
 * in bytes of the array pointed by *rss_key*. To be compatible, this length
 * will be checked in i40e only. Others assume 40 bytes to be used as before.
 *
 * The *rss_hf* field of the *rss_conf* structure indicates the different
 * types of IPv4/IPv6 packets to which the RSS hashing must be applied.
 * Supplying an *rss_hf* equal to zero disables the RSS feature.
 */
struct rte_eth_rss_conf {
	uint8_t *rss_key;    /**< If not NULL, 40-byte hash key. */
	uint8_t rss_key_len; /**< hash key length in bytes. */
	uint64_t rss_hf;     /**< Hash functions to apply - see below. */
};
字段描述
rss_key哈希key,如果为空,则使用网卡rss_key,如果不为空,则为40字节的数组用来作为has key
rss_key_len哈希key长度,rss_key数组字节数,为空则填写0,不为空则填写40
rss_hf哈希函数,用来标识根据IP进行哈希,还是根据IP+PORT进行哈希,还是根据IP+PORT+PROTOCOL进行哈希

2.2、RSS参数解释

常见的rss配置如下:

static struct rte_eth_conf port_conf = {
	.rxmode = {
		.mq_mode = ETH_MQ_RX_RSS,	// 启用RSS
        /* ... */
	},
	.rx_adv_conf = {
		.rss_conf = {
			.rss_key = NULL,		// 使用网卡默认rss_key, 一般都不能做到对称
			.rss_key_len = 0,		// rss_key数组长度
			.rss_hf = ETH_RSS_IP,	// 哈希函数,根据三层IP进行哈希
            		| ETH_RSS_UDP,	// 根据四层UDP协议进行哈希
                    | ETH_RSS_TCP,	// 根据四层TCP协议进行哈希
		},
	},
	/* ... */
};

其中rss_hf配置常用的三项参数,具体如下:

#define ETH_RSS_IP ( \
	ETH_RSS_IPV4 | \
	ETH_RSS_FRAG_IPV4 | \
	ETH_RSS_NONFRAG_IPV4_OTHER | \
	ETH_RSS_IPV6 | \
	ETH_RSS_FRAG_IPV6 | \
	ETH_RSS_NONFRAG_IPV6_OTHER | \
	ETH_RSS_IPV6_EX)
	
#define ETH_RSS_UDP ( \
	ETH_RSS_NONFRAG_IPV4_UDP | \
	ETH_RSS_NONFRAG_IPV6_UDP | \
	ETH_RSS_IPV6_UDP_EX)

#define ETH_RSS_TCP ( \
	ETH_RSS_NONFRAG_IPV4_TCP | \
	ETH_RSS_NONFRAG_IPV6_TCP | \
	ETH_RSS_IPV6_TCP_EX)

解释如下:按照上面配置,实现机制为,如果数据包四层传输层不是UDP或者TCP则按照三层网络层IP地址进行hash,对于源地址和目的地址相同的数据包获取到的RSS值相同,如果四层传输层是UDP或者TCP,怎按照IP+PROTOCOL进行哈希。不仅需要源地址和目的地址相同,还需要四层传输层相同。

2.3、RSS配置

可以看出,将RSSrss_key字段设置为NULL,会出现上下行数据包获取到的rss值不一样的问题,如何解决这个问题,就需要将rss_key这个字段设置一下,其中Intel 82599ES网卡使用ixgbe驱动,可以添加如下配置代码完成:

static uint8_t rss_intel_key[40] = {
	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 	
	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 	
	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 	
	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 	
	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 
};

static struct rte_eth_conf port_conf = {
	.rxmode = {
		.mq_mode = ETH_MQ_RX_RSS,
		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
		.split_hdr_size = 0,
		.offloads = DEV_RX_OFFLOAD_CHECKSUM,
	},
	.rx_adv_conf = {
		.rss_conf = {
			.rss_key = rss_intel_key,
			.rss_key_len = 40,
			.rss_hf = ETH_RSS_IP | ETH_RSS_UDP | ETH_RSS_TCP,
		},
	},
	.txmode = {
		.mq_mode = ETH_MQ_TX_NONE,
	},
};

重新编译代码,运行可执行程序,对端打包之后得到如下结果:

在这里插入图片描述

可以看到,这时得到的上下行数据包的rss值相同了。

3、生成RSS

由于rss这个值在网卡硬件接收阶段已经生成了,一次如果我们想要计算这个值,可以通过调用DPDK代码中API接口来实现,这样就可以通过软件方法得到和硬件一致的rss值了。

具体实现代码如下:

/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2016 Intel Corporation
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <setjmp.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <arpa/inet.h>
#include <stdbool.h>

#include <rte_common.h>
#include <rte_log.h>
#include <rte_malloc.h>
#include <rte_memory.h>
#include <rte_memcpy.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_atomic.h>
#include <rte_cycles.h>
#include <rte_prefetch.h>
#include <rte_lcore.h>
#include <rte_per_lcore.h>
#include <rte_branch_prediction.h>
#include <rte_interrupts.h>
#include <rte_random.h>
#include <rte_debug.h>
#include <rte_ether.h>
#include <rte_ethdev.h>
#include <rte_mempool.h>
#include <rte_mbuf.h>
#include <rte_string_fns.h>
#include <rte_ip.h>
#include <rte_thash.h>

#include "rss.h"

static volatile bool force_quit;

static void
signal_handler(int signum)
{
	if (signum == SIGINT || signum == SIGTERM) {
		printf("\n\nSignal %d received, preparing to exit...\n",
				signum);
		force_quit = true;
	}
}

static void get_tuple(void **tuple, char strsrcIpv4[], char strdstIpv4[], char strsrcIPV6[], char strdstIPV6[], 
	int *input_len) 
{
	//uint32_t input_len;
	//void *tuple;
	int ip_type = 0;
	struct rte_ipv4_tuple ipv4_tuple;
	struct rte_ipv6_tuple ipv6_tuple;
	struct rte_ipv4_hdr ipv4_hdr;
	struct rte_ipv6_hdr ipv6_hdr;
	memset(&ipv4_hdr, 0, sizeof(struct rte_ipv4_hdr));
	memset(&ipv6_hdr, 0, sizeof(struct rte_ipv6_hdr));
	memset(&ipv4_tuple, 0, sizeof(struct rte_ipv4_tuple));
	memset(&ipv6_tuple, 0, sizeof(struct rte_ipv6_tuple));

	inet_pton(AF_INET, strsrcIpv4, &ipv4_hdr.src_addr);
	inet_pton(AF_INET, strdstIpv4, &ipv4_hdr.dst_addr);

	inet_pton(AF_INET6, strsrcIPV6, &ipv6_hdr.src_addr);
	inet_pton(AF_INET6, strdstIPV6, &ipv6_hdr.dst_addr);

	//printf("src: %u, dst: %u\n", ipv4_hdr.src_addr, ipv4_hdr.dst_addr);

	ip_type = 4;
	
	if(ip_type == 4) {
		ipv4_tuple.src_addr = rte_be_to_cpu_32(ipv4_hdr.src_addr);
		ipv4_tuple.dst_addr = rte_be_to_cpu_32(ipv4_hdr.dst_addr);
		//*tuple = &ipv4_tuple;
		memcpy(*tuple, &ipv4_tuple, sizeof(ipv4_tuple));
		*input_len = RTE_THASH_V4_L3_LEN;
	} else if(ip_type == 6) {
		rte_thash_load_v6_addrs(&ipv6_hdr, (union rte_thash_tuple *)&ipv6_tuple);
		//*tuple = &ipv6_tuple;
		memcpy(*tuple, &ipv6_tuple, sizeof(ipv6_tuple));
		*input_len = RTE_THASH_V6_L3_LEN;
	} else {
		printf("IP type Error!\n");
	}
	//printf("input_len = %u\n", *input_len);
	return;
}


int
main(int argc, char **argv)
{
	(void)argc;
	(void)argv;
	
	uint32_t hash1, hash2;

	signal(SIGINT, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGPIPE, signal_handler);
	
	char strsrcIpv4[16] = {"10.20.90.237"};
	char strdstIpv4[16] = {"192.168.10.70"};
	
    char strsrcIPV6[64] = {"2409:891e:80:230b::86"};
    char strdstIPV6[64] = {"2409:891e:80:230b:ecd7:fca4:2de9:a288"};

	// 1.rss_key初始化
	rss_init();

	// 2.从参数中获取五元组信息(ip类型和源/目的IP)
	void *tuple1;
	void *tuple2;
	int input_len1, input_len2;
	get_tuple(&tuple1, strsrcIpv4, strdstIpv4, strsrcIPV6, strdstIPV6, &input_len1);
	get_tuple(&tuple2, strdstIpv4, strsrcIpv4, strdstIPV6, strsrcIPV6, &input_len2);
	
	// 3.获取RSS值
	hash1 = rss_hash_data((uint32_t *)tuple1, input_len1);
	hash2 = rss_hash_data((uint32_t *)tuple2, input_len2);
	
	printf("hash1 = %u\n", hash1);
	printf("hash2 = %u\n", hash2);

	return 0;
}

编译完成之后,直接执行,结果如下:

在这里插入图片描述

可以看到,软件计算出来的rss数值和硬件获取到的一样。

3.1、代码逻辑

简单介绍一下软件生成rss代码逻辑:

主要涉及到三个步骤:

1、rss_key初始化

这一步需要和DPDK中设置的rss_key保持一致,初始化接口如下:

在这里插入图片描述

2、从参数中获取五元组信息(ip类型和源/目的IP)

这一步主要为了得到五元组相关的信息,赋值完成之后调用rss_hash_data获取HASH值,即rss值,这里需关注几个参数:

参数1:IP地址,包括源/目的IP;

参数2:IP类型,IPV4或者IPV6

参数3:PORT值,包括源/目的PORT;(该测试样例中没有给PORT赋值)

参数4:input_len,这个值尤其重要,因为它决定了通过哪一层进行哈希,功能和rss_hf类似,具体赋值解释如下:

/**
 * length in dwords of input tuple to
 * calculate hash of ipv4 header only
 */
#define RTE_THASH_V4_L3_LEN	((sizeof(struct rte_ipv4_tuple) -	\
			sizeof(((struct rte_ipv4_tuple *)0)->sctp_tag)) / 4)

/**
 * length in dwords of input tuple to
 * calculate hash of ipv4 header +
 * transport header
 */
#define RTE_THASH_V4_L4_LEN	 ((sizeof(struct rte_ipv4_tuple)) / 4)

/**
 * length in dwords of input tuple to
 * calculate hash of ipv6 header only
 */
#define RTE_THASH_V6_L3_LEN	((sizeof(struct rte_ipv6_tuple) -       \
			sizeof(((struct rte_ipv6_tuple *)0)->sctp_tag)) / 4)

/**
 * length in dwords of input tuple to
 * calculate hash of ipv6 header +
 * transport header
 */
#define RTE_THASH_V6_L4_LEN	((sizeof(struct rte_ipv6_tuple)) / 4)

3、获取RSS

将第2步获取到的参数,传入到rss_hash_data接口,即可获取到对应的rss

4、总结

DPDK-L3FWD代码可参考文章,【DPDK】dpdk样例源码解析之三:dpdk-l3fwd_001

DPDK-RSS源代码下载链接

有问题欢迎在评论区探讨~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/67001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于Pytorch下载并进行部署

目录 1.关于Pytorch 2.Pytorch环境部署前提条件 3.部署方法 4.测试 1.关于Pytorch PyTorch是一个开源的Python机器学习库&#xff0c;基于Torch&#xff0c;用于计算机视觉、自然语言处理等应用程序。 2017年1月&#xff0c;由Facebook人工智能研究院&#xff08;FAIR&…

数据库设计 ER图

三个基本概念 Entity Entity Set表示一类事物&#xff0c;类似于面向对象中类的概念&#xff0c;而Entity Instance表示一个具体的事物&#xff0c;类似于对象的概念。 Entity中可以有属性(Attribute)&#xff0c;也可以与其他Entity之间存在关系(Relationship)。在设计数据库…

【最新版全插件】多功能同城优选小程序源码

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 1.为本地的线下商家提供线上销售渠道。一直以来本地商品、娱乐、休闲、旅游服务线上购买大家都是以美团为准。近几年来随着微信公众号、小程序的渗透力逐渐加强&#xff0c;越来越多的用…

【深度学习】实验6答案:图像自然语言描述生成(让计算机“看图说话”)

DL_class 学堂在线《深度学习》实验课代码报告&#xff08;其中实验1和实验6有配套PPT&#xff09;&#xff0c;授课老师为胡晓林老师。课程链接&#xff1a;https://www.xuetangx.com/training/DP080910033751/619488?channeli.area.manual_search。 持续更新中。 所有代码…

ElasticSearch实战

一、es集群的搭建 1.集群相关概念 单节点故障问题 单台服务器&#xff0c;往往都有最大的负载能力&#xff0c;超过这个阈值&#xff0c;服务器性能就会大大降低甚至不可用。单点的elasticsearch也是一样那单点的es服务器存在哪些可能出现的问题呢&#xff1f; 单台机器存储…

[附源码]计算机毕业设计基于springboot在线影院系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

ROS MoveIT1(Noetic)安装总结

前言 由于MoveIT2的Humble的教程好多用的还是moveit1的环境&#xff0c;所以又装了Ubutun20.04和ROS1&#xff08;Noetic&#xff09;。【2022年12月6日】 环境 系统&#xff1a;Ubutun20.04LTS Ros&#xff1a;Noetic 虚拟机&#xff1a;VMware 安装 ROS Noetic 安装教程…

【微信小程序】canvasToTempFilePath遇到的问题

在微信小程序开发中&#xff0c;经常需要将绘制好的canvas保存到本地&#xff0c;这就需要调用canvasToTempFilePath将canvas画布转为本地临时文件。遇到过的问题如下&#xff1a; 1.create bitmap failed 2.fail canvas is empty 这个问题就是canvas还没画为空拿不到转化的临…

Eclipse+Maven+Tomcat 集成开发环境配置

在Eclipse中创建的Dynamic Web Project 类型的Web 项目&#xff0c; 通过Run As -> Run on Server 可以添加本地安装的Tomcat&#xff0c;在Eclipse 中启动Tomcat 进行整合开发。 但是如果创建的是Maven类型的项目&#xff0c;如果没有导入额外的包还正常&#xff0c; 但是…

Nginx入门到搭建

前言 上一篇文章我们分享了Linux的软件安装以及项目后端的部署&#xff0c;本篇文章将要分享的内容是&#xff0c;Nginx的入门安装、反向代理、负载均衡等。 一、Nginx简介 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09; 代理服务…

如何让彩色网页变灰

如何让彩色网页变灰 在特殊的日子&#xff08;如清明节特殊纪念日等&#xff09;&#xff0c;需要让彩色网页变成灰色&#xff08;黑白色&#xff09;如下图所示&#xff0c;怎么做到呢&#xff1f; 下面先给出彩色正常的网页示例源码&#xff1a; <!DOCTYPE html> <…

消除数据库表中的重复组

重复组是在整个数据库表中重复的一系列字段/属性。大型和小型组织都面临着一个普遍的问题&#xff0c;这个问题可能会带来多种后果。例如&#xff0c;在不同区域中存在的同一组信息会导致数据冗余和数据不一致。而且&#xff0c;所有这些重复的数据可能会占用大量宝贵的磁盘空间…

【计算机图形学入门】笔记2:向量与线性代数(图形学中用到的线性代数)

02向量与线性代数&#xff08;图形学中用到的线性代数&#xff09;1.A Swift and Brutal Introduction to Linear Algebra!简单粗暴入门线性代数1.Graphics’ Dependencies 图形学依赖的一些知识2.Vectors 向量1.Dot product向量的点乘2.向量的叉乘Cross product3.矩阵Matrix4.…

Linux——进程并发控制(系统中的POSIX信息量机制、进程间通信)

目录 一、Linux系统中POSIX信号量机制 1、POSIX有名信号量 &#xff08;1&#xff09;常用函数 &#xff08;2&#xff09;有名信号量应用于多线程的例子 &#xff08;3&#xff09;有名信号量应用于多进程 2、POSIX无名信号量 &#xff08;1&#xff09;常用函数 &…

基于jsp+mysql+ssm大学生社交平台-计算机毕业设计

项目介绍 本系统需要满足校园网上社交方面的基本需要。需要实现用户所要求的功能&#xff0c;方便他们进行交流。在界面上力求做到美观、操作方面尽量避免由于会员操作不当带来系统的出错现象。对数据库操作的性能需要做到优化&#xff0c;数据库过大将会影响运行速度。编程过…

(四) Docker镜像

Docker镜像一、概述二、镜像加载原理三、镜像注意点四、Docker镜像commit操作五、总结一、概述 书面解释 是一种轻量级、可执行的独立软件包&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时…

开关电源环路稳定性分析(04)-电压控制模式

大家好&#xff0c;这里是大话硬件。 在前3节分析了一个开环电源是如何工作的&#xff0c;开环电源的弊端也很明显&#xff0c;无法维持输出的稳定&#xff0c;不能抗扰动&#xff0c;无法得到我们想要的电压等等。因此&#xff0c;开关电源的闭环环路对稳定性来说非常重要。 …

LeetCode简单题之统计共同度过的日子数

题目 Alice 和 Bob 计划分别去罗马开会。 给你四个字符串 arriveAlice &#xff0c;leaveAlice &#xff0c;arriveBob 和 leaveBob 。Alice 会在日期 arriveAlice 到 leaveAlice 之间在城市里&#xff08;日期为闭区间&#xff09;&#xff0c;而 Bob 在日期 arriveBob 到 l…

大数据:Storm和流处理简介

一、Storm 1.1 简介 Storm 是一个开源的分布式实时计算框架&#xff0c;可以以简单、可靠的方式进行大数据流的处理。通常用于实时分析&#xff0c;在线机器学习、持续计算、分布式 RPC、ETL 等场景。Storm 具有以下特点&#xff1a; 支持水平横向扩展&#xff1b;具有高容错…

信息安全技术 信息安全风险评估方法 汇总

概述 风险评估应贯穿于评估对象生命周期 各阶段中。评估对象生命周期各阶段中涉及的风险评估原则和方法昆一致的&#xff0c;但由干各阶段实施内容对象、安全需求不同.使得风险评估的对象、目的、要求等各方面也有所不同。在规划设计阶段&#xff0c;通过风险评估以确定评估对…