nmi_timer_int.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // SPDX-License-Identifier: GPL-2.0
  2. /**
  3. * @file nmi_timer_int.c
  4. *
  5. * @remark Copyright 2011 Advanced Micro Devices, Inc.
  6. *
  7. * @author Robert Richter <robert.richter@amd.com>
  8. */
  9. #include <linux/init.h>
  10. #include <linux/smp.h>
  11. #include <linux/errno.h>
  12. #include <linux/oprofile.h>
  13. #include <linux/perf_event.h>
  14. #ifdef CONFIG_OPROFILE_NMI_TIMER
  15. static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
  16. static int ctr_running;
  17. static struct perf_event_attr nmi_timer_attr = {
  18. .type = PERF_TYPE_HARDWARE,
  19. .config = PERF_COUNT_HW_CPU_CYCLES,
  20. .size = sizeof(struct perf_event_attr),
  21. .pinned = 1,
  22. .disabled = 1,
  23. };
  24. static void nmi_timer_callback(struct perf_event *event,
  25. struct perf_sample_data *data,
  26. struct pt_regs *regs)
  27. {
  28. event->hw.interrupts = 0; /* don't throttle interrupts */
  29. oprofile_add_sample(regs, 0);
  30. }
  31. static int nmi_timer_start_cpu(int cpu)
  32. {
  33. struct perf_event *event = per_cpu(nmi_timer_events, cpu);
  34. if (!event) {
  35. event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
  36. nmi_timer_callback, NULL);
  37. if (IS_ERR(event))
  38. return PTR_ERR(event);
  39. per_cpu(nmi_timer_events, cpu) = event;
  40. }
  41. if (event && ctr_running)
  42. perf_event_enable(event);
  43. return 0;
  44. }
  45. static void nmi_timer_stop_cpu(int cpu)
  46. {
  47. struct perf_event *event = per_cpu(nmi_timer_events, cpu);
  48. if (event && ctr_running)
  49. perf_event_disable(event);
  50. }
  51. static int nmi_timer_cpu_online(unsigned int cpu)
  52. {
  53. nmi_timer_start_cpu(cpu);
  54. return 0;
  55. }
  56. static int nmi_timer_cpu_predown(unsigned int cpu)
  57. {
  58. nmi_timer_stop_cpu(cpu);
  59. return 0;
  60. }
  61. static int nmi_timer_start(void)
  62. {
  63. int cpu;
  64. get_online_cpus();
  65. ctr_running = 1;
  66. for_each_online_cpu(cpu)
  67. nmi_timer_start_cpu(cpu);
  68. put_online_cpus();
  69. return 0;
  70. }
  71. static void nmi_timer_stop(void)
  72. {
  73. int cpu;
  74. get_online_cpus();
  75. for_each_online_cpu(cpu)
  76. nmi_timer_stop_cpu(cpu);
  77. ctr_running = 0;
  78. put_online_cpus();
  79. }
  80. static enum cpuhp_state hp_online;
  81. static void nmi_timer_shutdown(void)
  82. {
  83. struct perf_event *event;
  84. int cpu;
  85. cpuhp_remove_state(hp_online);
  86. for_each_possible_cpu(cpu) {
  87. event = per_cpu(nmi_timer_events, cpu);
  88. if (!event)
  89. continue;
  90. perf_event_disable(event);
  91. per_cpu(nmi_timer_events, cpu) = NULL;
  92. perf_event_release_kernel(event);
  93. }
  94. }
  95. static int nmi_timer_setup(void)
  96. {
  97. int err;
  98. u64 period;
  99. /* clock cycles per tick: */
  100. period = (u64)cpu_khz * 1000;
  101. do_div(period, HZ);
  102. nmi_timer_attr.sample_period = period;
  103. err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "oprofile/nmi:online",
  104. nmi_timer_cpu_online, nmi_timer_cpu_predown);
  105. if (err < 0) {
  106. nmi_timer_shutdown();
  107. return err;
  108. }
  109. hp_online = err;
  110. return 0;
  111. }
  112. int __init op_nmi_timer_init(struct oprofile_operations *ops)
  113. {
  114. int err = 0;
  115. err = nmi_timer_setup();
  116. if (err)
  117. return err;
  118. nmi_timer_shutdown(); /* only check, don't alloc */
  119. ops->create_files = NULL;
  120. ops->setup = nmi_timer_setup;
  121. ops->shutdown = nmi_timer_shutdown;
  122. ops->start = nmi_timer_start;
  123. ops->stop = nmi_timer_stop;
  124. ops->cpu_type = "timer";
  125. printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
  126. return 0;
  127. }
  128. #endif