perf arm-spe: Add snapshot mode support
authorGerman Gomez <german.gomez@arm.com>
Tue, 9 Nov 2021 16:30:07 +0000 (16:30 +0000)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 13 Nov 2021 21:11:50 +0000 (18:11 -0300)
This patch enables support for snapshot mode of arm_spe events,
including the implementation of the necessary callbacks (excluding
find_snapshot, which is to be included in a followup commit).

Reviewed-by: James Clark <james.clark@arm.com>
Reviewed-by: Leo Yan <leo.yan@linaro.org>
Signed-off-by: German Gomez <german.gomez@arm.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Link: https://lore.kernel.org/r/20211109163009.92072-2-german.gomez@arm.com
Tested-by: Leo Yan <leo.yan@linaro.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/arch/arm64/util/arm-spe.c

index a4420d4..f8b03d1 100644 (file)
@@ -84,6 +84,55 @@ static int arm_spe_info_fill(struct auxtrace_record *itr,
        return 0;
 }
 
+static void
+arm_spe_snapshot_resolve_auxtrace_defaults(struct record_opts *opts,
+                                          bool privileged)
+{
+       /*
+        * The default snapshot size is the auxtrace mmap size. If neither auxtrace mmap size nor
+        * snapshot size is specified, then the default is 4MiB for privileged users, 128KiB for
+        * unprivileged users.
+        *
+        * The default auxtrace mmap size is 4MiB/page_size for privileged users, 128KiB for
+        * unprivileged users. If an unprivileged user does not specify mmap pages, the mmap pages
+        * will be reduced from the default 512KiB/page_size to 256KiB/page_size, otherwise the
+        * user is likely to get an error as they exceed their mlock limmit.
+        */
+
+       /*
+        * No size were given to '-S' or '-m,', so go with the default
+        */
+       if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
+               if (privileged) {
+                       opts->auxtrace_mmap_pages = MiB(4) / page_size;
+               } else {
+                       opts->auxtrace_mmap_pages = KiB(128) / page_size;
+                       if (opts->mmap_pages == UINT_MAX)
+                               opts->mmap_pages = KiB(256) / page_size;
+               }
+       } else if (!opts->auxtrace_mmap_pages && !privileged && opts->mmap_pages == UINT_MAX) {
+               opts->mmap_pages = KiB(256) / page_size;
+       }
+
+       /*
+        * '-m,xyz' was specified but no snapshot size, so make the snapshot size as big as the
+        * auxtrace mmap area.
+        */
+       if (!opts->auxtrace_snapshot_size)
+               opts->auxtrace_snapshot_size = opts->auxtrace_mmap_pages * (size_t)page_size;
+
+       /*
+        * '-Sxyz' was specified but no auxtrace mmap area, so make the auxtrace mmap area big
+        * enough to fit the requested snapshot size.
+        */
+       if (!opts->auxtrace_mmap_pages) {
+               size_t sz = opts->auxtrace_snapshot_size;
+
+               sz = round_up(sz, page_size) / page_size;
+               opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
+       }
+}
+
 static int arm_spe_recording_options(struct auxtrace_record *itr,
                                     struct evlist *evlist,
                                     struct record_opts *opts)
@@ -115,6 +164,36 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
        if (!opts->full_auxtrace)
                return 0;
 
+       /*
+        * we are in snapshot mode.
+        */
+       if (opts->auxtrace_snapshot_mode) {
+               /*
+                * Command arguments '-Sxyz' and/or '-m,xyz' are missing, so fill those in with
+                * default values.
+                */
+               if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages)
+                       arm_spe_snapshot_resolve_auxtrace_defaults(opts, privileged);
+
+               /*
+                * Snapshot size can't be bigger than the auxtrace area.
+                */
+               if (opts->auxtrace_snapshot_size > opts->auxtrace_mmap_pages * (size_t)page_size) {
+                       pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
+                              opts->auxtrace_snapshot_size,
+                              opts->auxtrace_mmap_pages * (size_t)page_size);
+                       return -EINVAL;
+               }
+
+               /*
+                * Something went wrong somewhere - this shouldn't happen.
+                */
+               if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
+                       pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
+                       return -EINVAL;
+               }
+       }
+
        /* We are in full trace mode but '-m,xyz' wasn't specified */
        if (!opts->auxtrace_mmap_pages) {
                if (privileged) {
@@ -138,6 +217,9 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
                }
        }
 
+       if (opts->auxtrace_snapshot_mode)
+               pr_debug2("%sx snapshot size: %zu\n", ARM_SPE_PMU_NAME,
+                         opts->auxtrace_snapshot_size);
 
        /*
         * To obtain the auxtrace buffer file descriptor, the auxtrace event
@@ -172,6 +254,51 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
        return 0;
 }
 
+static int arm_spe_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
+                                        struct record_opts *opts,
+                                        const char *str)
+{
+       unsigned long long snapshot_size = 0;
+       char *endptr;
+
+       if (str) {
+               snapshot_size = strtoull(str, &endptr, 0);
+               if (*endptr || snapshot_size > SIZE_MAX)
+                       return -1;
+       }
+
+       opts->auxtrace_snapshot_mode = true;
+       opts->auxtrace_snapshot_size = snapshot_size;
+
+       return 0;
+}
+
+static int arm_spe_snapshot_start(struct auxtrace_record *itr)
+{
+       struct arm_spe_recording *ptr =
+                       container_of(itr, struct arm_spe_recording, itr);
+       struct evsel *evsel;
+
+       evlist__for_each_entry(ptr->evlist, evsel) {
+               if (evsel->core.attr.type == ptr->arm_spe_pmu->type)
+                       return evsel__disable(evsel);
+       }
+       return -EINVAL;
+}
+
+static int arm_spe_snapshot_finish(struct auxtrace_record *itr)
+{
+       struct arm_spe_recording *ptr =
+                       container_of(itr, struct arm_spe_recording, itr);
+       struct evsel *evsel;
+
+       evlist__for_each_entry(ptr->evlist, evsel) {
+               if (evsel->core.attr.type == ptr->arm_spe_pmu->type)
+                       return evsel__enable(evsel);
+       }
+       return -EINVAL;
+}
+
 static u64 arm_spe_reference(struct auxtrace_record *itr __maybe_unused)
 {
        struct timespec ts;
@@ -207,6 +334,9 @@ struct auxtrace_record *arm_spe_recording_init(int *err,
 
        sper->arm_spe_pmu = arm_spe_pmu;
        sper->itr.pmu = arm_spe_pmu;
+       sper->itr.snapshot_start = arm_spe_snapshot_start;
+       sper->itr.snapshot_finish = arm_spe_snapshot_finish;
+       sper->itr.parse_snapshot_options = arm_spe_parse_snapshot_options;
        sper->itr.recording_options = arm_spe_recording_options;
        sper->itr.info_priv_size = arm_spe_info_priv_size;
        sper->itr.info_fill = arm_spe_info_fill;