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