buffers: configurable buffer fault injector

When configured at compile time via the cmake
VPP_BUFFER_FAULT_INJECTOR option, the buffer allocator will appear to
fail a certain fraction of the time.

By default, the allocator succeeds 80% of the time. Detailed command
line configuration options are available, but only when the image has
been compiled with cmake option described above:

    vlib { buffer-alloc-success-rate [0.0 ... 1.0]
           buffer-alloc-success-seed <nnnn> }

Modify vlib_buffer_pool_create(...) so 0 is always an invalid buffer
index.

Debug images: add checks for bad buffer index enqueues, and also
verify that f->n_vectors doesn't accidentally map one or more
instances of the frame poison pattern 0xfefefefe.

Type: improvement

Signed-off-by: Dave Barach <dave@barachs.net>
Change-Id: Iab939858014463d1e664682805013d334d6fcbe5
This commit is contained in:
Dave Barach
2020-04-09 17:24:07 -04:00
committed by Florin Coras
parent c54162981c
commit c74b43c807
7 changed files with 108 additions and 0 deletions

View File

@ -11,9 +11,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
option(VPP_BUFFER_FAULT_INJECTOR "Include the buffer fault injector" OFF)
##############################################################################
# Generate vlib/config.h
##############################################################################
if(VPP_BUFFER_FAULT_INJECTOR)
set(BUFFER_ALLOC_FAULT_INJECTOR 1 CACHE STRING "fault injector on")
else()
set(BUFFER_ALLOC_FAULT_INJECTOR 0 CACHE STRING "fault injector off")
endif()
set(PRE_DATA_SIZE 128 CACHE STRING "Buffer headroom size.")
configure_file(
${CMAKE_SOURCE_DIR}/vlib/config.h.in

View File

@ -578,6 +578,14 @@ vlib_buffer_pool_create (vlib_main_t * vm, char *name, u32 data_size,
p = m->base + (j << m->log2_page_size) + i * alloc_size;
p += bm->ext_hdr_size;
/*
* Waste 1 buffer (maximum) so that 0 is never a valid buffer index.
* Allows various places to ASSERT (bi != 0). Much easier
* than debugging downstream crashes in successor nodes.
*/
if (p == m->base)
continue;
vlib_buffer_copy_template ((vlib_buffer_t *) p, &bp->buffer_template);
bi = vlib_get_buffer_index (vm, (vlib_buffer_t *) p);
@ -923,6 +931,24 @@ vlib_buffers_configure (vlib_main_t * vm, unformat_input_t * input)
VLIB_EARLY_CONFIG_FUNCTION (vlib_buffers_configure, "buffers");
#if VLIB_BUFFER_ALLOC_FAULT_INJECTOR > 0
u32
vlib_buffer_alloc_may_fail (vlib_main_t * vm, u32 n_buffers)
{
f64 r;
r = random_f64 (&vm->buffer_alloc_success_seed);
/* Fail this request? */
if (r > vm->buffer_alloc_success_rate)
n_buffers--;
/* 5% chance of returning nothing at all */
if (r > vm->buffer_alloc_success_rate && r > 0.95)
n_buffers = 0;
return n_buffers;
}
#endif
/** @endcond */
/*

View File

@ -571,6 +571,17 @@ vlib_buffer_alloc_from_pool (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
vlib_buffer_pool_thread_t *bpt;
u32 *src, *dst, len, n_left;
/* If buffer allocation fault injection is configured */
if (VLIB_BUFFER_ALLOC_FAULT_INJECTOR > 0)
{
u32 vlib_buffer_alloc_may_fail (vlib_main_t *, u32);
/* See how many buffers we're willing to allocate */
n_buffers = vlib_buffer_alloc_may_fail (vm, n_buffers);
if (n_buffers == 0)
return (n_buffers);
}
bp = vec_elt_at_index (bm->buffer_pools, buffer_pool_index);
bpt = vec_elt_at_index (bp->threads, vm->thread_index);

View File

@ -69,6 +69,8 @@
#define vlib_validate_buffer_enqueue_x2(vm,node,next_index,to_next,n_left_to_next,bi0,bi1,next0,next1) \
do { \
ASSERT (bi0 != 0); \
ASSERT (bi1 != 0); \
int enqueue_code = (next0 != next_index) + 2*(next1 != next_index); \
\
if (PREDICT_FALSE (enqueue_code != 0)) \
@ -137,6 +139,10 @@ do { \
#define vlib_validate_buffer_enqueue_x4(vm,node,next_index,to_next,n_left_to_next,bi0,bi1,bi2,bi3,next0,next1,next2,next3) \
do { \
ASSERT (bi0 != 0); \
ASSERT (bi1 != 0); \
ASSERT (bi2 != 0); \
ASSERT (bi3 != 0); \
/* After the fact: check the [speculative] enqueue to "next" */ \
u32 fix_speculation = (next_index ^ next0) | (next_index ^ next1) \
| (next_index ^ next2) | (next_index ^ next3); \
@ -217,6 +223,7 @@ do { \
*/
#define vlib_validate_buffer_enqueue_x1(vm,node,next_index,to_next,n_left_to_next,bi0,next0) \
do { \
ASSERT (bi0 != 0); \
if (PREDICT_FALSE (next0 != next_index)) \
{ \
vlib_put_next_frame (vm, node, next_index, n_left_to_next + 1); \

View File

@ -17,5 +17,6 @@
#define included_vlib_config_h
#define __PRE_DATA_SIZE @PRE_DATA_SIZE@
#define VLIB_BUFFER_ALLOC_FAULT_INJECTOR @BUFFER_ALLOC_FAULT_INJECTOR@
#endif

View File

@ -190,6 +190,31 @@ vlib_get_frame_to_node (vlib_main_t * vm, u32 to_node_index)
return vlib_get_frame (vm, f);
}
static inline void
vlib_validate_frame_indices (vlib_frame_t * f)
{
if (CLIB_DEBUG > 0)
{
int i;
u32 *from = vlib_frame_vector_args (f);
/* Check for bad buffer index values */
for (i = 0; i < f->n_vectors; i++)
{
if (from[i] == 0)
{
clib_warning ("BUG: buffer index 0 at index %d", i);
ASSERT (0);
}
else if (from[i] == 0xfefefefe)
{
clib_warning ("BUG: frame poison pattern at index %d", i);
ASSERT (0);
}
}
}
}
void
vlib_put_frame_to_node (vlib_main_t * vm, u32 to_node_index, vlib_frame_t * f)
{
@ -199,6 +224,8 @@ vlib_put_frame_to_node (vlib_main_t * vm, u32 to_node_index, vlib_frame_t * f)
if (f->n_vectors == 0)
return;
vlib_validate_frame_indices (f);
to_node = vlib_get_node (vm, to_node_index);
vec_add2 (vm->node_main.pending_frames, p, 1);
@ -432,6 +459,9 @@ vlib_put_next_frame_validate (vlib_main_t * vm,
f = vlib_get_frame (vm, nf->frame);
ASSERT (n_vectors_left <= VLIB_FRAME_SIZE);
vlib_validate_frame_indices (f);
n_after = VLIB_FRAME_SIZE - n_vectors_left;
n_before = f->n_vectors;
@ -1986,6 +2016,20 @@ vlib_main_configure (vlib_main_t * vm, unformat_input_t * input)
;
else if (unformat (input, "elog-post-mortem-dump"))
vm->elog_post_mortem_dump = 1;
else if (unformat (input, "buffer-alloc-success-rate %f",
&vm->buffer_alloc_success_rate))
{
if (VLIB_BUFFER_ALLOC_FAULT_INJECTOR == 0)
return clib_error_return
(0, "Buffer fault injection not configured");
}
else if (unformat (input, "buffer-alloc-success-seed %u",
&vm->buffer_alloc_success_seed))
{
if (VLIB_BUFFER_ALLOC_FAULT_INJECTOR == 0)
return clib_error_return
(0, "Buffer fault injection not configured");
}
else
return unformat_parse_error (input);
}
@ -2147,6 +2191,13 @@ vlib_main (vlib_main_t * volatile vm, unformat_input_t * input)
vec_validate (vm->processing_rpc_requests, 0);
_vec_len (vm->processing_rpc_requests) = 0;
/* Default params for the buffer allocator fault injector, if configured */
if (VLIB_BUFFER_ALLOC_FAULT_INJECTOR > 0)
{
vm->buffer_alloc_success_seed = 0xdeaddabe;
vm->buffer_alloc_success_rate = 0.80;
}
if ((error = vlib_call_all_config_functions (vm, input, 0 /* is_early */ )))
goto done;

View File

@ -276,6 +276,10 @@ typedef struct vlib_main_t
uword *processing_rpc_requests;
clib_spinlock_t pending_rpc_lock;
/* buffer fault injector */
u32 buffer_alloc_success_seed;
f64 buffer_alloc_success_rate;
} vlib_main_t;
/* Global main structure. */