memif: don't leak error strings in API handlers
[vpp.git] / src / plugins / perfmon / perfmon.c
index cc97888..e618f9b 100644 (file)
@@ -141,15 +141,19 @@ perfmon_set (vlib_main_t *vm, perfmon_bundle_t *b)
 
       vec_validate (pm->group_fds, i);
       pm->group_fds[i] = -1;
+      u8 n_events_opened = 0;
 
       for (int j = 0; j < b->n_events; j++)
        {
          int fd;
          perfmon_event_t *e = s->events + b->events[j];
+         if (!e->implemented)
+           continue;
          struct perf_event_attr pe = {
            .size = sizeof (struct perf_event_attr),
            .type = e->type_from_instance ? in->type : e->type,
            .config = e->config,
+           .config1 = e->config1,
            .exclude_kernel = e->exclude_kernel,
            .read_format =
              (PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED |
@@ -157,6 +161,7 @@ perfmon_set (vlib_main_t *vm, perfmon_bundle_t *b)
            .disabled = 1,
          };
 
+       perf_event_open:
          log_debug ("perf_event_open pe.type=%u pe.config=0x%x pid=%d "
                     "cpu=%d group_fd=%d",
                     pe.type, pe.config, in->pid, in->cpu, pm->group_fds[i]);
@@ -165,8 +170,17 @@ perfmon_set (vlib_main_t *vm, perfmon_bundle_t *b)
 
          if (fd == -1)
            {
-             err = clib_error_return_unix (0, "perf_event_open");
-             goto error;
+             if (errno ==
+                 EOPNOTSUPP) /* 64b counters not supported on aarch64 */
+               {
+                 pe.config1 = 2; /* retry with 32b counter width */
+                 goto perf_event_open;
+               }
+             else
+               {
+                 err = clib_error_return_unix (0, "perf_event_open");
+                 goto error;
+               }
            }
 
          vec_add1 (pm->fds_to_close, fd);
@@ -178,23 +192,24 @@ perfmon_set (vlib_main_t *vm, perfmon_bundle_t *b)
            {
              perfmon_thread_runtime_t *tr;
              tr = vec_elt_at_index (pm->thread_runtimes, i);
-             tr->mmap_pages[j] =
+             tr->mmap_pages[n_events_opened] =
                mmap (0, page_size, PROT_READ, MAP_SHARED, fd, 0);
 
-             if (tr->mmap_pages[j] == MAP_FAILED)
+             if (tr->mmap_pages[n_events_opened] == MAP_FAILED)
                {
                  err = clib_error_return_unix (0, "mmap");
                  goto error;
                }
            }
+         n_events_opened++;
        }
 
-      if (is_node)
+      if (is_node && n_events_opened)
        {
          perfmon_thread_runtime_t *rt;
          rt = vec_elt_at_index (pm->thread_runtimes, i);
          rt->bundle = b;
-         rt->n_events = b->n_events;
+         rt->n_events = n_events_opened;
          rt->n_nodes = n_nodes;
          rt->preserve_samples = b->preserve_samples;
          vec_validate_aligned (rt->node_stats, n_nodes - 1,
@@ -213,33 +228,6 @@ error:
   return err;
 }
 
-static_always_inline u32
-perfmon_mmap_read_index (const struct perf_event_mmap_page *mmap_page)
-{
-  u32 idx;
-  u32 seq;
-
-  /* See documentation in /usr/include/linux/perf_event.h, for more details
-   * but the 2 main important things are:
-   *  1) if seq != mmap_page->lock, it means the kernel is currently updating
-   *     the user page and we need to read it again
-   *  2) if idx == 0, it means the perf event is currently turned off and we
-   *     just need to read the kernel-updated 'offset', otherwise we must also
-   *     add the current hw value (hence rdmpc) */
-  do
-    {
-      seq = mmap_page->lock;
-      CLIB_COMPILER_BARRIER ();
-
-      idx = mmap_page->index;
-
-      CLIB_COMPILER_BARRIER ();
-    }
-  while (mmap_page->lock != seq);
-
-  return idx;
-}
-
 clib_error_t *
 perfmon_start (vlib_main_t *vm, perfmon_bundle_t *b)
 {
@@ -266,27 +254,17 @@ perfmon_start (vlib_main_t *vm, perfmon_bundle_t *b)
     }
   if (b->active_type == PERFMON_BUNDLE_TYPE_NODE)
     {
-      for (int i = 0; i < vec_len (pm->thread_runtimes); i++)
+      vlib_node_function_t *dispatch_wrapper = NULL;
+      err = b->src->config_dispatch_wrapper (b, &dispatch_wrapper);
+      if (err || !dispatch_wrapper)
        {
-         perfmon_thread_runtime_t *tr;
-         tr = vec_elt_at_index (pm->thread_runtimes, i);
-
-         for (int j = 0; j < b->n_events; j++)
-           {
-             tr->indexes[j] = perfmon_mmap_read_index (tr->mmap_pages[j]);
-
-             /* if a zero index is returned generate error */
-             if (!tr->indexes[j])
-               {
-                 perfmon_reset (vm);
-                 return clib_error_return (0, "invalid rdpmc index");
-               }
-           }
+         perfmon_reset (vm);
+         return err;
        }
 
       for (int i = 0; i < vlib_get_n_threads (); i++)
-       vlib_node_set_dispatch_wrapper (
-         vlib_get_main_by_index (i), perfmon_dispatch_wrappers[b->n_events]);
+       vlib_node_set_dispatch_wrapper (vlib_get_main_by_index (i),
+                                       dispatch_wrapper);
     }
   pm->sample_time = vlib_time_now (vm);
   pm->is_running = 1;
@@ -324,53 +302,6 @@ perfmon_stop (vlib_main_t *vm)
   return 0;
 }
 
-static_always_inline u8
-is_enough_counters (perfmon_bundle_t *b)
-{
-  u8 bl[PERFMON_EVENT_TYPE_MAX];
-  u8 cpu[PERFMON_EVENT_TYPE_MAX];
-
-  clib_memset (&bl, 0, sizeof (bl));
-  clib_memset (&cpu, 0, sizeof (cpu));
-
-  /* how many does this uarch support */
-  if (!clib_get_pmu_counter_count (&cpu[PERFMON_EVENT_TYPE_FIXED],
-                                  &cpu[PERFMON_EVENT_TYPE_GENERAL]))
-    return 0;
-
-  /* how many does the bundle require */
-  for (u16 i = 0; i < b->n_events; i++)
-    {
-      /* if source allows us to identify events, otherwise assume general */
-      if (b->src->get_event_type)
-       bl[b->src->get_event_type (b->events[i])]++;
-      else
-       bl[PERFMON_EVENT_TYPE_GENERAL]++;
-    }
-
-  /* consciously ignoring pseudo events here */
-  return cpu[PERFMON_EVENT_TYPE_GENERAL] >= bl[PERFMON_EVENT_TYPE_GENERAL] &&
-        cpu[PERFMON_EVENT_TYPE_FIXED] >= bl[PERFMON_EVENT_TYPE_FIXED];
-}
-
-static_always_inline u8
-is_bundle_supported (perfmon_bundle_t *b)
-{
-  perfmon_cpu_supports_t *supports = b->cpu_supports;
-
-  if (!is_enough_counters (b))
-    return 0;
-
-  if (!b->cpu_supports)
-    return 1;
-
-  for (int i = 0; i < b->n_cpu_supports; ++i)
-    if (supports[i].cpu_supports ())
-      return 1;
-
-  return 0;
-}
-
 static clib_error_t *
 perfmon_init (vlib_main_t *vm)
 {
@@ -413,7 +344,7 @@ perfmon_init (vlib_main_t *vm)
        }
 
       b->src = (perfmon_source_t *) p[0];
-      if (!is_bundle_supported (b))
+      if (b->src->bundle_support && !b->src->bundle_support (b))
        {
          log_debug ("skipping bundle '%s' - not supported", b->name);
          b = b->next;