DPDK-vpp 一次mbuf地址被踩的定位思路

上个月解决了一个mbuf地址异常导致程序coredump的问题,问题定位前后花了将近一个月的时间,期间也是一点定位思路都没有,写这篇文章希望有同样问题的提供一种解决思路。

问题背景

在业务转发流程中vpp 发包模块mtu 9000字节,多mbuf单链表串联场景下,其中一个mbuf的next地址是非法地址,从而导致程序coredump异常。

异常调用栈分析

分析过程发现发包mubuf数组中tx_pkts[-1]的next是非法地址导致程序异常。通过vlib_buf的next_buffer反推下一个mbuf和当前mbuf->next地址不一致,确定next地址被修改。此问题在测试环境上大概率复现,并且地址中间2字节被清0.

DPDK-vpp 一次mbuf地址被踩的定位思路_第1张图片

Mar 30 16:48 coredump.hdp_wk_0.24145
(gdb) bt
#0  ixgbe_xmit_pkts (tx_queue=0x7ffadfe00480, tx_pkts=0x7fb46fb4bbf8, nb_pkts=474)
    at drivers/net/ixgbe/ixgbe_rxtx.c:860
#1  0x00007fb4d0d71161 in rte_eth_tx_burst (nb_pkts=<optimized out>, tx_pkts=<optimized out>, queue_id=1, port_id=<optimized out>)
    at install-hdp-native/dpdk/include/rte_ethdev.h:2791
#2  tx_burst_vector_internal (flag=0, tx_vector=<optimized out>, xd=0x7fb4701f2840, vm=<optimized out>)
    at build-data/../vnet/vnet/devices/dpdk/device.c:488
#3  dpdk_interface_tx (vm=<optimized out>, node=<optimized out>, f=<optimized out>)
    at build-data/../vnet/vnet/devices/dpdk/device.c:1799
#4  0x00007fb5265502aa in dispatch_node (vm=0x7fb46a883b08, node=0x7fb46fe799d8, type=<optimized out>, dispatch_state=<optimized out>, frame=<optimized out>, 
    last_time_stamp=3244754031693250) at build-data/../vlib/vlib/main.c:1002
#5  0x00007fb526550495 in dispatch_pending_node (vm=vm@entry=0x7fb46a883b08, p=0x7fb46ef35d68, last_time_stamp=<optimized out>)
    at build-data/../vlib/vlib/main.c:1130
#6  0x00007fb526573239 in vlib_worker_thread_internal (vm=0x7fb46a883b08) at build-data/../vlib/vlib/threads.c:1450
#7  vlib_worker_thread_fn (arg=<optimized out>) at build-data/../vlib/vlib/threads.c:1495
#8  0x00007fb4979f1c80 in clib_calljmp () at build-data/../hdpinfra/hdpinfra/longjmp.S:110
#9  0x00007fb388174bc0 in ?? ()
#10 0x00000000005200e7 in eal_thread_loop (arg=<optimized out>)
    at lib/librte_eal/linuxapp/eal/eal_thread.c:186
#11 0x0000000000000000 in ?? ()
(gdb)

最开始在分析业务配置时,通过测试口述只是一个简单的ipsec业务场景,所以我们也把重点放在ipsec业务流程上去。通过加一些调式手段分析了一周也没有发现任何疑点。期间也阅读了vpp mempool申请,释放、ring队列等等代码(使用的是比较老的版本,mbuf pool池还是dpdk上申请和管理的,这点和最新vpp源码有差异)。dpdk中配置中有config/common_base文件中有一些debug开关可以打开如下:

#
# Compile librte_mbuf
#
CONFIG_RTE_LIBRTE_MBUF=y
CONFIG_RTE_LIBRTE_MBUF_DEBUG=y #由n修改为y
CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
CONFIG_RTE_PKTMBUF_HEADROOM=128

#
# Compile librte_mempool
#
CONFIG_RTE_LIBRTE_MEMPOOL=y
CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=4096
CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=y  #由n修改为y
CONFIG_RTE_LIBRTE_MEMPOOL_ADDR_DEBUG=y

上述开关打开后,异常调用栈总是报错在从mempool中的ring队列取mbuf时或者从mempool cache中取mbuf时,mbuf地址是非法的。
后续也做了下面一个调试:

1、关闭mempool中的cache; 相当于每次都是从mempool的ring队列中取mbuf。
2、mempool get和put时候,增加对ring队列上mbuf地址有效性检查。
3、在vpp node节点处理调度时,增加对ring队列mbuf地址有效性检查。
4、在mempool创建后,确定ring队列上地址时有效的。
5、将vpp修改成只有一个单线程,只有一个main核情况,-------这个比较关键。

修改dpdk的patch如下。

/*vpp相关修改*/
diff --git a/hdp/vlib/vlib/main.c b/hdp/vlib/vlib/main.c
index 8da914c..86d4b7f 100755
--- a/hdp/vlib/vlib/main.c
+++ b/hdp/vlib/vlib/main.c
@@ -996,7 +996,14 @@ dispatch_node (vlib_main_t * vm,
                   b->pre_data[log_index] = node->node_index;
               }
             }
-          n = node->function (vm, node, frame);
+          {
+              extern void common_ring_check_mbuf(struct rte_mempool *mp);
+              struct rte_mempool *mp = vm->buffer_main->pktmbuf_pools[0];
+              common_ring_check_mbuf(mp);
+              n = node->function (vm, node, frame);
+              common_ring_check_mbuf(mp);
+          
+          }
         }
       else
           n = node->function (vm, node, frame);
/*dpdk相关修改*/
diff --git a/sdk/dpdk-19.11/config/common_base b/sdk/dpdk-19.11/config/common_base
index d2ae57b..ad5062a 100644
--- a/sdk/dpdk-19.11/config/common_base
+++ b/sdk/dpdk-19.11/config/common_base
@@ -816,7 +816,7 @@ CONFIG_RTE_LIBRTE_STACK=y
 CONFIG_RTE_LIBRTE_MEMPOOL=y
 CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=4096
 CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=y
-CONFIG_RTE_LIBRTE_MEMPOOL_ADDR_DEBUG=y
+CONFIG_RTE_LIBRTE_MEMPOOL_ADDR_DEBUG=n
 
 #
 # Compile Mempool drivers
diff --git a/sdk/dpdk-19.11/drivers/mempool/ring/rte_mempool_ring.c b/sdk/dpdk-19.11/drivers/mempool/ring/rte_mempool_ring.c
index e78a568..0bcab86 100644
--- a/sdk/dpdk-19.11/drivers/mempool/ring/rte_mempool_ring.c
+++ b/sdk/dpdk-19.11/drivers/mempool/ring/rte_mempool_ring.c
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: BSD-3-Clause
+ /* SPDX-License-Identifier: BSD-3-Clause
  * Copyright(c) 2010-2016 Intel Corporation
  */
 
@@ -9,12 +9,64 @@
 #include 
 #include 
 
+unsigned int start_check = 0;
+
+void common_ring_check_mbuf(struct rte_mempool *mp)
+{
+    struct rte_ring *r = (struct rte_ring *)mp->pool_data;
+    void** ring = (void**)&r[1];
+    unsigned index;
+    const unsigned size = r->size;
+    unsigned mbuf_num = (r->prod.tail) - (r->cons.head);
+    unsigned idx = (r->cons.head)&(r->mask);
+
+    if ((start_check == 0) || (mbuf_num == 0))
+    {
+        return;
+    }
+
+    if ((idx + mbuf_num) < size)
+    {
+        for (index = 0; index < mbuf_num; index++,idx++)
+        {
+            if (rte_mempool_from_obj(ring[idx]) != mp)
+            {
+                rte_panic("MEMPOOL: bad mbuf:%p\n",ring[idx]);
+            }
+        }
+    }
+    else
+    {
+        for (index = 0; idx < size; index++, idx++)
+        {
+            if (rte_mempool_from_obj(ring[idx]) != mp)
+            {
+                rte_panic("MEMPOOL: bad mbuf:%p\n",ring[idx]);
+            }
+        }
+
+        for (idx = 0; index < mbuf_num; index++, idx++)
+        {
+            if (rte_mempool_from_obj(ring[idx]) != mp)
+            {
+                rte_panic("MEMPOOL: bad mbuf:%p\n",ring[idx]);
+            }
+        }
+    }
+}
+
 static int
 common_ring_mp_enqueue(struct rte_mempool *mp, void * const *obj_table,
 		unsigned n)
 {
-	return rte_ring_mp_enqueue_bulk(mp->pool_data,
+
+
+    common_ring_check_mbuf(mp);
+	int ret = rte_ring_mp_enqueue_bulk(mp->pool_data,
 			obj_table, n, NULL) == 0 ? -ENOBUFS : 0;
+
+    common_ring_check_mbuf(mp);
+    return ret;
 }
 
 static int
@@ -74,12 +126,15 @@ common_ring_mc_ensure_legality(struct rte_mempool *mp,
 static int
 common_ring_mc_dequeue(struct rte_mempool *mp, void **obj_table, unsigned n)
 {
+    common_ring_check_mbuf(mp);
     int ret = rte_ring_mc_dequeue_bulk(mp->pool_data, obj_table, n, NULL);
     ret = ret == 0 ? -ENOBUFS : 0;
-
+    
     if (0 == ret) {
         ret = common_ring_mc_ensure_legality(mp, obj_table, n);
     }
+    common_ring_check_mbuf(mp);
+
     return ret;
 }

问题稳定复现:

上述修改后,我们确认每次调度完bfd报文处理后,出现mbuf地址异常,后续的bfd处理node代码进行分析,发现问题存在原因。
获取udp地址不对,
在这里插入图片描述
DPDK-vpp 一次mbuf地址被踩的定位思路_第2张图片
对上面代码中udp_header->checksum=0xefef,查询挂的地址中间2字节被修改成0xefef。到这里我们基本确认问题点。
但是疑问的为什么会修改到ring队列存储地址上?
经过计算确实偏移到了ring的地址上,报文长度是96字节,因为没有进行字节序转换,所以便宜量时0x6000,计算如下:
(gdb) p &ring[idx] #异常mbuf的地址。
$8 = (void **) 0x7ffadf831af8。
在这里插入图片描述

问题回顾

1、没有详细分析测试的配置,从问题结论来看,只有流量触发bfd相关流程,才会出现此问题。我们可以从简化配置入手分析问题会少走很多弯路。
2、在第二周~~“在vpp node节点处理调度时,增加对ring队列mbuf地址有效性检查~~ ”已经加了这个检测问题,但是当时环境还是多核,误认为是其他是其他node导致。所以在解决此问题最好配置成单线程模式。
3、了解底层数据内存分布结构–vlib_buffer ret_mbuf ring mempool等加速问题定位效率。

你可能感兴趣的:(VPP+DPDK)