ebpf_cachestat.c 48 KB

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