Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Performance event support - PPC 8xx
0004  *
0005  * Copyright 2016 Christophe Leroy, CS Systemes d'Information
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     /* If it was the last user, stop counting to avoid useless overhead */
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);