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