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        .flags = 0,
  33        .txn_flags = 0,
  34};
  35/* Indicator whether the CPU-Measurement Counter Facility Support is ready */
  36static bool cpum_cf_initalized;
  37
  38/* CPU-measurement alerts for the counter facility */
  39static void cpumf_measurement_alert(struct ext_code ext_code,
  40                                    unsigned int alert, unsigned long unused)
  41{
  42        struct cpu_cf_events *cpuhw;
  43
  44        if (!(alert & CPU_MF_INT_CF_MASK))
  45                return;
  46
  47        inc_irq_stat(IRQEXT_CMC);
  48        cpuhw = this_cpu_ptr(&cpu_cf_events);
  49
  50        /* Measurement alerts are shared and might happen when the PMU
  51         * is not reserved.  Ignore these alerts in this case. */
  52        if (!(cpuhw->flags & PMU_F_RESERVED))
  53                return;
  54
  55        /* counter authorization change alert */
  56        if (alert & CPU_MF_INT_CF_CACA)
  57                qctri(&cpuhw->info);
  58
  59        /* loss of counter data alert */
  60        if (alert & CPU_MF_INT_CF_LCDA)
  61                pr_err("CPU[%i] Counter data was lost\n", smp_processor_id());
  62
  63        /* loss of MT counter data alert */
  64        if (alert & CPU_MF_INT_CF_MTDA)
  65                pr_warn("CPU[%i] MT counter data was lost\n",
  66                        smp_processor_id());
  67
  68        /* store alert for special handling by in-kernel users */
  69        atomic64_or(alert, &cpuhw->alert);
  70}
  71
  72#define PMC_INIT      0
  73#define PMC_RELEASE   1
  74static void cpum_cf_setup_cpu(void *flags)
  75{
  76        struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
  77
  78        switch (*((int *) flags)) {
  79        case PMC_INIT:
  80                memset(&cpuhw->info, 0, sizeof(cpuhw->info));
  81                qctri(&cpuhw->info);
  82                cpuhw->flags |= PMU_F_RESERVED;
  83                break;
  84
  85        case PMC_RELEASE:
  86                cpuhw->flags &= ~PMU_F_RESERVED;
  87                break;
  88        }
  89
  90        /* Disable CPU counter sets */
  91        lcctl(0);
  92}
  93
  94bool kernel_cpumcf_avail(void)
  95{
  96        return cpum_cf_initalized;
  97}
  98EXPORT_SYMBOL(kernel_cpumcf_avail);
  99
 100
 101/* Reserve/release functions for sharing perf hardware */
 102static DEFINE_SPINLOCK(cpumcf_owner_lock);
 103static void *cpumcf_owner;
 104
 105/* Initialize the CPU-measurement counter facility */
 106int __kernel_cpumcf_begin(void)
 107{
 108        int flags = PMC_INIT;
 109        int err = 0;
 110
 111        spin_lock(&cpumcf_owner_lock);
 112        if (cpumcf_owner)
 113                err = -EBUSY;
 114        else
 115                cpumcf_owner = __builtin_return_address(0);
 116        spin_unlock(&cpumcf_owner_lock);
 117        if (err)
 118                return err;
 119
 120        on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
 121        irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
 122
 123        return 0;
 124}
 125EXPORT_SYMBOL(__kernel_cpumcf_begin);
 126
 127/* Obtain the CPU-measurement alerts for the counter facility */
 128unsigned long kernel_cpumcf_alert(int clear)
 129{
 130        struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
 131        unsigned long alert;
 132
 133        alert = atomic64_read(&cpuhw->alert);
 134        if (clear)
 135                atomic64_set(&cpuhw->alert, 0);
 136
 137        return alert;
 138}
 139EXPORT_SYMBOL(kernel_cpumcf_alert);
 140
 141/* Release the CPU-measurement counter facility */
 142void __kernel_cpumcf_end(void)
 143{
 144        int flags = PMC_RELEASE;
 145
 146        on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
 147        irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
 148
 149        spin_lock(&cpumcf_owner_lock);
 150        cpumcf_owner = NULL;
 151        spin_unlock(&cpumcf_owner_lock);
 152}
 153EXPORT_SYMBOL(__kernel_cpumcf_end);
 154
 155static int cpum_cf_setup(unsigned int cpu, int flags)
 156{
 157        local_irq_disable();
 158        cpum_cf_setup_cpu(&flags);
 159        local_irq_enable();
 160        return 0;
 161}
 162
 163static int cpum_cf_online_cpu(unsigned int cpu)
 164{
 165        return cpum_cf_setup(cpu, PMC_INIT);
 166}
 167
 168static int cpum_cf_offline_cpu(unsigned int cpu)
 169{
 170        return cpum_cf_setup(cpu, PMC_RELEASE);
 171}
 172
 173static int __init cpum_cf_init(void)
 174{
 175        int rc;
 176
 177        if (!cpum_cf_avail())
 178                return -ENODEV;
 179
 180        /* clear bit 15 of cr0 to unauthorize problem-state to
 181         * extract measurement counters */
 182        ctl_clear_bit(0, 48);
 183
 184        /* register handler for measurement-alert interruptions */
 185        rc = register_external_irq(EXT_IRQ_MEASURE_ALERT,
 186                                   cpumf_measurement_alert);
 187        if (rc) {
 188                pr_err("Registering for CPU-measurement alerts "
 189                       "failed with rc=%i\n", rc);
 190                return rc;
 191        }
 192
 193        rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE,
 194                                "perf/s390/cf:online",
 195                                cpum_cf_online_cpu, cpum_cf_offline_cpu);
 196        if (!rc)
 197                cpum_cf_initalized = true;
 198
 199        return rc;
 200}
 201early_initcall(cpum_cf_init);
 202