Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/perf/8xx-pmu.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Performance event support - PPC 8xx
4
*
5
* Copyright 2016 Christophe Leroy, CS Systemes d'Information
6
*/
7
8
#include <linux/kernel.h>
9
#include <linux/sched.h>
10
#include <linux/perf_event.h>
11
#include <linux/percpu.h>
12
#include <linux/hardirq.h>
13
#include <asm/pmc.h>
14
#include <asm/machdep.h>
15
#include <asm/firmware.h>
16
#include <asm/ptrace.h>
17
#include <asm/text-patching.h>
18
#include <asm/inst.h>
19
20
#define PERF_8xx_ID_CPU_CYCLES 1
21
#define PERF_8xx_ID_HW_INSTRUCTIONS 2
22
#define PERF_8xx_ID_ITLB_LOAD_MISS 3
23
#define PERF_8xx_ID_DTLB_LOAD_MISS 4
24
25
#define C(x) PERF_COUNT_HW_CACHE_##x
26
#define DTLB_LOAD_MISS (C(DTLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16))
27
#define ITLB_LOAD_MISS (C(ITLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16))
28
29
extern unsigned long itlb_miss_counter, dtlb_miss_counter;
30
extern atomic_t instruction_counter;
31
32
static atomic_t insn_ctr_ref;
33
static atomic_t itlb_miss_ref;
34
static atomic_t dtlb_miss_ref;
35
36
static s64 get_insn_ctr(void)
37
{
38
int ctr;
39
unsigned long counta;
40
41
do {
42
ctr = atomic_read(&instruction_counter);
43
counta = mfspr(SPRN_COUNTA);
44
} while (ctr != atomic_read(&instruction_counter));
45
46
return ((s64)ctr << 16) | (counta >> 16);
47
}
48
49
static int event_type(struct perf_event *event)
50
{
51
switch (event->attr.type) {
52
case PERF_TYPE_HARDWARE:
53
if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES)
54
return PERF_8xx_ID_CPU_CYCLES;
55
if (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS)
56
return PERF_8xx_ID_HW_INSTRUCTIONS;
57
break;
58
case PERF_TYPE_HW_CACHE:
59
if (event->attr.config == ITLB_LOAD_MISS)
60
return PERF_8xx_ID_ITLB_LOAD_MISS;
61
if (event->attr.config == DTLB_LOAD_MISS)
62
return PERF_8xx_ID_DTLB_LOAD_MISS;
63
break;
64
case PERF_TYPE_RAW:
65
break;
66
default:
67
return -ENOENT;
68
}
69
return -EOPNOTSUPP;
70
}
71
72
static int mpc8xx_pmu_event_init(struct perf_event *event)
73
{
74
int type = event_type(event);
75
76
if (type < 0)
77
return type;
78
return 0;
79
}
80
81
static int mpc8xx_pmu_add(struct perf_event *event, int flags)
82
{
83
int type = event_type(event);
84
s64 val = 0;
85
86
if (type < 0)
87
return type;
88
89
switch (type) {
90
case PERF_8xx_ID_CPU_CYCLES:
91
val = get_tb();
92
break;
93
case PERF_8xx_ID_HW_INSTRUCTIONS:
94
if (atomic_inc_return(&insn_ctr_ref) == 1)
95
mtspr(SPRN_ICTRL, 0xc0080007);
96
val = get_insn_ctr();
97
break;
98
case PERF_8xx_ID_ITLB_LOAD_MISS:
99
if (atomic_inc_return(&itlb_miss_ref) == 1) {
100
unsigned long target = patch_site_addr(&patch__itlbmiss_perf);
101
102
patch_branch_site(&patch__itlbmiss_exit_1, target, 0);
103
}
104
val = itlb_miss_counter;
105
break;
106
case PERF_8xx_ID_DTLB_LOAD_MISS:
107
if (atomic_inc_return(&dtlb_miss_ref) == 1) {
108
unsigned long target = patch_site_addr(&patch__dtlbmiss_perf);
109
110
patch_branch_site(&patch__dtlbmiss_exit_1, target, 0);
111
}
112
val = dtlb_miss_counter;
113
break;
114
}
115
local64_set(&event->hw.prev_count, val);
116
return 0;
117
}
118
119
static void mpc8xx_pmu_read(struct perf_event *event)
120
{
121
int type = event_type(event);
122
s64 prev, val = 0, delta = 0;
123
124
if (type < 0)
125
return;
126
127
do {
128
prev = local64_read(&event->hw.prev_count);
129
switch (type) {
130
case PERF_8xx_ID_CPU_CYCLES:
131
val = get_tb();
132
delta = 16 * (val - prev);
133
break;
134
case PERF_8xx_ID_HW_INSTRUCTIONS:
135
val = get_insn_ctr();
136
delta = prev - val;
137
if (delta < 0)
138
delta += 0x1000000000000LL;
139
break;
140
case PERF_8xx_ID_ITLB_LOAD_MISS:
141
val = itlb_miss_counter;
142
delta = (s64)((s32)val - (s32)prev);
143
break;
144
case PERF_8xx_ID_DTLB_LOAD_MISS:
145
val = dtlb_miss_counter;
146
delta = (s64)((s32)val - (s32)prev);
147
break;
148
}
149
} while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
150
151
local64_add(delta, &event->count);
152
}
153
154
static void mpc8xx_pmu_del(struct perf_event *event, int flags)
155
{
156
ppc_inst_t insn = ppc_inst(PPC_RAW_MFSPR(10, SPRN_SPRG_SCRATCH2));
157
158
mpc8xx_pmu_read(event);
159
160
/* If it was the last user, stop counting to avoid useless overhead */
161
switch (event_type(event)) {
162
case PERF_8xx_ID_CPU_CYCLES:
163
break;
164
case PERF_8xx_ID_HW_INSTRUCTIONS:
165
if (atomic_dec_return(&insn_ctr_ref) == 0)
166
mtspr(SPRN_ICTRL, 7);
167
break;
168
case PERF_8xx_ID_ITLB_LOAD_MISS:
169
if (atomic_dec_return(&itlb_miss_ref) == 0)
170
patch_instruction_site(&patch__itlbmiss_exit_1, insn);
171
break;
172
case PERF_8xx_ID_DTLB_LOAD_MISS:
173
if (atomic_dec_return(&dtlb_miss_ref) == 0)
174
patch_instruction_site(&patch__dtlbmiss_exit_1, insn);
175
break;
176
}
177
}
178
179
static struct pmu mpc8xx_pmu = {
180
.event_init = mpc8xx_pmu_event_init,
181
.add = mpc8xx_pmu_add,
182
.del = mpc8xx_pmu_del,
183
.read = mpc8xx_pmu_read,
184
.capabilities = PERF_PMU_CAP_NO_INTERRUPT |
185
PERF_PMU_CAP_NO_NMI,
186
};
187
188
static int init_mpc8xx_pmu(void)
189
{
190
mtspr(SPRN_ICTRL, 7);
191
mtspr(SPRN_CMPA, 0);
192
mtspr(SPRN_COUNTA, 0xffff);
193
194
return perf_pmu_register(&mpc8xx_pmu, "cpu", PERF_TYPE_RAW);
195
}
196
197
early_initcall(init_mpc8xx_pmu);
198
199