k8s 多 pod 环境运行 dpdk 多进程问题

k8s 多 pod 环境运行 dpdk 多进程问题

问题描述

在 k8s 多个 pod 中运行 dpdk secondary 进程时,启动某个 dpdk secondary 进程时有如下报错信息:

EAL: Cannot initialize local memory map
EAL: FATAL: Cannot init memory
EAL: Cannot init memory

dpdk 版本:dpdk-19.11

初步排查

  1. 排查大页配置
    配置正常
  2. 排查 /dev/hugepages 目录共享配置
    配置正常

扩大调试信息

修改 dpdk secondary 进程 eal 启动参数,添加 –log-level=lib.eal:debug 选项,重新启动 pod,此时报错信息如下:

EAL: rte_fbarray_init(): couldn't lock /var/run/dpdk/rte/fbarray_memseg-2048k-0-0_1: Resource temporarily unavailable
EAL: Cannot initialize local memory map
EAL: FATAL: Cannot init memory
EAL: Cannot init memory

报错信息表明获取 /var/run/dpdk/rte 目录下特定文件的 lock 失败,需要阅读代码进一步定位。

阅读代码

函数调用栈

此问题中,函数调用栈如下所示:

rte_eal_init
	rte_eal_memory_init
		eal_memalloc_init
			rte_memseg_list_walk
				secondary_msl_create_walk
					rte_fbarray_init

rte_memseg_list_walk 函数遍历 mcfg->memsegs 中的所有 rte_memseg_list,执行 secondary_msl_create_walk 函数,为每一个 secondary 进程以 pid 为后缀在 /var/run/dpdk/rte 下面创建 fbarray_memseg-xxx_pid 文件。

问题出在 secondary_msl_create_walk 函数调用 rte_fbarray_init 初始化一个 fbarray 结构过程中,相关代码如下:

/* create distinct fbarrays for each secondary */
	snprintf(name, RTE_FBARRAY_NAME_LEN, "%s_%i",
		primary_msl->memseg_arr.name, getpid());

	ret = rte_fbarray_init(&local_msl->memseg_arr, name,
		primary_msl->memseg_arr.len,
		primary_msl->memseg_arr.elt_sz);
	if (ret < 0) {
		RTE_LOG(ERR, EAL, "Cannot initialize local memory map\n");
		return -1;
	}

上述代码中,使用 memseg_arr 中的 name 字段与进程的 pid 拼接成 name 字段,对照上文添加了 debug 信息后的输出,fbarray_memseg-2048k-0-0 为 memseg_arr 中 name 字段的值,1 为进程的 pid。

rte_fbarray_init 函数中关键代码如下:

   else if (flock(fd, LOCK_EX | LOCK_NB)) {
			RTE_LOG(DEBUG, EAL, "%s(): couldn't lock %s: %s\n",
					__func__, path, strerror(errno));
			rte_errno = EBUSY;
			goto fail;
		}

		/* take out a non-exclusive lock, so that other processes could
		 * still attach to it, but no other process could reinitialize
		 * it.
		 */
		if (flock(fd, LOCK_SH | LOCK_NB)) {
			rte_errno = errno;
			goto fail;
		}

第一次 flock 函数调用以非阻塞方式获取 fbarray_memseg-xxx-pid 文件的互斥锁,第二次 flock 函数调用以非阻塞方式获取 fbarray_memseg-xxx-pid 文件的共享锁。

第一次 flock 函数调用保证每个 secondary 自己的 fbarray_memseg 映射文件只被初始化一次,第二次 flock 调用保证在当前 secondary 进程初始化完成后 fbarray_memseg-xxx-pid 文件能够被其它进程 attach。

根本原因是什么?

在 k8s 多个 pod 中运行的 dpdk secondary 进程有自己独立的 pid namespace 空间,然而却需要在同一个共享目录—— /var/run/dpdk/rte 中以 pid 为后缀区分创建独立的文件名并获取互斥锁。

每个 pod 中运行的 dpdk secondary 进程的 pid 可能会相同,此时只能有一个进程能够获取到互斥锁,其它 pid 相同的进程会启动失败。

解决方案

方案一:

修改 k8s pod 配置,dpdk 进程所在的 pod 共享同一个 pid namespace。

方案二:

修改 dpdk 代码,使用其它标识保障 fbarray_memseg 映射文件的唯一性。

扩展问题

dpdk lib 源码目录中是否还存在其它使用进程 pid 区分的实现?

dpdk mp_channel 框架会为每个 dpdk 进程在 /var/run/dpdk/rte 目录中 bind 一个 unix 本地套接字,dpdk secondary 进程本地套接字名称生成代码如下:

if (rte_eal_process_type() == RTE_PROC_SECONDARY)
                snprintf(peer_name, sizeof(peer_name),
                                "%d_%"PRIx64, getpid(), rte_rdtsc());

上述代码使用 pid + tsc 时间来标识,即就两个进程的 pid 相同,只要在同一个机器上运行,tsc 的值是不同的,这样也不会存在冲突。

既然如此,是否可以修改 fbarray_memseg 后缀生成代码,也加上 tsc 来唯一标识呢?

答案是 yes,上文中的方案二可以使用此种方案实现。

总结

本文描述了 dpdk 多进程程序在 k8s 环境运行的一个问题,从 dpdk 代码实现上来看,dpdk 对这种场景的支持并不好,可能还存在其它问题。上半年我写的 程序启动顺序引发的血案之 dpdk 进程死锁 这篇博客描述了 dpdk 多进程共享用户态锁潜在的死锁问题,在这个场景中也存在。这些问题比较隐晦,缺少对 dpdk 底层原理的理解则很难在方案设计前期识别出来,是一个风险点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DPDK提供了一个示例应用程序l3fwd,它可以用于转发IPv4流量。以下是在Linux环境运行l3fwd的步骤: 1. 安装DPDK并设置环境变量。 2. 绑定网卡。使用DPDK需要将网卡与igb_uio驱动程序绑定。可以使用以下命令绑定网卡: sudo $RTE_SDK/usertools/dpdk-devbind.py --bind=igb_uio eth1 其中,eth1是要绑定的网卡名称。 3. 设置Hugepage。DPDK需要使用大页来提高性能。可以使用以下命令设置大页: sudo mkdir -p /mnt/huge sudo mount -t hugetlbfs nodev /mnt/huge sudo echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages 这将创建一个名为/mnt/huge的目录,并在其中创建1024个2MB大页。 4. 编译l3fwd程序。可以使用以下命令编译l3fwd程序: cd $RTE_SDK/examples/l3fwd make 5. 运行l3fwd程序。可以使用以下命令运行l3fwd程序: sudo ./build/l3fwd -l 0-3 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2),(2,0,3)" --parse-ptype 其中,-l选项指定要使用的CPU核心,-n选项指定要使用的内存通道数,-p选项指定要使用的网卡端口,--config选项指定端口与CPU核心的映射关系,--parse-ptype选项指定要解析的协议类型。 6. 测试l3fwd程序。可以使用以下命令向l3fwd程序发送流量: sudo $RTE_SDK/examples/l3fwd/build/app/testpmd -c 0x3 -n 4 -- -i --portmask=0x3 --forward-mode=io --auto-start 然后在testpmd程序中输入start命令开始发送流量。 以上是在Linux环境运行l3fwd程序的步骤。请注意,具体的命令和参数可能因系统配置不同而异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值