nmi_timer_int.c 3.5 KB

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