ebpf_cachestat.c 49 KB

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