~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/perf/8xx-pmu.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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/code-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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php