8xx-pmu.c 4.7 KB

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