]> www.infradead.org Git - nvme.git/commitdiff
perf kvm: Add kvm-stat for loongarch64
authorBibo Mao <maobibo@loongson.cn>
Wed, 10 Jul 2024 08:50:27 +0000 (16:50 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 10 Jul 2024 08:50:27 +0000 (16:50 +0800)
Add support for 'perf kvm stat' on loongarch64 platform, now only kvm
exit event is supported.

Here is example output about "perf kvm --host stat report" command

   Event name   Samples   Sample%     Time (ns)   Time%   Mean Time (ns)
    Mem Store     83969    51.00%     625697070   8.00%             7451
     Mem Read     37641    22.00%     112485730   1.00%             2988
    Interrupt     15542     9.00%      20620190   0.00%             1326
        IOCSR     15207     9.00%      94296190   1.00%             6200
    Hypercall      4873     2.00%      12265280   0.00%             2516
         Idle      3713     2.00%    6322055860  87.00%          1702681
          FPU      1819     1.00%       2750300   0.00%             1511
   Inst Fetch       502     0.00%       1341740   0.00%             2672
   Mem Modify       324     0.00%        602240   0.00%             1858
       CPUCFG        55     0.00%         77610   0.00%             1411
          CSR        12     0.00%         19690   0.00%             1640
         LASX         3     0.00%          4870   0.00%             1623
          LSX         2     0.00%          2100   0.00%             1050

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
tools/perf/arch/loongarch/Makefile
tools/perf/arch/loongarch/util/Build
tools/perf/arch/loongarch/util/header.c [new file with mode: 0644]
tools/perf/arch/loongarch/util/kvm-stat.c [new file with mode: 0644]

index 3992a67a87d9c6419bc72291a1d3e05d0ac29f30..c89d6bb6b184b18ac8b6215230cbc44986ee8355 100644 (file)
@@ -4,6 +4,7 @@ PERF_HAVE_DWARF_REGS := 1
 endif
 PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
 PERF_HAVE_JITDUMP := 1
+HAVE_KVM_STAT_SUPPORT := 1
 
 #
 # Syscall table generation for perf
index d776125a2d06832b5841c798bf39460357737ce9..b12d374d70964929ef39c211dfd5edd9cd51f01e 100644 (file)
@@ -1,5 +1,7 @@
+perf-y += header.o
 perf-y += perf_regs.o
 
 perf-$(CONFIG_DWARF)     += dwarf-regs.o
 perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
 perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+perf-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
new file mode 100644 (file)
index 0000000..d962dff
--- /dev/null
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of get_cpuid().
+ *
+ * Author: Nikita Shubin <n.shubin@yadro.com>
+ *         Bibo Mao <maobibo@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <api/fs/fs.h>
+#include <errno.h>
+#include "util/debug.h"
+#include "util/header.h"
+
+/*
+ * Output example from /proc/cpuinfo
+ *   CPU Family              : Loongson-64bit
+ *   Model Name              : Loongson-3C5000
+ *   CPU Revision            : 0x10
+ *   FPU Revision            : 0x01
+ */
+#define CPUINFO_MODEL  "Model Name"
+#define CPUINFO                "/proc/cpuinfo"
+
+static char *_get_field(const char *line)
+{
+       char *line2, *nl;
+
+       line2 = strrchr(line, ' ');
+       if (!line2)
+               return NULL;
+
+       line2++;
+       nl = strrchr(line, '\n');
+       if (!nl)
+               return NULL;
+
+       return strndup(line2, nl - line2);
+}
+
+static char *_get_cpuid(void)
+{
+       unsigned long line_sz;
+       char *line, *model, *cpuid;
+       FILE *file;
+
+       file = fopen(CPUINFO, "r");
+       if (file == NULL)
+               return NULL;
+
+       line = model = cpuid = NULL;
+       while (getline(&line, &line_sz, file) != -1) {
+               if (strncmp(line, CPUINFO_MODEL, strlen(CPUINFO_MODEL)))
+                       continue;
+
+               model = _get_field(line);
+               if (!model)
+                       goto out_free;
+               break;
+       }
+
+       if (model && (asprintf(&cpuid, "%s", model) < 0))
+               cpuid = NULL;
+
+out_free:
+       fclose(file);
+       free(model);
+       return cpuid;
+}
+
+int get_cpuid(char *buffer, size_t sz)
+{
+       int ret = 0;
+       char *cpuid = _get_cpuid();
+
+       if (!cpuid)
+               return EINVAL;
+
+       if (sz < strlen(cpuid)) {
+               ret = ENOBUFS;
+               goto out_free;
+       }
+
+       scnprintf(buffer, sz, "%s", cpuid);
+
+out_free:
+       free(cpuid);
+       return ret;
+}
+
+char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
+{
+       return _get_cpuid();
+}
diff --git a/tools/perf/arch/loongarch/util/kvm-stat.c b/tools/perf/arch/loongarch/util/kvm-stat.c
new file mode 100644 (file)
index 0000000..a7859a3
--- /dev/null
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <memory.h>
+#include "util/kvm-stat.h"
+#include "util/parse-events.h"
+#include "util/debug.h"
+#include "util/evsel.h"
+#include "util/evlist.h"
+#include "util/pmus.h"
+
+#define LOONGARCH_EXCEPTION_INT                0
+#define LOONGARCH_EXCEPTION_PIL                1
+#define LOONGARCH_EXCEPTION_PIS                2
+#define LOONGARCH_EXCEPTION_PIF                3
+#define LOONGARCH_EXCEPTION_PME                4
+#define LOONGARCH_EXCEPTION_FPD                15
+#define LOONGARCH_EXCEPTION_SXD                16
+#define LOONGARCH_EXCEPTION_ASXD       17
+#define LOONGARCH_EXCEPTION_GSPR       22
+#define  LOONGARCH_EXCEPTION_CPUCFG    100
+#define  LOONGARCH_EXCEPTION_CSR       101
+#define  LOONGARCH_EXCEPTION_IOCSR     102
+#define  LOONGARCH_EXCEPTION_IDLE      103
+#define  LOONGARCH_EXCEPTION_OTHERS    104
+#define LOONGARCH_EXCEPTION_HVC                23
+
+#define loongarch_exception_type                               \
+       {LOONGARCH_EXCEPTION_INT,  "Interrupt" },               \
+       {LOONGARCH_EXCEPTION_PIL,  "Mem Read" },                \
+       {LOONGARCH_EXCEPTION_PIS,  "Mem Store" },               \
+       {LOONGARCH_EXCEPTION_PIF,  "Inst Fetch" },              \
+       {LOONGARCH_EXCEPTION_PME,  "Mem Modify" },              \
+       {LOONGARCH_EXCEPTION_FPD,  "FPU" },                     \
+       {LOONGARCH_EXCEPTION_SXD,  "LSX" },                     \
+       {LOONGARCH_EXCEPTION_ASXD, "LASX" },                    \
+       {LOONGARCH_EXCEPTION_GSPR, "Privilege Error" },         \
+       {LOONGARCH_EXCEPTION_HVC,  "Hypercall" },               \
+       {LOONGARCH_EXCEPTION_CPUCFG, "CPUCFG" },                \
+       {LOONGARCH_EXCEPTION_CSR,    "CSR" },                   \
+       {LOONGARCH_EXCEPTION_IOCSR,  "IOCSR" },                 \
+       {LOONGARCH_EXCEPTION_IDLE,   "Idle" },                  \
+       {LOONGARCH_EXCEPTION_OTHERS, "Others" }
+
+define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
+
+const char *vcpu_id_str = "vcpu_id";
+const char *kvm_exit_reason = "reason";
+const char *kvm_entry_trace = "kvm:kvm_enter";
+const char *kvm_reenter_trace = "kvm:kvm_reenter";
+const char *kvm_exit_trace = "kvm:kvm_exit";
+const char *kvm_events_tp[] = {
+       "kvm:kvm_enter",
+       "kvm:kvm_reenter",
+       "kvm:kvm_exit",
+       "kvm:kvm_exit_gspr",
+       NULL,
+};
+
+static bool event_begin(struct evsel *evsel,
+                       struct perf_sample *sample, struct event_key *key)
+{
+       return exit_event_begin(evsel, sample, key);
+}
+
+static bool event_end(struct evsel *evsel,
+                     struct perf_sample *sample __maybe_unused,
+                     struct event_key *key __maybe_unused)
+{
+       /*
+        * LoongArch kvm is different with other architectures
+        *
+        * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
+        * kvm:kvm_exit event.
+        *   kvm:kvm_enter   means returning to vmm and then to guest
+        *   kvm:kvm_reenter means returning to guest immediately
+        */
+       return evsel__name_is(evsel, kvm_entry_trace) || evsel__name_is(evsel, kvm_reenter_trace);
+}
+
+static void event_gspr_get_key(struct evsel *evsel,
+                              struct perf_sample *sample, struct event_key *key)
+{
+       unsigned int insn;
+
+       key->key = LOONGARCH_EXCEPTION_OTHERS;
+       insn = evsel__intval(evsel, sample, "inst_word");
+
+       switch (insn >> 24) {
+       case 0:
+               /* CPUCFG inst trap */
+               if ((insn >> 10) == 0x1b)
+                       key->key = LOONGARCH_EXCEPTION_CPUCFG;
+               break;
+       case 4:
+               /* CSR inst trap */
+               key->key = LOONGARCH_EXCEPTION_CSR;
+               break;
+       case 6:
+               /* IOCSR inst trap */
+               if ((insn >> 15) == 0xc90)
+                       key->key = LOONGARCH_EXCEPTION_IOCSR;
+               else if ((insn >> 15) == 0xc91)
+                       /* Idle inst trap */
+                       key->key = LOONGARCH_EXCEPTION_IDLE;
+               break;
+       default:
+               key->key = LOONGARCH_EXCEPTION_OTHERS;
+               break;
+       }
+}
+
+static struct child_event_ops child_events[] = {
+       { .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
+       { NULL, NULL },
+};
+
+static struct kvm_events_ops exit_events = {
+       .is_begin_event = event_begin,
+       .is_end_event = event_end,
+       .child_ops = child_events,
+       .decode_key = exit_event_decode_key,
+       .name = "VM-EXIT"
+};
+
+struct kvm_reg_events_ops kvm_reg_events_ops[] = {
+       { .name = "vmexit", .ops = &exit_events, },
+       { NULL, NULL },
+};
+
+const char * const kvm_skip_events[] = {
+       NULL,
+};
+
+int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
+{
+       kvm->exit_reasons_isa = "loongarch64";
+       kvm->exit_reasons = loongarch_exit_reasons;
+       return 0;
+}