Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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);