vppinfra: numa vector placement support
[vpp.git] / src / vppinfra / vec.h
index dc7b908..9054eaa 100644 (file)
    Typically, the header is not present.  Headers allow for other
    data structures to be built atop CLIB vectors.
 
-   Users may specify the alignment for data elements via the
-   vec_*_aligned macros.
+   Users may specify the alignment for first data element of a vector
+   via the vec_*_aligned macros.
 
-   Vectors elements can be any C type e.g. (int, double, struct bar).
+   Vector elements can be any C type e.g. (int, double, struct bar).
    This is also true for data types built atop vectors (e.g. heap,
    pool, etc.).
 
-   Many macros have _a variants supporting alignment of vector data
-   and _h variants supporting non zero length vector headers.
-   The _ha variants support both.
+   Many macros have \_a variants supporting alignment of vector elements
+   and \_h variants supporting non-zero-length vector headers. The \_ha
+   variants support both.  Additionally cacheline alignment within a
+   vector element structure can be specified using the
+   CLIB_CACHE_LINE_ALIGN_MARK() macro.
 
    Standard programming error: memorize a pointer to the ith element
    of a vector then expand it. Vectors expand by 3/2, so such code
     @param data_bytes requested size in bytes
     @param header_bytes header size in bytes (may be zero)
     @param data_align alignment (may be zero)
+    @param numa_id numa id (may be zero)
     @return v_prime pointer to resized vector, may or may not equal v
 */
 void *vec_resize_allocate_memory (void *v,
                                  word length_increment,
                                  uword data_bytes,
-                                 uword header_bytes, uword data_align);
+                                 uword header_bytes, uword data_align,
+                                 uword numa_id);
 
 /** \brief Low-level vector resize function, usually not called directly
 
@@ -108,19 +112,25 @@ void *vec_resize_allocate_memory (void *v,
     @param data_bytes requested size in bytes
     @param header_bytes header size in bytes (may be zero)
     @param data_align alignment (may be zero)
+    @param numa_id (may be ~0)
     @return v_prime pointer to resized vector, may or may not equal v
 */
 
-#define _vec_resize(V,L,DB,HB,A) \
-  _vec_resize_inline(V,L,DB,HB,clib_max((__alignof__((V)[0])),(A)))
+#define _vec_resize_numa(V,L,DB,HB,A,S) \
+  _vec_resize_inline(V,L,DB,HB,clib_max((__alignof__((V)[0])),(A)),(S))
+
+#define _vec_resize(V,L,DB,HB,A)  \
+  _vec_resize_numa(V,L,DB,HB,A,VEC_NUMA_UNSPECIFIED)
 
 always_inline void *
 _vec_resize_inline (void *v,
                    word length_increment,
-                   uword data_bytes, uword header_bytes, uword data_align)
+                   uword data_bytes, uword header_bytes, uword data_align,
+                   uword numa_id)
 {
   vec_header_t *vh = _vec_find (v);
   uword new_data_bytes, aligned_header_bytes;
+  void *oldheap;
 
   aligned_header_bytes = vec_header_bytes (header_bytes);
 
@@ -130,22 +140,33 @@ _vec_resize_inline (void *v,
     {
       void *p = v - aligned_header_bytes;
 
+      if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
+       {
+         oldheap = clib_mem_get_per_cpu_heap ();
+         clib_mem_set_per_cpu_heap (clib_mem_get_per_numa_heap (numa_id));
+       }
+
       /* Vector header must start heap object. */
       ASSERT (clib_mem_is_heap_object (p));
 
       /* Typically we'll not need to resize. */
       if (new_data_bytes <= clib_mem_size (p))
        {
+         CLIB_MEM_UNPOISON (v, data_bytes);
          vh->len += length_increment;
+         if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
+           clib_mem_set_per_cpu_heap (oldheap);
          return v;
        }
+      if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
+       clib_mem_set_per_cpu_heap (oldheap);
     }
 
   /* Slow path: call helper function. */
   return vec_resize_allocate_memory (v, length_increment, data_bytes,
                                     header_bytes,
                                     clib_max (sizeof (vec_header_t),
-                                              data_align));
+                                              data_align), numa_id);
 }
 
 /** \brief Determine if vector will resize with next allocation
@@ -218,16 +239,32 @@ clib_mem_is_vec (void *v)
     @param N number of elements to add
     @param H header size in bytes (may be zero)
     @param A alignment (may be zero)
+    @param S numa_id (may be zero)
     @return V (value-result macro parameter)
 */
 
-#define vec_resize_ha(V,N,H,A)                                                 \
-do {                                                                           \
-  word _v(n) = (N);                                                            \
-  word _v(l) = vec_len (V);                                                    \
-  V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A));   \
+#define vec_resize_has(V,N,H,A,S)                               \
+do {                                                            \
+  word _v(n) = (N);                                             \
+  word _v(l) = vec_len (V);                                     \
+  V = _vec_resize_numa ((V), _v(n),                           \
+                          (_v(l) + _v(n)) * sizeof ((V)[0]),    \
+                          (H), (A),(S));                        \
 } while (0)
 
+/** \brief Resize a vector (less general version).
+   Add N elements to end of given vector V, return pointer to start of vector.
+   Vector will have room for H header bytes and will have user's data aligned
+   at alignment A (rounded to next power of 2).
+
+    @param V pointer to a vector
+    @param N number of elements to add
+    @param H header size in bytes (may be zero)
+    @param A alignment (may be zero)
+    @return V (value-result macro parameter)
+*/
+#define vec_resize_ha(V,N,H,A) vec_resize_has(V,N,H,A,VEC_NUMA_UNSPECIFIED)
+
 /** \brief Resize a vector (no header, unspecified alignment)
    Add N elements to end of given vector V, return pointer to start of vector.
    Vector will have room for H header bytes and will have user's data aligned
@@ -349,22 +386,35 @@ do {                                              \
     @param V pointer to a vector
     @param H size of header in bytes
     @param A alignment (may be zero)
+    @param S numa (may be VEC_NUMA_UNSPECIFIED)
 
     @return Vdup copy of vector
 */
 
-#define vec_dup_ha(V,H,A)                              \
+#define vec_dup_ha_numa(V,H,A,S)                      \
 ({                                                     \
   __typeof__ ((V)[0]) * _v(v) = 0;                     \
   uword _v(l) = vec_len (V);                           \
   if (_v(l) > 0)                                       \
     {                                                  \
-      vec_resize_ha (_v(v), _v(l), (H), (A));          \
-      clib_memcpy (_v(v), (V), _v(l) * sizeof ((V)[0]));\
+      vec_resize_has (_v(v), _v(l), (H), (A), (S));     \
+      clib_memcpy_fast (_v(v), (V), _v(l) * sizeof ((V)[0]));\
     }                                                  \
   _v(v);                                               \
 })
 
+/** \brief Return copy of vector (VEC_NUMA_UNSPECIFIED).
+
+    @param V pointer to a vector
+    @param H size of header in bytes
+    @param A alignment (may be zero)
+
+    @return Vdup copy of vector
+*/
+#define vec_dup_ha(V,H,A) \
+  vec_dup_ha_numa(V,H,A,VEC_NUMA_UNSPECIFIED)
+
+
 /** \brief Return copy of vector (no header, no alignment)
 
     @param V pointer to a vector
@@ -387,7 +437,7 @@ do {                                                \
     @param DST destination
     @param SRC source
 */
-#define vec_copy(DST,SRC) clib_memcpy (DST, SRC, vec_len (DST) * \
+#define vec_copy(DST,SRC) clib_memcpy_fast (DST, SRC, vec_len (DST) * \
                                       sizeof ((DST)[0]))
 
 /** \brief Clone a vector. Make a new vector with the
@@ -409,24 +459,40 @@ do {                                                                              \
     @param I vector index which will be valid upon return
     @param H header size in bytes (may be zero)
     @param A alignment (may be zero)
+    @param N numa_id (may be zero)
     @return V (value-result macro parameter)
 */
 
-#define vec_validate_ha(V,I,H,A)                                       \
-do {                                                                   \
-  STATIC_ASSERT(A==0 || ((A % sizeof(V[0]))==0) || ((sizeof(V[0]) % A) == 0),\
-                "vector validate aligned on incorrectly sized object"); \
-  word _v(i) = (I);                                                    \
-  word _v(l) = vec_len (V);                                            \
-  if (_v(i) >= _v(l))                                                  \
-    {                                                                  \
-      vec_resize_ha ((V), 1 + (_v(i) - _v(l)), (H), (A));              \
-      /* Must zero new space since user may have previously            \
-        used e.g. _vec_len (v) -= 10 */                                \
-      clib_memset ((V) + _v(l), 0, (1 + (_v(i) - _v(l))) * sizeof ((V)[0]));   \
-    }                                                                  \
+#define vec_validate_han(V,I,H,A,N)                                     \
+do {                                                                    \
+  void *oldheap;                                                        \
+  STATIC_ASSERT(A==0 || ((A % sizeof(V[0]))==0)                         \
+        || ((sizeof(V[0]) % A) == 0),                                   \
+    "vector validate aligned on incorrectly sized object");             \
+  word _v(i) = (I);                                                     \
+  word _v(l) = vec_len (V);                                             \
+  if (_v(i) >= _v(l))                                                   \
+    {                                                                   \
+      /* switch to the per-numa heap if directed */                   \
+      if (PREDICT_FALSE(N != VEC_NUMA_UNSPECIFIED))                   \
+        {                                                               \
+           oldheap = clib_mem_get_per_cpu_heap();                       \
+           clib_mem_set_per_cpu_heap (clib_mem_get_per_numa_heap(N)); \
+        }                                                               \
+                                                                        \
+      vec_resize_ha ((V), 1 + (_v(i) - _v(l)), (H), (A));               \
+      /* Must zero new space since user may have previously             \
+        used e.g. _vec_len (v) -= 10 */                                \
+      clib_memset ((V) + _v(l), 0,                                      \
+                   (1 + (_v(i) - _v(l))) * sizeof ((V)[0]));            \
+      /* Switch back to the global heap */                              \
+      if (PREDICT_FALSE (N != VEC_NUMA_UNSPECIFIED))                  \
+        clib_mem_set_per_cpu_heap (oldheap);                            \
+    }                                                                   \
 } while (0)
 
+#define vec_validate_ha(V,I,H,A) vec_validate_han(V,I,H,A,VEC_NUMA_UNSPECIFIED)
+
 /** \brief Make sure vector is long enough for given index
     (no header, unspecified alignment)
 
@@ -478,8 +544,6 @@ do {                                                                \
     @param V (possibly NULL) pointer to a vector.
     @param I vector index which will be valid upon return
     @param INIT initial value (can be a complex expression!)
-    @param H header size in bytes (may be zero)
-    @param A alignment (may be zero)
     @return V (value-result macro parameter)
 */
 
@@ -492,7 +556,6 @@ do {                                                                \
     @param V (possibly NULL) pointer to a vector.
     @param I vector index which will be valid upon return
     @param INIT initial value (can be a complex expression!)
-    @param H header size in bytes (may be zero)
     @param A alignment (may be zero)
     @return V (value-result macro parameter)
 */
@@ -526,7 +589,6 @@ do {                                                                        \
 
     @param V pointer to a vector
     @param E element to add
-    @param H header size in bytes (may be zero)
     @param A alignment (may be zero)
     @return V (value-result macro parameter)
 */
@@ -587,7 +649,7 @@ do {                                                                                \
   word _v(n) = (N);                                                            \
   word _v(l) = vec_len (V);                                                    \
   V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A));   \
-  clib_memcpy ((V) + _v(l), (E), _v(n) * sizeof ((V)[0]));                     \
+  clib_memcpy_fast ((V) + _v(l), (E), _v(n) * sizeof ((V)[0]));                        \
 } while (0)
 
 /** \brief Add N elements to end of vector V (no header, unspecified alignment)
@@ -749,7 +811,7 @@ do {                                                        \
   memmove ((V) + _v(m) + _v(n),                                \
           (V) + _v(m),                                 \
           (_v(l) - _v(m)) * sizeof ((V)[0]));          \
-  clib_memcpy ((V) + _v(m), (E),                       \
+  clib_memcpy_fast ((V) + _v(m), (E),                  \
               _v(n) * sizeof ((V)[0]));                \
 } while (0)
 
@@ -796,6 +858,7 @@ do {                                                                \
   if (_v(n) > 0)                                               \
     clib_memset ((V) + _v(l) - _v(n), 0, _v(n) * sizeof ((V)[0]));     \
   _vec_len (V) -= _v(n);                                       \
+  CLIB_MEM_POISON(vec_end(V), _v(n) * sizeof ((V)[0]));         \
 } while (0)
 
 /** \brief Delete the element at index I
@@ -810,6 +873,7 @@ do {                                                \
   if (_vec_del_i < _vec_del_l)                 \
     (v)[_vec_del_i] = (v)[_vec_del_l];         \
   _vec_len (v) = _vec_del_l;                   \
+  CLIB_MEM_POISON(vec_end(v), sizeof ((v)[0])); \
 } while (0)
 
 /** \brief Append v2 after v1. Result in v1.
@@ -824,7 +888,7 @@ do {                                                                        \
                                                                        \
   v1 = _vec_resize ((v1), _v(l2),                                      \
                    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, 0);        \
-  clib_memcpy ((v1) + _v(l1), (v2), _v(l2) * sizeof ((v2)[0]));                \
+  clib_memcpy_fast ((v1) + _v(l1), (v2), _v(l2) * sizeof ((v2)[0]));           \
 } while (0)
 
 /** \brief Append v2 after v1. Result in v1. Specified alignment.
@@ -840,7 +904,7 @@ do {                                                                        \
                                                                        \
   v1 = _vec_resize ((v1), _v(l2),                                      \
                    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, align);    \
-  clib_memcpy ((v1) + _v(l1), (v2), _v(l2) * sizeof ((v2)[0]));                \
+  clib_memcpy_fast ((v1) + _v(l1), (v2), _v(l2) * sizeof ((v2)[0]));           \
 } while (0)
 
 /** \brief Prepend v2 before v1. Result in v1.
@@ -856,7 +920,7 @@ do {                                                                    \
   v1 = _vec_resize ((v1), _v(l2),                                       \
                    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, 0);        \
   memmove ((v1) + _v(l2), (v1), _v(l1) * sizeof ((v1)[0]));             \
-  clib_memcpy ((v1), (v2), _v(l2) * sizeof ((v2)[0]));                  \
+  clib_memcpy_fast ((v1), (v2), _v(l2) * sizeof ((v2)[0]));                  \
 } while (0)
 
 /** \brief Prepend v2 before v1. Result in v1. Specified alignment
@@ -873,7 +937,7 @@ do {                                                                    \
   v1 = _vec_resize ((v1), _v(l2),                                       \
                    (_v(l1) + _v(l2)) * sizeof ((v1)[0]), 0, align);    \
   memmove ((v1) + _v(l2), (v1), _v(l1) * sizeof ((v1)[0]));             \
-  clib_memcpy ((v1), (v2), _v(l2) * sizeof ((v2)[0]));                  \
+  clib_memcpy_fast ((v1), (v2), _v(l2) * sizeof ((v2)[0]));                  \
 } while (0)
 
 
@@ -935,8 +999,8 @@ do {                                                \
 
 /** \brief Search a vector for the index of the entry that matches.
 
-    @param v1 Pointer to a vector
-    @param v2 Entry to match
+    @param v Pointer to a vector
+    @param E Entry to match
     @return index of match or ~0
 */
 #define vec_search(v,E)                                        \
@@ -955,8 +1019,8 @@ do {                                               \
 
 /** \brief Search a vector for the index of the entry that matches.
 
-    @param v1 Pointer to a vector
-    @param v2 Pointer to entry to match
+    @param v Pointer to a vector
+    @param E Pointer to entry to match
     @param fn Comparison function !0 => match
     @return index of match or ~0
 */
@@ -995,7 +1059,7 @@ do {                                                               \
     vec_reset_length (V);                       \
     vec_validate ((V), (L));                    \
     if ((S) && (L))                             \
-        clib_memcpy ((V), (S), (L));            \
+        clib_memcpy_fast ((V), (S), (L));            \
     (V)[(L)] = 0;                               \
   } while (0)