ebpf_cachestat.c 49 KB

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