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