ebpf_cachestat.c 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  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 up the main thread.
  260. *
  261. * @param ptr thread data.
  262. */
  263. static void ebpf_cachestat_cleanup(void *ptr)
  264. {
  265. ebpf_module_t *em = (ebpf_module_t *)ptr;
  266. if (!em->enabled)
  267. return;
  268. heartbeat_t hb;
  269. heartbeat_init(&hb);
  270. uint32_t tick = 2*USEC_PER_MS;
  271. while (!read_thread_closed) {
  272. usec_t dt = heartbeat_next(&hb, tick);
  273. UNUSED(dt);
  274. }
  275. ebpf_cleanup_publish_syscall(cachestat_counter_publish_aggregated);
  276. freez(cachestat_vector);
  277. freez(cachestat_values);
  278. if (probe_links) {
  279. struct bpf_program *prog;
  280. size_t i = 0 ;
  281. bpf_object__for_each_program(prog, objects) {
  282. bpf_link__destroy(probe_links[i]);
  283. i++;
  284. }
  285. if (objects)
  286. bpf_object__close(objects);
  287. }
  288. #ifdef LIBBPF_MAJOR_VERSION
  289. else if (bpf_obj)
  290. cachestat_bpf__destroy(bpf_obj);
  291. #endif
  292. }
  293. /*****************************************************************
  294. *
  295. * COMMON FUNCTIONS
  296. *
  297. *****************************************************************/
  298. /**
  299. * Update publish
  300. *
  301. * Update publish values before to write dimension.
  302. *
  303. * @param out structure that will receive data.
  304. * @param mpa calls for mark_page_accessed during the last second.
  305. * @param mbd calls for mark_buffer_dirty during the last second.
  306. * @param apcl calls for add_to_page_cache_lru during the last second.
  307. * @param apd calls for account_page_dirtied during the last second.
  308. */
  309. void cachestat_update_publish(netdata_publish_cachestat_t *out, uint64_t mpa, uint64_t mbd,
  310. uint64_t apcl, uint64_t apd)
  311. {
  312. // Adapted algorithm from https://github.com/iovisor/bcc/blob/master/tools/cachestat.py#L126-L138
  313. NETDATA_DOUBLE total = (NETDATA_DOUBLE) (((long long)mpa) - ((long long)mbd));
  314. if (total < 0)
  315. total = 0;
  316. NETDATA_DOUBLE misses = (NETDATA_DOUBLE) ( ((long long) apcl) - ((long long) apd) );
  317. if (misses < 0)
  318. misses = 0;
  319. // If hits are < 0, then its possible misses are overestimate due to possibly page cache read ahead adding
  320. // more pages than needed. In this case just assume misses as total and reset hits.
  321. NETDATA_DOUBLE hits = total - misses;
  322. if (hits < 0 ) {
  323. misses = total;
  324. hits = 0;
  325. }
  326. NETDATA_DOUBLE ratio = (total > 0) ? hits/total : 1;
  327. out->ratio = (long long )(ratio*100);
  328. out->hit = (long long)hits;
  329. out->miss = (long long)misses;
  330. }
  331. /**
  332. * Save previous values
  333. *
  334. * Save values used this time.
  335. *
  336. * @param publish
  337. */
  338. static void save_previous_values(netdata_publish_cachestat_t *publish) {
  339. publish->prev.mark_page_accessed = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED];
  340. publish->prev.account_page_dirtied = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED];
  341. publish->prev.add_to_page_cache_lru = cachestat_hash_values[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU];
  342. publish->prev.mark_buffer_dirty = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY];
  343. }
  344. /**
  345. * Calculate statistics
  346. *
  347. * @param publish the structure where we will store the data.
  348. */
  349. static void calculate_stats(netdata_publish_cachestat_t *publish) {
  350. if (!publish->prev.mark_page_accessed) {
  351. save_previous_values(publish);
  352. return;
  353. }
  354. uint64_t mpa = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED] - publish->prev.mark_page_accessed;
  355. uint64_t mbd = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY] - publish->prev.mark_buffer_dirty;
  356. uint64_t apcl = cachestat_hash_values[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU] - publish->prev.add_to_page_cache_lru;
  357. uint64_t apd = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED] - publish->prev.account_page_dirtied;
  358. save_previous_values(publish);
  359. // We are changing the original algorithm to have a smooth ratio.
  360. cachestat_update_publish(publish, mpa, mbd, apcl, apd);
  361. }
  362. /*****************************************************************
  363. *
  364. * APPS
  365. *
  366. *****************************************************************/
  367. /**
  368. * Apps Accumulator
  369. *
  370. * Sum all values read from kernel and store in the first address.
  371. *
  372. * @param out the vector with read values.
  373. */
  374. static void cachestat_apps_accumulator(netdata_cachestat_pid_t *out)
  375. {
  376. int i, end = (running_on_kernel >= NETDATA_KERNEL_V4_15) ? ebpf_nprocs : 1;
  377. netdata_cachestat_pid_t *total = &out[0];
  378. for (i = 1; i < end; i++) {
  379. netdata_cachestat_pid_t *w = &out[i];
  380. total->account_page_dirtied += w->account_page_dirtied;
  381. total->add_to_page_cache_lru += w->add_to_page_cache_lru;
  382. total->mark_buffer_dirty += w->mark_buffer_dirty;
  383. total->mark_page_accessed += w->mark_page_accessed;
  384. }
  385. }
  386. /**
  387. * Save Pid values
  388. *
  389. * Save the current values inside the structure
  390. *
  391. * @param out vector used to plot charts
  392. * @param publish vector with values read from hash tables.
  393. */
  394. static inline void cachestat_save_pid_values(netdata_publish_cachestat_t *out, netdata_cachestat_pid_t *publish)
  395. {
  396. if (!out->current.mark_page_accessed) {
  397. memcpy(&out->current, &publish[0], sizeof(netdata_cachestat_pid_t));
  398. return;
  399. }
  400. memcpy(&out->prev, &out->current, sizeof(netdata_cachestat_pid_t));
  401. memcpy(&out->current, &publish[0], sizeof(netdata_cachestat_pid_t));
  402. }
  403. /**
  404. * Fill PID
  405. *
  406. * Fill PID structures
  407. *
  408. * @param current_pid pid that we are collecting data
  409. * @param out values read from hash tables;
  410. */
  411. static void cachestat_fill_pid(uint32_t current_pid, netdata_cachestat_pid_t *publish)
  412. {
  413. netdata_publish_cachestat_t *curr = cachestat_pid[current_pid];
  414. if (!curr) {
  415. curr = callocz(1, sizeof(netdata_publish_cachestat_t));
  416. cachestat_pid[current_pid] = curr;
  417. cachestat_save_pid_values(curr, publish);
  418. return;
  419. }
  420. cachestat_save_pid_values(curr, publish);
  421. }
  422. /**
  423. * Read APPS table
  424. *
  425. * Read the apps table and store data inside the structure.
  426. */
  427. static void read_apps_table()
  428. {
  429. netdata_cachestat_pid_t *cv = cachestat_vector;
  430. uint32_t key;
  431. struct pid_stat *pids = root_of_pids;
  432. int fd = cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd;
  433. size_t length = sizeof(netdata_cachestat_pid_t)*ebpf_nprocs;
  434. while (pids) {
  435. key = pids->pid;
  436. if (bpf_map_lookup_elem(fd, &key, cv)) {
  437. pids = pids->next;
  438. continue;
  439. }
  440. cachestat_apps_accumulator(cv);
  441. cachestat_fill_pid(key, cv);
  442. // We are cleaning to avoid passing data read from one process to other.
  443. memset(cv, 0, length);
  444. pids = pids->next;
  445. }
  446. }
  447. /**
  448. * Update cgroup
  449. *
  450. * Update cgroup data based in
  451. */
  452. static void ebpf_update_cachestat_cgroup()
  453. {
  454. netdata_cachestat_pid_t *cv = cachestat_vector;
  455. int fd = cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd;
  456. size_t length = sizeof(netdata_cachestat_pid_t) * ebpf_nprocs;
  457. ebpf_cgroup_target_t *ect;
  458. pthread_mutex_lock(&mutex_cgroup_shm);
  459. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  460. struct pid_on_target2 *pids;
  461. for (pids = ect->pids; pids; pids = pids->next) {
  462. int pid = pids->pid;
  463. netdata_cachestat_pid_t *out = &pids->cachestat;
  464. if (likely(cachestat_pid) && cachestat_pid[pid]) {
  465. netdata_publish_cachestat_t *in = cachestat_pid[pid];
  466. memcpy(out, &in->current, sizeof(netdata_cachestat_pid_t));
  467. } else {
  468. memset(cv, 0, length);
  469. if (bpf_map_lookup_elem(fd, &pid, cv)) {
  470. continue;
  471. }
  472. cachestat_apps_accumulator(cv);
  473. memcpy(out, cv, sizeof(netdata_cachestat_pid_t));
  474. }
  475. }
  476. }
  477. pthread_mutex_unlock(&mutex_cgroup_shm);
  478. }
  479. /**
  480. * Create apps charts
  481. *
  482. * Call ebpf_create_chart to create the charts on apps submenu.
  483. *
  484. * @param em a pointer to the structure with the default values.
  485. */
  486. void ebpf_cachestat_create_apps_charts(struct ebpf_module *em, void *ptr)
  487. {
  488. struct target *root = ptr;
  489. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_HIT_RATIO_CHART,
  490. "Hit ratio",
  491. EBPF_COMMON_DIMENSION_PERCENTAGE,
  492. NETDATA_CACHESTAT_SUBMENU,
  493. NETDATA_EBPF_CHART_TYPE_LINE,
  494. 20090,
  495. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  496. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  497. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_DIRTY_CHART,
  498. "Number of dirty pages",
  499. EBPF_CACHESTAT_DIMENSION_PAGE,
  500. NETDATA_CACHESTAT_SUBMENU,
  501. NETDATA_EBPF_CHART_TYPE_STACKED,
  502. 20091,
  503. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  504. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  505. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_HIT_CHART,
  506. "Number of accessed files",
  507. EBPF_CACHESTAT_DIMENSION_HITS,
  508. NETDATA_CACHESTAT_SUBMENU,
  509. NETDATA_EBPF_CHART_TYPE_STACKED,
  510. 20092,
  511. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  512. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  513. ebpf_create_charts_on_apps(NETDATA_CACHESTAT_MISSES_CHART,
  514. "Files out of page cache",
  515. EBPF_CACHESTAT_DIMENSION_MISSES,
  516. NETDATA_CACHESTAT_SUBMENU,
  517. NETDATA_EBPF_CHART_TYPE_STACKED,
  518. 20093,
  519. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  520. root, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  521. }
  522. /*****************************************************************
  523. *
  524. * MAIN LOOP
  525. *
  526. *****************************************************************/
  527. /**
  528. * Read global counter
  529. *
  530. * Read the table with number of calls for all functions
  531. */
  532. static void read_global_table()
  533. {
  534. uint32_t idx;
  535. netdata_idx_t *val = cachestat_hash_values;
  536. netdata_idx_t *stored = cachestat_values;
  537. int fd = cachestat_maps[NETDATA_CACHESTAT_GLOBAL_STATS].map_fd;
  538. for (idx = NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU; idx < NETDATA_CACHESTAT_END; idx++) {
  539. if (!bpf_map_lookup_elem(fd, &idx, stored)) {
  540. int i;
  541. int end = ebpf_nprocs;
  542. netdata_idx_t total = 0;
  543. for (i = 0; i < end; i++)
  544. total += stored[i];
  545. val[idx] = total;
  546. }
  547. }
  548. }
  549. /**
  550. * Socket read hash
  551. *
  552. * This is the thread callback.
  553. * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
  554. *
  555. * @param ptr It is a NULL value for this thread.
  556. *
  557. * @return It always returns NULL.
  558. */
  559. void *ebpf_cachestat_read_hash(void *ptr)
  560. {
  561. read_thread_closed = 0;
  562. heartbeat_t hb;
  563. heartbeat_init(&hb);
  564. ebpf_module_t *em = (ebpf_module_t *)ptr;
  565. usec_t step = NETDATA_LATENCY_CACHESTAT_SLEEP_MS * em->update_every;
  566. while (!close_ebpf_plugin) {
  567. usec_t dt = heartbeat_next(&hb, step);
  568. (void)dt;
  569. read_global_table();
  570. }
  571. read_thread_closed = 1;
  572. return NULL;
  573. }
  574. /**
  575. * Send global
  576. *
  577. * Send global charts to Netdata
  578. */
  579. static void cachestat_send_global(netdata_publish_cachestat_t *publish)
  580. {
  581. calculate_stats(publish);
  582. netdata_publish_syscall_t *ptr = cachestat_counter_publish_aggregated;
  583. ebpf_one_dimension_write_charts(
  584. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_RATIO_CHART, ptr[NETDATA_CACHESTAT_IDX_RATIO].dimension,
  585. publish->ratio);
  586. ebpf_one_dimension_write_charts(
  587. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_DIRTY_CHART, ptr[NETDATA_CACHESTAT_IDX_DIRTY].dimension,
  588. cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY]);
  589. ebpf_one_dimension_write_charts(
  590. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_CHART, ptr[NETDATA_CACHESTAT_IDX_HIT].dimension, publish->hit);
  591. ebpf_one_dimension_write_charts(
  592. NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_MISSES_CHART, ptr[NETDATA_CACHESTAT_IDX_MISS].dimension,
  593. publish->miss);
  594. }
  595. /**
  596. * Cachestat sum PIDs
  597. *
  598. * Sum values for all PIDs associated to a group
  599. *
  600. * @param publish output structure.
  601. * @param root structure with listed IPs
  602. */
  603. void ebpf_cachestat_sum_pids(netdata_publish_cachestat_t *publish, struct pid_on_target *root)
  604. {
  605. memcpy(&publish->prev, &publish->current,sizeof(publish->current));
  606. memset(&publish->current, 0, sizeof(publish->current));
  607. netdata_cachestat_pid_t *dst = &publish->current;
  608. while (root) {
  609. int32_t pid = root->pid;
  610. netdata_publish_cachestat_t *w = cachestat_pid[pid];
  611. if (w) {
  612. netdata_cachestat_pid_t *src = &w->current;
  613. dst->account_page_dirtied += src->account_page_dirtied;
  614. dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
  615. dst->mark_buffer_dirty += src->mark_buffer_dirty;
  616. dst->mark_page_accessed += src->mark_page_accessed;
  617. }
  618. root = root->next;
  619. }
  620. }
  621. /**
  622. * Send data to Netdata calling auxiliary functions.
  623. *
  624. * @param root the target list.
  625. */
  626. void ebpf_cache_send_apps_data(struct target *root)
  627. {
  628. struct target *w;
  629. collected_number value;
  630. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_HIT_RATIO_CHART);
  631. for (w = root; w; w = w->next) {
  632. if (unlikely(w->exposed && w->processes)) {
  633. ebpf_cachestat_sum_pids(&w->cachestat, w->root_pid);
  634. netdata_cachestat_pid_t *current = &w->cachestat.current;
  635. netdata_cachestat_pid_t *prev = &w->cachestat.prev;
  636. uint64_t mpa = current->mark_page_accessed - prev->mark_page_accessed;
  637. uint64_t mbd = current->mark_buffer_dirty - prev->mark_buffer_dirty;
  638. w->cachestat.dirty = mbd;
  639. uint64_t apcl = current->add_to_page_cache_lru - prev->add_to_page_cache_lru;
  640. uint64_t apd = current->account_page_dirtied - prev->account_page_dirtied;
  641. cachestat_update_publish(&w->cachestat, mpa, mbd, apcl, apd);
  642. value = (collected_number) w->cachestat.ratio;
  643. // Here we are using different approach to have a chart more smooth
  644. write_chart_dimension(w->name, value);
  645. }
  646. }
  647. write_end_chart();
  648. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_DIRTY_CHART);
  649. for (w = root; w; w = w->next) {
  650. if (unlikely(w->exposed && w->processes)) {
  651. value = (collected_number) w->cachestat.dirty;
  652. write_chart_dimension(w->name, value);
  653. }
  654. }
  655. write_end_chart();
  656. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_HIT_CHART);
  657. for (w = root; w; w = w->next) {
  658. if (unlikely(w->exposed && w->processes)) {
  659. value = (collected_number) w->cachestat.hit;
  660. write_chart_dimension(w->name, value);
  661. }
  662. }
  663. write_end_chart();
  664. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_CACHESTAT_MISSES_CHART);
  665. for (w = root; w; w = w->next) {
  666. if (unlikely(w->exposed && w->processes)) {
  667. value = (collected_number) w->cachestat.miss;
  668. write_chart_dimension(w->name, value);
  669. }
  670. }
  671. write_end_chart();
  672. }
  673. /**
  674. * Cachestat sum PIDs
  675. *
  676. * Sum values for all PIDs associated to a group
  677. *
  678. * @param publish output structure.
  679. * @param root structure with listed IPs
  680. */
  681. void ebpf_cachestat_sum_cgroup_pids(netdata_publish_cachestat_t *publish, struct pid_on_target2 *root)
  682. {
  683. memcpy(&publish->prev, &publish->current,sizeof(publish->current));
  684. memset(&publish->current, 0, sizeof(publish->current));
  685. netdata_cachestat_pid_t *dst = &publish->current;
  686. while (root) {
  687. netdata_cachestat_pid_t *src = &root->cachestat;
  688. dst->account_page_dirtied += src->account_page_dirtied;
  689. dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
  690. dst->mark_buffer_dirty += src->mark_buffer_dirty;
  691. dst->mark_page_accessed += src->mark_page_accessed;
  692. root = root->next;
  693. }
  694. }
  695. /**
  696. * Calc chart values
  697. *
  698. * Do necessary math to plot charts.
  699. */
  700. void ebpf_cachestat_calc_chart_values()
  701. {
  702. ebpf_cgroup_target_t *ect;
  703. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  704. ebpf_cachestat_sum_cgroup_pids(&ect->publish_cachestat, ect->pids);
  705. netdata_cachestat_pid_t *current = &ect->publish_cachestat.current;
  706. netdata_cachestat_pid_t *prev = &ect->publish_cachestat.prev;
  707. uint64_t mpa = current->mark_page_accessed - prev->mark_page_accessed;
  708. uint64_t mbd = current->mark_buffer_dirty - prev->mark_buffer_dirty;
  709. ect->publish_cachestat.dirty = mbd;
  710. uint64_t apcl = current->add_to_page_cache_lru - prev->add_to_page_cache_lru;
  711. uint64_t apd = current->account_page_dirtied - prev->account_page_dirtied;
  712. cachestat_update_publish(&ect->publish_cachestat, mpa, mbd, apcl, apd);
  713. }
  714. }
  715. /**
  716. * Create Systemd cachestat Charts
  717. *
  718. * Create charts when systemd is enabled
  719. *
  720. * @param update_every value to overwrite the update frequency set by the server.
  721. **/
  722. static void ebpf_create_systemd_cachestat_charts(int update_every)
  723. {
  724. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_HIT_RATIO_CHART,
  725. "Hit ratio",
  726. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_SUBMENU,
  727. NETDATA_EBPF_CHART_TYPE_LINE, 21100,
  728. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  729. NETDATA_SYSTEMD_CACHESTAT_HIT_RATIO_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  730. update_every);
  731. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_DIRTY_CHART,
  732. "Number of dirty pages",
  733. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_SUBMENU,
  734. NETDATA_EBPF_CHART_TYPE_LINE, 21101,
  735. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  736. NETDATA_SYSTEMD_CACHESTAT_MODIFIED_CACHE_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  737. update_every);
  738. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_HIT_CHART, "Number of accessed files",
  739. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_SUBMENU,
  740. NETDATA_EBPF_CHART_TYPE_LINE, 21102,
  741. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  742. NETDATA_SYSTEMD_CACHESTAT_HIT_FILE_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  743. update_every);
  744. ebpf_create_charts_on_systemd(NETDATA_CACHESTAT_MISSES_CHART, "Files out of page cache",
  745. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_SUBMENU,
  746. NETDATA_EBPF_CHART_TYPE_LINE, 21103,
  747. ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
  748. NETDATA_SYSTEMD_CACHESTAT_MISS_FILES_CONTEXT, NETDATA_EBPF_MODULE_NAME_CACHESTAT,
  749. update_every);
  750. }
  751. /**
  752. * Send Cache Stat charts
  753. *
  754. * Send collected data to Netdata.
  755. *
  756. * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned
  757. * otherwise function returns 1 to avoid chart recreation
  758. */
  759. static int ebpf_send_systemd_cachestat_charts()
  760. {
  761. int ret = 1;
  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. } else
  768. ret = 0;
  769. }
  770. write_end_chart();
  771. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_DIRTY_CHART);
  772. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  773. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  774. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.dirty);
  775. }
  776. }
  777. write_end_chart();
  778. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_HIT_CHART);
  779. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  780. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  781. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.hit);
  782. }
  783. }
  784. write_end_chart();
  785. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_MISSES_CHART);
  786. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  787. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  788. write_chart_dimension(ect->name, (long long)ect->publish_cachestat.miss);
  789. }
  790. }
  791. write_end_chart();
  792. return ret;
  793. }
  794. /**
  795. * Send Directory Cache charts
  796. *
  797. * Send collected data to Netdata.
  798. */
  799. static void ebpf_send_specific_cachestat_data(char *type, netdata_publish_cachestat_t *npc)
  800. {
  801. write_begin_chart(type, NETDATA_CACHESTAT_HIT_RATIO_CHART);
  802. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_RATIO].name, (long long)npc->ratio);
  803. write_end_chart();
  804. write_begin_chart(type, NETDATA_CACHESTAT_DIRTY_CHART);
  805. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY].name, (long long)npc->dirty);
  806. write_end_chart();
  807. write_begin_chart(type, NETDATA_CACHESTAT_HIT_CHART);
  808. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT].name, (long long)npc->hit);
  809. write_end_chart();
  810. write_begin_chart(type, NETDATA_CACHESTAT_MISSES_CHART);
  811. write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS].name, (long long)npc->miss);
  812. write_end_chart();
  813. }
  814. /**
  815. * Create specific cache Stat charts
  816. *
  817. * Create charts for cgroup/application.
  818. *
  819. * @param type the chart type.
  820. * @param update_every value to overwrite the update frequency set by the server.
  821. */
  822. static void ebpf_create_specific_cachestat_charts(char *type, int update_every)
  823. {
  824. ebpf_create_chart(type, NETDATA_CACHESTAT_HIT_RATIO_CHART,
  825. "Hit ratio",
  826. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  827. NETDATA_CGROUP_CACHESTAT_HIT_RATIO_CONTEXT,
  828. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5200,
  829. ebpf_create_global_dimension,
  830. cachestat_counter_publish_aggregated, 1, update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  831. ebpf_create_chart(type, NETDATA_CACHESTAT_DIRTY_CHART,
  832. "Number of dirty pages",
  833. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  834. NETDATA_CGROUP_CACHESTAT_MODIFIED_CACHE_CONTEXT,
  835. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5201,
  836. ebpf_create_global_dimension,
  837. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY], 1,
  838. update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  839. ebpf_create_chart(type, NETDATA_CACHESTAT_HIT_CHART,
  840. "Number of accessed files",
  841. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  842. NETDATA_CGROUP_CACHESTAT_HIT_FILES_CONTEXT,
  843. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5202,
  844. ebpf_create_global_dimension,
  845. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT], 1,
  846. update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  847. ebpf_create_chart(type, NETDATA_CACHESTAT_MISSES_CHART,
  848. "Files out of page cache",
  849. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_CGROUP_SUBMENU,
  850. NETDATA_CGROUP_CACHESTAT_MISS_FILES_CONTEXT,
  851. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5203,
  852. ebpf_create_global_dimension,
  853. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS], 1,
  854. update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  855. }
  856. /**
  857. * Obsolete specific cache stat charts
  858. *
  859. * Obsolete charts for cgroup/application.
  860. *
  861. * @param type the chart type.
  862. * @param update_every value to overwrite the update frequency set by the server.
  863. */
  864. static void ebpf_obsolete_specific_cachestat_charts(char *type, int update_every)
  865. {
  866. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_HIT_RATIO_CHART,
  867. "Hit ratio",
  868. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_SUBMENU,
  869. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_HIT_RATIO_CONTEXT,
  870. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5200, update_every);
  871. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_DIRTY_CHART,
  872. "Number of dirty pages",
  873. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_SUBMENU,
  874. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_MODIFIED_CACHE_CONTEXT,
  875. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5201, update_every);
  876. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_HIT_CHART,
  877. "Number of accessed files",
  878. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_SUBMENU,
  879. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_HIT_FILES_CONTEXT,
  880. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5202, update_every);
  881. ebpf_write_chart_obsolete(type, NETDATA_CACHESTAT_MISSES_CHART,
  882. "Files out of page cache",
  883. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_SUBMENU,
  884. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_CACHESTAT_MISS_FILES_CONTEXT,
  885. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5203, update_every);
  886. }
  887. /**
  888. * Send data to Netdata calling auxiliary functions.
  889. *
  890. * @param update_every value to overwrite the update frequency set by the server.
  891. */
  892. void ebpf_cachestat_send_cgroup_data(int update_every)
  893. {
  894. if (!ebpf_cgroup_pids)
  895. return;
  896. pthread_mutex_lock(&mutex_cgroup_shm);
  897. ebpf_cgroup_target_t *ect;
  898. ebpf_cachestat_calc_chart_values();
  899. int has_systemd = shm_ebpf_cgroup.header->systemd_enabled;
  900. if (has_systemd) {
  901. static int systemd_charts = 0;
  902. if (!systemd_charts) {
  903. ebpf_create_systemd_cachestat_charts(update_every);
  904. systemd_charts = 1;
  905. }
  906. systemd_charts = ebpf_send_systemd_cachestat_charts();
  907. }
  908. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  909. if (ect->systemd)
  910. continue;
  911. if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART) && ect->updated) {
  912. ebpf_create_specific_cachestat_charts(ect->name, update_every);
  913. ect->flags |= NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART;
  914. }
  915. if (ect->flags & NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART) {
  916. if (ect->updated) {
  917. ebpf_send_specific_cachestat_data(ect->name, &ect->publish_cachestat);
  918. } else {
  919. ebpf_obsolete_specific_cachestat_charts(ect->name, update_every);
  920. ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_CACHESTAT_CHART;
  921. }
  922. }
  923. }
  924. pthread_mutex_unlock(&mutex_cgroup_shm);
  925. }
  926. /**
  927. * Main loop for this collector.
  928. */
  929. static void cachestat_collector(ebpf_module_t *em)
  930. {
  931. cachestat_threads.thread = mallocz(sizeof(netdata_thread_t));
  932. cachestat_threads.start_routine = ebpf_cachestat_read_hash;
  933. netdata_thread_create(cachestat_threads.thread, cachestat_threads.name, NETDATA_THREAD_OPTION_JOINABLE,
  934. ebpf_cachestat_read_hash, em);
  935. netdata_publish_cachestat_t publish;
  936. memset(&publish, 0, sizeof(publish));
  937. int apps = em->apps_charts;
  938. int cgroups = em->cgroup_charts;
  939. int update_every = em->update_every;
  940. int counter = update_every - 1;
  941. while (!close_ebpf_plugin) {
  942. pthread_mutex_lock(&collect_data_mutex);
  943. pthread_cond_wait(&collect_data_cond_var, &collect_data_mutex);
  944. if (++counter == update_every) {
  945. counter = 0;
  946. if (apps)
  947. read_apps_table();
  948. if (cgroups)
  949. ebpf_update_cachestat_cgroup();
  950. pthread_mutex_lock(&lock);
  951. cachestat_send_global(&publish);
  952. if (apps)
  953. ebpf_cache_send_apps_data(apps_groups_root_target);
  954. if (cgroups)
  955. ebpf_cachestat_send_cgroup_data(update_every);
  956. pthread_mutex_unlock(&lock);
  957. }
  958. pthread_mutex_unlock(&collect_data_mutex);
  959. }
  960. }
  961. /*****************************************************************
  962. *
  963. * INITIALIZE THREAD
  964. *
  965. *****************************************************************/
  966. /**
  967. * Create global charts
  968. *
  969. * Call ebpf_create_chart to create the charts for the collector.
  970. *
  971. * @param em a pointer to `struct ebpf_module`
  972. */
  973. static void ebpf_create_memory_charts(ebpf_module_t *em)
  974. {
  975. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_RATIO_CHART,
  976. "Hit ratio",
  977. EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_CACHESTAT_SUBMENU,
  978. NULL,
  979. NETDATA_EBPF_CHART_TYPE_LINE,
  980. 21100,
  981. ebpf_create_global_dimension,
  982. cachestat_counter_publish_aggregated, 1, em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  983. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_DIRTY_CHART,
  984. "Number of dirty pages",
  985. EBPF_CACHESTAT_DIMENSION_PAGE, NETDATA_CACHESTAT_SUBMENU,
  986. NULL,
  987. NETDATA_EBPF_CHART_TYPE_LINE,
  988. 21101,
  989. ebpf_create_global_dimension,
  990. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY], 1,
  991. em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  992. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_HIT_CHART,
  993. "Number of accessed files",
  994. EBPF_CACHESTAT_DIMENSION_HITS, NETDATA_CACHESTAT_SUBMENU,
  995. NULL,
  996. NETDATA_EBPF_CHART_TYPE_LINE,
  997. 21102,
  998. ebpf_create_global_dimension,
  999. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT], 1,
  1000. em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  1001. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_CACHESTAT_MISSES_CHART,
  1002. "Files out of page cache",
  1003. EBPF_CACHESTAT_DIMENSION_MISSES, NETDATA_CACHESTAT_SUBMENU,
  1004. NULL,
  1005. NETDATA_EBPF_CHART_TYPE_LINE,
  1006. 21103,
  1007. ebpf_create_global_dimension,
  1008. &cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS], 1,
  1009. em->update_every, NETDATA_EBPF_MODULE_NAME_CACHESTAT);
  1010. fflush(stdout);
  1011. }
  1012. /**
  1013. * Allocate vectors used with this thread.
  1014. *
  1015. * We are not testing the return, because callocz does this and shutdown the software
  1016. * case it was not possible to allocate.
  1017. *
  1018. * @param apps is apps enabled?
  1019. */
  1020. static void ebpf_cachestat_allocate_global_vectors(int apps)
  1021. {
  1022. if (apps)
  1023. cachestat_pid = callocz((size_t)pid_max, sizeof(netdata_publish_cachestat_t *));
  1024. cachestat_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_cachestat_pid_t));
  1025. cachestat_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
  1026. memset(cachestat_hash_values, 0, NETDATA_CACHESTAT_END * sizeof(netdata_idx_t));
  1027. memset(cachestat_counter_aggregated_data, 0, NETDATA_CACHESTAT_END * sizeof(netdata_syscall_stat_t));
  1028. memset(cachestat_counter_publish_aggregated, 0, NETDATA_CACHESTAT_END * sizeof(netdata_publish_syscall_t));
  1029. }
  1030. /*****************************************************************
  1031. *
  1032. * MAIN THREAD
  1033. *
  1034. *****************************************************************/
  1035. /**
  1036. * Update Internal value
  1037. *
  1038. * Update values used during runtime.
  1039. */
  1040. static void ebpf_cachestat_set_internal_value()
  1041. {
  1042. static char *account_page[] = { "account_page_dirtied", "__set_page_dirty", "__folio_mark_dirty" };
  1043. if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_16)
  1044. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name = account_page[NETDATA_CACHESTAT_FOLIO_DIRTY];
  1045. else if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_15)
  1046. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name = account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY];
  1047. else
  1048. cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name = account_page[NETDATA_CACHESTAT_ACCOUNT_PAGE_DIRTY];
  1049. }
  1050. /*
  1051. * Load BPF
  1052. *
  1053. * Load BPF files.
  1054. *
  1055. * @param em the structure with configuration
  1056. */
  1057. static int ebpf_cachestat_load_bpf(ebpf_module_t *em)
  1058. {
  1059. int ret = 0;
  1060. if (em->load == EBPF_LOAD_LEGACY) {
  1061. probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &objects);
  1062. if (!probe_links) {
  1063. ret = -1;
  1064. }
  1065. }
  1066. #ifdef LIBBPF_MAJOR_VERSION
  1067. else {
  1068. bpf_obj = cachestat_bpf__open();
  1069. if (!bpf_obj)
  1070. ret = -1;
  1071. else
  1072. ret = ebpf_cachestat_load_and_attach(bpf_obj, em);
  1073. }
  1074. #endif
  1075. if (ret)
  1076. error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->thread_name);
  1077. return ret;
  1078. }
  1079. /**
  1080. * Cachestat thread
  1081. *
  1082. * Thread used to make cachestat thread
  1083. *
  1084. * @param ptr a pointer to `struct ebpf_module`
  1085. *
  1086. * @return It always return NULL
  1087. */
  1088. void *ebpf_cachestat_thread(void *ptr)
  1089. {
  1090. netdata_thread_cleanup_push(ebpf_cachestat_cleanup, ptr);
  1091. ebpf_module_t *em = (ebpf_module_t *)ptr;
  1092. em->maps = cachestat_maps;
  1093. ebpf_update_pid_table(&cachestat_maps[NETDATA_CACHESTAT_PID_STATS], em);
  1094. if (!em->enabled)
  1095. goto endcachestat;
  1096. ebpf_cachestat_set_internal_value();
  1097. #ifdef LIBBPF_MAJOR_VERSION
  1098. ebpf_adjust_thread_load(em, default_btf);
  1099. #endif
  1100. if (ebpf_cachestat_load_bpf(em)) {
  1101. em->enabled = CONFIG_BOOLEAN_NO;
  1102. goto endcachestat;
  1103. }
  1104. ebpf_cachestat_allocate_global_vectors(em->apps_charts);
  1105. int algorithms[NETDATA_CACHESTAT_END] = {
  1106. NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX
  1107. };
  1108. ebpf_global_labels(cachestat_counter_aggregated_data, cachestat_counter_publish_aggregated,
  1109. cachestat_counter_dimension_name, cachestat_counter_dimension_name,
  1110. algorithms, NETDATA_CACHESTAT_END);
  1111. pthread_mutex_lock(&lock);
  1112. ebpf_update_stats(&plugin_statistics, em);
  1113. ebpf_create_memory_charts(em);
  1114. pthread_mutex_unlock(&lock);
  1115. cachestat_collector(em);
  1116. endcachestat:
  1117. if (!em->enabled)
  1118. ebpf_update_disabled_plugin_stats(em);
  1119. netdata_thread_cleanup_pop(1);
  1120. return NULL;
  1121. }