linux/arch/s390/kernel/perf_cpum_cf_common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * CPU-Measurement Counter Facility Support - Common Layer
   4 *
   5 *  Copyright IBM Corp. 2019
   6 *  Author(s): Hendrik Brueckner <brueckner@linux.ibm.com>
   7 */
   8#define KMSG_COMPONENT  "cpum_cf_common"
   9#define pr_fmt(fmt)     KMSG_COMPONENT ": " fmt
  10
  11#include <linux/kernel.h>
  12#include <linux/kernel_stat.h>
  13#include <linux/percpu.h>
  14#include <linux/notifier.h>
  15#include <linux/init.h>
  16#include <linux/export.h>
  17#include <asm/ctl_reg.h>
  18#include <asm/irq.h>
  19#include <asm/cpu_mcf.h>
  20
  21/* Per-CPU event structure for the counter facility */
  22DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events) = {
  23        .ctr_set = {
  24                [CPUMF_CTR_SET_BASIC]   = ATOMIC_INIT(0),
  25                [CPUMF_CTR_SET_USER]    = ATOMIC_INIT(0),
  26                [CPUMF_CTR_SET_CRYPTO]  = ATOMIC_INIT(0),
  27                [CPUMF_CTR_SET_EXT]     = ATOMIC_INIT(0),
  28                [CPUMF_CTR_SET_MT_DIAG] = ATOMIC_INIT(0),
  29        },
  30        .alert = ATOMIC64_INIT(0),
  31        .state = 0,
  32        .dev_state = 0,
  33        .flags = 0,
  34        .used = 0,
  35        .usedss = 0,
  36        .sets = 0
  37};
  38/* Indicator whether the CPU-Measurement Counter Facility Support is ready */
  39static bool cpum_cf_initalized;
  40
  41/* CPU-measurement alerts for the counter facility */
  42static void cpumf_measurement_alert(struct ext_code ext_code,
  43                                    unsigned int alert, unsigned long unused)
  44{
  45        struct cpu_cf_events *cpuhw;
  46
  47        if (!(alert & CPU_MF_INT_CF_MASK))
  48                return;
  49
  50        inc_irq_stat(IRQEXT_CMC);
  51        cpuhw = this_cpu_ptr(&cpu_cf_events);
  52
  53        /* Measurement alerts are shared and might happen when the PMU
  54         * is not reserved.  Ignore these alerts in this case. */
  55        if (!(cpuhw->flags & PMU_F_RESERVED))
  56                return;
  57
  58        /* counter authorization change alert */
  59        if (alert & CPU_MF_INT_CF_CACA)
  60                qctri(&cpuhw->info);
  61
  62        /* loss of counter data alert */
  63        if (alert & CPU_MF_INT_CF_LCDA)
  64                pr_err("CPU[%i] Counter data was lost\n", smp_processor_id());
  65
  66        /* loss of MT counter data alert */
  67        if (alert & CPU_MF_INT_CF_MTDA)
  68                pr_warn("CPU[%i] MT counter data was lost\n",
  69                        smp_processor_id());
  70
  71        /* store alert for special handling by in-kernel users */
  72        atomic64_or(alert, &cpuhw->alert);
  73}
  74
  75#define PMC_INIT      0
  76#define PMC_RELEASE   1
  77static void cpum_cf_setup_cpu(void *flags)
  78{
  79        struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
  80
  81        switch (*((int *) flags)) {
  82        case PMC_INIT:
  83                memset(&cpuhw->info, 0, sizeof(cpuhw->info));
  84                qctri(&cpuhw->info);
  85                cpuhw->flags |= PMU_F_RESERVED;
  86                break;
  87
  88        case PMC_RELEASE:
  89                cpuhw->flags &= ~PMU_F_RESERVED;
  90                break;
  91        }
  92
  93        /* Disable CPU counter sets */
  94        lcctl(0);
  95}
  96
  97bool kernel_cpumcf_avail(void)
  98{
  99        return cpum_cf_initalized;
 100}
 101EXPORT_SYMBOL(kernel_cpumcf_avail);
 102
 103/* Initialize the CPU-measurement counter facility */
 104int __kernel_cpumcf_begin(void)
 105{
 106        int flags = PMC_INIT;
 107
 108        on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
 109        irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
 110
 111        return 0;
 112}
 113EXPORT_SYMBOL(__kernel_cpumcf_begin);
 114
 115/* Obtain the CPU-measurement alerts for the counter facility */
 116unsigned long kernel_cpumcf_alert(int clear)
 117{
 118        struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
 119        unsigned long alert;
 120
 121        alert = atomic64_read(&cpuhw->alert);
 122        if (clear)
 123                atomic64_set(&cpuhw->alert, 0);
 124
 125        return alert;
 126}
 127EXPORT_SYMBOL(kernel_cpumcf_alert);
 128
 129/* Release the CPU-measurement counter facility */
 130void __kernel_cpumcf_end(void)
 131{
 132        int flags = PMC_RELEASE;
 133
 134        on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
 135        irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
 136}
 137EXPORT_SYMBOL(__kernel_cpumcf_end);
 138
 139static int cpum_cf_setup(unsigned int cpu, int flags)
 140{
 141        local_irq_disable();
 142        cpum_cf_setup_cpu(&flags);
 143        local_irq_enable();
 144        return 0;
 145}
 146
 147static int cpum_cf_online_cpu(unsigned int cpu)
 148{
 149        cpum_cf_setup(cpu, PMC_INIT);
 150        return cfset_online_cpu(cpu);
 151}
 152
 153static int cpum_cf_offline_cpu(unsigned int cpu)
 154{
 155        cfset_offline_cpu(cpu);
 156        return cpum_cf_setup(cpu, PMC_RELEASE);
 157}
 158
 159/* Return the maximum possible counter set size (in number of 8 byte counters)
 160 * depending on type and model number.
 161 */
 162size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset,
 163                           struct cpumf_ctr_info *info)
 164{
 165        size_t ctrset_size = 0;
 166
 167        switch (ctrset) {
 168        case CPUMF_CTR_SET_BASIC:
 169                if (info->cfvn >= 1)
 170                        ctrset_size = 6;
 171                break;
 172        case CPUMF_CTR_SET_USER:
 173                if (info->cfvn == 1)
 174                        ctrset_size = 6;
 175                else if (info->cfvn >= 3)
 176                        ctrset_size = 2;
 177                break;
 178        case CPUMF_CTR_SET_CRYPTO:
 179                if (info->csvn >= 1 && info->csvn <= 5)
 180                        ctrset_size = 16;
 181                else if (info->csvn == 6)
 182                        ctrset_size = 20;
 183                break;
 184        case CPUMF_CTR_SET_EXT:
 185                if (info->csvn == 1)
 186                        ctrset_size = 32;
 187                else if (info->csvn == 2)
 188                        ctrset_size = 48;
 189                else if (info->csvn >= 3 && info->csvn <= 5)
 190                        ctrset_size = 128;
 191                else if (info->csvn == 6)
 192                        ctrset_size = 160;
 193                break;
 194        case CPUMF_CTR_SET_MT_DIAG:
 195                if (info->csvn > 3)
 196                        ctrset_size = 48;
 197                break;
 198        case CPUMF_CTR_SET_MAX:
 199                break;
 200        }
 201
 202        return ctrset_size;
 203}
 204
 205static int __init cpum_cf_init(void)
 206{
 207        int rc;
 208
 209        if (!cpum_cf_avail())
 210                return -ENODEV;
 211
 212        /* clear bit 15 of cr0 to unauthorize problem-state to
 213         * extract measurement counters */
 214        ctl_clear_bit(0, 48);
 215
 216        /* register handler for measurement-alert interruptions */
 217        rc = register_external_irq(EXT_IRQ_MEASURE_ALERT,
 218                                   cpumf_measurement_alert);
 219        if (rc) {
 220                pr_err("Registering for CPU-measurement alerts "
 221                       "failed with rc=%i\n", rc);
 222                return rc;
 223        }
 224
 225        rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE,
 226                                "perf/s390/cf:online",
 227                                cpum_cf_online_cpu, cpum_cf_offline_cpu);
 228        if (!rc)
 229                cpum_cf_initalized = true;
 230
 231        return rc;
 232}
 233early_initcall(cpum_cf_init);
 234