buffers: configurable buffer fault injector 61/26461/3
authorDave Barach <dave@barachs.net>
Thu, 9 Apr 2020 21:24:07 +0000 (17:24 -0400)
committerFlorin Coras <florin.coras@gmail.com>
Mon, 13 Apr 2020 15:54:31 +0000 (15:54 +0000)
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

src/vlib/CMakeLists.txt
src/vlib/buffer.c
src/vlib/buffer_funcs.h
src/vlib/buffer_node.h
src/vlib/config.h.in
src/vlib/main.c
src/vlib/main.h

index c2a0d63..67d1f6d 100644 (file)
 # 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
index 43b1dd6..f5b8137 100644 (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 */
 /*
index 98ee205..b2076e6 100644 (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);
 
index bd82b10..0fa18d6 100644 (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);  \
index a24cb84..5b7d5da 100644 (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
index f723d10..fb3eb10 100644 (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;
 
index 0e65c81..598c3ba 100644 (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. */