linux/tools/perf/arch/x86/util/kvm-stat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <errno.h>
   3#include "../../util/kvm-stat.h"
   4#include "../../util/evsel.h"
   5#include <asm/svm.h>
   6#include <asm/vmx.h>
   7#include <asm/kvm.h>
   8
   9define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
  10define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
  11
  12static struct kvm_events_ops exit_events = {
  13        .is_begin_event = exit_event_begin,
  14        .is_end_event = exit_event_end,
  15        .decode_key = exit_event_decode_key,
  16        .name = "VM-EXIT"
  17};
  18
  19const char *vcpu_id_str = "vcpu_id";
  20const int decode_str_len = 20;
  21const char *kvm_exit_reason = "exit_reason";
  22const char *kvm_entry_trace = "kvm:kvm_entry";
  23const char *kvm_exit_trace = "kvm:kvm_exit";
  24
  25/*
  26 * For the mmio events, we treat:
  27 * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
  28 * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
  29 */
  30static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
  31                               struct event_key *key)
  32{
  33        key->key  = perf_evsel__intval(evsel, sample, "gpa");
  34        key->info = perf_evsel__intval(evsel, sample, "type");
  35}
  36
  37#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
  38#define KVM_TRACE_MMIO_READ 1
  39#define KVM_TRACE_MMIO_WRITE 2
  40
  41static bool mmio_event_begin(struct perf_evsel *evsel,
  42                             struct perf_sample *sample, struct event_key *key)
  43{
  44        /* MMIO read begin event in kernel. */
  45        if (kvm_exit_event(evsel))
  46                return true;
  47
  48        /* MMIO write begin event in kernel. */
  49        if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
  50            perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
  51                mmio_event_get_key(evsel, sample, key);
  52                return true;
  53        }
  54
  55        return false;
  56}
  57
  58static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
  59                           struct event_key *key)
  60{
  61        /* MMIO write end event in kernel. */
  62        if (kvm_entry_event(evsel))
  63                return true;
  64
  65        /* MMIO read end event in kernel.*/
  66        if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
  67            perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
  68                mmio_event_get_key(evsel, sample, key);
  69                return true;
  70        }
  71
  72        return false;
  73}
  74
  75static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
  76                                  struct event_key *key,
  77                                  char *decode)
  78{
  79        scnprintf(decode, decode_str_len, "%#lx:%s",
  80                  (unsigned long)key->key,
  81                  key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
  82}
  83
  84static struct kvm_events_ops mmio_events = {
  85        .is_begin_event = mmio_event_begin,
  86        .is_end_event = mmio_event_end,
  87        .decode_key = mmio_event_decode_key,
  88        .name = "MMIO Access"
  89};
  90
  91 /* The time of emulation pio access is from kvm_pio to kvm_entry. */
  92static void ioport_event_get_key(struct perf_evsel *evsel,
  93                                 struct perf_sample *sample,
  94                                 struct event_key *key)
  95{
  96        key->key  = perf_evsel__intval(evsel, sample, "port");
  97        key->info = perf_evsel__intval(evsel, sample, "rw");
  98}
  99
 100static bool ioport_event_begin(struct perf_evsel *evsel,
 101                               struct perf_sample *sample,
 102                               struct event_key *key)
 103{
 104        if (!strcmp(evsel->name, "kvm:kvm_pio")) {
 105                ioport_event_get_key(evsel, sample, key);
 106                return true;
 107        }
 108
 109        return false;
 110}
 111
 112static bool ioport_event_end(struct perf_evsel *evsel,
 113                             struct perf_sample *sample __maybe_unused,
 114                             struct event_key *key __maybe_unused)
 115{
 116        return kvm_entry_event(evsel);
 117}
 118
 119static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
 120                                    struct event_key *key,
 121                                    char *decode)
 122{
 123        scnprintf(decode, decode_str_len, "%#llx:%s",
 124                  (unsigned long long)key->key,
 125                  key->info ? "POUT" : "PIN");
 126}
 127
 128static struct kvm_events_ops ioport_events = {
 129        .is_begin_event = ioport_event_begin,
 130        .is_end_event = ioport_event_end,
 131        .decode_key = ioport_event_decode_key,
 132        .name = "IO Port Access"
 133};
 134
 135const char *kvm_events_tp[] = {
 136        "kvm:kvm_entry",
 137        "kvm:kvm_exit",
 138        "kvm:kvm_mmio",
 139        "kvm:kvm_pio",
 140        NULL,
 141};
 142
 143struct kvm_reg_events_ops kvm_reg_events_ops[] = {
 144        { .name = "vmexit", .ops = &exit_events },
 145        { .name = "mmio", .ops = &mmio_events },
 146        { .name = "ioport", .ops = &ioport_events },
 147        { NULL, NULL },
 148};
 149
 150const char * const kvm_skip_events[] = {
 151        "HLT",
 152        NULL,
 153};
 154
 155int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
 156{
 157        if (strstr(cpuid, "Intel")) {
 158                kvm->exit_reasons = vmx_exit_reasons;
 159                kvm->exit_reasons_isa = "VMX";
 160        } else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) {
 161                kvm->exit_reasons = svm_exit_reasons;
 162                kvm->exit_reasons_isa = "SVM";
 163        } else
 164                return -ENOTSUP;
 165
 166        return 0;
 167}
 168