• 能否在 vpp 外部释放 vpp 创建的 vlib buffer?


    需求

    基于 vpp 引流,vpp 负责将流量外发到其它 dpdk secondary 进程中进行处理,当外部的 dpdk secondary 进程异常退出后,需要回收这部分报文,避免 buffer 泄露。

    vpp 中创建 vlibbuffer mempool 的代码

    vpp 在初始化时会为每一个 worker 创建一个 vpp mempool + 一个 vpp no cache mempool,vpp 自己实现了名为 vpp mempool_ops 与 vpp-no-cache mempool_ops,分别绑定到这两个 mempool 中。
    相关代码如下:

      rte_mempool_set_ops_byname (mp, "vpp", NULL);
      rte_mempool_set_ops_byname (nmp, "vpp-no-cache", NULL);
    
    • 1
    • 2

    vpp 实现的 mempool_ops 只在 vpp 内部能够使用,不能在其它非 vpp 程序中使用,因此不能在其它非 vpp 程序中直接释放 vpp 创建的 vlibbuffer(mbuf)。

    能否在非 vpp worker 线程中释放 vpp 创建的报文?

    非 vpp worker 线程,其每线程数据 __os_thread_index 非法。当在这些线程中释放 vlibbuffer 时,上层接口最终会调用到 mempool_ops 中的 enqueue 方法,在 vpp 中对应 dpdk_ops_vpp_enqueue 接函数,由于 __os_thread_index 非法,则调用 vlib_get_main 将不能获取到一个合法的 vlib_maint_t 结构,vlibbuffer 无法正常释放。

    vpp 中相关代码如下:

    static_always_inline uword
    os_get_thread_index (void)
    {
      return __os_thread_index;
    }
    
    always_inline vlib_main_t *
    vlib_get_main (void)
    {
      vlib_main_t *vm;
      vm = vlib_mains[vlib_get_thread_index ()];
      ASSERT (vm);
      return vm;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    __os_thread_index 是每线程变量,vpp 自己创建的线程会初始化此变量,此变量将作为下标用于获取当前线程的 vlib_main_t 结构,非管理线程不能获取。

    如果提前记录每个 vlibbuffer 所在的 worker 的 index 下标,在释放前主动修改 __os_thread_index 每线程变量看似能够解决这个问题。

    然而继续阅读 vpp 注册的 dpdk_ops_vpp_enqueue 函数,发现它会调用 vlib_buffer_pool_put 将 buffer 还入vlib_buffer_pool 中。

    vlib_buffer_pool_put 函数部分代码如下:

    static_always_inline void
    vlib_buffer_pool_put (vlib_main_t * vm, u8 buffer_pool_index,
    		      u32 * buffers, u32 n_buffers)
    {
    .....................................................
      if(vm->thread_index != 0)
      {
      	n_cached = bpt->n_cached;
      	n_empty = VLIB_BUFFER_POOL_PER_THREAD_CACHE_SZ - n_cached;
      	if (n_buffers <= n_empty)
        {
          vlib_buffer_copy_indices (bpt->cached_buffers + n_cached,
    				buffers, n_buffers);
          bpt->n_cached = n_cached + n_buffers;
          return;
        }
    
      	vlib_buffer_copy_indices (bpt->cached_buffers + n_cached,
    			    buffers + n_buffers - n_empty, n_empty);
      	bpt->n_cached = VLIB_BUFFER_POOL_PER_THREAD_CACHE_SZ;
      }
    
      clib_spinlock_lock (&bp->lock);
      vlib_buffer_copy_indices (bp->buffers + bp->n_avail, buffers,
    			    n_buffers - n_empty);
      bp->n_avail += n_buffers - n_empty;
      clib_spinlock_unlock (&bp->lock);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    此函数首先尝试将报文还入 bpt->cached_buffers 中,然后将其余的报文还入 bp->buffers 中,还入 bp->buffers 的过程使用互斥锁保护,而 bpt->cached_buffers 没有任何锁保护,意味着同时只能有一个线程访问它。当多个线程同时访问时就会出现异常,故而不能在其它线程中释放 vpp 特定 worker 上分配的报文。

  • 相关阅读:
    GIT rebase 命令使用
    你会自动化测试吗?不要再自欺欺人了
    【无标题】
    UI组件库Kendo UI for Vue原生组件中文 - 按钮概述
    【k8s总结】
    java毕业设计校园管理系统mybatis+源码+调试部署+系统+数据库+lw
    下载安装各种版本的Vscode以及解决VScode官网下载慢的问题
    hbase操作学习
    计算机毕业设计springboot+vue基本微信小程序的汽车俱乐部系统
    淘宝/天猫API:item_search_jupage-天天特价
  • 原文地址:https://blog.csdn.net/Longyu_wlz/article/details/127568984