bufmon: add buffer monitoring plugin

This plugin allow to keep track of buffer usage in VPP graph nodes. The
main use is to detect buffer leakages.

Type: feature

Change-Id: Iadcf4ab98207fab6e2fa375060879bc2a25b711e
Signed-off-by: Benoît Ganne <bganne@cisco.com>
This commit is contained in:
Benoît Ganne
2021-03-09 15:37:49 +01:00
committed by Damjan Marion
parent a13100f3aa
commit e09a2337b8
8 changed files with 422 additions and 24 deletions

@ -764,6 +764,11 @@ I: srtp
M: Florin Coras <fcoras@cisco.com>
F: src/plugins/srtp/
Plugin - bufmon
I: bufmon
M: Benoît Ganne <bganne@cisco.com>
F: src/plugins/bufmon/
cJSON
I: cjson
M: Ole Troan <ot@cisco.com>

@ -0,0 +1,17 @@
# Copyright (c) 2020 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_vpp_plugin(bufmon
SOURCES
bufmon.c
)

@ -0,0 +1,8 @@
---
name: Buffers monitoring plugin
maintainer: Benoît Ganne <bganne@cisco.com>
features:
- monitor buffer utiization in VPP graph nodes
description: "monitor buffer utiization in VPP graph nodes"
state: production
properties: [CLI, MULTITHREAD]

313
src/plugins/bufmon/bufmon.c Normal file

File diff suppressed because it is too large Load Diff

@ -0,0 +1,24 @@
# Buffers monitoring plugin {#bufmon_doc}
This plugin enables to track buffer utilization in the VPP graph nodes. The
main use is to detect buffer leakage.
It works by keeping track of number of buffer allocations and free in graph
nodes and also of number of buffers received in input frames and in output
frames.
The formula to compute the number of "buffered" buffers in a node is simply:
#buffered = #alloc + #input - #free - #output
Note: monitoring will impact performances.
## Basic usage
1. Turn buffer traces on:
```
~# vppctl set buffer traces on
```
2. Monitor buffer usage:
```
~# vppctl show buffer traces verbose
```
3. Turn buffer traces off:
```
~# vppctl set buffer traces off
```

@ -615,20 +615,26 @@ format_vlib_buffer_pool (u8 * s, va_list * va)
return s;
}
static clib_error_t *
show_buffers (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
u8 *
format_vlib_buffer_pool_all (u8 *s, va_list *va)
{
vlib_main_t *vm = va_arg (*va, vlib_main_t *);
vlib_buffer_main_t *bm = vm->buffer_main;
vlib_buffer_pool_t *bp;
vlib_cli_output (vm, "%U", format_vlib_buffer_pool, vm, 0);
s = format (s, "%U", format_vlib_buffer_pool, vm, 0);
/* *INDENT-OFF* */
vec_foreach (bp, bm->buffer_pools)
vlib_cli_output (vm, "%U", format_vlib_buffer_pool, vm, bp);
/* *INDENT-ON* */
s = format (s, "\n%U", format_vlib_buffer_pool, vm, bp);
return s;
}
static clib_error_t *
show_buffers (vlib_main_t *vm, unformat_input_t *input,
vlib_cli_command_t *cmd)
{
vlib_cli_output (vm, "%U", format_vlib_buffer_pool_all, vm);
return 0;
}
@ -971,6 +977,20 @@ vlib_buffer_alloc_may_fail (vlib_main_t * vm, u32 n_buffers)
}
#endif
__clib_export int
vlib_buffer_set_alloc_free_callback (
vlib_main_t *vm, vlib_buffer_alloc_free_callback_t *alloc_callback_fn,
vlib_buffer_alloc_free_callback_t *free_callback_fn)
{
vlib_buffer_main_t *bm = vm->buffer_main;
if ((alloc_callback_fn && bm->alloc_callback_fn) ||
(free_callback_fn && bm->free_callback_fn))
return 1;
bm->alloc_callback_fn = alloc_callback_fn;
bm->free_callback_fn = free_callback_fn;
return 0;
}
/** @endcond */
/*
* fd.io coding-style-patch-verification: ON

@ -472,6 +472,10 @@ typedef struct
#define VLIB_BUFFER_MAX_NUMA_NODES 32
typedef u32 (vlib_buffer_alloc_free_callback_t) (struct vlib_main_t *vm,
u8 buffer_pool_index,
u32 *buffers, u32 n_buffers);
typedef struct
{
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
@ -481,12 +485,9 @@ typedef struct
uword buffer_mem_size;
vlib_buffer_pool_t *buffer_pools;
/* Hash table mapping buffer index into number
0 => allocated but free, 1 => allocated and not-free.
If buffer index is not in hash table then this buffer
has never been allocated. */
uword *buffer_known_hash;
clib_spinlock_t buffer_known_hash_lockp;
vlib_buffer_alloc_free_callback_t *alloc_callback_fn;
vlib_buffer_alloc_free_callback_t *free_callback_fn;
u8 default_buffer_pool_index_for_numa[VLIB_BUFFER_MAX_NUMA_NODES];
/* config */
@ -495,12 +496,25 @@ typedef struct
u32 default_data_size;
clib_mem_page_sz_t log2_page_size;
/* Hash table mapping buffer index into number
0 => allocated but free, 1 => allocated and not-free.
If buffer index is not in hash table then this buffer
has never been allocated. */
uword *buffer_known_hash;
clib_spinlock_t buffer_known_hash_lockp;
/* logging */
vlib_log_class_t log_default;
} vlib_buffer_main_t;
clib_error_t *vlib_buffer_main_init (struct vlib_main_t *vm);
format_function_t format_vlib_buffer_pool_all;
int vlib_buffer_set_alloc_free_callback (
struct vlib_main_t *vm, vlib_buffer_alloc_free_callback_t *alloc_callback_fn,
vlib_buffer_alloc_free_callback_t *free_callback_fn);
extern u16 __vlib_buffer_external_hdr_size;
#define VLIB_BUFFER_SET_EXT_HDR_SIZE(x) \
static void __clib_constructor \

@ -626,11 +626,7 @@ vlib_buffer_alloc_from_pool (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
src = bpt->cached_buffers + len - n_buffers;
vlib_buffer_copy_indices (dst, src, n_buffers);
bpt->n_cached -= n_buffers;
if (CLIB_DEBUG > 0)
vlib_buffer_validate_alloc_free (vm, buffers, n_buffers,
VLIB_BUFFER_KNOWN_FREE);
return n_buffers;
goto done;
}
/* alloc bigger than cache - take buffers directly from main pool */
@ -638,11 +634,7 @@ vlib_buffer_alloc_from_pool (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
{
n_buffers = vlib_buffer_pool_get (vm, buffer_pool_index, buffers,
n_buffers);
if (CLIB_DEBUG > 0)
vlib_buffer_validate_alloc_free (vm, buffers, n_buffers,
VLIB_BUFFER_KNOWN_FREE);
return n_buffers;
goto done;
}
/* take everything available in the cache */
@ -670,11 +662,13 @@ vlib_buffer_alloc_from_pool (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
n_buffers -= n_left;
done:
/* Verify that buffers are known free. */
if (CLIB_DEBUG > 0)
vlib_buffer_validate_alloc_free (vm, buffers, n_buffers,
VLIB_BUFFER_KNOWN_FREE);
if (PREDICT_FALSE (bm->alloc_callback_fn != 0))
bm->alloc_callback_fn (vm, buffer_pool_index, buffers, n_buffers);
return n_buffers;
}
@ -776,6 +770,7 @@ static_always_inline void
vlib_buffer_pool_put (vlib_main_t * vm, u8 buffer_pool_index,
u32 * buffers, u32 n_buffers)
{
vlib_buffer_main_t *bm = vm->buffer_main;
vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
vlib_buffer_pool_thread_t *bpt = vec_elt_at_index (bp->threads,
vm->thread_index);
@ -784,6 +779,8 @@ vlib_buffer_pool_put (vlib_main_t * vm, u8 buffer_pool_index,
if (CLIB_DEBUG > 0)
vlib_buffer_validate_alloc_free (vm, buffers, n_buffers,
VLIB_BUFFER_KNOWN_ALLOCATED);
if (PREDICT_FALSE (bm->free_callback_fn != 0))
bm->free_callback_fn (vm, buffer_pool_index, buffers, n_buffers);
n_cached = bpt->n_cached;
n_empty = VLIB_BUFFER_POOL_PER_THREAD_CACHE_SZ - n_cached;