8xx-pmu.c 5.3 KB

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