hsw_ext_idle.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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 SandyBridge monitor. Implements the new package C-states
  7. * (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU.
  8. */
  9. #if defined(__i386__) || defined(__x86_64__)
  10. #include <stdio.h>
  11. #include <stdint.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "helpers/helpers.h"
  15. #include "idle_monitor/cpupower-monitor.h"
  16. #define MSR_PKG_C8_RESIDENCY 0x00000630
  17. #define MSR_PKG_C9_RESIDENCY 0x00000631
  18. #define MSR_PKG_C10_RESIDENCY 0x00000632
  19. #define MSR_TSC 0x10
  20. enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT,
  21. TSC = 0xFFFF };
  22. static int hsw_ext_get_count_percent(unsigned int self_id, double *percent,
  23. unsigned int cpu);
  24. static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = {
  25. {
  26. .name = "PC8",
  27. .desc = N_("Processor Package C8"),
  28. .id = PC8,
  29. .range = RANGE_PACKAGE,
  30. .get_count_percent = hsw_ext_get_count_percent,
  31. },
  32. {
  33. .name = "PC9",
  34. .desc = N_("Processor Package C9"),
  35. .id = PC9,
  36. .range = RANGE_PACKAGE,
  37. .get_count_percent = hsw_ext_get_count_percent,
  38. },
  39. {
  40. .name = "PC10",
  41. .desc = N_("Processor Package C10"),
  42. .id = PC10,
  43. .range = RANGE_PACKAGE,
  44. .get_count_percent = hsw_ext_get_count_percent,
  45. },
  46. };
  47. static unsigned long long tsc_at_measure_start;
  48. static unsigned long long tsc_at_measure_end;
  49. static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT];
  50. static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT];
  51. /* valid flag for all CPUs. If a MSR read failed it will be zero */
  52. static int *is_valid;
  53. static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val,
  54. unsigned int cpu)
  55. {
  56. int msr;
  57. switch (id) {
  58. case PC8:
  59. msr = MSR_PKG_C8_RESIDENCY;
  60. break;
  61. case PC9:
  62. msr = MSR_PKG_C9_RESIDENCY;
  63. break;
  64. case PC10:
  65. msr = MSR_PKG_C10_RESIDENCY;
  66. break;
  67. case TSC:
  68. msr = MSR_TSC;
  69. break;
  70. default:
  71. return -1;
  72. };
  73. if (read_msr(cpu, msr, val))
  74. return -1;
  75. return 0;
  76. }
  77. static int hsw_ext_get_count_percent(unsigned int id, double *percent,
  78. unsigned int cpu)
  79. {
  80. *percent = 0.0;
  81. if (!is_valid[cpu])
  82. return -1;
  83. *percent = (100.0 *
  84. (current_count[id][cpu] - previous_count[id][cpu])) /
  85. (tsc_at_measure_end - tsc_at_measure_start);
  86. dprint("%s: previous: %llu - current: %llu - (%u)\n",
  87. hsw_ext_cstates[id].name, previous_count[id][cpu],
  88. current_count[id][cpu], cpu);
  89. dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
  90. hsw_ext_cstates[id].name,
  91. (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
  92. current_count[id][cpu] - previous_count[id][cpu],
  93. *percent, cpu);
  94. return 0;
  95. }
  96. static int hsw_ext_start(void)
  97. {
  98. int num, cpu;
  99. unsigned long long val;
  100. for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
  101. for (cpu = 0; cpu < cpu_count; cpu++) {
  102. hsw_ext_get_count(num, &val, cpu);
  103. previous_count[num][cpu] = val;
  104. }
  105. }
  106. hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu);
  107. return 0;
  108. }
  109. static int hsw_ext_stop(void)
  110. {
  111. unsigned long long val;
  112. int num, cpu;
  113. hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu);
  114. for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
  115. for (cpu = 0; cpu < cpu_count; cpu++) {
  116. is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu);
  117. current_count[num][cpu] = val;
  118. }
  119. }
  120. return 0;
  121. }
  122. struct cpuidle_monitor intel_hsw_ext_monitor;
  123. static struct cpuidle_monitor *hsw_ext_register(void)
  124. {
  125. int num;
  126. if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
  127. || cpupower_cpu_info.family != 6)
  128. return NULL;
  129. switch (cpupower_cpu_info.model) {
  130. case 0x45: /* HSW */
  131. break;
  132. default:
  133. return NULL;
  134. }
  135. is_valid = calloc(cpu_count, sizeof(int));
  136. for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
  137. previous_count[num] = calloc(cpu_count,
  138. sizeof(unsigned long long));
  139. current_count[num] = calloc(cpu_count,
  140. sizeof(unsigned long long));
  141. }
  142. intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name);
  143. return &intel_hsw_ext_monitor;
  144. }
  145. void hsw_ext_unregister(void)
  146. {
  147. int num;
  148. free(is_valid);
  149. for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
  150. free(previous_count[num]);
  151. free(current_count[num]);
  152. }
  153. }
  154. struct cpuidle_monitor intel_hsw_ext_monitor = {
  155. .name = "HaswellExtended",
  156. .hw_states = hsw_ext_cstates,
  157. .hw_states_num = HSW_EXT_CSTATE_COUNT,
  158. .start = hsw_ext_start,
  159. .stop = hsw_ext_stop,
  160. .do_register = hsw_ext_register,
  161. .unregister = hsw_ext_unregister,
  162. .needs_root = 1,
  163. .overflow_s = 922000000 /* 922337203 seconds TSC overflow
  164. at 20GHz */
  165. };
  166. #endif /* defined(__i386__) || defined(__x86_64__) */