前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >探究分段场景下vlib_buf在收发包的处理

探究分段场景下vlib_buf在收发包的处理

作者头像
dpdk-vpp源码解读
发布2023-03-07 17:15:25
2.4K3
发布2023-03-07 17:15:25
举报
文章被收录于专栏:DPDK VPP源码分析DPDK VPP源码分析

在使用vpp老版本copy报文的时候,经常遇到mbuf泄露的问题,根本原因是在vlib_buffer分段场景下没有将rte_mbuf进行串联,导致dpdk发包时造成了泄漏。最新的版本已经彻底解决了此问题。下面来分析一下:

rte_mbuf、vlib_buf 关系及内存分布

首先,先来了解一下dpdk plugins。在plugins目录下除dpdk_plugin.so外,其他模块是不能直接访问dpdk相关函数的,vpp支持dpdk以静态和动态库的方式,具体在dpdk目录下CMakeLists.txt可以看到查询dpdk库。如下:

代码语言:javascript
复制
find_path(DPDK_INCLUDE_DIR PATH_SUFFIXES dpdk NAMES rte_config.h)
vpp_plugin_find_library(dpdk DPDK_LIB "libdpdk.a")
if (NOT DPDK_INCLUDE_DIR)
  message(WARNING "-- DPDK headers not found - dpdk plugin disabled")
  return()
endif()
if (NOT DPDK_LIB)
  vpp_plugin_find_library(dpdk DPDK_SHLIB "libdpdk.so")
  set(DPDK_IS_SHARED_LIB 1)
  message(WARNING "-- linking dpdk plugin against DPDK shared libs")
endif()

可以通过ldd命令确认当前使用静态库还是动态库.没有搜到libdpdk.so,就是使用的静态库方式。

代码语言:javascript
复制
 ldd /usr/lib/x86_64-linux-gnu/vpp_plugins/dpdk_plugin.so
        linux-vdso.so.1 (0x00007ffd901cb000)
        libnuma.so.1 => /usr/lib/x86_64-linux-gnu/libnuma.so.1 (0x00007fbf050a0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbf04caf000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fbf0a3c4000)

vpp其他模块是无法访问到dpdk相关的函数的,是因为vpp启动时以dlopen动态加载的so库,使用需要使用需要dlsym函数负责动态加载符号(函数),第一个参数是句柄(由dlopen时返回的),第二个参数就是给定的函数名称。类似如下:

代码语言:javascript
复制
//链接并打开动态库
void *handle = dlopen(dlib_path, RTLD_GLOBAL | RTLD_NOW);
//获取add函数
//注意:函数指针接收的add函数有几个参数和什么返回类型要一致
 CalculatorFuncPointer add_func = dlsym(handle, "add");
 int add_ret = add_func(10, 20);

vpp为提供使用dlsym的接口,但是可以使用另外一种方式,就是注册钩子函数来解决。可以参考线程创建的钩子函数:

代码语言:javascript
复制
 static vlib_thread_callbacks_t callbacks = {
  .vlib_launch_thread_cb = &dpdk_launch_thread,
  .vlib_thread_set_lcore_cb = &dpdk_thread_set_lcore,
};

static clib_error_t *
dpdk_thread_init (vlib_main_t * vm)
{
  vlib_thread_cb_register (vm, &callbacks);
  return 0;
}

VLIB_INIT_FUNCTION (dpdk_thread_init);

RteMbuf内存分布图如下:详细请阅读以前文章:vlib ----buffer pool 内存初始化(2)

分段场景下的串联关系如下(两段链表方式):

曾经遇到过使用dpdk来申请报文缓存地址,在ring队列中将rte_mbuf的内存地址写异常的问题,就是通过这种反相推理找到确认被踩的情况。

使用dpdk-收包接口函数

vpp默认以polling的方式来从dpdk绑定的网卡来收包,具体收包节点在src\plugins\dpdk\device\node.c文件。下面vpp从网卡收包节点的定义及节点函数说明。

代码语言:javascript
复制
VLIB_NODE_FN (dpdk_input_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
                vlib_frame_t * f)
{
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_t *xd;
  uword n_rx_packets = 0;
  vnet_device_input_runtime_t *rt = (void *) node->runtime_data;
  vnet_device_and_queue_t *dq;
  u32 thread_index = node->thread_index;
  /*轮询此cpu上的所有设备的输入或者中断。会存在一个cpu绑多个接口的情况
   * Poll all devices on this cpu for input/interrupts.
   */
  /* *INDENT-OFF* */
  foreach_device_and_queue (dq, rt->devices_and_queues)
    {
      xd = vec_elt_at_index(dm->devices, dq->dev_instance);
      n_rx_packets += dpdk_device_input (vm, dm, xd, node, thread_index,
                     dq->queue_id);
    }
  /* *INDENT-ON* */
  return n_rx_packets;
}

/* *dpdk-input节点定义* */
VLIB_REGISTER_NODE (dpdk_input_node) = {
  .type = VLIB_NODE_TYPE_INPUT,
  .name = "dpdk-input",
  .sibling_of = "device-input",
  .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
  /* Will be enabled if/when hardware is detected. */
  .state = VLIB_NODE_STATE_DISABLED,
  .format_buffer = format_ethernet_header_with_length,
  .format_trace = format_dpdk_rx_trace,
  .n_errors = DPDK_N_ERROR,
  .error_strings = dpdk_error_strings,
};

在dpdk_device_input函数中调用dpdkpmd收包通用接口从网卡描述符收取报文,具体流程可以参考:DPDK 网卡收包流程

下面分段报文的处理,需要通过rte_mbuf 串联关系讲vlib_buf也串联起来。

代码语言:javascript
复制
/*通过dpdk pmd接口从网卡收取报文 
get up to DPDK_RX_BURST_SZ buffers from PMD */
  while (n_rx_packets < DPDK_RX_BURST_SZ)
    {
      n = rte_eth_rx_burst (xd->port_id, queue_id,
                ptd->mbufs + n_rx_packets,
                DPDK_RX_BURST_SZ - n_rx_packets);
      n_rx_packets += n;

      if (n < 32)
    break;
    }
/*处理从网卡描述符收到的报文。*/
static_always_inline uword
dpdk_process_rx_burst (vlib_main_t * vm, dpdk_per_thread_data_t * ptd,
               uword n_rx_packets, int maybe_multiseg,
               u16 * or_flagsp)
{
....
while (n_left)
    {
      /*通过rte_mbuf的的搭配vlib-buf头*/
      b[0] = vlib_buffer_from_rte_mbuf (mb[0]);
      /*前64字节赋值模板*/
      vlib_buffer_copy_template (b[0], &bt);
      or_flags |= dpdk_ol_flags_extract (mb, flags, 1);
      flags += 1;

      b[0]->current_data = mb[0]->data_off - RTE_PKTMBUF_HEADROOM;
      n_bytes += b[0]->current_length = mb[0]->data_len;
      /*多分段的场景,需要通过rte_mbuf 串联关系讲vlib_buf也串联起来*/
      if (maybe_multiseg)
    n_bytes += dpdk_process_subseq_segs (vm, b[0], mb[0], &bt);
      /*跟踪记录node执行节点trace初始化*/
      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);

      /* next */
      mb += 1;
      n_left -= 1;
    }
....
}

使用dpdk 发包接口函数

对应的在plugins目录下dpdk模块文件中src\plugins\dpdk\device\device.c定义dpdk对应的设备类。

代码语言:javascript
复制
/*对应的通用的dpdk tx接口函数*/
VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
                         vlib_node_runtime_t * node,
                         vlib_frame_t * f)
/* *dpdk对应的设备类结构体描述* */
VNET_DEVICE_CLASS (dpdk_device_class) = {
  .name = "dpdk",
  .admin_up_down_function = dpdk_interface_admin_up_down,
  .subif_add_del_function = dpdk_subif_add_del_function,
  .rx_redirect_to_node = dpdk_set_interface_next_node,
  .mac_addr_change_function = dpdk_set_mac_address,
  .mac_addr_add_del_function = dpdk_add_del_mac_address,
  .format_flow = format_dpdk_flow,
  .flow_ops_function = dpdk_flow_ops_fn,
  .set_rss_queues_function = dpdk_interface_set_rss_queues,
};

在对应发包函数中会对分段vlib_buf的连对应的mbuf进行串联,以保证在调用dpdk库中对应网卡的pmd驱动发包接口使用。

代码语言:javascript
复制
      /*判断是否存在多段vlib_buf串联场景,将对应的mbuf也串联起来*/
      dpdk_validate_rte_mbuf (vm, b[0], 1);
      /*设置网卡tx 卸载功能*/
      dpdk_buffer_tx_offload (xd, b[0], mb[0]);

所以我们其他模块操作分段vlib_buf时,并不需要将考虑将rte_mbufd的进行处理。比如vlib_buffer_copy中只是将vlib-buf头进行也串联。

在项目开发中遇到一个使用vlib_buffer_copy的问题,就是不会赋值current_config_index字段(用于获取当前节点的next0节点)进行复制。会导致报文走到其他的几点上。

总结

在使用vpp过程中,经常遇到rte_mbuf泄露的问题,而了解使用原理对分析问题很有帮助。本文以报文分段场景的处理为背景,学习了报文从哪来(dpdk-input),到哪去(dpdk tx)的实现逻辑。希望本文的介绍对你有所帮助。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DPDK VPP源码分析 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • rte_mbuf、vlib_buf 关系及内存分布
  • 使用dpdk-收包接口函数
  • 使用dpdk 发包接口函数
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档