• 能否在 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 上分配的报文。

  • 相关阅读:
    【2022国赛模拟】摆(bigben)——行列式、杜教筛
    量化策略:CTA,市场中性,指数增强
    信息学奥赛一本通:2050:【例5.20】字串包含
    荧光标记肽,FITC-胰高血糖素样肽-1,FITC-glucagon-like peptide-1,GLP-1
    leetcode:1323. 6 和 9 组成的最大数字(python3解法)
    godot引擎学习2
    推广PowerBI,你想好了么?
    SpringCloudAlibaba——Sentinel
    不知道怎么把英文文档翻译成中文?手把手教你怎么操作
    java入坑之类加载器
  • 原文地址:https://blog.csdn.net/Longyu_wlz/article/details/127568984