ebpf_cachestat.c 50 KB

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