zl程序教程

您现在的位置是:首页 >  其他

当前栏目

vpp IPsec with DPDK Cryptodev have buffer resource leak.

2023-02-19 12:21:06 时间

此刻是否已经准备下班或者已经在下班的路上,首先提前祝大家五一快乐,北京疫情正处于关键阶段,各位出行注意防护。

上个月底在运行环境上出现程序内存泄漏的问题,通过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