vppinfra: numa vector placement support 54/24454/12
authorDave Barach <dave@barachs.net>
Tue, 21 Jan 2020 17:34:55 +0000 (12:34 -0500)
committerFlorin Coras <florin.coras@gmail.com>
Wed, 5 Feb 2020 23:38:56 +0000 (23:38 +0000)
Type: feature

Signed-off-by: Dave Barach <dave@barachs.net>
Change-Id: I7e7d95a089dd849c1f01ecea84529d8dbf239f21

19 files changed:
src/plugins/http_static/static_server.c
src/vlib/cli.c
src/vlib/threads.c
src/vlib/threads.h
src/vlib/threads_cli.c
src/vnet/unix/gdb_funcs.c
src/vpp/api/api.c
src/vpp/vnet/main.c
src/vppinfra/CMakeLists.txt
src/vppinfra/bihash_template.c
src/vppinfra/config.h.in
src/vppinfra/mem.h
src/vppinfra/mem_dlmalloc.c
src/vppinfra/os.h
src/vppinfra/pool.h
src/vppinfra/unix-misc.c
src/vppinfra/vec.c
src/vppinfra/vec.h
src/vppinfra/vec_bootstrap.h

index 5dfa347..ddc8a9e 100644 (file)
@@ -133,8 +133,10 @@ http_static_server_session_alloc (u32 thread_index)
 {
   http_static_server_main_t *hsm = &http_static_server_main;
   http_session_t *hs;
-  pool_get (hsm->sessions[thread_index], hs);
-  memset (hs, 0, sizeof (*hs));
+  pool_get_aligned_zero_numa (hsm->sessions[thread_index], hs,
+                             0 /* not aligned */ ,
+                             1 /* zero */ ,
+                             os_get_numa_index ());
   hs->session_index = hs - hsm->sessions[thread_index];
   hs->thread_index = thread_index;
   hs->timer_handle = ~0;
index bb6c574..8504988 100644 (file)
@@ -733,9 +733,10 @@ show_memory_usage (vlib_main_t * vm,
                   unformat_input_t * input, vlib_cli_command_t * cmd)
 {
   int verbose __attribute__ ((unused)) = 0;
-  int api_segment = 0, stats_segment = 0, main_heap = 0;
+  int api_segment = 0, stats_segment = 0, main_heap = 0, numa_heaps = 0;
   clib_error_t *error;
   u32 index = 0;
+  int i;
   uword clib_mem_trace_enable_disable (uword enable);
   uword was_enabled;
 
@@ -750,6 +751,8 @@ show_memory_usage (vlib_main_t * vm,
        stats_segment = 1;
       else if (unformat (input, "main-heap"))
        main_heap = 1;
+      else if (unformat (input, "numa-heaps"))
+       numa_heaps = 1;
       else
        {
          error = clib_error_return (0, "unknown input `%U'",
@@ -758,9 +761,9 @@ show_memory_usage (vlib_main_t * vm,
        }
     }
 
-  if ((api_segment + stats_segment + main_heap) == 0)
+  if ((api_segment + stats_segment + main_heap + numa_heaps) == 0)
     return clib_error_return
-      (0, "Please supply one of api-segment, stats-segment or main-heap");
+      (0, "Need one of api-segment, stats-segment, main-heap or numa-heaps");
 
   if (api_segment)
     {
@@ -801,6 +804,7 @@ show_memory_usage (vlib_main_t * vm,
       vec_free (s);
     }
 
+
 #if USE_DLMALLOC == 0
   /* *INDENT-OFF* */
   foreach_vlib_main (
@@ -849,6 +853,32 @@ show_memory_usage (vlib_main_t * vm,
        /* Restore the trace flag */
        clib_mem_trace_enable_disable (was_enabled);
       }
+    if (numa_heaps)
+      {
+       struct dlmallinfo mi;
+       void *mspace;
+
+       for (i = 0; i < ARRAY_LEN (clib_per_numa_mheaps); i++)
+         {
+           if (clib_per_numa_mheaps[i] == 0)
+             continue;
+           if (clib_per_numa_mheaps[i] == clib_per_cpu_mheaps[i])
+             {
+               vlib_cli_output (vm, "Numa %d uses the main heap...", i);
+               continue;
+             }
+           was_enabled = clib_mem_trace_enable_disable (0);
+           mspace = clib_per_numa_mheaps[i];
+
+           mi = mspace_mallinfo (mspace);
+           vlib_cli_output (vm, "Numa %d:", i);
+           vlib_cli_output (vm, "  %U\n", format_page_map,
+                            pointer_to_uword (mspace_least_addr (mspace)),
+                            mi.arena);
+           vlib_cli_output (vm, "  %U\n", format_mheap,
+                            clib_per_numa_mheaps[index], verbose);
+         }
+      }
   }
 #endif /* USE_DLMALLOC */
   return 0;
@@ -857,7 +887,8 @@ show_memory_usage (vlib_main_t * vm,
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_memory_usage_command, static) = {
   .path = "show memory",
-  .short_help = "show memory [api-segment][stats-segment][verbose]",
+  .short_help = "show memory [api-segment][stats-segment][verbose]\n"
+  "            [numa-heaps]",
   .function = show_memory_usage,
 };
 /* *INDENT-ON* */
@@ -905,6 +936,7 @@ enable_disable_memory_trace (vlib_main_t * vm,
   int api_segment = 0;
   int stats_segment = 0;
   int main_heap = 0;
+  u32 numa_id = ~0;
   void *oldheap;
 
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -920,6 +952,8 @@ enable_disable_memory_trace (vlib_main_t * vm,
        stats_segment = 1;
       else if (unformat (line_input, "main-heap"))
        main_heap = 1;
+      else if (unformat (line_input, "numa-heap %d", &numa_id))
+       ;
       else
        {
          unformat_free (line_input);
@@ -928,10 +962,12 @@ enable_disable_memory_trace (vlib_main_t * vm,
     }
   unformat_free (line_input);
 
-  if ((api_segment + stats_segment + main_heap + (enable == 0)) == 0)
+  if ((api_segment + stats_segment + main_heap + (enable == 0)
+       + (numa_id != ~0)) == 0)
     {
       return clib_error_return
-       (0, "Need one of main-heap, stats-segment or api-segment");
+       (0, "Need one of main-heap, stats-segment, api-segment,\n"
+        "numa-heap <nn> or disable");
     }
 
   /* Turn off current trace, if any */
@@ -975,13 +1011,31 @@ enable_disable_memory_trace (vlib_main_t * vm,
       clib_mem_trace (main_heap);
     }
 
+  if (numa_id != ~0)
+    {
+      if (numa_id >= ARRAY_LEN (clib_per_numa_mheaps))
+       return clib_error_return (0, "Numa %d out of range", numa_id);
+      if (clib_per_numa_mheaps[numa_id] == 0)
+       return clib_error_return (0, "Numa %d heap not active", numa_id);
+
+      if (clib_per_numa_mheaps[numa_id] == clib_mem_get_heap ())
+       return clib_error_return (0, "Numa %d uses the main heap...",
+                                 numa_id);
+      current_traced_heap = clib_per_numa_mheaps[numa_id];
+      oldheap = clib_mem_set_heap (current_traced_heap);
+      clib_mem_trace (1);
+      clib_mem_set_heap (oldheap);
+    }
+
+
   return 0;
 }
 
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = {
   .path = "memory-trace",
-  .short_help = "memory-trace on|off [api-segment][stats-segment][main-heap]\n",
+  .short_help = "memory-trace on|off [api-segment][stats-segment][main-heap]\n"
+  "                   [numa-heap <numa-id>]\n",
   .function = enable_disable_memory_trace,
 };
 /* *INDENT-ON* */
index e6733d5..a827e35 100644 (file)
@@ -577,12 +577,12 @@ vlib_worker_thread_bootstrap_fn (void *arg)
   return rv;
 }
 
-static void
-vlib_get_thread_core_socket (vlib_worker_thread_t * w, unsigned cpu_id)
+void
+vlib_get_thread_core_numa (vlib_worker_thread_t * w, unsigned cpu_id)
 {
   const char *sys_cpu_path = "/sys/devices/system/cpu/cpu";
   u8 *p = 0;
-  int core_id = -1, socket_id = -1;
+  int core_id = -1, numa_id = -1;
 
   p = format (p, "%s%u/topology/core_id%c", sys_cpu_path, cpu_id, 0);
   clib_sysfs_read ((char *) p, "%d", &core_id);
@@ -590,11 +590,11 @@ vlib_get_thread_core_socket (vlib_worker_thread_t * w, unsigned cpu_id)
   p =
     format (p, "%s%u/topology/physical_package_id%c", sys_cpu_path, cpu_id,
            0);
-  clib_sysfs_read ((char *) p, "%d", &socket_id);
+  clib_sysfs_read ((char *) p, "%d", &numa_id);
   vec_free (p);
 
   w->core_id = core_id;
-  w->socket_id = socket_id;
+  w->numa_id = numa_id;
 }
 
 static clib_error_t *
@@ -602,9 +602,29 @@ vlib_launch_thread_int (void *fp, vlib_worker_thread_t * w, unsigned cpu_id)
 {
   vlib_thread_main_t *tm = &vlib_thread_main;
   void *(*fp_arg) (void *) = fp;
+  void *numa_heap;
 
   w->cpu_id = cpu_id;
-  vlib_get_thread_core_socket (w, cpu_id);
+  vlib_get_thread_core_numa (w, cpu_id);
+  os_set_numa_index (w->numa_id);
+
+  /* Set up NUMA-bound heap if indicated */
+  if (clib_per_numa_mheaps[w->numa_id] == 0)
+    {
+      /* If the user requested a NUMA heap, create it... */
+      if (tm->numa_heap_size)
+       {
+         numa_heap = clib_mem_init_thread_safe_numa
+           (0 /* DIY */ , tm->numa_heap_size);
+         clib_per_numa_mheaps[w->numa_id] = numa_heap;
+       }
+      else
+       {
+         /* Or, use the main heap */
+         clib_per_numa_mheaps[w->numa_id] = w->thread_mheap;
+       }
+    }
+
   if (tm->cb.vlib_launch_thread_cb && !w->registration->use_pthreads)
     return tm->cb.vlib_launch_thread_cb (fp, (void *) w, cpu_id);
   else
@@ -1242,6 +1262,9 @@ cpu_config (vlib_main_t * vm, unformat_input_t * input)
        ;
       else if (unformat (input, "skip-cores %u", &tm->skip_cores))
        ;
+      else if (unformat (input, "numa-heap-size %U",
+                        unformat_memory_size, &tm->numa_heap_size))
+       ;
       else if (unformat (input, "coremask-%s %U", &name,
                         unformat_bitmap_mask, &bitmap) ||
               unformat (input, "corelist-%s %U", &name,
index 312323c..c1188ce 100644 (file)
@@ -110,7 +110,7 @@ typedef struct
   long lwp;
   int cpu_id;
   int core_id;
-  int socket_id;
+  int numa_id;
   pthread_t thread_id;
 } vlib_worker_thread_t;
 
@@ -338,6 +338,10 @@ typedef struct
   /* callbacks */
   vlib_thread_callbacks_t cb;
   int extern_thread_mgmt;
+
+  /* NUMA-bound heap size */
+  uword numa_heap_size;
+
 } vlib_thread_main_t;
 
 extern vlib_thread_main_t vlib_thread_main;
@@ -613,6 +617,9 @@ void
 vlib_process_signal_event_mt_helper (vlib_process_signal_event_mt_args_t *
                                     args);
 void vlib_rpc_call_main_thread (void *function, u8 * args, u32 size);
+void vlib_get_thread_core_numa (vlib_worker_thread_t * w, unsigned cpu_id);
+
+
 #endif /* included_vlib_threads_h */
 
 /*
index 65b3e2a..bcb85ec 100644 (file)
@@ -67,8 +67,8 @@ show_threads_fn (vlib_main_t * vm,
       if (cpu_id > -1)
        {
          int core_id = w->core_id;
-         int socket_id = w->socket_id;
-         line = format (line, "%-7u%-7u%-7u%", cpu_id, core_id, socket_id);
+         int numa_id = w->numa_id;
+         line = format (line, "%-7u%-7u%-7u%", cpu_id, core_id, numa_id);
        }
       else
        {
index 6f0dd20..29011f0 100644 (file)
@@ -37,6 +37,21 @@ vl (void *p)
   return vec_len (p);
 }
 
+/**
+ * @brief GDB callable function: pvh - Return vector header of vector
+ *
+ * @param *p - void - address of vector
+ *
+ * @return vh - vec_header_t, the vector header
+ *
+ */
+vec_header_t *
+pvh (void *p)
+{
+  return _vec_find (p);
+}
+
+
 /**
  * @brief GDB callable function: pe - call pool_elts - number of elements in a pool
  *
index 2ed9cee..f652205 100644 (file)
@@ -273,7 +273,7 @@ get_thread_data (vl_api_thread_data_t * td, int index)
   td->pid = htonl (w->lwp);
   td->cpu_id = htonl (w->cpu_id);
   td->core = htonl (w->core_id);
-  td->cpu_socket = htonl (w->socket_id);
+  td->cpu_socket = htonl (w->numa_id);
 }
 
 static void
index ea8e4f8..7c7dd98 100644 (file)
@@ -20,6 +20,7 @@
 #include <vppinfra/cpu.h>
 #include <vlib/vlib.h>
 #include <vlib/unix/unix.h>
+#include <vlib/threads.h>
 #include <vnet/plugin/plugin.h>
 #include <vnet/ethernet/ethernet.h>
 #include <vpp/app/version.h>
@@ -107,6 +108,7 @@ main (int argc, char *argv[])
   u32 size;
   int main_core = 1;
   cpu_set_t cpuset;
+  void *main_heap;
 
 #if __x86_64__
   CLIB_UNUSED (const char *msg)
@@ -273,8 +275,17 @@ defaulted:
   vl_msg_api_set_first_available_msg_id (VL_MSG_FIRST_AVAILABLE);
 
   /* Allocate main heap */
-  if (clib_mem_init_thread_safe (0, main_heap_size))
+  if ((main_heap = clib_mem_init_thread_safe (0, main_heap_size)))
     {
+      vlib_worker_thread_t tmp;
+
+      /* Figure out which numa runs the main thread */
+      vlib_get_thread_core_numa (&tmp, main_core);
+      __os_numa_index = tmp.numa_id;
+
+      /* and use the main heap as that numa's numa heap */
+      clib_mem_set_per_numa_heap (main_heap);
+
       vm->init_functions_called = hash_create (0, /* value bytes */ 0);
       vpe_main_init (vm);
       return vlib_unix_main (argc, argv);
index 1c234cc..60e6eef 100644 (file)
@@ -24,6 +24,13 @@ else(VPP_USE_DLMALLOC)
   set(DLMALLOC 0)
 endif(VPP_USE_DLMALLOC)
 
+find_library(NUMA numa)
+if (NUMA)
+  set(NUMA_LIBRARY_FOUND 1)
+else(NUMA)
+  set(NUMA_LIBRARY_FOUND 0)
+endif()
+
 configure_file(
   ${CMAKE_SOURCE_DIR}/vppinfra/config.h.in
   ${CMAKE_BINARY_DIR}/vppinfra/config.h
@@ -210,7 +217,7 @@ endif(VPP_USE_DLMALLOC)
 
 add_vpp_library(vppinfra
   SOURCES ${VPPINFRA_SRCS}
-  LINK_LIBRARIES m
+  LINK_LIBRARIES m ${NUMA}
   INSTALL_HEADERS ${VPPINFRA_HEADERS}
   COMPONENT libvppinfra
 )
index dda3596..2b37842 100644 (file)
@@ -179,7 +179,6 @@ void BV (clib_bihash_master_init_svm)
                        sizeof (vec_header_t) +
                        BIHASH_FREELIST_LENGTH * sizeof (u64));
   freelist_vh->len = BIHASH_FREELIST_LENGTH;
-  freelist_vh->dlmalloc_header_offset = 0xDEADBEEF;
   h->sh->freelists_as_u64 =
     (u64) BV (clib_bihash_get_offset) (h, freelist_vh->vector_data);
   h->freelists = (void *) (freelist_vh->vector_data);
index a7a22a6..b236663 100644 (file)
@@ -21,6 +21,7 @@
 #endif
 
 #define USE_DLMALLOC @DLMALLOC@
+#define HAVE_NUMA_LIBRARY @NUMA_LIBRARY_FOUND@
 
 #define CLIB_TARGET_TRIPLET "@CMAKE_C_COMPILER_TARGET@"
 #endif
index d4819b7..5492e10 100644 (file)
 #include <vppinfra/sanitizer.h>
 
 #define CLIB_MAX_MHEAPS 256
+#define CLIB_MAX_NUMAS 8
+
+/* Unspecified NUMA socket */
+#define VEC_NUMA_UNSPECIFIED (0xFF)
 
 /* Per CPU heaps. */
 extern void *clib_per_cpu_mheaps[CLIB_MAX_MHEAPS];
+extern void *clib_per_numa_mheaps[CLIB_MAX_NUMAS];
+
+always_inline void *
+clib_mem_get_per_cpu_heap (void)
+{
+  int cpu = os_get_thread_index ();
+  return clib_per_cpu_mheaps[cpu];
+}
+
+always_inline void *
+clib_mem_set_per_cpu_heap (u8 * new_heap)
+{
+  int cpu = os_get_thread_index ();
+  void *old = clib_per_cpu_mheaps[cpu];
+  clib_per_cpu_mheaps[cpu] = new_heap;
+  return old;
+}
+
+always_inline void *
+clib_mem_get_per_numa_heap (u32 numa_id)
+{
+  ASSERT (numa_id >= 0 && numa_id < ARRAY_LEN (clib_per_numa_mheaps));
+  return clib_per_numa_mheaps[numa_id];
+}
+
+always_inline void *
+clib_mem_set_per_numa_heap (u8 * new_heap)
+{
+  int numa = os_get_numa_index ();
+  void *old = clib_per_numa_mheaps[numa];
+  clib_per_numa_mheaps[numa] = new_heap;
+  return old;
+}
 
 always_inline void
 clib_mem_set_thread_index (void)
@@ -81,22 +118,6 @@ clib_mem_set_thread_index (void)
   ASSERT (__os_thread_index > 0);
 }
 
-always_inline void *
-clib_mem_get_per_cpu_heap (void)
-{
-  int cpu = os_get_thread_index ();
-  return clib_per_cpu_mheaps[cpu];
-}
-
-always_inline void *
-clib_mem_set_per_cpu_heap (u8 * new_heap)
-{
-  int cpu = os_get_thread_index ();
-  void *old = clib_per_cpu_mheaps[cpu];
-  clib_per_cpu_mheaps[cpu] = new_heap;
-  return old;
-}
-
 always_inline uword
 clib_mem_size_nocheck (void *p)
 {
@@ -287,6 +308,7 @@ clib_mem_set_heap (void *heap)
 
 void *clib_mem_init (void *heap, uword size);
 void *clib_mem_init_thread_safe (void *memory, uword memory_size);
+void *clib_mem_init_thread_safe_numa (void *memory, uword memory_size);
 
 void clib_mem_exit (void);
 
index 68901a5..38226e2 100644 (file)
 #include <vppinfra/hash.h>
 #include <vppinfra/elf_clib.h>
 #include <vppinfra/sanitizer.h>
+#include <numaif.h>
 
 void *clib_per_cpu_mheaps[CLIB_MAX_MHEAPS];
+void *clib_per_numa_mheaps[CLIB_MAX_NUMAS];
 
 typedef struct
 {
@@ -202,8 +204,8 @@ mheap_trace_main_free (mheap_trace_main_t * tm)
 
 /* Initialize CLIB heap based on memory/size given by user.
    Set memory to 0 and CLIB will try to allocate its own heap. */
-void *
-clib_mem_init (void *memory, uword memory_size)
+static void *
+clib_mem_init_internal (void *memory, uword memory_size, int set_heap)
 {
   u8 *heap;
 
@@ -217,7 +219,8 @@ clib_mem_init (void *memory, uword memory_size)
 
   CLIB_MEM_POISON (mspace_least_addr (heap), mspace_footprint (heap));
 
-  clib_mem_set_heap (heap);
+  if (set_heap)
+    clib_mem_set_heap (heap);
 
   if (mheap_trace_main.lock == 0)
     clib_spinlock_init (&mheap_trace_main.lock);
@@ -225,10 +228,63 @@ clib_mem_init (void *memory, uword memory_size)
   return heap;
 }
 
+void *
+clib_mem_init (void *memory, uword memory_size)
+{
+  return clib_mem_init_internal (memory, memory_size,
+                                1 /* do clib_mem_set_heap */ );
+}
+
 void *
 clib_mem_init_thread_safe (void *memory, uword memory_size)
 {
-  return clib_mem_init (memory, memory_size);
+  return clib_mem_init_internal (memory, memory_size,
+                                1 /* do clib_mem_set_heap */ );
+}
+
+void *
+clib_mem_init_thread_safe_numa (void *memory, uword memory_size)
+{
+  void *heap;
+  unsigned long this_numa;
+
+  heap =
+    clib_mem_init_internal (memory, memory_size,
+                           0 /* do NOT clib_mem_set_heap */ );
+
+  ASSERT (heap);
+
+  this_numa = os_get_numa_index ();
+
+#if HAVE_NUMA_LIBRARY > 0
+  unsigned long nodemask = 1 << this_numa;
+  void *page_base;
+  unsigned long page_mask;
+  long rv;
+
+  /*
+   * Bind the heap to the current thread's NUMA node.
+   * heap is not naturally page-aligned, so fix it.
+   */
+
+  page_mask = ~(clib_mem_get_page_size () - 1);
+  page_base = (void *) (((unsigned long) heap) & page_mask);
+
+  clib_warning ("Bind heap at %llx size %llx to NUMA numa %d",
+               page_base, memory_size, this_numa);
+
+  rv = mbind (page_base, memory_size, MPOL_BIND /* mode */ ,
+             &nodemask /* nodemask */ ,
+             BITS (nodemask) /* max node number */ ,
+             MPOL_MF_MOVE /* flags */ );
+
+  if (rv < 0)
+    clib_unix_warning ("mbind");
+#else
+  clib_warning ("mbind unavailable, can't bind to numa %d", this_numa);
+#endif
+
+  return heap;
 }
 
 u8 *
index 50a4ad9..cd3b428 100644 (file)
@@ -57,6 +57,7 @@ void os_out_of_memory (void);
 f64 os_cpu_clock_frequency (void);
 
 extern __thread uword __os_thread_index;
+extern __thread uword __os_numa_index;
 
 static_always_inline uword
 os_get_thread_index (void)
@@ -70,6 +71,18 @@ os_set_thread_index (uword thread_index)
   __os_thread_index = thread_index;
 }
 
+static_always_inline uword
+os_get_numa_index (void)
+{
+  return __os_numa_index;
+}
+
+static_always_inline void
+os_set_numa_index (uword numa_index)
+{
+  __os_numa_index = numa_index;
+}
+
 static_always_inline uword
 os_get_cpu_number (void) __attribute__ ((deprecated));
 
index e6ffe1e..db950d2 100644 (file)
@@ -185,12 +185,13 @@ pool_free_elts (void *v)
 
    First search free list.  If nothing is free extend vector of objects.
 */
-#define _pool_get_aligned_internal(P,E,A,Z)                             \
+#define _pool_get_aligned_internal_numa(P,E,A,Z,N)                      \
 do {                                                                    \
   pool_header_t * _pool_var (p) = pool_header (P);                      \
   uword _pool_var (l);                                                  \
                                                                         \
-  STATIC_ASSERT(A==0 || ((A % sizeof(P[0]))==0) || ((sizeof(P[0]) % A) == 0), \
+  STATIC_ASSERT(A==0 || ((A % sizeof(P[0]))==0)                         \
+                || ((sizeof(P[0]) % A) == 0),                           \
                 "Pool aligned alloc of incorrectly sized object");      \
   _pool_var (l) = 0;                                                    \
   if (P)                                                                \
@@ -199,11 +200,12 @@ do {                                                                    \
   if (_pool_var (l) > 0)                                                \
     {                                                                   \
       /* Return free element from free list. */                         \
-      uword _pool_var (i) = _pool_var (p)->free_indices[_pool_var (l) - 1]; \
+      uword _pool_var (i) =                                             \
+        _pool_var (p)->free_indices[_pool_var (l) - 1];                 \
       (E) = (P) + _pool_var (i);                                        \
-      _pool_var (p)->free_bitmap =                                     \
-       clib_bitmap_andnoti_notrim (_pool_var (p)->free_bitmap,        \
-                                    _pool_var (i));                    \
+      _pool_var (p)->free_bitmap =                                      \
+       clib_bitmap_andnoti_notrim (_pool_var (p)->free_bitmap,         \
+                                    _pool_var (i));                    \
       _vec_len (_pool_var (p)->free_indices) = _pool_var (l) - 1;       \
       CLIB_MEM_UNPOISON((E), sizeof((E)[0]));                           \
     }                                                                   \
@@ -216,17 +218,30 @@ do {                                                                    \
           os_out_of_memory();                                           \
         }                                                               \
       /* Nothing on free list, make a new element and return it. */     \
-      P = _vec_resize (P,                                               \
+      P = _vec_resize_numa (P,                                          \
                       /* length_increment */ 1,                        \
                       /* new size */ (vec_len (P) + 1) * sizeof (P[0]), \
                       pool_aligned_header_bytes,                       \
-                      /* align */ (A));                                \
+                       /* align */ (A),                                 \
+                       /* numa */ (N));                                 \
       E = vec_end (P) - 1;                                              \
-    }                                                                  \
+    }                                                                   \
   if (Z)                                                                \
-    memset(E, 0, sizeof(*E));                                          \
+    memset(E, 0, sizeof(*E));                                           \
 } while (0)
 
+#define pool_get_aligned_zero_numa(P,E,A,Z,S) \
+  _pool_get_aligned_internal_numa(P,E,A,Z,S)
+
+#define pool_get_aligned_numa(P,E,A,S) \
+  _pool_get_aligned_internal_numa(P,E,A,0/*zero*/,S)
+
+#define pool_get_numa(P,E,S) \
+  _pool_get_aligned_internal_numa(P,E,0/*align*/,0/*zero*/,S)
+
+#define _pool_get_aligned_internal(P,E,A,Z) \
+  _pool_get_aligned_internal_numa(P,E,A,Z,VEC_NUMA_UNSPECIFIED)
+
 /** Allocate an object E from a pool P with alignment A */
 #define pool_get_aligned(P,E,A) _pool_get_aligned_internal(P,E,A,0)
 
index f693102..54016ed 100644 (file)
@@ -46,6 +46,7 @@
 #include <stdio.h>             /* for sprintf */
 
 __thread uword __os_thread_index = 0;
+__thread uword __os_numa_index = 0;
 
 clib_error_t *
 clib_file_n_bytes (char *file, uword * result)
index 16372e9..2ee7895 100644 (file)
@@ -44,16 +44,24 @@ 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)
 {
   vec_header_t *vh = _vec_find (v);
   uword old_alloc_bytes, new_alloc_bytes;
   void *old, *new;
+  void *oldheap;
 
   header_bytes = vec_header_bytes (header_bytes);
 
   data_bytes += 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));
+    }
+
   if (!v)
     {
       new = clib_mem_alloc_aligned_at_offset (data_bytes, data_align, header_bytes, 1  /* yes, call os_out_of_memory */
@@ -64,6 +72,9 @@ vec_resize_allocate_memory (void *v,
       CLIB_MEM_POISON (new + data_bytes, new_alloc_bytes - data_bytes);
       v = new + header_bytes;
       _vec_len (v) = length_increment;
+      _vec_numa (v) = numa_id;
+      if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
+       clib_mem_set_per_cpu_heap (oldheap);
       return v;
     }
 
@@ -79,6 +90,8 @@ vec_resize_allocate_memory (void *v,
   if (data_bytes <= old_alloc_bytes)
     {
       CLIB_MEM_UNPOISON (v, data_bytes);
+      if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
+       clib_mem_set_per_cpu_heap (oldheap);
       return v;
     }
 
@@ -110,6 +123,10 @@ vec_resize_allocate_memory (void *v,
   memset (v + old_alloc_bytes, 0, new_alloc_bytes - old_alloc_bytes);
   CLIB_MEM_POISON (new + data_bytes, new_alloc_bytes - data_bytes);
 
+  _vec_numa ((v + header_bytes)) = numa_id;
+  if (PREDICT_FALSE (numa_id != VEC_NUMA_UNSPECIFIED))
+    clib_mem_set_per_cpu_heap (oldheap);
+
   return v + header_bytes;
 }
 
index 021b229..9054eaa 100644 (file)
     @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
 
@@ -110,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);
 
@@ -132,6 +140,12 @@ _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));
 
@@ -140,15 +154,19 @@ _vec_resize_inline (void *v,
        {
          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
@@ -221,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
@@ -352,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));          \
+      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
@@ -412,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)
 
index fbb01b6..879703c 100644 (file)
@@ -58,11 +58,14 @@ typedef struct
   u64 len;
 #else
   u32 len; /**< Number of elements in vector (NOT its allocated length). */
-  u32 dlmalloc_header_offset;  /**< offset to memory allocator offset  */
+  u8 numa_id; /**< NUMA id */
+  u8 vpad[3]; /**< pad to 8 bytes */
 #endif
   u8 vector_data[0];  /**< Vector data . */
 } vec_header_t;
 
+#define VEC_NUMA_UNSPECIFIED (0xFF)
+
 /** \brief Find the vector header
 
     Given the user's pointer to a vector, find the corresponding
@@ -128,7 +131,7 @@ vec_aligned_header_end (void *v, uword header_bytes, uword align)
 
 /** \brief Number of elements in vector (lvalue-capable)
 
-   _vec_len (v) does not check for null, but can be used as a lvalue
+   _vec_len (v) does not check for null, but can be used as an lvalue
    (e.g. _vec_len (v) = 99).
 */
 
@@ -142,6 +145,20 @@ vec_aligned_header_end (void *v, uword header_bytes, uword align)
 
 #define vec_len(v)     ((v) ? _vec_len(v) : 0)
 
+/** \brief Vector's NUMA id (lvalue-capable)
+
+    _vec_numa(v) does not check for null, but can be used as an lvalue
+    (e.g. _vec_numa(v) = 1).
+*/
+
+#define _vec_numa(v) (_vec_find(v)->numa_id)
+
+/** \brief Return vector's NUMA ID (rvalue-only, NULL tolerant)
+    vec_numa(v) checks for NULL, but cannot be used as an lvalue.
+*/
+#define vec_numa(v) ((v) ? _vec_numa(v) : 0)
+
+
 /** \brief Number of data bytes in vector. */
 
 #define vec_bytes(v) (vec_len (v) * sizeof (v[0]))
@@ -208,6 +225,17 @@ for (var = vec_end (vec) - 1; var >= (vec); var--)
 #define vec_foreach_index_backwards(var,v) \
   for ((var) = vec_len((v)) - 1; (var) >= 0; (var)--)
 
+/** \brief return the NUMA index for a vector */
+always_inline uword
+vec_get_numa (void *v)
+{
+  vec_header_t *vh;
+  if (v == 0)
+    return 0;
+  vh = _vec_find (v);
+  return vh->numa_id;
+}
+
 #endif /* included_clib_vec_bootstrap_h */
 
 /*