Linux Audio

Check our new training course

Loading...
v5.4
  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);
v6.2
  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);