在不支持 sse3 及其以后的 x86_64 向量指令的环境中运行 dpdk 程序的修改思路
问题描述
某 qemu 虚机环境使用 qemu 模拟的 cpu,不支持 SSSE3、SSE4 及之后的向量指令,运行 dpdk 程序后有如下报错信息:
ERROR: This system does not support "SSSE3".
Please check that RTE_MACHINE is set correctly.
EAL: FATAL: unsupported cpu type.
运行环境的 cpu 支持的 cpuflags 如下:
fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pse36 clflush mmx fxsr sse sse2 syscall nx lm rep_good nopl xtopology cpuid tsc_known_freq pni cx16 x2apic hypervisor lahf_lm cpuid_fault pti
确认仅支持 sse、sse2 以及 pni(SSE3)向量指令,其余更高级的指令并不支持。这里需要值得一提的是 pni 标志,全称是 Prescott New Instructions,实际是 SSE3 指令集的别名。
备注:使用的 dpdk 版本为 19.11,虚拟机使用的网卡是 virtio 网卡,下文内容均基于此版本实现展开。
dpdk 中检测编译配置的 cpuflags 与运行时机器的 cpuflags 一致行的过程
dpdk 项目中 mk/rte.cpuflags.mk 负责生成 RTE_COMPILE_TIME_CPULAGS 宏的内容,并为 dpdk 源程序添加此宏定义。
一般在 dpdk 编译之前会使用脚本指定不同的编译类型生成不同的编译配置 .config 文件,.config 文件由 config 目录下保存的文件模版生成而来,其中保存了 RTE_MACHINE 的配置内容,部分列举如下:
config/defconfig_x86_64-native-linux-gcc:6:CONFIG_RTE_MACHINE="native"
config/defconfig_x86_64-native-bsdapp-clang:6:CONFIG_RTE_MACHINE="native"
config/defconfig_arm64-octeontx2-linux-gcc:7:CONFIG_RTE_MACHINE="octeontx2"
...........................................................................
CONFIG_RTE_MACHINE 内容在编译脚本中被使用,其值作为子目录名称,在 mk/machine/ 目录中配置 MACHINE_CFLAGS 变量以获取到 gcc -march 选项的值配置,指定 machine 的类型。native 类型的配置如下:
MACHINE_CFLAGS = -march=native
此后 dpdk mk/rte.cpuflags.mk 脚本调用 gcc -dM -E 并添加 MACHINE_CFLAGS 及其它相关变量来生成编译目标对依赖的不同 cpuflags 的支持情况并配置 CPUFLAGS 变量,配置示例如下:
AUTO_CPUFLAGS := $(shell $(CC) $(MACHINE_CFLAGS) $(WERROR_FLAGS) $(EXTRA_CFLAGS) -dM -E - < /dev/null)
# adding flags to CPUFLAGS
ifneq ($(filter $(AUTO_CPUFLAGS),__SSE__),)
CPUFLAGS += SSE
endif
ifneq ($(filter $(AUTO_CPUFLAGS),__SSE2__),)
CPUFLAGS += SSE2
endif
................................................
AUTO_CPUFLAGS 指定的命令行的运行示例如下:
[root@localhost mk]$ gcc -march=native -dM - -E < /dev/null | grep SSE
#define __SSE4_1__ 1
#define __SSE4_2__ 1
#define __SSE2_MATH__ 1
#define __SSE_MATH__ 1
#define __SSE2__ 1
#define __SSSE3__ 1
#define __SSE__ 1
#define __SSE3__ 1
能够看到 gcc 会生成目标机器支持的 cpuflags 的内部宏定义,dpdk 通过判断这些宏来设置 CPUFLAGS,最后用 CPUFLAGS 转化设置为 RTE_COMPILE_TIME_CPUFLAGS,其值示例如下:
RTE_CPUFLAG_SSE,RTE_CPUFLAG_SSE2,RTE_CPUFLAG_SSE3,RTE_CPUFLAG_SSSE3,RTE_CPUFLAG_SSE4_1,RTE_CPUFLAG_SSE4_2
同时 dpdk 会为每个 CPUFLAGS 设置 RTE_MACHINE_CPUFLAG_XXX 宏作为编译参数(XXX 为单个 CPUFLAG 的名称),供 dpdk 内部代码使用。
dpdk 内部 rte_cpu_is_supported 函数中引用了 RTE_COMPILE_TIME_CPUFLAGS 宏,保存了编译时的 cpuflags。rte_cpu_is_supported 函数在运行时会检测运行环境的 cpu 是否支持 RTE_COMPILE_TIME_CPUFLAGS 宏定义的每个依赖的 cpuflags,不支持则打印错误信息,dpdk 程序终止运行。
备注:gcc 官方手册中对 -march 指定 native 参数的解释:
-march=native
This selects the CPU to generate code for at compilation time by determining the processor type of the compiling machine. Using -march=native enables all instruction subsets supported by the local machine (hence the result might not run on different machines). Using -mtune=native produces code optimized for the local machine under the constraints of the selected instruction set.
dpdk 中对向量指令的使用情况是怎样的?
在 dpdk-19.11 对向量指令的使用情况 这篇文章中,我列举了 dpdk-19.11 对向量指令的使用情况,有了这个输入,就清楚了哪里会产生问题。
如何让 dpdk 程序去掉对 SSSE3、SSE4 等更高级指令的使用?
1. 从编译上识别依赖,修改 .config 文件关闭相关模块
在编译前指定 EXTRA_CFLAGS 变量值为 -march=nocona,从 gcc 官方手册中查到,此 machine 支持 MMX、SSE、SSE2、SSE3 与 FXSR 指令集与目标虚拟机支持的向量指令基本一致。
识别后关闭能够关闭的模块。
2. 对于必须使用的模块,单独修改 Makefile 文件配置单独的 -march 并分析如何避免使用向量指令
有两个主要示例:
- rte_memcpy.h 中 rte_memcpy 函数替换为标准的 memcpy 函数
- rte_hash 模块,修改代码中调用 hash_create 的逻辑,在 hash 创建后调用 rte_hash_set_cmp_func 函数将 cmp 函数设置为 memcpu 不使用向量指令实现
总结一下,修改思路如下:
- 识别依赖 ssse3、sse4 及更高级向量指令的内容,优先在编译时关闭
- 不能关闭的配置正常编译 ssse3、sse4 等向量指令,配置不使用向量指令的执行逻辑,如 hash 模块、acl 模块等