0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/sched.h>
0010 #include <linux/perf_event.h>
0011 #include <linux/percpu.h>
0012 #include <linux/hardirq.h>
0013 #include <asm/pmc.h>
0014 #include <asm/machdep.h>
0015 #include <asm/firmware.h>
0016 #include <asm/ptrace.h>
0017 #include <asm/code-patching.h>
0018 #include <asm/inst.h>
0019
0020 #define PERF_8xx_ID_CPU_CYCLES 1
0021 #define PERF_8xx_ID_HW_INSTRUCTIONS 2
0022 #define PERF_8xx_ID_ITLB_LOAD_MISS 3
0023 #define PERF_8xx_ID_DTLB_LOAD_MISS 4
0024
0025 #define C(x) PERF_COUNT_HW_CACHE_##x
0026 #define DTLB_LOAD_MISS (C(DTLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16))
0027 #define ITLB_LOAD_MISS (C(ITLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16))
0028
0029 extern unsigned long itlb_miss_counter, dtlb_miss_counter;
0030 extern atomic_t instruction_counter;
0031
0032 static atomic_t insn_ctr_ref;
0033 static atomic_t itlb_miss_ref;
0034 static atomic_t dtlb_miss_ref;
0035
0036 static s64 get_insn_ctr(void)
0037 {
0038 int ctr;
0039 unsigned long counta;
0040
0041 do {
0042 ctr = atomic_read(&instruction_counter);
0043 counta = mfspr(SPRN_COUNTA);
0044 } while (ctr != atomic_read(&instruction_counter));
0045
0046 return ((s64)ctr << 16) | (counta >> 16);
0047 }
0048
0049 static int event_type(struct perf_event *event)
0050 {
0051 switch (event->attr.type) {
0052 case PERF_TYPE_HARDWARE:
0053 if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES)
0054 return PERF_8xx_ID_CPU_CYCLES;
0055 if (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS)
0056 return PERF_8xx_ID_HW_INSTRUCTIONS;
0057 break;
0058 case PERF_TYPE_HW_CACHE:
0059 if (event->attr.config == ITLB_LOAD_MISS)
0060 return PERF_8xx_ID_ITLB_LOAD_MISS;
0061 if (event->attr.config == DTLB_LOAD_MISS)
0062 return PERF_8xx_ID_DTLB_LOAD_MISS;
0063 break;
0064 case PERF_TYPE_RAW:
0065 break;
0066 default:
0067 return -ENOENT;
0068 }
0069 return -EOPNOTSUPP;
0070 }
0071
0072 static int mpc8xx_pmu_event_init(struct perf_event *event)
0073 {
0074 int type = event_type(event);
0075
0076 if (type < 0)
0077 return type;
0078 return 0;
0079 }
0080
0081 static int mpc8xx_pmu_add(struct perf_event *event, int flags)
0082 {
0083 int type = event_type(event);
0084 s64 val = 0;
0085
0086 if (type < 0)
0087 return type;
0088
0089 switch (type) {
0090 case PERF_8xx_ID_CPU_CYCLES:
0091 val = get_tb();
0092 break;
0093 case PERF_8xx_ID_HW_INSTRUCTIONS:
0094 if (atomic_inc_return(&insn_ctr_ref) == 1)
0095 mtspr(SPRN_ICTRL, 0xc0080007);
0096 val = get_insn_ctr();
0097 break;
0098 case PERF_8xx_ID_ITLB_LOAD_MISS:
0099 if (atomic_inc_return(&itlb_miss_ref) == 1) {
0100 unsigned long target = patch_site_addr(&patch__itlbmiss_perf);
0101
0102 patch_branch_site(&patch__itlbmiss_exit_1, target, 0);
0103 }
0104 val = itlb_miss_counter;
0105 break;
0106 case PERF_8xx_ID_DTLB_LOAD_MISS:
0107 if (atomic_inc_return(&dtlb_miss_ref) == 1) {
0108 unsigned long target = patch_site_addr(&patch__dtlbmiss_perf);
0109
0110 patch_branch_site(&patch__dtlbmiss_exit_1, target, 0);
0111 }
0112 val = dtlb_miss_counter;
0113 break;
0114 }
0115 local64_set(&event->hw.prev_count, val);
0116 return 0;
0117 }
0118
0119 static void mpc8xx_pmu_read(struct perf_event *event)
0120 {
0121 int type = event_type(event);
0122 s64 prev, val = 0, delta = 0;
0123
0124 if (type < 0)
0125 return;
0126
0127 do {
0128 prev = local64_read(&event->hw.prev_count);
0129 switch (type) {
0130 case PERF_8xx_ID_CPU_CYCLES:
0131 val = get_tb();
0132 delta = 16 * (val - prev);
0133 break;
0134 case PERF_8xx_ID_HW_INSTRUCTIONS:
0135 val = get_insn_ctr();
0136 delta = prev - val;
0137 if (delta < 0)
0138 delta += 0x1000000000000LL;
0139 break;
0140 case PERF_8xx_ID_ITLB_LOAD_MISS:
0141 val = itlb_miss_counter;
0142 delta = (s64)((s32)val - (s32)prev);
0143 break;
0144 case PERF_8xx_ID_DTLB_LOAD_MISS:
0145 val = dtlb_miss_counter;
0146 delta = (s64)((s32)val - (s32)prev);
0147 break;
0148 }
0149 } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
0150
0151 local64_add(delta, &event->count);
0152 }
0153
0154 static void mpc8xx_pmu_del(struct perf_event *event, int flags)
0155 {
0156 ppc_inst_t insn = ppc_inst(PPC_RAW_MFSPR(10, SPRN_SPRG_SCRATCH2));
0157
0158 mpc8xx_pmu_read(event);
0159
0160
0161 switch (event_type(event)) {
0162 case PERF_8xx_ID_CPU_CYCLES:
0163 break;
0164 case PERF_8xx_ID_HW_INSTRUCTIONS:
0165 if (atomic_dec_return(&insn_ctr_ref) == 0)
0166 mtspr(SPRN_ICTRL, 7);
0167 break;
0168 case PERF_8xx_ID_ITLB_LOAD_MISS:
0169 if (atomic_dec_return(&itlb_miss_ref) == 0)
0170 patch_instruction_site(&patch__itlbmiss_exit_1, insn);
0171 break;
0172 case PERF_8xx_ID_DTLB_LOAD_MISS:
0173 if (atomic_dec_return(&dtlb_miss_ref) == 0)
0174 patch_instruction_site(&patch__dtlbmiss_exit_1, insn);
0175 break;
0176 }
0177 }
0178
0179 static struct pmu mpc8xx_pmu = {
0180 .event_init = mpc8xx_pmu_event_init,
0181 .add = mpc8xx_pmu_add,
0182 .del = mpc8xx_pmu_del,
0183 .read = mpc8xx_pmu_read,
0184 .capabilities = PERF_PMU_CAP_NO_INTERRUPT |
0185 PERF_PMU_CAP_NO_NMI,
0186 };
0187
0188 static int init_mpc8xx_pmu(void)
0189 {
0190 mtspr(SPRN_ICTRL, 7);
0191 mtspr(SPRN_CMPA, 0);
0192 mtspr(SPRN_COUNTA, 0xffff);
0193
0194 return perf_pmu_register(&mpc8xx_pmu, "cpu", PERF_TYPE_RAW);
0195 }
0196
0197 early_initcall(init_mpc8xx_pmu);