ebpf_cachestat.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "ebpf.h"
  3. #include "ebpf_cachestat.h"
  4. static char *cachestat_counter_dimension_name[NETDATA_CACHESTAT_END] = { "ratio", "dirty", "hit",
  5. "miss" };
  6. static netdata_syscall_stat_t cachestat_counter_aggregated_data[NETDATA_CACHESTAT_END];
  7. static netdata_publish_syscall_t cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_END];
  8. netdata_cachestat_pid_t *cachestat_vector = NULL;
  9. static netdata_idx_t cachestat_hash_values[NETDATA_CACHESTAT_END];
  10. static netdata_idx_t *cachestat_values = NULL;
  11. ebpf_local_maps_t cachestat_maps[] = {{.name = "cstat_global", .internal_input = NETDATA_CACHESTAT_END,
  12. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  13. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  14. {.name = "cstat_pid", .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
  15. .user_input = 0,
  16. .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID,
  17. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  18. {.name = "cstat_ctrl", .internal_input = NETDATA_CONTROLLER_END,
  19. .user_input = 0,
  20. .type = NETDATA_EBPF_MAP_CONTROLLER,
  21. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  22. {.name = NULL, .internal_input = 0, .user_input = 0,
  23. .type = NETDATA_EBPF_MAP_CONTROLLER,
  24. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}};
  25. struct config cachestat_config = { .first_section = NULL,
  26. .last_section = NULL,
  27. .mutex = NETDATA_MUTEX_INITIALIZER,
  28. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  29. .rwlock = AVL_LOCK_INITIALIZER } };
  30. netdata_ebpf_targets_t cachestat_targets[] = { {.name = "add_to_page_cache_lru", .mode = EBPF_LOAD_TRAMPOLINE},
  31. {.name = "mark_page_accessed", .mode = EBPF_LOAD_TRAMPOLINE},
  32. {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE},
  33. {.name = "mark_buffer_dirty", .mode = EBPF_LOAD_TRAMPOLINE},
  34. {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
  35. static char *account_page[NETDATA_CACHESTAT_ACCOUNT_DIRTY_END] ={ "account_page_dirtied",
  36. "__set_page_dirty", "__folio_mark_dirty" };
  37. #ifdef LIBBPF_MAJOR_VERSION
  38. /**
  39. * Disable probe
  40. *
  41. * Disable all probes to use exclusively another method.
  42. *
  43. * @param obj is the main structure for bpf objects
  44. */
  45. static void ebpf_cachestat_disable_probe(struct cachestat_bpf *obj)
  46. {
  47. bpf_program__set_autoload(obj->progs.netdata_add_to_page_cache_lru_kprobe, false);
  48. bpf_program__set_autoload(obj->progs.netdata_mark_page_accessed_kprobe, false);
  49. bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_kprobe, false);
  50. bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_kprobe, false);
  51. bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_kprobe, false);
  52. bpf_program__set_autoload(obj->progs.netdata_mark_buffer_dirty_kprobe, false);
  53. bpf_program__set_autoload(obj->progs.netdata_release_task_kprobe, false);
  54. }
  55. /*
  56. * Disable specific probe
  57. *
  58. * Disable probes according the kernel version
  59. *
  60. * @param obj is the main structure for bpf objects
  61. */
  62. static void ebpf_cachestat_disable_specific_probe(struct cachestat_bpf *obj)
  63. {
  64. if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  65. account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
  66. bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_kprobe, false);
  67. bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_kprobe, false);
  68. } else if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  69. account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
  70. bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_kprobe, false);
  71. bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_kprobe, false);
  72. } else {
  73. bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_kprobe, false);
  74. bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_kprobe, false);
  75. }
  76. }
  77. /*
  78. * Disable trampoline
  79. *
  80. * Disable all trampoline to use exclusively another method.
  81. *
  82. * @param obj is the main structure for bpf objects.
  83. */
  84. static void ebpf_cachestat_disable_trampoline(struct cachestat_bpf *obj)
  85. {
  86. bpf_program__set_autoload(obj->progs.netdata_add_to_page_cache_lru_fentry, false);
  87. bpf_program__set_autoload(obj->progs.netdata_mark_page_accessed_fentry, false);
  88. bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_fentry, false);
  89. bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_fentry, false);
  90. bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_fentry, false);
  91. bpf_program__set_autoload(obj->progs.netdata_mark_buffer_dirty_fentry, false);
  92. bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false);
  93. }
  94. /*
  95. * Disable specific trampoline
  96. *
  97. * Disable trampoline according to kernel version.
  98. *
  99. * @param obj is the main structure for bpf objects.
  100. */
  101. static void ebpf_cachestat_disable_specific_trampoline(struct cachestat_bpf *obj)
  102. {
  103. if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  104. account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
  105. bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_fentry, false);
  106. bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_fentry, false);
  107. } else if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  108. account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
  109. bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_fentry, false);
  110. bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_fentry, false);
  111. } else {
  112. bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_fentry, false);
  113. bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_fentry, false);
  114. }
  115. }
  116. /**
  117. * Set trampoline target
  118. *
  119. * Set the targets we will monitor.
  120. *
  121. * @param obj is the main structure for bpf objects.
  122. */
  123. static inline void netdata_set_trampoline_target(struct cachestat_bpf *obj)
  124. {
  125. bpf_program__set_attach_target(obj->progs.netdata_add_to_page_cache_lru_fentry, 0,
  126. cachestat_targets[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU].name);
  127. bpf_program__set_attach_target(obj->progs.netdata_mark_page_accessed_fentry, 0,
  128. cachestat_targets[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED].name);
  129. if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  130. account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
  131. bpf_program__set_attach_target(obj->progs.netdata_folio_mark_dirty_fentry, 0,
  132. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
  133. } else if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  134. account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
  135. bpf_program__set_attach_target(obj->progs.netdata_set_page_dirty_fentry, 0,
  136. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
  137. } else {
  138. bpf_program__set_attach_target(obj->progs.netdata_account_page_dirtied_fentry, 0,
  139. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
  140. }
  141. bpf_program__set_attach_target(obj->progs.netdata_mark_buffer_dirty_fentry, 0,
  142. cachestat_targets[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY].name);
  143. bpf_program__set_attach_target(obj->progs.netdata_release_task_fentry, 0,
  144. EBPF_COMMON_FNCT_CLEAN_UP);
  145. }
  146. /**
  147. * Mount Attach Probe
  148. *
  149. * Attach probes to target
  150. *
  151. * @param obj is the main structure for bpf objects.
  152. *
  153. * @return It returns 0 on success and -1 otherwise.
  154. */
  155. static int ebpf_cachestat_attach_probe(struct cachestat_bpf *obj)
  156. {
  157. obj->links.netdata_add_to_page_cache_lru_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_add_to_page_cache_lru_kprobe,
  158. false,
  159. cachestat_targets[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU].name);
  160. int ret = libbpf_get_error(obj->links.netdata_add_to_page_cache_lru_kprobe);
  161. if (ret)
  162. return -1;
  163. obj->links.netdata_mark_page_accessed_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_mark_page_accessed_kprobe,
  164. false,
  165. cachestat_targets[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED].name);
  166. ret = libbpf_get_error(obj->links.netdata_mark_page_accessed_kprobe);
  167. if (ret)
  168. return -1;
  169. if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  170. account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
  171. obj->links.netdata_folio_mark_dirty_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_folio_mark_dirty_kprobe,
  172. false,
  173. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
  174. ret = libbpf_get_error(obj->links.netdata_folio_mark_dirty_kprobe);
  175. } else if (!strcmp(cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
  176. account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
  177. obj->links.netdata_set_page_dirty_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_set_page_dirty_kprobe,
  178. false,
  179. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
  180. ret = libbpf_get_error(obj->links.netdata_set_page_dirty_kprobe);
  181. } else {
  182. obj->links.netdata_account_page_dirtied_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_account_page_dirtied_kprobe,
  183. false,
  184. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
  185. ret = libbpf_get_error(obj->links.netdata_account_page_dirtied_kprobe);
  186. }
  187. if (ret)
  188. return -1;
  189. obj->links.netdata_mark_buffer_dirty_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_mark_buffer_dirty_kprobe,
  190. false,
  191. cachestat_targets[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY].name);
  192. ret = libbpf_get_error(obj->links.netdata_mark_buffer_dirty_kprobe);
  193. if (ret)
  194. return -1;
  195. obj->links.netdata_release_task_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_release_task_kprobe,
  196. false,
  197. EBPF_COMMON_FNCT_CLEAN_UP);
  198. ret = libbpf_get_error(obj->links.netdata_release_task_kprobe);
  199. if (ret)
  200. return -1;
  201. return 0;
  202. }
  203. /**
  204. * Adjust Map Size
  205. *
  206. * Resize maps according input from users.
  207. *
  208. * @param obj is the main structure for bpf objects.
  209. * @param em structure with configuration
  210. */
  211. static void ebpf_cachestat_adjust_map_size(struct cachestat_bpf *obj, ebpf_module_t *em)
  212. {
  213. ebpf_update_map_size(obj->maps.cstat_pid, &cachestat_maps[NETDATA_CACHESTAT_PID_STATS],
  214. em, bpf_map__name(obj->maps.cstat_pid));
  215. }
  216. /**
  217. * Set hash tables
  218. *
  219. * Set the values for maps according the value given by kernel.
  220. *
  221. * @param obj is the main structure for bpf objects.
  222. */
  223. static void ebpf_cachestat_set_hash_tables(struct cachestat_bpf *obj)
  224. {
  225. cachestat_maps[NETDATA_CACHESTAT_GLOBAL_STATS].map_fd = bpf_map__fd(obj->maps.cstat_global);
  226. cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd = bpf_map__fd(obj->maps.cstat_pid);
  227. cachestat_maps[NETDATA_CACHESTAT_CTRL].map_fd = bpf_map__fd(obj->maps.cstat_ctrl);
  228. }
  229. /**
  230. * Disable Release Task
  231. *
  232. * Disable release task when apps is not enabled.
  233. *
  234. * @param obj is the main structure for bpf objects.
  235. */
  236. static void ebpf_cachestat_disable_release_task(struct cachestat_bpf *obj)
  237. {
  238. bpf_program__set_autoload(obj->progs.netdata_release_task_kprobe, false);
  239. bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false);
  240. }
  241. /**
  242. * Load and attach
  243. *
  244. * Load and attach the eBPF code in kernel.
  245. *
  246. * @param obj is the main structure for bpf objects.
  247. * @param em structure with configuration
  248. *
  249. * @return it returns 0 on success and -1 otherwise
  250. */
  251. static inline int ebpf_cachestat_load_and_attach(struct cachestat_bpf *obj, ebpf_module_t *em)
  252. {
  253. netdata_ebpf_targets_t *mt = em->targets;
  254. netdata_ebpf_program_loaded_t test = mt[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU].mode;
  255. if (test == EBPF_LOAD_TRAMPOLINE) {
  256. ebpf_cachestat_disable_probe(obj);
  257. ebpf_cachestat_disable_specific_trampoline(obj);
  258. netdata_set_trampoline_target(obj);
  259. } else {
  260. ebpf_cachestat_disable_trampoline(obj);
  261. ebpf_cachestat_disable_specific_probe(obj);
  262. }
  263. ebpf_cachestat_adjust_map_size(obj, em);
  264. if (!em->apps_charts && !em->cgroup_charts)
  265. ebpf_cachestat_disable_release_task(obj);
  266. int ret = cachestat_bpf__load(obj);
  267. if (ret) {
  268. return ret;
  269. }
  270. ret = (test == EBPF_LOAD_TRAMPOLINE) ? cachestat_bpf__attach(obj) : ebpf_cachestat_attach_probe(obj);
  271. if (!ret) {
  272. ebpf_cachestat_set_hash_tables(obj);
  273. ebpf_update_controller(cachestat_maps[NETDATA_CACHESTAT_CTRL].map_fd, em);
  274. }
  275. return ret;
  276. }
  277. #endif
  278. /*****************************************************************
  279. *
  280. * FUNCTIONS TO CLOSE THE THREAD
  281. *
  282. *****************************************************************/
  283. /**
  284. * Cachestat Free
  285. *
  286. * Cleanup variables after child threads to stop
  287. *
  288. * @param ptr thread data.
  289. */
  290. static void ebpf_cachestat_free(ebpf_module_t *em)
  291. {
  292. pthread_mutex_lock(&ebpf_exit_cleanup);
  293. em->enabled = NETDATA_THREAD_EBPF_STOPPING;
  294. pthread_mutex_unlock(&ebpf_exit_cleanup);
  295. freez(cachestat_vector);
  296. freez(cachestat_values);
  297. pthread_mutex_lock(&ebpf_exit_cleanup);
  298. em->enabled = NETDATA_THREAD_EBPF_STOPPED;
  299. pthread_mutex_unlock(&ebpf_exit_cleanup);
  300. }
  301. /**
  302. * Cachestat exit.
  303. *
  304. * Cancel child and exit.
  305. *
  306. * @param ptr thread data.
  307. */
  308. static void ebpf_cachestat_exit(void *ptr)
  309. {
  310. ebpf_module_t *em = (ebpf_module_t *)ptr;
  311. ebpf_cachestat_free(em);
  312. }
  313. /*****************************************************************
  314. *
  315. * COMMON FUNCTIONS
  316. *
  317. *****************************************************************/
  318. /**
  319. * Update publish
  320. *
  321. * Update publish values before to write dimension.
  322. *
  323. * @param out structure that will receive data.
  324. * @param mpa calls for mark_page_accessed during the last second.
  325. * @param mbd calls for mark_buffer_dirty during the last second.
  326. * @param apcl calls for add_to_page_cache_lru during the last second.
  327. * @param apd calls for account_page_dirtied during the last second.
  328. */
  329. void cachestat_update_publish(netdata_publish_cachestat_t *out, uint64_t mpa, uint64_t mbd,
  330. uint64_t apcl, uint64_t apd)
  331. {
  332. // Adapted algorithm from https://github.com/iovisor/bcc/blob/master/tools/cachestat.py#L126-L138
  333. NETDATA_DOUBLE total = (NETDATA_DOUBLE) (((long long)mpa) - ((long long)mbd));
  334. if (total < 0)
  335. total = 0;
  336. NETDATA_DOUBLE misses = (NETDATA_DOUBLE) ( ((long long) apcl) - ((long long) apd) );
  337. if (misses < 0)
  338. misses = 0;
  339. // If hits are < 0, then its possible misses are overestimate due to possibly page cache read ahead adding
  340. // more pages than needed. In this case just assume misses as total and reset hits.
  341. NETDATA_DOUBLE hits = total - misses;
  342. if (hits < 0 ) {
  343. misses = total;
  344. hits = 0;
  345. }
  346. NETDATA_DOUBLE ratio = (total > 0) ? hits/total : 1;
  347. out->ratio = (long long )(ratio*100);
  348. out->hit = (long long)hits;
  349. out->miss = (long long)misses;
  350. }
  351. /**
  352. * Save previous values
  353. *
  354. * Save values used this time.
  355. *
  356. * @param publish
  357. */
  358. static void save_previous_values(netdata_publish_cachestat_t *publish) {
  359. publish->prev.mark_page_accessed = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED];
  360. publish->prev.account_page_dirtied = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED];
  361. publish->prev.add_to_page_cache_lru = cachestat_hash_values[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU];
  362. publish->prev.mark_buffer_dirty = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY];
  363. }
  364. /**
  365. * Calculate statistics
  366. *
  367. * @param publish the structure where we will store the data.
  368. */
  369. static void calculate_stats(netdata_publish_cachestat_t *publish) {
  370. if (!publish->prev.mark_page_accessed) {
  371. save_previous_values(publish);
  372. return;
  373. }
  374. uint64_t mpa = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED] - publish->prev.mark_page_accessed;
  375. uint64_t mbd = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY] - publish->prev.mark_buffer_dirty;
  376. uint64_t apcl = cachestat_hash_values[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU] - publish->prev.add_to_page_cache_lru;
  377. uint64_t apd = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED] - publish->prev.account_page_dirtied;
  378. save_previous_values(publish);
  379. // We are changing the original algorithm to have a smooth ratio.
  380. cachestat_update_publish(publish, mpa, mbd, apcl, apd);
  381. }
  382. /*****************************************************************
  383. *
  384. * APPS
  385. *
  386. *****************************************************************/
  387. /**
  388. * Apps Accumulator
  389. *
  390. * Sum all values read from kernel and store in the first address.
  391. *
  392. * @param out the vector with read values.
  393. */
  394. static void cachestat_apps_accumulator(netdata_cachestat_pid_t *out)
  395. {
  396. int i, end = (running_on_kernel >= NETDATA_KERNEL_V4_15) ? ebpf_nprocs : 1;
  397. netdata_cachestat_pid_t *total = &out[0];
  398. for (i = 1; i < end; i++) {
  399. netdata_cachestat_pid_t *w = &out[i];
  400. total->account_page_dirtied += w->account_page_dirtied;
  401. total->add_to_page_cache_lru += w->add_to_page_cache_lru;
  402. total->mark_buffer_dirty += w->mark_buffer_dirty;
  403. total->mark_page_accessed += w->mark_page_accessed;
  404. }
  405. }
  406. /**
  407. * Save Pid values
  408. *
  409. * Save the current values inside the structure
  410. *
  411. * @param out vector used to plot charts
  412. * @param publish vector with values read from hash tables.
  413. */
  414. static inline void cachestat_save_pid_values(netdata_publish_cachestat_t *out, netdata_cachestat_pid_t *publish)
  415. {
  416. if (!out->current.mark_page_accessed) {
  417. memcpy(&out->current, &publish[0], sizeof(netdata_cachestat_pid_t));
  418. return;
  419. }
  420. memcpy(&out->prev, &out->current, sizeof(netdata_cachestat_pid_t));
  421. memcpy(&out->current, &publish[0], sizeof(netdata_cachestat_pid_t));
  422. }
  423. /**
  424. * Fill PID
  425. *
  426. * Fill PID structures
  427. *
  428. * @param current_pid pid that we are collecting data
  429. * @param out values read from hash tables;
  430. */
  431. static void cachestat_fill_pid(uint32_t current_pid, netdata_cachestat_pid_t *publish)
  432. {
  433. netdata_publish_cachestat_t *curr = cachestat_pid[current_pid];
  434. if (!curr) {
  435. curr = ebpf_publish_cachestat_get();
  436. cachestat_pid[current_pid] = curr;
  437. cachestat_save_pid_values(curr, publish);
  438. return;
  439. }
  440. cachestat_save_pid_values(curr, publish);
  441. }
  442. /**
  443. * Read APPS table
  444. *
  445. * Read the apps table and store data inside the structure.
  446. */
  447. static void read_apps_table()
  448. {
  449. netdata_cachestat_pid_t *cv = cachestat_vector;
  450. uint32_t key;
  451. struct ebpf_pid_stat *pids = ebpf_root_of_pids;
  452. int fd = cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd;
  453. size_t length = sizeof(netdata_cachestat_pid_t)*ebpf_nprocs;
  454. while (pids) {
  455. key = pids->pid;
  456. if (bpf_map_lookup_elem(fd, &key, cv)) {
  457. pids = pids->next;
  458. continue;
  459. }
  460. cachestat_apps_accumulator(cv);
  461. cachestat_fill_pid(key, cv);
  462. // We are cleaning to avoid passing data read from one process to other.
  463. memset(cv, 0, length);
  464. pids = pids->next;
  465. }
  466. }
  467. /**
  468. * Update cgroup
  469. *
  470. * Update cgroup data based in
  471. */
  472. static void ebpf_update_cachestat_cgroup()
  473. {
  474. netdata_cachestat_pid_t *cv = cachestat_vector;
  475. int fd = cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd;
  476. size_t length = sizeof(netdata_cachestat_pid_t) * ebpf_nprocs;
  477. ebpf_cgroup_target_t *ect;
  478. pthread_mutex_lock(&mutex_cgroup_shm);
  479. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  480. struct pid_on_target2 *pids;
  481. for (pids = ect->pids; pids; pids = pids->next) {
  482. int pid = pids->pid;
  483. netdata_cachestat_pid_t *out = &pids->cachestat;
  484. if (likely(cachestat_pid) && cachestat_pid[pid]) {
  485. netdata_publish_cachestat_t *in = cachestat_pid[pid];
  486. memcpy(out, &in->current, sizeof(netdata_cachestat_pid_t));
  487. } else {
  488. memset(cv, 0, length);
  489. if (bpf_map_lookup_elem(fd, &pid, cv)) {
  490. continue;
  491. }
  492. cachestat_apps_accumulator(cv);
  493. memcpy(out, cv, sizeof(netdata_cachestat_pid_t));
  494. }
  495. }
  496. }
  497. pthread_mutex_unlock(&mutex_cgroup_shm);
  498. }
  499. /**
  500. * Create apps charts
  501. *
  502. * Call ebpf_create_chart to create the charts on apps submenu.
  503. *
  504. * @param em a pointer to the structure with the default values.
  505. */
  506. void ebpf_cachestat_create_apps_charts(struct ebpf_module *em, void *ptr)
  507. {
  508. struct ebpf_target *root = ptr;
  509. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_HIT_RATIO_CHART,
  510. "Hit ratio",
  511. EBPF_COMMON_DIMENSION_PERCENTAGE,
  512. NETDATA_CACHESTAT_SUBMENU,
  513. NETDATA_EBPF_CHART_TYPE_LINE,
  514. 20090,
  515. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  516. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  517. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_DIRTY_CHART,
  518. "Number of dirty pages",
  519. EBPF_CACHESTAT_DIMENSION_PAGE,
  520. NETDATA_CACHESTAT_SUBMENU,
  521. NETDATA_EBPF_CHART_TYPE_STACKED,
  522. 20091,
  523. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  524. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  525. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_HIT_CHART,
  526. "Number of accessed files",
  527. EBPF_CACHESTAT_DIMENSION_HITS,
  528. NETDATA_CACHESTAT_SUBMENU,
  529. NETDATA_EBPF_CHART_TYPE_STACKED,
  530. 20092,
  531. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  532. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  533. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_MISSES_CHART,
  534. "Files out of page cache",
  535. EBPF_CACHESTAT_DIMENSION_MISSES,
  536. NETDATA_CACHESTAT_SUBMENU,
  537. NETDATA_EBPF_CHART_TYPE_STACKED,
  538. 20093,
  539. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  540. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  541. em->apps_charts |= NETDATA_EBPF_APPS_FLAG_CHART_CREATED;
  542. }
  543. /*****************************************************************
  544. *
  545. * MAIN LOOP
  546. *
  547. *****************************************************************/
  548. /**
  549. * Read global counter
  550. *
  551. * Read the table with number of calls for all functions
  552. */
  553. static void ebpf_cachestat_read_global_table()
  554. {
  555. uint32_t idx;
  556. netdata_idx_t *val = cachestat_hash_values;
  557. netdata_idx_t *stored = cachestat_values;
  558. int fd = cachestat_maps[NETDATA_CACHESTAT_GLOBAL_STATS].map_fd;
  559. for (idx = NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU; idx < NETDATA_CACHESTAT_END; idx++) {
  560. if (!bpf_map_lookup_elem(fd, &idx, stored)) {
  561. int i;
  562. int end = ebpf_nprocs;
  563. netdata_idx_t total = 0;
  564. for (i = 0; i < end; i++)
  565. total += stored[i];
  566. val[idx] = total;
  567. }
  568. }
  569. }
  570. /**
  571. * Send global
  572. *
  573. * Send global charts to Netdata
  574. */
  575. static void cachestat_send_global(netdata_publish_cachestat_t *publish)
  576. {
  577. calculate_stats(publish);
  578. netdata_publish_syscall_t *ptr = cachestat_counter_publish_aggregated;
  579. ebpf_one_dimension_write_charts(
  580. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_RATIO_CHART, ptr[NETDATA_CACHESTAT_IDX_RATIO].dimension,
  581. publish->ratio);
  582. ebpf_one_dimension_write_charts(
  583. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_DIRTY_CHART, ptr[NETDATA_CACHESTAT_IDX_DIRTY].dimension,
  584. cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY]);
  585. ebpf_one_dimension_write_charts(
  586. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_CHART, ptr[NETDATA_CACHESTAT_IDX_HIT].dimension, publish->hit);
  587. ebpf_one_dimension_write_charts(
  588. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_MISSES_CHART, ptr[NETDATA_CACHESTAT_IDX_MISS].dimension,
  589. publish->miss);
  590. }
  591. /**
  592. * Cachestat sum PIDs
  593. *
  594. * Sum values for all PIDs associated to a group
  595. *
  596. * @param publish output structure.
  597. * @param root structure with listed IPs
  598. */
  599. void ebpf_cachestat_sum_pids(netdata_publish_cachestat_t *publish, struct ebpf_pid_on_target *root)
  600. {
  601. memcpy(&publish->prev, &publish->current,sizeof(publish->current));
  602. memset(&publish->current, 0, sizeof(publish->current));
  603. netdata_cachestat_pid_t *dst = &publish->current;
  604. while (root) {
  605. int32_t pid = root->pid;
  606. netdata_publish_cachestat_t *w = cachestat_pid[pid];
  607. if (w) {
  608. netdata_cachestat_pid_t *src = &w->current;
  609. dst->account_page_dirtied += src->account_page_dirtied;
  610. dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
  611. dst->mark_buffer_dirty += src->mark_buffer_dirty;
  612. dst->mark_page_accessed += src->mark_page_accessed;
  613. }
  614. root = root->next;
  615. }
  616. }
  617. /**
  618. * Send data to Netdata calling auxiliary functions.
  619. *
  620. * @param root the target list.
  621. */
  622. void ebpf_cache_send_apps_data(struct ebpf_target *root)
  623. {
  624. struct ebpf_target *w;
  625. collected_number value;
  626. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_HIT_RATIO_CHART);
  627. for (w = root; w; w = w->next) {
  628. if (unlikely(w->exposed && w->processes)) {
  629. ebpf_cachestat_sum_pids(&w->cachestat, w->root_pid);
  630. netdata_cachestat_pid_t *current = &w->cachestat.current;
  631. netdata_cachestat_pid_t *prev = &w->cachestat.prev;
  632. uint64_t mpa = current->mark_page_accessed - prev->mark_page_accessed;
  633. uint64_t mbd = current->mark_buffer_dirty - prev->mark_buffer_dirty;
  634. w->cachestat.dirty = mbd;
  635. uint64_t apcl = current->add_to_page_cache_lru - prev->add_to_page_cache_lru;
  636. uint64_t apd = current->account_page_dirtied - prev->account_page_dirtied;
  637. cachestat_update_publish(&w->cachestat, mpa, mbd, apcl, apd);
  638. value = (collected_number) w->cachestat.ratio;
  639. // Here we are using different approach to have a chart more smooth
  640. write_chart_dimension(w->name, value);
  641. }
  642. }
  643. write_end_chart();
  644. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_DIRTY_CHART);
  645. for (w = root; w; w = w->next) {
  646. if (unlikely(w->exposed && w->processes)) {
  647. value = (collected_number) w->cachestat.dirty;
  648. write_chart_dimension(w->name, value);
  649. }
  650. }
  651. write_end_chart();
  652. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_HIT_CHART);
  653. for (w = root; w; w = w->next) {
  654. if (unlikely(w->exposed && w->processes)) {
  655. value = (collected_number) w->cachestat.hit;
  656. write_chart_dimension(w->name, value);
  657. }
  658. }
  659. write_end_chart();
  660. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_MISSES_CHART);
  661. for (w = root; w; w = w->next) {
  662. if (unlikely(w->exposed && w->processes)) {
  663. value = (collected_number) w->cachestat.miss;
  664. write_chart_dimension(w->name, value);
  665. }
  666. }
  667. write_end_chart();
  668. }
  669. /**
  670. * Cachestat sum PIDs
  671. *
  672. * Sum values for all PIDs associated to a group
  673. *
  674. * @param publish output structure.
  675. * @param root structure with listed IPs
  676. */
  677. void ebpf_cachestat_sum_cgroup_pids(netdata_publish_cachestat_t *publish, struct pid_on_target2 *root)
  678. {
  679. memcpy(&publish->prev, &publish->current,sizeof(publish->current));
  680. memset(&publish->current, 0, sizeof(publish->current));
  681. netdata_cachestat_pid_t *dst = &publish->current;
  682. while (root) {
  683. netdata_cachestat_pid_t *src = &root->cachestat;
  684. dst->account_page_dirtied += src->account_page_dirtied;
  685. dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
  686. dst->mark_buffer_dirty += src->mark_buffer_dirty;
  687. dst->mark_page_accessed += src->mark_page_accessed;
  688. root = root->next;
  689. }
  690. }
  691. /**
  692. * Calc chart values
  693. *
  694. * Do necessary math to plot charts.
  695. */
  696. void ebpf_cachestat_calc_chart_values()
  697. {
  698. ebpf_cgroup_target_t *ect;
  699. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  700. ebpf_cachestat_sum_cgroup_pids(&ect->publish_cachestat, ect->pids);
  701. netdata_cachestat_pid_t *current = &ect->publish_cachestat.current;
  702. netdata_cachestat_pid_t *prev = &ect->publish_cachestat.prev;
  703. uint64_t mpa = current->mark_page_accessed - prev->mark_page_accessed;
  704. uint64_t mbd = current->mark_buffer_dirty - prev->mark_buffer_dirty;
  705. ect->publish_cachestat.dirty = mbd;
  706. uint64_t apcl = current->add_to_page_cache_lru - prev->add_to_page_cache_lru;
  707. uint64_t apd = current->account_page_dirtied - prev->account_page_dirtied;
  708. cachestat_update_publish(&ect->publish_cachestat, mpa, mbd, apcl, apd);
  709. }
  710. }
  711. /**
  712. * Create Systemd cachestat Charts
  713. *
  714. * Create charts when systemd is enabled
  715. *
  716. * @param update_every value to overwrite the update frequency set by the server.
  717. **/
  718. static void ebpf_create_systemd_cachestat_charts(int update_every)
  719. {
  720. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_HIT_RATIO_CHART,
  721. "Hit ratio",
  722. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_SUBMENU,
  723. NETDATA_EBPF_CHART_TYPE_LINE, 21100,
  724. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  725. NETDATA_SYSTEMD_CACHESTAT_HIT_RATIO_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  726. update_every);
  727. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_DIRTY_CHART,
  728. "Number of dirty pages",
  729. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_SUBMENU,
  730. NETDATA_EBPF_CHART_TYPE_LINE, 21101,
  731. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  732. NETDATA_SYSTEMD_CACHESTAT_MODIFIED_CACHE_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  733. update_every);
  734. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_HIT_CHART, "Number of accessed files",
  735. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_SUBMENU,
  736. NETDATA_EBPF_CHART_TYPE_LINE, 21102,
  737. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  738. NETDATA_SYSTEMD_CACHESTAT_HIT_FILE_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  739. update_every);
  740. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_MISSES_CHART, "Files out of page cache",
  741. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_SUBMENU,
  742. NETDATA_EBPF_CHART_TYPE_LINE, 21103,
  743. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  744. NETDATA_SYSTEMD_CACHESTAT_MISS_FILES_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  745. update_every);
  746. }
  747. /**
  748. * Send Cache Stat charts
  749. *
  750. * Send collected data to Netdata.
  751. */
  752. static void ebpf_send_systemd_cachestat_charts()
  753. {
  754. ebpf_cgroup_target_t *ect;
  755. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_HIT_RATIO_CHART);
  756. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  757. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  758. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.ratio);
  759. }
  760. }
  761. write_end_chart();
  762. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_DIRTY_CHART);
  763. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  764. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  765. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.dirty);
  766. }
  767. }
  768. write_end_chart();
  769. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_HIT_CHART);
  770. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  771. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  772. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.hit);
  773. }
  774. }
  775. write_end_chart();
  776. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_MISSES_CHART);
  777. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  778. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  779. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.miss);
  780. }
  781. }
  782. write_end_chart();
  783. }
  784. /**
  785. * Send Directory Cache charts
  786. *
  787. * Send collected data to Netdata.
  788. */
  789. static void ebpf_send_specific_cachestat_data(char *type, netdata_publish_cachestat_t *npc)
  790. {
  791. write_begin_chart(type, NETDATA_CACHESTAT_HIT_RATIO_CHART);
  792. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_RATIO].name, (long long)npc->ratio);
  793. write_end_chart();
  794. write_begin_chart(type, NETDATA_CACHESTAT_DIRTY_CHART);
  795. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY].name, (long long)npc->dirty);
  796. write_end_chart();
  797. write_begin_chart(type, NETDATA_CACHESTAT_HIT_CHART);
  798. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT].name, (long long)npc->hit);
  799. write_end_chart();
  800. write_begin_chart(type, NETDATA_CACHESTAT_MISSES_CHART);
  801. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS].name, (long long)npc->miss);
  802. write_end_chart();
  803. }
  804. /**
  805. * Create specific cache Stat charts
  806. *
  807. * Create charts for cgroup/application.
  808. *
  809. * @param type the chart type.
  810. * @param update_every value to overwrite the update frequency set by the server.
  811. */
  812. static void ebpf_create_specific_cachestat_charts(char *type, int update_every)
  813. {
  814. ebpf_create_chart(type, NETDATA_CACHESTAT_HIT_RATIO_CHART,
  815. "Hit ratio",
  816. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  817. NETDATA_CGROUP_CACHESTAT_HIT_RATIO_CONTEXT,
  818. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5200,
  819. ebpf_create_global_dimension,
  820. cachestat_counter_publish_aggregated, 1, update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  821. ebpf_create_chart(type, NETDATA_CACHESTAT_DIRTY_CHART,
  822. "Number of dirty pages",
  823. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  824. NETDATA_CGROUP_CACHESTAT_MODIFIED_CACHE_CONTEXT,
  825. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5201,
  826. ebpf_create_global_dimension,
  827. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY], 1,
  828. update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  829. ebpf_create_chart(type, NETDATA_CACHESTAT_HIT_CHART,
  830. "Number of accessed files",
  831. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  832. NETDATA_CGROUP_CACHESTAT_HIT_FILES_CONTEXT,
  833. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5202,
  834. ebpf_create_global_dimension,
  835. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT], 1,
  836. update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  837. ebpf_create_chart(type, NETDATA_CACHESTAT_MISSES_CHART,
  838. "Files out of page cache",
  839. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  840. NETDATA_CGROUP_CACHESTAT_MISS_FILES_CONTEXT,
  841. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5203,
  842. ebpf_create_global_dimension,
  843. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS], 1,
  844. update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  845. }
  846. /**
  847. * Obsolete specific cache stat charts
  848. *
  849. * Obsolete charts for cgroup/application.
  850. *
  851. * @param type the chart type.
  852. * @param update_every value to overwrite the update frequency set by the server.
  853. */
  854. static void ebpf_obsolete_specific_cachestat_charts(char *type, int update_every)
  855. {
  856. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_HIT_RATIO_CHART,
  857. "Hit ratio",
  858. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_SUBMENU,
  859. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_HIT_RATIO_CONTEXT,
  860. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5200, update_every);
  861. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_DIRTY_CHART,
  862. "Number of dirty pages",
  863. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_SUBMENU,
  864. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_MODIFIED_CACHE_CONTEXT,
  865. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5201, update_every);
  866. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_HIT_CHART,
  867. "Number of accessed files",
  868. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_SUBMENU,
  869. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_HIT_FILES_CONTEXT,
  870. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5202, update_every);
  871. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_MISSES_CHART,
  872. "Files out of page cache",
  873. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_SUBMENU,
  874. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_MISS_FILES_CONTEXT,
  875. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5203, update_every);
  876. }
  877. /**
  878. * Send data to Netdata calling auxiliary functions.
  879. *
  880. * @param update_every value to overwrite the update frequency set by the server.
  881. */
  882. void ebpf_cachestat_send_cgroup_data(int update_every)
  883. {
  884. if (!ebpf_cgroup_pids)
  885. return;
  886. pthread_mutex_lock(&mutex_cgroup_shm);
  887. ebpf_cgroup_target_t *ect;
  888. ebpf_cachestat_calc_chart_values();
  889. int has_systemd = shm_ebpf_cgroup.header->systemd_enabled;
  890. if (has_systemd) {
  891. if (send_cgroup_chart) {
  892. ebpf_create_systemd_cachestat_charts(update_every);
  893. }
  894. ebpf_send_systemd_cachestat_charts();
  895. }
  896. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  897. if (ect->systemd)
  898. continue;
  899. if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART) && ect->updated) {
  900. ebpf_create_specific_cachestat_charts(ect->name, update_every);
  901. ect->flags |= NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART;
  902. }
  903. if (ect->flags & NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART) {
  904. if (ect->updated) {
  905. ebpf_send_specific_cachestat_data(ect->name, &ect->publish_cachestat);
  906. } else {
  907. ebpf_obsolete_specific_cachestat_charts(ect->name, update_every);
  908. ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART;
  909. }
  910. }
  911. }
  912. pthread_mutex_unlock(&mutex_cgroup_shm);
  913. }
  914. /**
  915. * Main loop for this collector.
  916. */
  917. static void cachestat_collector(ebpf_module_t *em)
  918. {
  919. netdata_publish_cachestat_t publish;
  920. memset(&publish, 0, sizeof(publish));
  921. int cgroups = em->cgroup_charts;
  922. int update_every = em->update_every;
  923. heartbeat_t hb;
  924. heartbeat_init(&hb);
  925. int counter = update_every - 1;
  926. //This will be cancelled by its parent
  927. while (!ebpf_exit_plugin) {
  928. (void)heartbeat_next(&hb, USEC_PER_SEC);
  929. if (ebpf_exit_plugin || ++counter != update_every)
  930. continue;
  931. counter = 0;
  932. netdata_apps_integration_flags_t apps = em->apps_charts;
  933. ebpf_cachestat_read_global_table();
  934. pthread_mutex_lock(&collect_data_mutex);
  935. if (apps)
  936. read_apps_table();
  937. if (cgroups)
  938. ebpf_update_cachestat_cgroup();
  939. pthread_mutex_lock(&lock);
  940. cachestat_send_global(&publish);
  941. if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
  942. ebpf_cache_send_apps_data(apps_groups_root_target);
  943. #ifdef NETDATA_DEV_MODE
  944. if (ebpf_aral_cachestat_pid)
  945. ebpf_send_data_aral_chart(ebpf_aral_cachestat_pid, em);
  946. #endif
  947. if (cgroups)
  948. ebpf_cachestat_send_cgroup_data(update_every);
  949. pthread_mutex_unlock(&lock);
  950. pthread_mutex_unlock(&collect_data_mutex);
  951. }
  952. }
  953. /*****************************************************************
  954. *
  955. * INITIALIZE THREAD
  956. *
  957. *****************************************************************/
  958. /**
  959. * Create global charts
  960. *
  961. * Call ebpf_create_chart to create the charts for the collector.
  962. *
  963. * @param em a pointer to `struct ebpf_module`
  964. */
  965. static void ebpf_create_memory_charts(ebpf_module_t *em)
  966. {
  967. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_RATIO_CHART,
  968. "Hit ratio",
  969. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_SUBMENU,
  970. NULL,
  971. NETDATA_EBPF_CHART_TYPE_LINE,
  972. 21100,
  973. ebpf_create_global_dimension,
  974. cachestat_counter_publish_aggregated, 1, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  975. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_DIRTY_CHART,
  976. "Number of dirty pages",
  977. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_SUBMENU,
  978. NULL,
  979. NETDATA_EBPF_CHART_TYPE_LINE,
  980. 21101,
  981. ebpf_create_global_dimension,
  982. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY], 1,
  983. em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  984. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_CHART,
  985. "Number of accessed files",
  986. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_SUBMENU,
  987. NULL,
  988. NETDATA_EBPF_CHART_TYPE_LINE,
  989. 21102,
  990. ebpf_create_global_dimension,
  991. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT], 1,
  992. em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  993. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_MISSES_CHART,
  994. "Files out of page cache",
  995. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_SUBMENU,
  996. NULL,
  997. NETDATA_EBPF_CHART_TYPE_LINE,
  998. 21103,
  999. ebpf_create_global_dimension,
  1000. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS], 1,
  1001. em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  1002. fflush(stdout);
  1003. }
  1004. /**
  1005. * Allocate vectors used with this thread.
  1006. *
  1007. * We are not testing the return, because callocz does this and shutdown the software
  1008. * case it was not possible to allocate.
  1009. *
  1010. * @param apps is apps enabled?
  1011. */
  1012. static void ebpf_cachestat_allocate_global_vectors(int apps)
  1013. {
  1014. if (apps) {
  1015. cachestat_pid = callocz((size_t)pid_max, sizeof(netdata_publish_cachestat_t *));
  1016. ebpf_cachestat_aral_init();
  1017. cachestat_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_cachestat_pid_t));
  1018. }
  1019. cachestat_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
  1020. memset(cachestat_hash_values, 0, NETDATA_CACHESTAT_END * sizeof(netdata_idx_t));
  1021. memset(cachestat_counter_aggregated_data, 0, NETDATA_CACHESTAT_END * sizeof(netdata_syscall_stat_t));
  1022. memset(cachestat_counter_publish_aggregated, 0, NETDATA_CACHESTAT_END * sizeof(netdata_publish_syscall_t));
  1023. }
  1024. /*****************************************************************
  1025. *
  1026. * MAIN THREAD
  1027. *
  1028. *****************************************************************/
  1029. /**
  1030. * Update Internal value
  1031. *
  1032. * Update values used during runtime.
  1033. *
  1034. * @return It returns 0 when one of the functions is present and -1 otherwise.
  1035. */
  1036. static int ebpf_cachestat_set_internal_value()
  1037. {
  1038. ebpf_addresses_t address = {.function = NULL, .hash = 0, .addr = 0};
  1039. int i;
  1040. for (i = 0; i < NETDATA_CACHESTAT_ACCOUNT_DIRTY_END ; i++) {
  1041. address.function = account_page[i];
  1042. ebpf_load_addresses(&address, -1);
  1043. if (address.addr)
  1044. break;
  1045. }
  1046. if (!address.addr) {
  1047. error("%s cachestat.", NETDATA_EBPF_DEFAULT_FNT_NOT_FOUND);
  1048. return -1;
  1049. }
  1050. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name = address.function;
  1051. return 0;
  1052. }
  1053. /*
  1054. * Load BPF
  1055. *
  1056. * Load BPF files.
  1057. *
  1058. * @param em the structure with configuration
  1059. */
  1060. static int ebpf_cachestat_load_bpf(ebpf_module_t *em)
  1061. {
  1062. int ret = 0;
  1063. ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU].mode);
  1064. if (em->load & EBPF_LOAD_LEGACY) {
  1065. em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
  1066. if (!em->probe_links) {
  1067. ret = -1;
  1068. }
  1069. }
  1070. #ifdef LIBBPF_MAJOR_VERSION
  1071. else {
  1072. cachestat_bpf_obj = cachestat_bpf__open();
  1073. if (!cachestat_bpf_obj)
  1074. ret = -1;
  1075. else
  1076. ret = ebpf_cachestat_load_and_attach(cachestat_bpf_obj, em);
  1077. }
  1078. #endif
  1079. if (ret)
  1080. error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->thread_name);
  1081. return ret;
  1082. }
  1083. /**
  1084. * Cachestat thread
  1085. *
  1086. * Thread used to make cachestat thread
  1087. *
  1088. * @param ptr a pointer to `struct ebpf_module`
  1089. *
  1090. * @return It always return NULL
  1091. */
  1092. void *ebpf_cachestat_thread(void *ptr)
  1093. {
  1094. netdata_thread_cleanup_push(ebpf_cachestat_exit, ptr);
  1095. ebpf_module_t *em = (ebpf_module_t *)ptr;
  1096. em->maps = cachestat_maps;
  1097. ebpf_update_pid_table(&cachestat_maps[NETDATA_CACHESTAT_PID_STATS], em);
  1098. if (ebpf_cachestat_set_internal_value()) {
  1099. goto endcachestat;
  1100. }
  1101. #ifdef LIBBPF_MAJOR_VERSION
  1102. ebpf_adjust_thread_load(em, default_btf);
  1103. #endif
  1104. if (ebpf_cachestat_load_bpf(em)) {
  1105. goto endcachestat;
  1106. }
  1107. ebpf_cachestat_allocate_global_vectors(em->apps_charts);
  1108. int algorithms[NETDATA_CACHESTAT_END] = {
  1109. NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX
  1110. };
  1111. ebpf_global_labels(cachestat_counter_aggregated_data, cachestat_counter_publish_aggregated,
  1112. cachestat_counter_dimension_name, cachestat_counter_dimension_name,
  1113. algorithms, NETDATA_CACHESTAT_END);
  1114. pthread_mutex_lock(&lock);
  1115. ebpf_update_stats(&plugin_statistics, em);
  1116. ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps);
  1117. ebpf_create_memory_charts(em);
  1118. #ifdef NETDATA_DEV_MODE
  1119. if (ebpf_aral_cachestat_pid)
  1120. ebpf_statistic_create_aral_chart(NETDATA_EBPF_CACHESTAT_ARAL_NAME, em);
  1121. #endif
  1122. pthread_mutex_unlock(&lock);
  1123. cachestat_collector(em);
  1124. endcachestat:
  1125. ebpf_update_disabled_plugin_stats(em);
  1126. netdata_thread_cleanup_pop(1);
  1127. return NULL;
  1128. }