stats: buffer gauge callbacks use index instead of name.
[vpp.git] / src / vlib / buffer_funcs.h
index ce8d1ef..04f775d 100644 (file)
 
 #include <vppinfra/hash.h>
 #include <vppinfra/fifo.h>
+#include <vlib/buffer.h>
+#include <vlib/physmem_funcs.h>
+#include <vlib/main.h>
+#include <vlib/node.h>
 
 /** \file
     vlib buffer access methods.
 */
 
+always_inline void
+vlib_buffer_validate (vlib_main_t * vm, vlib_buffer_t * b)
+{
+  vlib_buffer_main_t *bm = vm->buffer_main;
+  vlib_buffer_pool_t *bp;
+
+  /* reference count in allocated buffer always must be 1 or higher */
+  ASSERT (b->ref_count > 0);
+
+  /* verify that buffer pool index is valid */
+  bp = vec_elt_at_index (bm->buffer_pools, b->buffer_pool_index);
+  ASSERT (pointer_to_uword (b) >= bp->start);
+  ASSERT (pointer_to_uword (b) < bp->start + bp->size -
+         (bp->data_size + sizeof (vlib_buffer_t)));
+}
+
+always_inline void *
+vlib_buffer_ptr_from_index (uword buffer_mem_start, u32 buffer_index,
+                           uword offset)
+{
+  offset += ((uword) buffer_index) << CLIB_LOG2_CACHE_LINE_BYTES;
+  return uword_to_pointer (buffer_mem_start + offset, vlib_buffer_t *);
+}
 
 /** \brief Translate buffer index into buffer pointer
 
@@ -58,10 +85,17 @@ always_inline vlib_buffer_t *
 vlib_get_buffer (vlib_main_t * vm, u32 buffer_index)
 {
   vlib_buffer_main_t *bm = vm->buffer_main;
-  uword offset = ((uword) buffer_index) << CLIB_LOG2_CACHE_LINE_BYTES;
-  ASSERT (offset < bm->buffer_mem_size);
+  vlib_buffer_t *b;
 
-  return uword_to_pointer (bm->buffer_mem_start + offset, void *);
+  b = vlib_buffer_ptr_from_index (bm->buffer_mem_start, buffer_index, 0);
+  vlib_buffer_validate (vm, b);
+  return b;
+}
+
+static_always_inline u32
+vlib_buffer_get_default_data_size (vlib_main_t * vm)
+{
+  return vm->buffer_main->default_data_size;
 }
 
 static_always_inline void
@@ -108,9 +142,7 @@ static_always_inline void
 vlib_get_buffers_with_offset (vlib_main_t * vm, u32 * bi, void **b, int count,
                              i32 offset)
 {
-#if defined (CLIB_HAVE_VEC256) || defined (CLIB_HAVE_VEC128)
   uword buffer_mem_start = vm->buffer_main->buffer_mem_start;
-#endif
 #ifdef CLIB_HAVE_VEC256
   u64x4 off = u64x4_splat (buffer_mem_start + offset);
   /* if count is not const, compiler will not unroll while loop
@@ -146,10 +178,10 @@ vlib_get_buffers_with_offset (vlib_main_t * vm, u32 * bi, void **b, int count,
       u64x2_store_unaligned ((b0 << CLIB_LOG2_CACHE_LINE_BYTES) + off, b);
       u64x2_store_unaligned ((b1 << CLIB_LOG2_CACHE_LINE_BYTES) + off, b + 2);
 #else
-      b[0] = ((u8 *) vlib_get_buffer (vm, bi[0])) + offset;
-      b[1] = ((u8 *) vlib_get_buffer (vm, bi[1])) + offset;
-      b[2] = ((u8 *) vlib_get_buffer (vm, bi[2])) + offset;
-      b[3] = ((u8 *) vlib_get_buffer (vm, bi[3])) + offset;
+      b[0] = vlib_buffer_ptr_from_index (buffer_mem_start, bi[0], offset);
+      b[1] = vlib_buffer_ptr_from_index (buffer_mem_start, bi[1], offset);
+      b[2] = vlib_buffer_ptr_from_index (buffer_mem_start, bi[2], offset);
+      b[3] = vlib_buffer_ptr_from_index (buffer_mem_start, bi[3], offset);
 #endif
       b += 4;
       bi += 4;
@@ -157,7 +189,7 @@ vlib_get_buffers_with_offset (vlib_main_t * vm, u32 * bi, void **b, int count,
     }
   while (count)
     {
-      b[0] = ((u8 *) vlib_get_buffer (vm, bi[0])) + offset;
+      b[0] = vlib_buffer_ptr_from_index (buffer_mem_start, bi[0], offset);
       b += 1;
       bi += 1;
       count -= 1;
@@ -608,7 +640,7 @@ vlib_buffer_alloc_to_ring_from_pool (vlib_main_t * vm, u32 * ring, u32 start,
   return n_alloc;
 }
 
-static void
+static_always_inline void
 vlib_buffer_pool_put (vlib_main_t * vm, u8 buffer_pool_index,
                      u32 * buffers, u32 n_buffers)
 {
@@ -616,6 +648,10 @@ vlib_buffer_pool_put (vlib_main_t * vm, u8 buffer_pool_index,
   vlib_buffer_pool_thread_t *bpt =
     vec_elt_at_index (bp->threads, vm->thread_index);
 
+  if (CLIB_DEBUG > 0)
+    vlib_buffer_validate_alloc_free (vm, buffers, n_buffers,
+                                    VLIB_BUFFER_KNOWN_ALLOCATED);
+
   vec_add_aligned (bpt->cached_buffers, buffers, n_buffers,
                   CLIB_CACHE_LINE_BYTES);
 
@@ -706,9 +742,10 @@ vlib_buffer_free_inline (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
       vlib_buffer_copy_template (b[3], &bt);
       n_queue += 4;
 
-      if (CLIB_DEBUG > 0)
-       vlib_buffer_validate_alloc_free (vm, buffers, 4,
-                                        VLIB_BUFFER_KNOWN_ALLOCATED);
+      vlib_buffer_validate (vm, b[0]);
+      vlib_buffer_validate (vm, b[1]);
+      vlib_buffer_validate (vm, b[2]);
+      vlib_buffer_validate (vm, b[3]);
 
       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[1]);
@@ -734,31 +771,27 @@ vlib_buffer_free_inline (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
 
       if (PREDICT_FALSE (buffer_pool_index != b[0]->buffer_pool_index))
        {
-         buffer_pool_index = b[0]->buffer_pool_index;
-#if defined(CLIB_HAVE_VEC128) && !__aarch64__
-         bpi_vec.buffer_pool_index = buffer_pool_index;
-#endif
-         bp = vlib_get_buffer_pool (vm, buffer_pool_index);
-         vlib_buffer_copy_template (&bt, &bp->buffer_template);
 
          if (n_queue)
            {
              vlib_buffer_pool_put (vm, buffer_pool_index, queue, n_queue);
              n_queue = 0;
            }
+
+         buffer_pool_index = b[0]->buffer_pool_index;
+#if defined(CLIB_HAVE_VEC128) && !__aarch64__
+         bpi_vec.buffer_pool_index = buffer_pool_index;
+#endif
+         bp = vlib_get_buffer_pool (vm, buffer_pool_index);
+         vlib_buffer_copy_template (&bt, &bp->buffer_template);
        }
 
-      ASSERT (pointer_to_uword (b[0]) >= bp->start &&
-             pointer_to_uword (b[0]) <
-             bp->start + bp->size - (bp->data_size + sizeof (*b[0])));
+      vlib_buffer_validate (vm, b[0]);
 
       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
 
       if (clib_atomic_sub_fetch (&b[0]->ref_count, 1) == 0)
        {
-         if (CLIB_DEBUG > 0)
-           vlib_buffer_validate_alloc_free (vm, &bi, 1,
-                                            VLIB_BUFFER_KNOWN_ALLOCATED);
          vlib_buffer_copy_template (b[0], &bt);
          queue[n_queue++] = bi;
        }
@@ -1134,7 +1167,7 @@ vlib_buffer_chain_append_data (vlib_main_t * vm,
                               vlib_buffer_t * first,
                               vlib_buffer_t * last, void *data, u16 data_len)
 {
-  u32 n_buffer_bytes = VLIB_BUFFER_DATA_SIZE;
+  u32 n_buffer_bytes = vlib_buffer_get_default_data_size (vm);
   ASSERT (n_buffer_bytes >= last->current_length + last->current_data);
   u16 len = clib_min (data_len,
                      n_buffer_bytes - last->current_length -
@@ -1188,122 +1221,141 @@ vlib_packet_template_free (vlib_main_t * vm, vlib_packet_template_t * t)
   vec_free (t->packet_data);
 }
 
-/**
- * @brief compress buffer chain in a way where the first buffer is at least
- * VLIB_BUFFER_CLONE_HEAD_SIZE long
- *
- * @param[in] vm - vlib_main
- * @param[in,out] first - first buffer in chain
- * @param[in,out] discard_vector - vector of buffer indexes which were removed
- * from the chain
- */
-always_inline void
-vlib_buffer_chain_compress (vlib_main_t * vm,
-                           vlib_buffer_t * first, u32 ** discard_vector)
+always_inline u32
+vlib_buffer_space_left_at_end (vlib_main_t * vm, vlib_buffer_t * b)
 {
-  if (first->current_length >= VLIB_BUFFER_CLONE_HEAD_SIZE ||
-      !(first->flags & VLIB_BUFFER_NEXT_PRESENT))
+  return b->data + vlib_buffer_get_default_data_size (vm) -
+    ((u8 *) vlib_buffer_get_current (b) + b->current_length);
+}
+
+always_inline u32
+vlib_buffer_chain_linearize (vlib_main_t * vm, vlib_buffer_t * b)
+{
+  vlib_buffer_t *db = b, *sb, *first = b;
+  int is_cloned = 0;
+  u32 bytes_left = 0, data_size;
+  u16 src_left, dst_left, n_buffers = 1;
+  u8 *dp, *sp;
+  u32 to_free = 0;
+
+  if (PREDICT_TRUE ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0))
+    return 1;
+
+  data_size = vlib_buffer_get_default_data_size (vm);
+
+  dst_left = vlib_buffer_space_left_at_end (vm, b);
+
+  while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     {
-      /* this is already big enough or not a chain */
-      return;
+      b = vlib_get_buffer (vm, b->next_buffer);
+      if (b->ref_count > 1)
+       is_cloned = 1;
+      bytes_left += b->current_length;
+      n_buffers++;
     }
 
-  u32 want_first_size = clib_min (VLIB_BUFFER_CLONE_HEAD_SIZE,
-                                 VLIB_BUFFER_DATA_SIZE -
-                                 first->current_data);
-  do
+  /* if buffer is cloned, create completely new chain - unless everything fits
+   * into one buffer */
+  if (is_cloned && bytes_left >= dst_left)
     {
-      vlib_buffer_t *second = vlib_get_buffer (vm, first->next_buffer);
-      u32 need = want_first_size - first->current_length;
-      u32 amount_to_copy = clib_min (need, second->current_length);
-      clib_memcpy_fast (((u8 *) vlib_buffer_get_current (first)) +
-                       first->current_length,
-                       vlib_buffer_get_current (second), amount_to_copy);
-      first->current_length += amount_to_copy;
-      second->current_data += amount_to_copy;
-      second->current_length -= amount_to_copy;
-      if (first->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID)
-       {
-         first->total_length_not_including_first_buffer -= amount_to_copy;
-       }
-      if (!second->current_length)
+      u32 len = 0;
+      u32 space_needed = bytes_left - dst_left;
+      u32 tail;
+
+      if (vlib_buffer_alloc (vm, &tail, 1) == 0)
+       return 0;
+
+      ++n_buffers;
+      len += data_size;
+      b = vlib_get_buffer (vm, tail);
+
+      while (len < space_needed)
        {
-         vec_add1 (*discard_vector, first->next_buffer);
-         if (second->flags & VLIB_BUFFER_NEXT_PRESENT)
-           {
-             first->next_buffer = second->next_buffer;
-           }
-         else
+         u32 bi;
+         if (vlib_buffer_alloc (vm, &bi, 1) == 0)
            {
-             first->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
+             vlib_buffer_free_one (vm, tail);
+             return 0;
            }
-         second->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
+         b->flags = VLIB_BUFFER_NEXT_PRESENT;
+         b->next_buffer = bi;
+         b = vlib_get_buffer (vm, bi);
+         len += data_size;
+         n_buffers++;
        }
+      sb = vlib_get_buffer (vm, first->next_buffer);
+      to_free = first->next_buffer;
+      first->next_buffer = tail;
     }
-  while ((first->current_length < want_first_size) &&
-        (first->flags & VLIB_BUFFER_NEXT_PRESENT));
-}
+  else
+    sb = vlib_get_buffer (vm, first->next_buffer);
 
-/**
- * @brief linearize buffer chain - the first buffer is filled, if needed,
- * buffers are allocated and filled, returns free space in last buffer or
- * negative on failure
- *
- * @param[in] vm - vlib_main
- * @param[in,out] first - first buffer in chain
- */
-always_inline int
-vlib_buffer_chain_linearize (vlib_main_t * vm, vlib_buffer_t * first)
-{
-  vlib_buffer_t *b = first;
-  u32 buf_len = VLIB_BUFFER_DATA_SIZE;
-  // free buffer chain starting from the second buffer
-  int free_count = (b->flags & VLIB_BUFFER_NEXT_PRESENT) != 0;
-  u32 chain_to_free = b->next_buffer;
-
-  u32 len = vlib_buffer_length_in_chain (vm, b);
-  u32 free_len = buf_len - b->current_data - b->current_length;
-  int alloc_len = clib_max (len - free_len, 0);        //use the free len in the first buffer
-  int n_buffers = (alloc_len + buf_len - 1) / buf_len;
-  u32 new_buffers[n_buffers];
+  src_left = sb->current_length;
+  sp = vlib_buffer_get_current (sb);
+  dp = vlib_buffer_get_tail (db);
 
-  u32 n_alloc = vlib_buffer_alloc (vm, new_buffers, n_buffers);
-  if (n_alloc != n_buffers)
+  while (bytes_left)
     {
-      vlib_buffer_free_no_next (vm, new_buffers, n_alloc);
-      return -1;
+      u16 bytes_to_copy;
+
+      if (dst_left == 0)
+       {
+         if (db != first)
+           db->current_data = 0;
+         db->current_length = dp - (u8 *) vlib_buffer_get_current (db);
+         ASSERT (db->flags & VLIB_BUFFER_NEXT_PRESENT);
+         db = vlib_get_buffer (vm, db->next_buffer);
+         dst_left = data_size;
+         dp = db->data;
+       }
+
+      while (src_left == 0)
+       {
+         ASSERT (sb->flags & VLIB_BUFFER_NEXT_PRESENT);
+         sb = vlib_get_buffer (vm, sb->next_buffer);
+         src_left = sb->current_length;
+         sp = vlib_buffer_get_current (sb);
+       }
+
+      bytes_to_copy = clib_min (dst_left, src_left);
+
+      if (dp != sp)
+       {
+         if (sb == db)
+           bytes_to_copy = clib_min (bytes_to_copy, sp - dp);
+
+         clib_memcpy_fast (dp, sp, bytes_to_copy);
+       }
+
+      src_left -= bytes_to_copy;
+      dst_left -= bytes_to_copy;
+      dp += bytes_to_copy;
+      sp += bytes_to_copy;
+      bytes_left -= bytes_to_copy;
     }
+  if (db != first)
+    db->current_data = 0;
+  db->current_length = dp - (u8 *) vlib_buffer_get_current (db);
 
-  vlib_buffer_t *s = b;
-  while (s->flags & VLIB_BUFFER_NEXT_PRESENT)
+  if (is_cloned && to_free)
+    vlib_buffer_free_one (vm, to_free);
+  else
     {
-      s = vlib_get_buffer (vm, s->next_buffer);
-      int d_free_len = buf_len - b->current_data - b->current_length;
-      ASSERT (d_free_len >= 0);
-      // chain buf and split write
-      u32 copy_len = clib_min (d_free_len, s->current_length);
-      u8 *d = vlib_buffer_put_uninit (b, copy_len);
-      clib_memcpy (d, vlib_buffer_get_current (s), copy_len);
-      int rest = s->current_length - copy_len;
-      if (rest > 0)
+      if (db->flags & VLIB_BUFFER_NEXT_PRESENT)
+       vlib_buffer_free_one (vm, db->next_buffer);
+      db->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
+      b = first;
+      n_buffers = 1;
+      while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
        {
-         //prev buf is full
-         ASSERT (vlib_buffer_get_tail (b) == b->data + buf_len);
-         ASSERT (n_buffers > 0);
-         b = vlib_buffer_chain_buffer (vm, b, new_buffers[--n_buffers]);
-         //make full use of the new buffers
-         b->current_data = 0;
-         d = vlib_buffer_put_uninit (b, rest);
-         clib_memcpy (d, vlib_buffer_get_current (s) + copy_len, rest);
+         b = vlib_get_buffer (vm, b->next_buffer);
+         ++n_buffers;
        }
     }
-  vlib_buffer_free (vm, &chain_to_free, free_count);
-  b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
-  if (b == first)              /* no buffers addeed */
-    b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
-  ASSERT (len == vlib_buffer_length_in_chain (vm, first));
-  ASSERT (n_buffers == 0);
-  return buf_len - b->current_data - b->current_length;
+
+  first->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
+
+  return n_buffers;
 }
 
 #endif /* included_vlib_buffer_funcs_h */