linux/arch/s390/kernel/perf_event.c
<<
>>
Prefs
   1/*
   2 * Performance event support for s390x
   3 *
   4 *  Copyright IBM Corp. 2012
   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/percpu.h>
  17#include <linux/export.h>
  18#include <asm/irq.h>
  19#include <asm/cpu_mf.h>
  20#include <asm/lowcore.h>
  21#include <asm/processor.h>
  22
  23const char *perf_pmu_name(void)
  24{
  25        if (cpum_cf_avail() || cpum_sf_avail())
  26                return "CPU-measurement facilities (CPUMF)";
  27        return "pmu";
  28}
  29EXPORT_SYMBOL(perf_pmu_name);
  30
  31int perf_num_counters(void)
  32{
  33        int num = 0;
  34
  35        if (cpum_cf_avail())
  36                num += PERF_CPUM_CF_MAX_CTR;
  37
  38        return num;
  39}
  40EXPORT_SYMBOL(perf_num_counters);
  41
  42void perf_event_print_debug(void)
  43{
  44        struct cpumf_ctr_info cf_info;
  45        unsigned long flags;
  46        int cpu;
  47
  48        if (!cpum_cf_avail())
  49                return;
  50
  51        local_irq_save(flags);
  52
  53        cpu = smp_processor_id();
  54        memset(&cf_info, 0, sizeof(cf_info));
  55        if (!qctri(&cf_info)) {
  56                pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n",
  57                        cpu, cf_info.cfvn, cf_info.csvn,
  58                        cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl);
  59                print_hex_dump_bytes("CPUMF Query: ", DUMP_PREFIX_OFFSET,
  60                                     &cf_info, sizeof(cf_info));
  61        }
  62
  63        local_irq_restore(flags);
  64}
  65
  66/* See also arch/s390/kernel/traps.c */
  67static unsigned long __store_trace(struct perf_callchain_entry *entry,
  68                                   unsigned long sp,
  69                                   unsigned long low, unsigned long high)
  70{
  71        struct stack_frame *sf;
  72        struct pt_regs *regs;
  73
  74        while (1) {
  75                sp = sp & PSW_ADDR_INSN;
  76                if (sp < low || sp > high - sizeof(*sf))
  77                        return sp;
  78                sf = (struct stack_frame *) sp;
  79                perf_callchain_store(entry, sf->gprs[8] & PSW_ADDR_INSN);
  80                /* Follow the backchain. */
  81                while (1) {
  82                        low = sp;
  83                        sp = sf->back_chain & PSW_ADDR_INSN;
  84                        if (!sp)
  85                                break;
  86                        if (sp <= low || sp > high - sizeof(*sf))
  87                                return sp;
  88                        sf = (struct stack_frame *) sp;
  89                        perf_callchain_store(entry,
  90                                             sf->gprs[8] & PSW_ADDR_INSN);
  91                }
  92                /* Zero backchain detected, check for interrupt frame. */
  93                sp = (unsigned long) (sf + 1);
  94                if (sp <= low || sp > high - sizeof(*regs))
  95                        return sp;
  96                regs = (struct pt_regs *) sp;
  97                perf_callchain_store(entry, sf->gprs[8] & PSW_ADDR_INSN);
  98                low = sp;
  99                sp = regs->gprs[15];
 100        }
 101}
 102
 103void perf_callchain_kernel(struct perf_callchain_entry *entry,
 104                           struct pt_regs *regs)
 105{
 106        unsigned long head;
 107        struct stack_frame *head_sf;
 108
 109        if (user_mode(regs))
 110                return;
 111
 112        head = regs->gprs[15];
 113        head_sf = (struct stack_frame *) head;
 114
 115        if (!head_sf || !head_sf->back_chain)
 116                return;
 117
 118        head = head_sf->back_chain;
 119        head = __store_trace(entry, head, S390_lowcore.async_stack - ASYNC_SIZE,
 120                             S390_lowcore.async_stack);
 121
 122        __store_trace(entry, head, S390_lowcore.thread_info,
 123                      S390_lowcore.thread_info + THREAD_SIZE);
 124}
 125