linux/arch/s390/kernel/perf_event.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Performance event support for s390x
   4 *
   5 *  Copyright IBM Corp. 2012, 2013
   6 *  Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
   7 */
   8#define KMSG_COMPONENT  "perf"
   9#define pr_fmt(fmt)     KMSG_COMPONENT ": " fmt
  10
  11#include <linux/kernel.h>
  12#include <linux/perf_event.h>
  13#include <linux/kvm_host.h>
  14#include <linux/percpu.h>
  15#include <linux/export.h>
  16#include <linux/seq_file.h>
  17#include <linux/spinlock.h>
  18#include <linux/sysfs.h>
  19#include <asm/irq.h>
  20#include <asm/cpu_mf.h>
  21#include <asm/lowcore.h>
  22#include <asm/processor.h>
  23#include <asm/sysinfo.h>
  24#include <asm/unwind.h>
  25
  26static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs)
  27{
  28        struct stack_frame *stack = (struct stack_frame *) regs->gprs[15];
  29
  30        if (!stack)
  31                return NULL;
  32
  33        return (struct kvm_s390_sie_block *) stack->empty1[0];
  34}
  35
  36static bool is_in_guest(struct pt_regs *regs)
  37{
  38        if (user_mode(regs))
  39                return false;
  40#if IS_ENABLED(CONFIG_KVM)
  41        return instruction_pointer(regs) == (unsigned long) &sie_exit;
  42#else
  43        return false;
  44#endif
  45}
  46
  47static unsigned long guest_is_user_mode(struct pt_regs *regs)
  48{
  49        return sie_block(regs)->gpsw.mask & PSW_MASK_PSTATE;
  50}
  51
  52static unsigned long instruction_pointer_guest(struct pt_regs *regs)
  53{
  54        return sie_block(regs)->gpsw.addr;
  55}
  56
  57unsigned long perf_instruction_pointer(struct pt_regs *regs)
  58{
  59        return is_in_guest(regs) ? instruction_pointer_guest(regs)
  60                                 : instruction_pointer(regs);
  61}
  62
  63static unsigned long perf_misc_guest_flags(struct pt_regs *regs)
  64{
  65        return guest_is_user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER
  66                                        : PERF_RECORD_MISC_GUEST_KERNEL;
  67}
  68
  69static unsigned long perf_misc_flags_sf(struct pt_regs *regs)
  70{
  71        struct perf_sf_sde_regs *sde_regs;
  72        unsigned long flags;
  73
  74        sde_regs = (struct perf_sf_sde_regs *) &regs->int_parm_long;
  75        if (sde_regs->in_guest)
  76                flags = user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER
  77                                        : PERF_RECORD_MISC_GUEST_KERNEL;
  78        else
  79                flags = user_mode(regs) ? PERF_RECORD_MISC_USER
  80                                        : PERF_RECORD_MISC_KERNEL;
  81        return flags;
  82}
  83
  84unsigned long perf_misc_flags(struct pt_regs *regs)
  85{
  86        /* Check if the cpum_sf PMU has created the pt_regs structure.
  87         * In this case, perf misc flags can be easily extracted.  Otherwise,
  88         * do regular checks on the pt_regs content.
  89         */
  90        if (regs->int_code == 0x1407 && regs->int_parm == CPU_MF_INT_SF_PRA)
  91                if (!regs->gprs[15])
  92                        return perf_misc_flags_sf(regs);
  93
  94        if (is_in_guest(regs))
  95                return perf_misc_guest_flags(regs);
  96
  97        return user_mode(regs) ? PERF_RECORD_MISC_USER
  98                               : PERF_RECORD_MISC_KERNEL;
  99}
 100
 101static void print_debug_cf(void)
 102{
 103        struct cpumf_ctr_info cf_info;
 104        int cpu = smp_processor_id();
 105
 106        memset(&cf_info, 0, sizeof(cf_info));
 107        if (!qctri(&cf_info))
 108                pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n",
 109                        cpu, cf_info.cfvn, cf_info.csvn,
 110                        cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl);
 111}
 112
 113static void print_debug_sf(void)
 114{
 115        struct hws_qsi_info_block si;
 116        int cpu = smp_processor_id();
 117
 118        memset(&si, 0, sizeof(si));
 119        if (qsi(&si))
 120                return;
 121
 122        pr_info("CPU[%i] CPUM_SF: basic=%i diag=%i min=%lu max=%lu cpu_speed=%u\n",
 123                cpu, si.as, si.ad, si.min_sampl_rate, si.max_sampl_rate,
 124                si.cpu_speed);
 125
 126        if (si.as)
 127                pr_info("CPU[%i] CPUM_SF: Basic-sampling: a=%i e=%i c=%i"
 128                        " bsdes=%i tear=%016lx dear=%016lx\n", cpu,
 129                        si.as, si.es, si.cs, si.bsdes, si.tear, si.dear);
 130        if (si.ad)
 131                pr_info("CPU[%i] CPUM_SF: Diagnostic-sampling: a=%i e=%i c=%i"
 132                        " dsdes=%i tear=%016lx dear=%016lx\n", cpu,
 133                        si.ad, si.ed, si.cd, si.dsdes, si.tear, si.dear);
 134}
 135
 136void perf_event_print_debug(void)
 137{
 138        unsigned long flags;
 139
 140        local_irq_save(flags);
 141        if (cpum_cf_avail())
 142                print_debug_cf();
 143        if (cpum_sf_avail())
 144                print_debug_sf();
 145        local_irq_restore(flags);
 146}
 147
 148/* Service level infrastructure */
 149static void sl_print_counter(struct seq_file *m)
 150{
 151        struct cpumf_ctr_info ci;
 152
 153        memset(&ci, 0, sizeof(ci));
 154        if (qctri(&ci))
 155                return;
 156
 157        seq_printf(m, "CPU-MF: Counter facility: version=%u.%u "
 158                   "authorization=%04x\n", ci.cfvn, ci.csvn, ci.auth_ctl);
 159}
 160
 161static void sl_print_sampling(struct seq_file *m)
 162{
 163        struct hws_qsi_info_block si;
 164
 165        memset(&si, 0, sizeof(si));
 166        if (qsi(&si))
 167                return;
 168
 169        if (!si.as && !si.ad)
 170                return;
 171
 172        seq_printf(m, "CPU-MF: Sampling facility: min_rate=%lu max_rate=%lu"
 173                   " cpu_speed=%u\n", si.min_sampl_rate, si.max_sampl_rate,
 174                   si.cpu_speed);
 175        if (si.as)
 176                seq_printf(m, "CPU-MF: Sampling facility: mode=basic"
 177                           " sample_size=%u\n", si.bsdes);
 178        if (si.ad)
 179                seq_printf(m, "CPU-MF: Sampling facility: mode=diagnostic"
 180                           " sample_size=%u\n", si.dsdes);
 181}
 182
 183static void service_level_perf_print(struct seq_file *m,
 184                                     struct service_level *sl)
 185{
 186        if (cpum_cf_avail())
 187                sl_print_counter(m);
 188        if (cpum_sf_avail())
 189                sl_print_sampling(m);
 190}
 191
 192static struct service_level service_level_perf = {
 193        .seq_print = service_level_perf_print,
 194};
 195
 196static int __init service_level_perf_register(void)
 197{
 198        return register_service_level(&service_level_perf);
 199}
 200arch_initcall(service_level_perf_register);
 201
 202void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
 203                           struct pt_regs *regs)
 204{
 205        struct unwind_state state;
 206        unsigned long addr;
 207
 208        unwind_for_each_frame(&state, current, regs, 0) {
 209                addr = unwind_get_return_address(&state);
 210                if (!addr || perf_callchain_store(entry, addr))
 211                        return;
 212        }
 213}
 214
 215/* Perf definitions for PMU event attributes in sysfs */
 216ssize_t cpumf_events_sysfs_show(struct device *dev,
 217                                struct device_attribute *attr, char *page)
 218{
 219        struct perf_pmu_events_attr *pmu_attr;
 220
 221        pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
 222        return sprintf(page, "event=0x%04llx\n", pmu_attr->id);
 223}
 224