perf_cpum_cf_common.c (5505B)
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 || info->csvn == 7) 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 || info->csvn == 7) 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);