Commit 9daa8123 authored by Alexander Yarygin's avatar Alexander Yarygin Committed by Arnaldo Carvalho de Melo

perf kvm: Move arch specific code into arch/

Parts of a 'perf kvm stat' code make sense only for x86.

Let's move this code into the arch/x86/kvm-stat.c file and add
util/kvm-stat.h for generic structure definitions.

Add a global array 'kvm_reg_events_ops' for accessing the arch-specific
'kvm_events_ops' from generic code.

Since the several global arrays (i.e. 'kvm_events_tp') have been moved
to arch/*, we can not know their sizes and use them directly in
builtin-kvm.c. This patch fixes that problem by adding trimming NULL
element to each array and changing the behavior of their handlers in
generic code.
Reviewed-by: default avatarDavid Ahern <dsahern@gmail.com>
Reviewed-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarAlexander Yarygin <yarygin@linux.vnet.ibm.com>
Acked-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1404397747-20939-3-git-send-email-yarygin@linux.vnet.ibm.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 44b38021
......@@ -300,6 +300,7 @@ LIB_H += ui/progress.h
LIB_H += ui/util.h
LIB_H += ui/ui.h
LIB_H += util/data.h
LIB_H += util/kvm-stat.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
......
......@@ -16,3 +16,4 @@ LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o
LIB_H += arch/$(ARCH)/util/tsc.h
HAVE_KVM_STAT_SUPPORT := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o
#include "../../util/kvm-stat.h"
#include <asm/kvm_perf.h>
define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
static struct kvm_events_ops exit_events = {
.is_begin_event = exit_event_begin,
.is_end_event = exit_event_end,
.decode_key = exit_event_decode_key,
.name = "VM-EXIT"
};
/*
* For the mmio events, we treat:
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
*/
static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "gpa");
key->info = perf_evsel__intval(evsel, sample, "type");
}
#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
#define KVM_TRACE_MMIO_READ 1
#define KVM_TRACE_MMIO_WRITE 2
static bool mmio_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
/* MMIO read begin event in kernel. */
if (kvm_exit_event(evsel))
return true;
/* MMIO write begin event in kernel. */
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
mmio_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
/* MMIO write end event in kernel. */
if (kvm_entry_event(evsel))
return true;
/* MMIO read end event in kernel.*/
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
mmio_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
struct event_key *key,
char *decode)
{
scnprintf(decode, DECODE_STR_LEN, "%#lx:%s",
(unsigned long)key->key,
key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
}
static struct kvm_events_ops mmio_events = {
.is_begin_event = mmio_event_begin,
.is_end_event = mmio_event_end,
.decode_key = mmio_event_decode_key,
.name = "MMIO Access"
};
/* The time of emulation pio access is from kvm_pio to kvm_entry. */
static void ioport_event_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "port");
key->info = perf_evsel__intval(evsel, sample, "rw");
}
static bool ioport_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
if (!strcmp(evsel->name, "kvm:kvm_pio")) {
ioport_event_get_key(evsel, sample, key);
return true;
}
return false;
}
static bool ioport_event_end(struct perf_evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
return kvm_entry_event(evsel);
}
static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
struct event_key *key,
char *decode)
{
scnprintf(decode, DECODE_STR_LEN, "%#llx:%s",
(unsigned long long)key->key,
key->info ? "POUT" : "PIN");
}
static struct kvm_events_ops ioport_events = {
.is_begin_event = ioport_event_begin,
.is_end_event = ioport_event_end,
.decode_key = ioport_event_decode_key,
.name = "IO Port Access"
};
const char * const kvm_events_tp[] = {
"kvm:kvm_entry",
"kvm:kvm_exit",
"kvm:kvm_mmio",
"kvm:kvm_pio",
NULL,
};
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
{ .name = "vmexit", .ops = &exit_events },
{ .name = "mmio", .ops = &mmio_events },
{ .name = "ioport", .ops = &ioport_events },
{ NULL, NULL },
};
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
{
if (strstr(cpuid, "Intel")) {
kvm->exit_reasons = vmx_exit_reasons;
kvm->exit_reasons_isa = "VMX";
} else if (strstr(cpuid, "AMD")) {
kvm->exit_reasons = svm_exit_reasons;
kvm->exit_reasons_isa = "SVM";
} else
return -ENOTSUP;
return 0;
}
This diff is collapsed.
#ifndef __PERF_KVM_STAT_H
#define __PERF_KVM_STAT_H
#include "../perf.h"
#include "evsel.h"
#include "evlist.h"
#include "session.h"
#include "tool.h"
#include "stat.h"
struct event_key {
#define INVALID_KEY (~0ULL)
u64 key;
int info;
};
struct kvm_event_stats {
u64 time;
struct stats stats;
};
struct kvm_event {
struct list_head hash_entry;
struct rb_node rb;
struct event_key key;
struct kvm_event_stats total;
#define DEFAULT_VCPU_NUM 8
int max_vcpu;
struct kvm_event_stats *vcpu;
};
typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
struct kvm_event_key {
const char *name;
key_cmp_fun key;
};
struct perf_kvm_stat;
struct kvm_events_ops {
bool (*is_begin_event)(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
bool (*is_end_event)(struct perf_evsel *evsel,
struct perf_sample *sample, struct event_key *key);
void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key,
char *decode);
const char *name;
};
struct exit_reasons_table {
unsigned long exit_code;
const char *reason;
};
#define EVENTS_BITS 12
#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS)
struct perf_kvm_stat {
struct perf_tool tool;
struct record_opts opts;
struct perf_evlist *evlist;
struct perf_session *session;
const char *file_name;
const char *report_event;
const char *sort_key;
int trace_vcpu;
struct exit_reasons_table *exit_reasons;
const char *exit_reasons_isa;
struct kvm_events_ops *events_ops;
key_cmp_fun compare;
struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
u64 total_time;
u64 total_count;
u64 lost_events;
u64 duration;
const char *pid_str;
struct intlist *pid_list;
struct rb_root result;
int timerfd;
unsigned int display_time;
bool live;
};
struct kvm_reg_events_ops {
const char *name;
struct kvm_events_ops *ops;
};
void exit_event_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
bool exit_event_begin(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
bool exit_event_end(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
void exit_event_decode_key(struct perf_kvm_stat *kvm,
struct event_key *key,
char *decode);
bool kvm_exit_event(struct perf_evsel *evsel);
bool kvm_entry_event(struct perf_evsel *evsel);
#define define_exit_reasons_table(name, symbols) \
static struct exit_reasons_table name[] = { \
symbols, { -1, NULL } \
}
/*
* arch specific callbacks and data structures
*/
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid);
extern const char * const kvm_events_tp[];
extern struct kvm_reg_events_ops kvm_reg_events_ops[];
#endif /* __PERF_KVM_STAT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment