snb_idle.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
  3. *
  4. * Licensed under the terms of the GNU GPL License version 2.
  5. *
  6. * Based on Len Brown's <lenb@kernel.org> turbostat tool.
  7. */
  8. #if defined(__i386__) || defined(__x86_64__)
  9. #include <stdio.h>
  10. #include <stdint.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "helpers/helpers.h"
  14. #include "idle_monitor/cpupower-monitor.h"
  15. #define MSR_PKG_C2_RESIDENCY 0x60D
  16. #define MSR_PKG_C7_RESIDENCY 0x3FA
  17. #define MSR_CORE_C7_RESIDENCY 0x3FE
  18. #define MSR_TSC 0x10
  19. enum intel_snb_id { C7 = 0, PC2, PC7, SNB_CSTATE_COUNT, TSC = 0xFFFF };
  20. static int snb_get_count_percent(unsigned int self_id, double *percent,
  21. unsigned int cpu);
  22. static cstate_t snb_cstates[SNB_CSTATE_COUNT] = {
  23. {
  24. .name = "C7",
  25. .desc = N_("Processor Core C7"),
  26. .id = C7,
  27. .range = RANGE_CORE,
  28. .get_count_percent = snb_get_count_percent,
  29. },
  30. {
  31. .name = "PC2",
  32. .desc = N_("Processor Package C2"),
  33. .id = PC2,
  34. .range = RANGE_PACKAGE,
  35. .get_count_percent = snb_get_count_percent,
  36. },
  37. {
  38. .name = "PC7",
  39. .desc = N_("Processor Package C7"),
  40. .id = PC7,
  41. .range = RANGE_PACKAGE,
  42. .get_count_percent = snb_get_count_percent,
  43. },
  44. };
  45. static unsigned long long tsc_at_measure_start;
  46. static unsigned long long tsc_at_measure_end;
  47. static unsigned long long *previous_count[SNB_CSTATE_COUNT];
  48. static unsigned long long *current_count[SNB_CSTATE_COUNT];
  49. /* valid flag for all CPUs. If a MSR read failed it will be zero */
  50. static int *is_valid;
  51. static int snb_get_count(enum intel_snb_id id, unsigned long long *val,
  52. unsigned int cpu)
  53. {
  54. int msr;
  55. switch (id) {
  56. case C7:
  57. msr = MSR_CORE_C7_RESIDENCY;
  58. break;
  59. case PC2:
  60. msr = MSR_PKG_C2_RESIDENCY;
  61. break;
  62. case PC7:
  63. msr = MSR_PKG_C7_RESIDENCY;
  64. break;
  65. case TSC:
  66. msr = MSR_TSC;
  67. break;
  68. default:
  69. return -1;
  70. };
  71. if (read_msr(cpu, msr, val))
  72. return -1;
  73. return 0;
  74. }
  75. static int snb_get_count_percent(unsigned int id, double *percent,
  76. unsigned int cpu)
  77. {
  78. *percent = 0.0;
  79. if (!is_valid[cpu])
  80. return -1;
  81. *percent = (100.0 *
  82. (current_count[id][cpu] - previous_count[id][cpu])) /
  83. (tsc_at_measure_end - tsc_at_measure_start);
  84. dprint("%s: previous: %llu - current: %llu - (%u)\n",
  85. snb_cstates[id].name, previous_count[id][cpu],
  86. current_count[id][cpu], cpu);
  87. dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
  88. snb_cstates[id].name,
  89. (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
  90. current_count[id][cpu] - previous_count[id][cpu],
  91. *percent, cpu);
  92. return 0;
  93. }
  94. static int snb_start(void)
  95. {
  96. int num, cpu;
  97. unsigned long long val;
  98. for (num = 0; num < SNB_CSTATE_COUNT; num++) {
  99. for (cpu = 0; cpu < cpu_count; cpu++) {
  100. snb_get_count(num, &val, cpu);
  101. previous_count[num][cpu] = val;
  102. }
  103. }
  104. snb_get_count(TSC, &tsc_at_measure_start, 0);
  105. return 0;
  106. }
  107. static int snb_stop(void)
  108. {
  109. unsigned long long val;
  110. int num, cpu;
  111. snb_get_count(TSC, &tsc_at_measure_end, 0);
  112. for (num = 0; num < SNB_CSTATE_COUNT; num++) {
  113. for (cpu = 0; cpu < cpu_count; cpu++) {
  114. is_valid[cpu] = !snb_get_count(num, &val, cpu);
  115. current_count[num][cpu] = val;
  116. }
  117. }
  118. return 0;
  119. }
  120. struct cpuidle_monitor intel_snb_monitor;
  121. static struct cpuidle_monitor *snb_register(void)
  122. {
  123. int num;
  124. if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
  125. || cpupower_cpu_info.family != 6)
  126. return NULL;
  127. switch (cpupower_cpu_info.model) {
  128. case 0x2A: /* SNB */
  129. case 0x2D: /* SNB Xeon */
  130. case 0x3A: /* IVB */
  131. case 0x3E: /* IVB Xeon */
  132. case 0x3C: /* HSW */
  133. case 0x3F: /* HSW */
  134. case 0x45: /* HSW */
  135. case 0x46: /* HSW */
  136. break;
  137. default:
  138. return NULL;
  139. }
  140. is_valid = calloc(cpu_count, sizeof(int));
  141. for (num = 0; num < SNB_CSTATE_COUNT; num++) {
  142. previous_count[num] = calloc(cpu_count,
  143. sizeof(unsigned long long));
  144. current_count[num] = calloc(cpu_count,
  145. sizeof(unsigned long long));
  146. }
  147. intel_snb_monitor.name_len = strlen(intel_snb_monitor.name);
  148. return &intel_snb_monitor;
  149. }
  150. void snb_unregister(void)
  151. {
  152. int num;
  153. free(is_valid);
  154. for (num = 0; num < SNB_CSTATE_COUNT; num++) {
  155. free(previous_count[num]);
  156. free(current_count[num]);
  157. }
  158. }
  159. struct cpuidle_monitor intel_snb_monitor = {
  160. .name = "SandyBridge",
  161. .hw_states = snb_cstates,
  162. .hw_states_num = SNB_CSTATE_COUNT,
  163. .start = snb_start,
  164. .stop = snb_stop,
  165. .do_register = snb_register,
  166. .unregister = snb_unregister,
  167. .needs_root = 1,
  168. .overflow_s = 922000000 /* 922337203 seconds TSC overflow
  169. at 20GHz */
  170. };
  171. #endif /* defined(__i386__) || defined(__x86_64__) */