From c74b43c80789f5e437dfe4cc491157b45a7f222e Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Thu, 9 Apr 2020 17:24:07 -0400 Subject: [PATCH] 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 } 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 Change-Id: Iab939858014463d1e664682805013d334d6fcbe5 --- src/vlib/CMakeLists.txt | 8 ++++++++ src/vlib/buffer.c | 26 +++++++++++++++++++++++++ src/vlib/buffer_funcs.h | 11 +++++++++++ src/vlib/buffer_node.h | 7 +++++++ src/vlib/config.h.in | 1 + src/vlib/main.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ src/vlib/main.h | 4 ++++ 7 files changed, 108 insertions(+) diff --git a/src/vlib/CMakeLists.txt b/src/vlib/CMakeLists.txt index c2a0d63f4d8..67d1f6d05c7 100644 --- a/src/vlib/CMakeLists.txt +++ b/src/vlib/CMakeLists.txt @@ -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 diff --git a/src/vlib/buffer.c b/src/vlib/buffer.c index 43b1dd63761..f5b813793a3 100644 --- a/src/vlib/buffer.c +++ b/src/vlib/buffer.c @@ -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 */ /* diff --git a/src/vlib/buffer_funcs.h b/src/vlib/buffer_funcs.h index 98ee2055833..b2076e60de4 100644 --- a/src/vlib/buffer_funcs.h +++ b/src/vlib/buffer_funcs.h @@ -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); diff --git a/src/vlib/buffer_node.h b/src/vlib/buffer_node.h index bd82b1037a9..0fa18d6dde2 100644 --- a/src/vlib/buffer_node.h +++ b/src/vlib/buffer_node.h @@ -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); \ diff --git a/src/vlib/config.h.in b/src/vlib/config.h.in index a24cb848b82..5b7d5da64e4 100644 --- a/src/vlib/config.h.in +++ b/src/vlib/config.h.in @@ -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 diff --git a/src/vlib/main.c b/src/vlib/main.c index f723d106120..fb3eb108736 100644 --- a/src/vlib/main.c +++ b/src/vlib/main.c @@ -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; diff --git a/src/vlib/main.h b/src/vlib/main.h index 0e65c817ae1..598c3ba836f 100644 --- a/src/vlib/main.h +++ b/src/vlib/main.h @@ -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. */ -- 2.16.6