Skip to content

Commit

Permalink
perf kvm: Add stat support on s390
Browse files Browse the repository at this point in the history
On s390, the vmexit event has a tree-like structure: between
exit_event_begin and exit_event_end several other events may happen and
with each of them refining the previous ones.

This patch adds a decoder for such events to the generic code and also
the files <asm/kvm_perf.h> and kvm-stat.c for s390.

Commands 'perf kvm stat record', 'report' and 'live' are supported.

Reviewed-by: David Ahern <[email protected]>
Signed-off-by: Alexander Yarygin <[email protected]>
Acked-by: Christian Borntraeger <[email protected]>
Cc: Christian Borntraeger <[email protected]>
Cc: Cornelia Huck <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
  • Loading branch information
zpp0 authored and acmel committed Jul 16, 2014
1 parent 54c801f commit 3be8e2a
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 11 deletions.
1 change: 1 addition & 0 deletions arch/s390/include/uapi/asm/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ header-y += ioctls.h
header-y += ipcbuf.h
header-y += kvm.h
header-y += kvm_para.h
header-y += kvm_perf.h
header-y += kvm_virtio.h
header-y += mman.h
header-y += monwriter.h
Expand Down
25 changes: 25 additions & 0 deletions arch/s390/include/uapi/asm/kvm_perf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Definitions for perf-kvm on s390
*
* Copyright 2014 IBM Corp.
* Author(s): Alexander Yarygin <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*/

#ifndef __LINUX_KVM_PERF_S390_H
#define __LINUX_KVM_PERF_S390_H

#include <asm/sie.h>

#define DECODE_STR_LEN 40

#define VCPU_ID "id"

#define KVM_ENTRY_TRACE "kvm:kvm_s390_sie_enter"
#define KVM_EXIT_TRACE "kvm:kvm_s390_sie_exit"
#define KVM_EXIT_REASON "icptcode"

#endif
16 changes: 9 additions & 7 deletions tools/perf/Documentation/perf-kvm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ There are a couple of variants of perf kvm:
'perf kvm stat <command>' to run a command and gather performance counter
statistics.
Especially, perf 'kvm stat record/report' generates a statistical analysis
of KVM events. Currently, vmexit, mmio and ioport events are supported.
'perf kvm stat record <command>' records kvm events and the events between
start and end <command>.
of KVM events. Currently, vmexit, mmio (x86 only) and ioport (x86 only)
events are supported. 'perf kvm stat record <command>' records kvm events
and the events between start and end <command>.
And this command produces a file which contains tracing results of kvm
events.

Expand Down Expand Up @@ -103,8 +103,8 @@ STAT REPORT OPTIONS
analyze events which occures on this vcpu. (default: all vcpus)

--event=<value>::
event to be analyzed. Possible values: vmexit, mmio, ioport.
(default: vmexit)
event to be analyzed. Possible values: vmexit, mmio (x86 only),
ioport (x86 only). (default: vmexit)
-k::
--key=<value>::
Sorting key. Possible values: sample (default, sort by samples
Expand Down Expand Up @@ -138,7 +138,8 @@ STAT LIVE OPTIONS


--event=<value>::
event to be analyzed. Possible values: vmexit, mmio, ioport.
event to be analyzed. Possible values: vmexit,
mmio (x86 only), ioport (x86 only).
(default: vmexit)

-k::
Expand All @@ -147,7 +148,8 @@ STAT LIVE OPTIONS
number), time (sort by average time).

--duration=<value>::
Show events other than HLT that take longer than duration usecs.
Show events other than HLT (x86 only) or Wait state (s390 only)
that take longer than duration usecs.

SEE ALSO
--------
Expand Down
2 changes: 2 additions & 0 deletions tools/perf/MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ arch/x86/include/uapi/asm/svm.h
arch/x86/include/uapi/asm/vmx.h
arch/x86/include/uapi/asm/kvm.h
arch/x86/include/uapi/asm/kvm_perf.h
arch/s390/include/uapi/asm/sie.h
arch/s390/include/uapi/asm/kvm_perf.h
2 changes: 2 additions & 0 deletions tools/perf/arch/s390/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
HAVE_KVM_STAT_SUPPORT := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o
105 changes: 105 additions & 0 deletions tools/perf/arch/s390/util/kvm-stat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Arch specific functions for perf kvm stat.
*
* Copyright 2014 IBM Corp.
* Author(s): Alexander Yarygin <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*/

#include "../../util/kvm-stat.h"
#include <asm/kvm_perf.h>

define_exit_reasons_table(sie_exit_reasons, sie_intercept_code);
define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes);
define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes);
define_exit_reasons_table(sie_diagnose_codes, diagnose_codes);
define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes);

static void event_icpt_insn_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
unsigned long insn;

insn = perf_evsel__intval(evsel, sample, "instruction");
key->key = icpt_insn_decoder(insn);
key->exit_reasons = sie_icpt_insn_codes;
}

static void event_sigp_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "order_code");
key->exit_reasons = sie_sigp_order_codes;
}

static void event_diag_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "code");
key->exit_reasons = sie_diagnose_codes;
}

static void event_icpt_prog_get_key(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "code");
key->exit_reasons = sie_icpt_prog_codes;
}

static struct child_event_ops child_events[] = {
{ .name = "kvm:kvm_s390_intercept_instruction",
.get_key = event_icpt_insn_get_key },
{ .name = "kvm:kvm_s390_handle_sigp",
.get_key = event_sigp_get_key },
{ .name = "kvm:kvm_s390_handle_diag",
.get_key = event_diag_get_key },
{ .name = "kvm:kvm_s390_intercept_prog",
.get_key = event_icpt_prog_get_key },
{ NULL, NULL },
};

static struct kvm_events_ops exit_events = {
.is_begin_event = exit_event_begin,
.is_end_event = exit_event_end,
.child_ops = child_events,
.decode_key = exit_event_decode_key,
.name = "VM-EXIT"
};

const char * const kvm_events_tp[] = {
"kvm:kvm_s390_sie_enter",
"kvm:kvm_s390_sie_exit",
"kvm:kvm_s390_intercept_instruction",
"kvm:kvm_s390_handle_sigp",
"kvm:kvm_s390_handle_diag",
"kvm:kvm_s390_intercept_prog",
NULL,
};

struct kvm_reg_events_ops kvm_reg_events_ops[] = {
{ .name = "vmexit", .ops = &exit_events },
{ NULL, NULL },
};

const char * const kvm_skip_events[] = {
"Wait state",
NULL,
};

int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
{
if (strstr(cpuid, "IBM/S390")) {
kvm->exit_reasons = sie_exit_reasons;
kvm->exit_reasons_isa = "SIE";
} else
return -ENOTSUP;

return 0;
}
52 changes: 48 additions & 4 deletions tools/perf/builtin-kvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ void exit_event_decode_key(struct perf_kvm_stat *kvm,
struct event_key *key,
char *decode)
{
const char *exit_reason = get_exit_reason(kvm, kvm->exit_reasons,
const char *exit_reason = get_exit_reason(kvm, key->exit_reasons,
key->key);

scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason);
Expand Down Expand Up @@ -261,6 +261,43 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
return true;
}

static bool is_child_event(struct perf_kvm_stat *kvm,
struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
struct child_event_ops *child_ops;

child_ops = kvm->events_ops->child_ops;

if (!child_ops)
return false;

for (; child_ops->name; child_ops++) {
if (!strcmp(evsel->name, child_ops->name)) {
child_ops->get_key(evsel, sample, key);
return true;
}
}

return false;
}

static bool handle_child_event(struct perf_kvm_stat *kvm,
struct vcpu_event_record *vcpu_record,
struct event_key *key,
struct perf_sample *sample __maybe_unused)
{
struct kvm_event *event = NULL;

if (key->key != INVALID_KEY)
event = find_create_kvm_event(kvm, key);

vcpu_record->last_event = event;

return true;
}

static bool skip_event(const char *event)
{
const char * const *skip_events;
Expand Down Expand Up @@ -361,7 +398,8 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
struct perf_sample *sample)
{
struct vcpu_event_record *vcpu_record;
struct event_key key = {.key = INVALID_KEY};
struct event_key key = { .key = INVALID_KEY,
.exit_reasons = kvm->exit_reasons };

vcpu_record = per_vcpu_record(thread, evsel, sample);
if (!vcpu_record)
Expand All @@ -375,6 +413,9 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
if (kvm->events_ops->is_begin_event(evsel, sample, &key))
return handle_begin_event(kvm, vcpu_record, &key, sample->time);

if (is_child_event(kvm, evsel, sample, &key))
return handle_child_event(kvm, vcpu_record, &key, sample);

if (kvm->events_ops->is_end_event(evsel, sample, &key))
return handle_end_event(kvm, vcpu_record, &key, sample);

Expand Down Expand Up @@ -1143,7 +1184,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
{
const struct option kvm_events_report_options[] = {
OPT_STRING(0, "event", &kvm->report_event, "report event",
"event for reporting: vmexit, mmio, ioport"),
"event for reporting: vmexit, "
"mmio (x86 only), ioport (x86 only)"),
OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu,
"vcpu id to report"),
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
Expand Down Expand Up @@ -1249,7 +1291,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
"key for sorting: sample(sort by samples number)"
" time (sort by avg time)"),
OPT_U64(0, "duration", &kvm->duration,
"show events other than HALT that take longer than duration usecs"),
"show events other than"
" HLT (x86 only) or Wait state (s390 only)"
" that take longer than duration usecs"),
OPT_END()
};
const char * const live_usage[] = {
Expand Down
9 changes: 9 additions & 0 deletions tools/perf/util/kvm-stat.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct event_key {
#define INVALID_KEY (~0ULL)
u64 key;
int info;
struct exit_reasons_table *exit_reasons;
};

struct kvm_event_stats {
Expand Down Expand Up @@ -41,12 +42,20 @@ struct kvm_event_key {

struct perf_kvm_stat;

struct child_event_ops {
void (*get_key)(struct perf_evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
const char *name;
};

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);
struct child_event_ops *child_ops;
void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key,
char *decode);
const char *name;
Expand Down

0 comments on commit 3be8e2a

Please sign in to comment.