cpustat_kern.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/version.h>
  3. #include <linux/ptrace.h>
  4. #include <uapi/linux/bpf.h>
  5. #include <bpf/bpf_helpers.h>
  6. /*
  7. * The CPU number, cstate number and pstate number are based
  8. * on 96boards Hikey with octa CA53 CPUs.
  9. *
  10. * Every CPU have three idle states for cstate:
  11. * WFI, CPU_OFF, CLUSTER_OFF
  12. *
  13. * Every CPU have 5 operating points:
  14. * 208MHz, 432MHz, 729MHz, 960MHz, 1200MHz
  15. *
  16. * This code is based on these assumption and other platforms
  17. * need to adjust these definitions.
  18. */
  19. #define MAX_CPU 8
  20. #define MAX_PSTATE_ENTRIES 5
  21. #define MAX_CSTATE_ENTRIES 3
  22. static int cpu_opps[] = { 208000, 432000, 729000, 960000, 1200000 };
  23. /*
  24. * my_map structure is used to record cstate and pstate index and
  25. * timestamp (Idx, Ts), when new event incoming we need to update
  26. * combination for new state index and timestamp (Idx`, Ts`).
  27. *
  28. * Based on (Idx, Ts) and (Idx`, Ts`) we can calculate the time
  29. * interval for the previous state: Duration(Idx) = Ts` - Ts.
  30. *
  31. * Every CPU has one below array for recording state index and
  32. * timestamp, and record for cstate and pstate saperately:
  33. *
  34. * +--------------------------+
  35. * | cstate timestamp |
  36. * +--------------------------+
  37. * | cstate index |
  38. * +--------------------------+
  39. * | pstate timestamp |
  40. * +--------------------------+
  41. * | pstate index |
  42. * +--------------------------+
  43. */
  44. #define MAP_OFF_CSTATE_TIME 0
  45. #define MAP_OFF_CSTATE_IDX 1
  46. #define MAP_OFF_PSTATE_TIME 2
  47. #define MAP_OFF_PSTATE_IDX 3
  48. #define MAP_OFF_NUM 4
  49. struct {
  50. __uint(type, BPF_MAP_TYPE_ARRAY);
  51. __type(key, u32);
  52. __type(value, u64);
  53. __uint(max_entries, MAX_CPU * MAP_OFF_NUM);
  54. } my_map SEC(".maps");
  55. /* cstate_duration records duration time for every idle state per CPU */
  56. struct {
  57. __uint(type, BPF_MAP_TYPE_ARRAY);
  58. __type(key, u32);
  59. __type(value, u64);
  60. __uint(max_entries, MAX_CPU * MAX_CSTATE_ENTRIES);
  61. } cstate_duration SEC(".maps");
  62. /* pstate_duration records duration time for every operating point per CPU */
  63. struct {
  64. __uint(type, BPF_MAP_TYPE_ARRAY);
  65. __type(key, u32);
  66. __type(value, u64);
  67. __uint(max_entries, MAX_CPU * MAX_PSTATE_ENTRIES);
  68. } pstate_duration SEC(".maps");
  69. /*
  70. * The trace events for cpu_idle and cpu_frequency are taken from:
  71. * /sys/kernel/debug/tracing/events/power/cpu_idle/format
  72. * /sys/kernel/debug/tracing/events/power/cpu_frequency/format
  73. *
  74. * These two events have same format, so define one common structure.
  75. */
  76. struct cpu_args {
  77. u64 pad;
  78. u32 state;
  79. u32 cpu_id;
  80. };
  81. /* calculate pstate index, returns MAX_PSTATE_ENTRIES for failure */
  82. static u32 find_cpu_pstate_idx(u32 frequency)
  83. {
  84. u32 i;
  85. for (i = 0; i < sizeof(cpu_opps) / sizeof(u32); i++) {
  86. if (frequency == cpu_opps[i])
  87. return i;
  88. }
  89. return i;
  90. }
  91. SEC("tracepoint/power/cpu_idle")
  92. int bpf_prog1(struct cpu_args *ctx)
  93. {
  94. u64 *cts, *pts, *cstate, *pstate, prev_state, cur_ts, delta;
  95. u32 key, cpu, pstate_idx;
  96. u64 *val;
  97. if (ctx->cpu_id > MAX_CPU)
  98. return 0;
  99. cpu = ctx->cpu_id;
  100. key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_TIME;
  101. cts = bpf_map_lookup_elem(&my_map, &key);
  102. if (!cts)
  103. return 0;
  104. key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_IDX;
  105. cstate = bpf_map_lookup_elem(&my_map, &key);
  106. if (!cstate)
  107. return 0;
  108. key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_TIME;
  109. pts = bpf_map_lookup_elem(&my_map, &key);
  110. if (!pts)
  111. return 0;
  112. key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_IDX;
  113. pstate = bpf_map_lookup_elem(&my_map, &key);
  114. if (!pstate)
  115. return 0;
  116. prev_state = *cstate;
  117. *cstate = ctx->state;
  118. if (!*cts) {
  119. *cts = bpf_ktime_get_ns();
  120. return 0;
  121. }
  122. cur_ts = bpf_ktime_get_ns();
  123. delta = cur_ts - *cts;
  124. *cts = cur_ts;
  125. /*
  126. * When state doesn't equal to (u32)-1, the cpu will enter
  127. * one idle state; for this case we need to record interval
  128. * for the pstate.
  129. *
  130. * OPP2
  131. * +---------------------+
  132. * OPP1 | |
  133. * ---------+ |
  134. * | Idle state
  135. * +---------------
  136. *
  137. * |<- pstate duration ->|
  138. * ^ ^
  139. * pts cur_ts
  140. */
  141. if (ctx->state != (u32)-1) {
  142. /* record pstate after have first cpu_frequency event */
  143. if (!*pts)
  144. return 0;
  145. delta = cur_ts - *pts;
  146. pstate_idx = find_cpu_pstate_idx(*pstate);
  147. if (pstate_idx >= MAX_PSTATE_ENTRIES)
  148. return 0;
  149. key = cpu * MAX_PSTATE_ENTRIES + pstate_idx;
  150. val = bpf_map_lookup_elem(&pstate_duration, &key);
  151. if (val)
  152. __sync_fetch_and_add((long *)val, delta);
  153. /*
  154. * When state equal to (u32)-1, the cpu just exits from one
  155. * specific idle state; for this case we need to record
  156. * interval for the pstate.
  157. *
  158. * OPP2
  159. * -----------+
  160. * | OPP1
  161. * | +-----------
  162. * | Idle state |
  163. * +---------------------+
  164. *
  165. * |<- cstate duration ->|
  166. * ^ ^
  167. * cts cur_ts
  168. */
  169. } else {
  170. key = cpu * MAX_CSTATE_ENTRIES + prev_state;
  171. val = bpf_map_lookup_elem(&cstate_duration, &key);
  172. if (val)
  173. __sync_fetch_and_add((long *)val, delta);
  174. }
  175. /* Update timestamp for pstate as new start time */
  176. if (*pts)
  177. *pts = cur_ts;
  178. return 0;
  179. }
  180. SEC("tracepoint/power/cpu_frequency")
  181. int bpf_prog2(struct cpu_args *ctx)
  182. {
  183. u64 *pts, *cstate, *pstate, prev_state, cur_ts, delta;
  184. u32 key, cpu, pstate_idx;
  185. u64 *val;
  186. cpu = ctx->cpu_id;
  187. key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_TIME;
  188. pts = bpf_map_lookup_elem(&my_map, &key);
  189. if (!pts)
  190. return 0;
  191. key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_IDX;
  192. pstate = bpf_map_lookup_elem(&my_map, &key);
  193. if (!pstate)
  194. return 0;
  195. key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_IDX;
  196. cstate = bpf_map_lookup_elem(&my_map, &key);
  197. if (!cstate)
  198. return 0;
  199. prev_state = *pstate;
  200. *pstate = ctx->state;
  201. if (!*pts) {
  202. *pts = bpf_ktime_get_ns();
  203. return 0;
  204. }
  205. cur_ts = bpf_ktime_get_ns();
  206. delta = cur_ts - *pts;
  207. *pts = cur_ts;
  208. /* When CPU is in idle, bail out to skip pstate statistics */
  209. if (*cstate != (u32)(-1))
  210. return 0;
  211. /*
  212. * The cpu changes to another different OPP (in below diagram
  213. * change frequency from OPP3 to OPP1), need recording interval
  214. * for previous frequency OPP3 and update timestamp as start
  215. * time for new frequency OPP1.
  216. *
  217. * OPP3
  218. * +---------------------+
  219. * OPP2 | |
  220. * ---------+ |
  221. * | OPP1
  222. * +---------------
  223. *
  224. * |<- pstate duration ->|
  225. * ^ ^
  226. * pts cur_ts
  227. */
  228. pstate_idx = find_cpu_pstate_idx(*pstate);
  229. if (pstate_idx >= MAX_PSTATE_ENTRIES)
  230. return 0;
  231. key = cpu * MAX_PSTATE_ENTRIES + pstate_idx;
  232. val = bpf_map_lookup_elem(&pstate_duration, &key);
  233. if (val)
  234. __sync_fetch_and_add((long *)val, delta);
  235. return 0;
  236. }
  237. char _license[] SEC("license") = "GPL";
  238. u32 _version SEC("version") = LINUX_VERSION_CODE;