此刻是否已经准备下班或者已经在下班的路上,首先提前祝大家五一快乐,北京疫情正处于关键阶段,各位出行注意防护。
上个月底在运行环境上出现程序内存泄漏的问题,通过vpp的日志打印和show error 信息确定了导致buffer泄漏的原因,目前vpp 21.01原生问题,此问题是和小组内几个同事一起分析定位的,但目前只是解决了导致泄漏的问题,并未从根本上定位产生根因,还需要持续跟踪。由于本人并未有Mallanox网卡的配置及使用经验,有遇到相同问题的欢迎一起讨论。从跟上解决它。下面就来展开说一下。问题链接:buffer又泄漏了,头大...。
show dpdk buffer 查询到socket0 可用buffer内存只有764个,并且show interface rx-error 错误信息中显示rx-no-buffer确定是存在buffer泄漏导致vpp无法收包。
name="dpdk_mbuf_pool_socket0" available = 764 allocated = 127236 total = 128000
name="dpdk_mbuf_pool_socket1" available = 125952 allocated = 2048 total = 128000
name="dpdk_mbuf_pool_socket2" available = 128000 allocated = 0 total = 128000
name="dpdk_mbuf_pool_socket3" available = 128000 allocated = 0 total = 128000
当前环境是4个numa节点,出问题的网卡绑定在numa0上,对应mbuf pool资源池就是dpdk_mbuf_pool_socket0。这里可以看出来dpdk mubuf资源是区分numa节点的,这个也是numa系统架构图决定的,在《深入浅出dpdk》2.9章节中有说明。电子书可以在:https://github.com/jin13417/dpdk-vpp-learning获取。
另外强烈推荐阅读CSDN博客中一篇文章《numa架构cpu拓扑结构》,对NUMA相关的概念node、cocket、core、thread基于cache分布情况都有详细的解读。
上面的CPU拓扑架构图还不完整,每个node都有一个对应的本地内存。假设node0的本地内存标记为mem0,node1的本地内存标记为mem1。mem0对于node0就是本地内存,mem1对于node0就是远端内存;反之对于mem1亦有类似关系
文章链接:https://blog.csdn.net/weijitao/article/details/52884422
buffer泄漏问题一般都存在vpp node节点针对异常场景的处理中,一般是通过show error [verbose] 信息中看到异常处理逻辑中的统计信息,结合统计信息来分析代码。
show error cli不只是统计异常信息,在一些功能模块中可以通过增加统计计数来确定vpp node节点在多包处理中是否存在问题。比如接口policer中增加多包处理,怎么能确定多包处理代码代码书写是否正确(写过vpp代码的都了解,多包处理一般都是通过复制黏贴来操作的,bi0 复制出bi1 bi2 bi3 复制过程中有一个未修改正确,就会导致报文存在问题,通过这种方式可以验证一下) show error [verbose] 带verbose会打印出详细每个worker线程的统计信息,不带则是各个worker核之间的总和。
当前问题从系统日志中查询到存在大量的ipsec的异常打印,通过异常打印确定到相关的模块及接口函数,如下图所示:
接下来就是重点排查dpdk_esp_decrypt_inline函数,打开代码开头处理逻辑就发现了问题,函数直接返回了,没有对buffer资源进行释放(vpp中能释放buffer资源的主要在drop节点,或者接口tx节点?)。
#当前版本19.01版本always_inline uword
dpdk_esp_decrypt_inline (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * from_frame, int is_ip6)
{
.....
ret = crypto_alloc_ops (numa, ops, n_left_from);
if (ret)
{
if (is_ip6)
vlib_node_increment_counter (vm, dpdk_esp6_decrypt_node.index,
ESP_DECRYPT_ERROR_DISCARD, 1);
else
vlib_node_increment_counter (vm, dpdk_esp4_decrypt_node.index,
ESP_DECRYPT_ERROR_DISCARD, 1);
/* Discard whole frame 直接返回了,没有回收buffer资源*/
return n_left_from;
}
.....
}
通过查询error计数统计,确定代码确实走到上面的分支,此问题就会导致网卡收发包mempool资源mbuf泄漏。
108522 dpdk-esp4-decrypt Not enough crypto operations, discarding frame
查阅了一下vpp的其他版本,在21.01版本中,发现已经修改了此问题。但是只是解决crypto_alloc_ops 申请失败导致dpdk mempool资源池buffer泄漏的问题,但是并未解决为什么会出现crypto_alloc_ops 失败的现象。只能通过阅读代码去分析dpdk cryptodev大致的处理逻辑,再结合打印日志,确定了导致crypto_alloc_ops失败的原因。
#vpp历史提交记录解决了alloc fail 导致的内存泄漏patch记录dpdk-ipsec: don't leak buffers on crypto alloc failure
Type: fix
Signed-off-by: Christian Hopps <chopps@labn.net>
Change-Id: I4dee2ea723631e1bd95b33a74b9431d984565aef
https://github.com/FDio/vpp/commit/f6cb04460465d48a155aa3363106a82d160c7328
通过分析代码了解了crypto_alloc_ops是有独立的报文缓存池mempool资源,和网卡收发包缓存池是独立的。
/*data->crypto_op 试试dpdk crypto dev mempool缓存池,缓存池buffer数量同dpdk接口收发包buffer数量*/
static_always_inline i32
crypto_alloc_ops (u8 numa, struct rte_crypto_op ** ops, u32 n)
{
dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
crypto_data_t *data = vec_elt_at_index (dcm->data, numa);
i32 ret;
ret = rte_mempool_get_bulk (data->crypto_op, (void **) ops, n);
/* *INDENT-OFF* */
data->crypto_op_get_failed += ! !ret;
/* *INDENT-ON* */
return ret;
}
继续回到了分析ipsec打印日志内容"payload 65508 not multiple of 16"。通过分析确认是异常场景下处理逻辑存在问题,异常时将原始报文送到error-drop节点丢弃,但是从dpdk crypto mempool申请的资源未释放掉,导致资源泄漏,引起crypto_alloc_ops函数申请mbuf资源失败。
diff --git a/src/plugins/dpdk/ipsec/esp_decrypt.c b/src/plugins/dpdk/ipsec/esp_decrypt.c
index 4981de334..3bbeab765 100644
--- a/src/plugins/dpdk/ipsec/esp_decrypt.c
+++ b/src/plugins/dpdk/ipsec/esp_decrypt.c
@@ -163,7 +163,6 @@ dpdk_esp_decrypt_inline (vlib_main_t * vm,
CLIB_PREFETCH (mb0, CLIB_CACHE_LINE_BYTES, STORE);
op = ops[0];
- ops += 1;
ASSERT (op->status == RTE_CRYPTO_OP_STATUS_NOT_PROCESSED);
dpdk_op_priv_t *priv = crypto_op_get_priv (op);
@@ -353,6 +352,7 @@ dpdk_esp_decrypt_inline (vlib_main_t * vm,
crypto_op_setup (is_aead, mb0, op, session, cipher_off, cipher_len,
0, auth_len, aad, digest, digest_paddr);
+ ops += 1;
trace:
if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
{
此问题vpp原生代码未解决,给vpp官方vpp-dev邮箱发送邮件询问,从2106版本开始ipsec dpdk crypto架构进行重构,支持异步模式,旧代码不再维护。 https://lists.fd.io/g/vpp-dev/topic/vpp2101_ipsec_with_dpdk/90172971
到此你是否以为问题已经彻底解决了?那为什么会出现"payload 65508 not multiple of 16"日志打印,很明显payload长度也是不正确的。此时排查了其他设备也存在类似的问题,对现场环境进行了抓包确认。
1、发送端ipsec报文是bfd报文。
2、接收端ipsec解密流程trace流程。
05:34:33:469054: dpdk-input
TwentyFiveGigabitEthernet37/0/0 rx queue 0
buffer 0x4f62: current data 0, length 136, free-list 0, clone-count 0, totlen-nifb 6, trace 0xc5b
ext-hdr-valid
next-buffer 0x863a, segment length 44, clone-count 0
next-buffer 0xcd86, segment length 34, clone-count 0
l4-cksum-computed l4-cksum-correct
PKT MBUF: port 0, nb_segs 2, pkt_len 142
buf_len 2176, data_len 136, ol_flags 0x180, data_off 128, phys_addr 0xc013d900
packet_type 0x291 l2_len 0 l3_len 0 outer_l2_len 0 outer_l3_len 0
rss 0x0 fdir.hi 0x0 fdir.lo 0x0
Packet Offload Flags
PKT_RX_IP_CKSUM_GOOD (0x0080) IP cksum of RX pkt. is valid
PKT_RX_L4_CKSUM_GOOD (0x0100) L4 cksum of RX pkt. is valid
Packet Types
RTE_PTYPE_L2_ETHER (0x0001) Ethernet packet
RTE_PTYPE_L3_IPV4_EXT_UNKNOWN (0x0090) IPv4 packet with or without extension headers
RTE_PTYPE_L4_UDP (0x0200) UDP packet
IP4: 50:98:b8:d3:aa:01 -> b8:59:9f:25:04:d8
UDP: 125.41.187.218 -> 10.144.223.138
tos 0x00, ttl 238, length 128, checksum 0xacb2
fragment id 0xfc9b
UDP: 4500 -> 4500
length 108, checksum 0x0000
05:34:33:469054: ethernet-input
05:34:33:469055: ip4-input-no-checksum
05:34:33:469055: ip4-lookup
05:34:33:469056: ip4-local
05:34:33:469056: ip4-udp-lookup
UDP: src-port 4500 dst-port 4500
05:34:33:469056: ipsec-if-input
IPSec43: spi 162cc3b6 seq 116066 data packet(next decrypt)
05:34:33:469057: dpdk-esp4-decrypt
cipher aes-cbc-256 auth sha1-96
ESP: spi 372032438, seq 116066
05:34:33:469066: error-drop
ip4-udp-lookup: blackholed packets
上面异常的ipsec报文在dpdk-esp4-decrypt node节点被丢弃掉,原因应该也是"payload 65508 not multiple of 16"。在dpdk-input节点打印vlib_buffer_t和rte_mbuf的信息中存在不匹配的地方。
1、报文长度只有142个字节,却存在多个mbuf链条。
2、rte_mbuf中信息nb_segs显示为2,而vlib_buffer_t结构打印了三个buffer索引。
排查了一下show trace流程中打印函数是否存在异常导致,并没有发现什么问题?目前怀疑是dpdk mlx5 pmd驱动存在问题,查询了一下dpdk pmd近期几个版本的改动,收包函数代码并没有大的变更。mlx5 PMD整体代码变更还是比较大的。此问题原因未知。当前使用配置环境如下?有遇到类似的问题欢迎一起交流。
使用的Mallanox网卡,dpdk版本18.11
0000:a3:00.0 'MT27710 Family [ConnectX-4 Lx]' if=ens9f0 drv=mlx5_core unused=igb_uio,vfio-pci,uio_pci_generic
[root@jinsh11 ]# ethtool -i ens9f0
driver: mlx5_core
version: 4.5-1.0.1 #固件版本
firmware-version: 14.22.1002 (HUA0000000023)
expansion-rom-version:
bus-info: 0000:a3:00.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: yes
[root@jinsh11]# uname -r #内核版本
4.19.5
扩展:
ipsec报文处理逻辑中并不支持多mbuf存储的情况,一般的处理都是在解密流程中对报文进行转换,事先申请一片大内存10000字节,将多mbuf报文按顺序拷贝到缓存中,再进行解密。
针对tcp报文解决思路就相对简单一些,tcp协议是通过修改tcp报文mss字段来告诉对端缓存区的长度,相关分析可以看这篇文章:learning:tcp mss clamp learning:MSS application in IPSec tunnel
本文分享自 DPDK VPP源码分析 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!