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