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