aperfmperf.c 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /*
  2. * x86 APERF/MPERF KHz calculation for
  3. * /sys/.../cpufreq/scaling_cur_freq
  4. *
  5. * Copyright (C) 2017 Intel Corp.
  6. * Author: Len Brown <len.brown@intel.com>
  7. *
  8. * This file is licensed under GPLv2.
  9. */
  10. #include <linux/delay.h>
  11. #include <linux/ktime.h>
  12. #include <linux/math64.h>
  13. #include <linux/percpu.h>
  14. #include <linux/smp.h>
  15. struct aperfmperf_sample {
  16. unsigned int khz;
  17. ktime_t time;
  18. u64 aperf;
  19. u64 mperf;
  20. };
  21. static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
  22. #define APERFMPERF_CACHE_THRESHOLD_MS 10
  23. #define APERFMPERF_REFRESH_DELAY_MS 20
  24. #define APERFMPERF_STALE_THRESHOLD_MS 1000
  25. /*
  26. * aperfmperf_snapshot_khz()
  27. * On the current CPU, snapshot APERF, MPERF, and jiffies
  28. * unless we already did it within 10ms
  29. * calculate kHz, save snapshot
  30. */
  31. static void aperfmperf_snapshot_khz(void *dummy)
  32. {
  33. u64 aperf, aperf_delta;
  34. u64 mperf, mperf_delta;
  35. struct aperfmperf_sample *s = this_cpu_ptr(&samples);
  36. ktime_t now = ktime_get();
  37. s64 time_delta = ktime_ms_delta(now, s->time);
  38. unsigned long flags;
  39. /* Don't bother re-computing within the cache threshold time. */
  40. if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
  41. return;
  42. local_irq_save(flags);
  43. rdmsrl(MSR_IA32_APERF, aperf);
  44. rdmsrl(MSR_IA32_MPERF, mperf);
  45. local_irq_restore(flags);
  46. aperf_delta = aperf - s->aperf;
  47. mperf_delta = mperf - s->mperf;
  48. /*
  49. * There is no architectural guarantee that MPERF
  50. * increments faster than we can read it.
  51. */
  52. if (mperf_delta == 0)
  53. return;
  54. s->time = now;
  55. s->aperf = aperf;
  56. s->mperf = mperf;
  57. /* If the previous iteration was too long ago, discard it. */
  58. if (time_delta > APERFMPERF_STALE_THRESHOLD_MS)
  59. s->khz = 0;
  60. else
  61. s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
  62. }
  63. unsigned int arch_freq_get_on_cpu(int cpu)
  64. {
  65. unsigned int khz;
  66. if (!cpu_khz)
  67. return 0;
  68. if (!static_cpu_has(X86_FEATURE_APERFMPERF))
  69. return 0;
  70. smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
  71. khz = per_cpu(samples.khz, cpu);
  72. if (khz)
  73. return khz;
  74. msleep(APERFMPERF_REFRESH_DELAY_MS);
  75. smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
  76. return per_cpu(samples.khz, cpu);
  77. }