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