libperf: Add evsel mmap support
authorRob Herring <robh@kernel.org>
Wed, 14 Apr 2021 16:07:37 +0000 (11:07 -0500)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 15 Apr 2021 19:38:51 +0000 (16:38 -0300)
In order to support usersapce access, an event must be mmapped. While
there's already mmap support for evlist, the usecase is a bit different
than the self monitoring with userspace access. So let's add new
perf_evsel__mmap()/perf_evsel_munmap() functions to mmap/munmap an
evsel. This allows implementing userspace access as a fastpath for
perf_evsel__read().

The mmapped address is returned by perf_evsel__mmap_base() which
primarily for users/tests to check if userspace access is enabled.

Signed-off-by: Rob Herring <robh@kernel.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Itaru Kitayama <itaru.kitayama@gmail.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will@kernel.org>
Link: http://lore.kernel.org/lkml/20210414155412.3697605-2-robh@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/lib/perf/Documentation/libperf.txt
tools/lib/perf/evsel.c
tools/lib/perf/include/internal/evsel.h
tools/lib/perf/include/perf/evsel.h
tools/lib/perf/libperf.map

index 0c74c30ed23a1b67e2f474f0fb9887b5439005b6..63ae5e0195ce994df0f27b3c7bb274caffb0fed9 100644 (file)
@@ -136,6 +136,9 @@ SYNOPSIS
                        struct perf_thread_map *threads);
   void perf_evsel__close(struct perf_evsel *evsel);
   void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu);
+  int perf_evsel__mmap(struct perf_evsel *evsel, int pages);
+  void perf_evsel__munmap(struct perf_evsel *evsel);
+  void *perf_evsel__mmap_base(struct perf_evsel *evsel, int cpu, int thread);
   int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
                        struct perf_counts_values *count);
   int perf_evsel__enable(struct perf_evsel *evsel);
index 4dc06289f4c75334619705ffa19bef32f6ebb504..cd203998e87542b7486373619058c85b7126d954 100644 (file)
 #include <stdlib.h>
 #include <internal/xyarray.h>
 #include <internal/cpumap.h>
+#include <internal/mmap.h>
 #include <internal/threadmap.h>
 #include <internal/lib.h>
 #include <linux/string.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
 
 void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr)
 {
@@ -38,6 +40,7 @@ void perf_evsel__delete(struct perf_evsel *evsel)
 }
 
 #define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y))
+#define MMAP(e, x, y) (e->mmap ? ((struct perf_mmap *) xyarray__entry(e->mmap, x, y)) : NULL)
 
 int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
 {
@@ -55,6 +58,13 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
        return evsel->fd != NULL ? 0 : -ENOMEM;
 }
 
+static int perf_evsel__alloc_mmap(struct perf_evsel *evsel, int ncpus, int nthreads)
+{
+       evsel->mmap = xyarray__new(ncpus, nthreads, sizeof(struct perf_mmap));
+
+       return evsel->mmap != NULL ? 0 : -ENOMEM;
+}
+
 static int
 sys_perf_event_open(struct perf_event_attr *attr,
                    pid_t pid, int cpu, int group_fd,
@@ -156,6 +166,72 @@ void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu)
        perf_evsel__close_fd_cpu(evsel, cpu);
 }
 
+void perf_evsel__munmap(struct perf_evsel *evsel)
+{
+       int cpu, thread;
+
+       if (evsel->fd == NULL || evsel->mmap == NULL)
+               return;
+
+       for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) {
+               for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
+                       int fd = FD(evsel, cpu, thread);
+                       struct perf_mmap *map = MMAP(evsel, cpu, thread);
+
+                       if (fd < 0)
+                               continue;
+
+                       perf_mmap__munmap(map);
+               }
+       }
+
+       xyarray__delete(evsel->mmap);
+       evsel->mmap = NULL;
+}
+
+int perf_evsel__mmap(struct perf_evsel *evsel, int pages)
+{
+       int ret, cpu, thread;
+       struct perf_mmap_param mp = {
+               .prot = PROT_READ | PROT_WRITE,
+               .mask = (pages * page_size) - 1,
+       };
+
+       if (evsel->fd == NULL || evsel->mmap)
+               return -EINVAL;
+
+       if (perf_evsel__alloc_mmap(evsel, xyarray__max_x(evsel->fd), xyarray__max_y(evsel->fd)) < 0)
+               return -ENOMEM;
+
+       for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) {
+               for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
+                       int fd = FD(evsel, cpu, thread);
+                       struct perf_mmap *map = MMAP(evsel, cpu, thread);
+
+                       if (fd < 0)
+                               continue;
+
+                       perf_mmap__init(map, NULL, false, NULL);
+
+                       ret = perf_mmap__mmap(map, &mp, fd, cpu);
+                       if (ret) {
+                               perf_evsel__munmap(evsel);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+void *perf_evsel__mmap_base(struct perf_evsel *evsel, int cpu, int thread)
+{
+       if (FD(evsel, cpu, thread) < 0 || MMAP(evsel, cpu, thread) == NULL)
+               return NULL;
+
+       return MMAP(evsel, cpu, thread)->base;
+}
+
 int perf_evsel__read_size(struct perf_evsel *evsel)
 {
        u64 read_format = evsel->attr.read_format;
index 1ffd083b235eb21b4f815b53c062710716f9035c..1c067d088bc640c5584841f16fedf5aa1ea56a69 100644 (file)
@@ -41,6 +41,7 @@ struct perf_evsel {
        struct perf_cpu_map     *own_cpus;
        struct perf_thread_map  *threads;
        struct xyarray          *fd;
+       struct xyarray          *mmap;
        struct xyarray          *sample_id;
        u64                     *id;
        u32                      ids;
index c82ec39a4ad064dc349fc65ce6feb7553442605b..60eae25076d3dc83ac6d2d2501e1b88e0942bbca 100644 (file)
@@ -27,6 +27,9 @@ LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *
                                 struct perf_thread_map *threads);
 LIBPERF_API void perf_evsel__close(struct perf_evsel *evsel);
 LIBPERF_API void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu);
+LIBPERF_API int perf_evsel__mmap(struct perf_evsel *evsel, int pages);
+LIBPERF_API void perf_evsel__munmap(struct perf_evsel *evsel);
+LIBPERF_API void *perf_evsel__mmap_base(struct perf_evsel *evsel, int cpu, int thread);
 LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
                                 struct perf_counts_values *count);
 LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel);
index 7be1af8a546c049e8f89f1e9e77f7324bef243ea..c0c7ceb11060a98fe5e9b8337122182ff915b4ff 100644 (file)
@@ -23,6 +23,9 @@ LIBPERF_0.0.1 {
                perf_evsel__disable;
                perf_evsel__open;
                perf_evsel__close;
+               perf_evsel__mmap;
+               perf_evsel__munmap;
+               perf_evsel__mmap_base;
                perf_evsel__read;
                perf_evsel__cpus;
                perf_evsel__threads;