On 03/07/14 16:29, Alexander Yarygin wrote:
> 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.
> 
> Signed-off-by: Alexander Yarygin <yary...@linux.vnet.ibm.com>

Acked-by: Christian Borntraeger <borntrae...@de.ibm.com>
Would be good if Paolo and David could ack the KVM/perf parts.
Then this should also go into next merge window.

> ---
>  arch/s390/include/uapi/asm/Kbuild     |    1 +
>  arch/s390/include/uapi/asm/kvm_perf.h |   25 ++++++++
>  tools/perf/Documentation/perf-kvm.txt |   10 ++--
>  tools/perf/MANIFEST                   |    2 +
>  tools/perf/arch/s390/Makefile         |    2 +
>  tools/perf/arch/s390/util/kvm-stat.c  |  105 
> +++++++++++++++++++++++++++++++++
>  tools/perf/builtin-kvm.c              |   52 ++++++++++++++--
>  tools/perf/util/kvm-stat.h            |    9 +++
>  8 files changed, 198 insertions(+), 8 deletions(-)
>  create mode 100644 arch/s390/include/uapi/asm/kvm_perf.h
>  create mode 100644 tools/perf/arch/s390/util/kvm-stat.c
> 
> diff --git a/arch/s390/include/uapi/asm/Kbuild 
> b/arch/s390/include/uapi/asm/Kbuild
> index 6a9a9eb..0e2b54d 100644
> --- a/arch/s390/include/uapi/asm/Kbuild
> +++ b/arch/s390/include/uapi/asm/Kbuild
> @@ -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
> diff --git a/arch/s390/include/uapi/asm/kvm_perf.h 
> b/arch/s390/include/uapi/asm/kvm_perf.h
> new file mode 100644
> index 0000000..3972827
> --- /dev/null
> +++ b/arch/s390/include/uapi/asm/kvm_perf.h
> @@ -0,0 +1,25 @@
> +/*
> + * Definitions for perf-kvm on s390
> + *
> + * Copyright 2014 IBM Corp.
> + * Author(s): Alexander Yarygin <yary...@linux.vnet.ibm.com>
> + *
> + * 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
> diff --git a/tools/perf/Documentation/perf-kvm.txt 
> b/tools/perf/Documentation/perf-kvm.txt
> index 52276a6..abf2925 100644
> --- a/tools/perf/Documentation/perf-kvm.txt
> +++ b/tools/perf/Documentation/perf-kvm.txt
> @@ -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
> @@ -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::
> @@ -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
>  --------
> diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
> index 02b485d..344c4d3 100644
> --- a/tools/perf/MANIFEST
> +++ b/tools/perf/MANIFEST
> @@ -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
> diff --git a/tools/perf/arch/s390/Makefile b/tools/perf/arch/s390/Makefile
> index 744e629..798ac73 100644
> --- a/tools/perf/arch/s390/Makefile
> +++ b/tools/perf/arch/s390/Makefile
> @@ -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
> diff --git a/tools/perf/arch/s390/util/kvm-stat.c 
> b/tools/perf/arch/s390/util/kvm-stat.c
> new file mode 100644
> index 0000000..a5dbc07
> --- /dev/null
> +++ b/tools/perf/arch/s390/util/kvm-stat.c
> @@ -0,0 +1,105 @@
> +/*
> + * Arch specific functions for perf kvm stat.
> + *
> + * Copyright 2014 IBM Corp.
> + * Author(s): Alexander Yarygin <yary...@linux.vnet.ibm.com>
> + *
> + * 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;
> +}
> diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
> index fc2d63d..43367eb 100644
> --- a/tools/perf/builtin-kvm.c
> +++ b/tools/perf/builtin-kvm.c
> @@ -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);
> @@ -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;
> @@ -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)
> @@ -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);
> 
> @@ -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",
> @@ -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[] = {
> diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h
> index ba937ca..0b5a8cd 100644
> --- a/tools/perf/util/kvm-stat.h
> +++ b/tools/perf/util/kvm-stat.h
> @@ -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 {
> @@ -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;
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to