基于 vpp 引流,vpp 负责将流量外发到其它 dpdk secondary 进程中进行处理,当外部的 dpdk secondary 进程异常退出后,需要回收这部分报文,避免 buffer 泄露。
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);
vpp 实现的 mempool_ops 只在 vpp 内部能够使用,不能在其它非 vpp 程序中使用,因此不能在其它非 vpp 程序中直接释放 vpp 创建的 vlibbuffer(mbuf)。
非 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;
}
__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);
}
此函数首先尝试将报文还入 bpt->cached_buffers 中,然后将其余的报文还入 bp->buffers 中,还入 bp->buffers 的过程使用互斥锁保护,而 bpt->cached_buffers 没有任何锁保护,意味着同时只能有一个线程访问它。当多个线程同时访问时就会出现异常,故而不能在其它线程中释放 vpp 特定 worker 上分配的报文。