linux/arch/s390/kernel/perf_event.c
<<
>>
Prefs
   1/*
   2 * Performance event support for s390x
   3 *
   4 *  Copyright IBM Corp. 2012, 2013
   5 *  Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License (version 2 only)
   9 * as published by the Free Software Foundation.
  10 */
  11#define KMSG_COMPONENT  "perf"
  12#define pr_fmt(fmt)     KMSG_COMPONENT ": " fmt
  13
  14#include <linux/kernel.h>
  15#include <linux/perf_event.h>
  16#include <linux/kvm_host.h>
  17#include <linux/percpu.h>
  18#include <linux/export.h>
  19#include <linux/seq_file.h>
  20#include <linux/spinlock.h>
  21#include <linux/sysfs.h>
  22#include <asm/irq.h>
  23#include <asm/cpu_mf.h>
  24#include <asm/lowcore.h>
  25#include <asm/processor.h>
  26#include <asm/sysinfo.h>
  27
  28const char *perf_pmu_name(void)
  29{
  30        if (cpum_cf_avail() || cpum_sf_avail())
  31                return "CPU-Measurement Facilities (CPU-MF)";
  32        return "pmu";
  33}
  34EXPORT_SYMBOL(perf_pmu_name);
  35
  36int perf_num_counters(void)
  37{
  38        int num = 0;
  39
  40        if (cpum_cf_avail())
  41                num += PERF_CPUM_CF_MAX_CTR;
  42        if (cpum_sf_avail())
  43                num += PERF_CPUM_SF_MAX_CTR;
  44
  45        return num;
  46}
  47EXPORT_SYMBOL(perf_num_counters);
  48
  49static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs)
  50{
  51        struct stack_frame *stack = (struct stack_frame *) regs->gprs[15];
  52
  53        if (!stack)
  54                return NULL;
  55
  56        return (struct kvm_s390_sie_block *) stack->empty1[0];
  57}
  58
  59static bool is_in_guest(struct pt_regs *regs)
  60{
  61        if (user_mode(regs))
  62                return false;
  63#if IS_ENABLED(CONFIG_KVM)
  64        return instruction_pointer(regs) == (unsigned long) &sie_exit;
  65#else
  66        return false;
  67#endif
  68}
  69
  70static unsigned long guest_is_user_mode(struct pt_regs *regs)
  71{
  72        return sie_block(regs)->gpsw.mask & PSW_MASK_PSTATE;
  73}
  74
  75static unsigned long instruction_pointer_guest(struct pt_regs *regs)
  76{
  77        return sie_block(regs)->gpsw.addr;
  78}
  79
  80unsigned long perf_instruction_pointer(struct pt_regs *regs)
  81{
  82        return is_in_guest(regs) ? instruction_pointer_guest(regs)
  83                                 : instruction_pointer(regs);
  84}
  85
  86static unsigned long perf_misc_guest_flags(struct pt_regs *regs)
  87{
  88        return guest_is_user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER
  89                                        : PERF_RECORD_MISC_GUEST_KERNEL;
  90}
  91
  92static unsigned long perf_misc_flags_sf(struct pt_regs *regs)
  93{
  94        struct perf_sf_sde_regs *sde_regs;
  95        unsigned long flags;
  96
  97        sde_regs = (struct perf_sf_sde_regs *) &regs->int_parm_long;
  98        if (sde_regs->in_guest)
  99                flags = user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER
 100                                        : PERF_RECORD_MISC_GUEST_KERNEL;
 101        else
 102                flags = user_mode(regs) ? PERF_RECORD_MISC_USER
 103                                        : PERF_RECORD_MISC_KERNEL;
 104        return flags;
 105}
 106
 107unsigned long perf_misc_flags(struct pt_regs *regs)
 108{
 109        /* Check if the cpum_sf PMU has created the pt_regs structure.
 110         * In this case, perf misc flags can be easily extracted.  Otherwise,
 111         * do regular checks on the pt_regs content.
 112         */
 113        if (regs->int_code == 0x1407 && regs->int_parm == CPU_MF_INT_SF_PRA)
 114                if (!regs->gprs[15])
 115                        return perf_misc_flags_sf(regs);
 116
 117        if (is_in_guest(regs))
 118                return perf_misc_guest_flags(regs);
 119
 120        return user_mode(regs) ? PERF_RECORD_MISC_USER
 121                               : PERF_RECORD_MISC_KERNEL;
 122}
 123
 124static void print_debug_cf(void)
 125{
 126        struct cpumf_ctr_info cf_info;
 127        int cpu = smp_processor_id();
 128
 129        memset(&cf_info, 0, sizeof(cf_info));
 130        if (!qctri(&cf_info))
 131                pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n",
 132                        cpu, cf_info.cfvn, cf_info.csvn,
 133                        cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl);
 134}
 135
 136static void print_debug_sf(void)
 137{
 138        struct hws_qsi_info_block si;
 139        int cpu = smp_processor_id();
 140
 141        memset(&si, 0, sizeof(si));
 142        if (qsi(&si))
 143                return;
 144
 145        pr_info("CPU[%i] CPUM_SF: basic=%i diag=%i min=%lu max=%lu cpu_speed=%u\n",
 146                cpu, si.as, si.ad, si.min_sampl_rate, si.max_sampl_rate,
 147                si.cpu_speed);
 148
 149        if (si.as)
 150                pr_info("CPU[%i] CPUM_SF: Basic-sampling: a=%i e=%i c=%i"
 151                        " bsdes=%i tear=%016lx dear=%016lx\n", cpu,
 152                        si.as, si.es, si.cs, si.bsdes, si.tear, si.dear);
 153        if (si.ad)
 154                pr_info("CPU[%i] CPUM_SF: Diagnostic-sampling: a=%i e=%i c=%i"
 155                        " dsdes=%i tear=%016lx dear=%016lx\n", cpu,
 156                        si.ad, si.ed, si.cd, si.dsdes, si.tear, si.dear);
 157}
 158
 159void perf_event_print_debug(void)
 160{
 161        unsigned long flags;
 162
 163        local_irq_save(flags);
 164        if (cpum_cf_avail())
 165                print_debug_cf();
 166        if (cpum_sf_avail())
 167                print_debug_sf();
 168        local_irq_restore(flags);
 169}
 170
 171/* Service level infrastructure */
 172static void sl_print_counter(struct seq_file *m)
 173{
 174        struct cpumf_ctr_info ci;
 175
 176        memset(&ci, 0, sizeof(ci));
 177        if (qctri(&ci))
 178                return;
 179
 180        seq_printf(m, "CPU-MF: Counter facility: version=%u.%u "
 181                   "authorization=%04x\n", ci.cfvn, ci.csvn, ci.auth_ctl);
 182}
 183
 184static void sl_print_sampling(struct seq_file *m)
 185{
 186        struct hws_qsi_info_block si;
 187
 188        memset(&si, 0, sizeof(si));
 189        if (qsi(&si))
 190                return;
 191
 192        if (!si.as && !si.ad)
 193                return;
 194
 195        seq_printf(m, "CPU-MF: Sampling facility: min_rate=%lu max_rate=%lu"
 196                   " cpu_speed=%u\n", si.min_sampl_rate, si.max_sampl_rate,
 197                   si.cpu_speed);
 198        if (si.as)
 199                seq_printf(m, "CPU-MF: Sampling facility: mode=basic"
 200                           " sample_size=%u\n", si.bsdes);
 201        if (si.ad)
 202                seq_printf(m, "CPU-MF: Sampling facility: mode=diagnostic"
 203                           " sample_size=%u\n", si.dsdes);
 204}
 205
 206static void service_level_perf_print(struct seq_file *m,
 207                                     struct service_level *sl)
 208{
 209        if (cpum_cf_avail())
 210                sl_print_counter(m);
 211        if (cpum_sf_avail())
 212                sl_print_sampling(m);
 213}
 214
 215static struct service_level service_level_perf = {
 216        .seq_print = service_level_perf_print,
 217};
 218
 219static int __init service_level_perf_register(void)
 220{
 221        return register_service_level(&service_level_perf);
 222}
 223arch_initcall(service_level_perf_register);
 224
 225static int __perf_callchain_kernel(void *data, unsigned long address)
 226{
 227        struct perf_callchain_entry *entry = data;
 228
 229        perf_callchain_store(entry, address);
 230        return 0;
 231}
 232
 233void perf_callchain_kernel(struct perf_callchain_entry *entry,
 234                           struct pt_regs *regs)
 235{
 236        if (user_mode(regs))
 237                return;
 238        dump_trace(__perf_callchain_kernel, entry, NULL, regs->gprs[15]);
 239}
 240
 241/* Perf definitions for PMU event attributes in sysfs */
 242ssize_t cpumf_events_sysfs_show(struct device *dev,
 243                                struct device_attribute *attr, char *page)
 244{
 245        struct perf_pmu_events_attr *pmu_attr;
 246
 247        pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
 248        return sprintf(page, "event=0x%04llx,name=%s\n",
 249                       pmu_attr->id, attr->attr.name);
 250}
 251
 252/* Reserve/release functions for sharing perf hardware */
 253static DEFINE_SPINLOCK(perf_hw_owner_lock);
 254static void *perf_sampling_owner;
 255
 256int perf_reserve_sampling(void)
 257{
 258        int err;
 259
 260        err = 0;
 261        spin_lock(&perf_hw_owner_lock);
 262        if (perf_sampling_owner) {
 263                pr_warn("The sampling facility is already reserved by %p\n",
 264                        perf_sampling_owner);
 265                err = -EBUSY;
 266        } else
 267                perf_sampling_owner = __builtin_return_address(0);
 268        spin_unlock(&perf_hw_owner_lock);
 269        return err;
 270}
 271EXPORT_SYMBOL(perf_reserve_sampling);
 272
 273void perf_release_sampling(void)
 274{
 275        spin_lock(&perf_hw_owner_lock);
 276        WARN_ON(!perf_sampling_owner);
 277        perf_sampling_owner = NULL;
 278        spin_unlock(&perf_hw_owner_lock);
 279}
 280EXPORT_SYMBOL(perf_release_sampling);
 281