ebpf_swap.c 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "ebpf.h"
  3. #include "ebpf_swap.h"
  4. static char *swap_dimension_name[NETDATA_SWAP_END] = { "read", "write" };
  5. static netdata_syscall_stat_t swap_aggregated_data[NETDATA_SWAP_END];
  6. static netdata_publish_syscall_t swap_publish_aggregated[NETDATA_SWAP_END];
  7. static netdata_idx_t swap_hash_values[NETDATA_SWAP_END];
  8. static netdata_idx_t *swap_values = NULL;
  9. netdata_publish_swap_t *swap_vector = NULL;
  10. struct config swap_config = { .first_section = NULL,
  11. .last_section = NULL,
  12. .mutex = NETDATA_MUTEX_INITIALIZER,
  13. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  14. .rwlock = AVL_LOCK_INITIALIZER } };
  15. static ebpf_local_maps_t swap_maps[] = {{.name = "tbl_pid_swap", .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
  16. .user_input = 0,
  17. .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID,
  18. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  19. #ifdef LIBBPF_MAJOR_VERSION
  20. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  21. #endif
  22. },
  23. {.name = "swap_ctrl", .internal_input = NETDATA_CONTROLLER_END,
  24. .user_input = 0,
  25. .type = NETDATA_EBPF_MAP_CONTROLLER,
  26. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  27. #ifdef LIBBPF_MAJOR_VERSION
  28. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  29. #endif
  30. },
  31. {.name = "tbl_swap", .internal_input = NETDATA_SWAP_END,
  32. .user_input = 0,
  33. .type = NETDATA_EBPF_MAP_STATIC,
  34. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  35. #ifdef LIBBPF_MAJOR_VERSION
  36. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  37. #endif
  38. },
  39. {.name = NULL, .internal_input = 0, .user_input = 0,
  40. #ifdef LIBBPF_MAJOR_VERSION
  41. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  42. #endif
  43. }};
  44. netdata_ebpf_targets_t swap_targets[] = { {.name = "swap_readpage", .mode = EBPF_LOAD_TRAMPOLINE},
  45. {.name = "swap_writepage", .mode = EBPF_LOAD_TRAMPOLINE},
  46. {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
  47. #ifdef LIBBPF_MAJOR_VERSION
  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_swap_disable_probe(struct swap_bpf *obj)
  56. {
  57. bpf_program__set_autoload(obj->progs.netdata_swap_readpage_probe, false);
  58. bpf_program__set_autoload(obj->progs.netdata_swap_writepage_probe, false);
  59. }
  60. /*
  61. * Disable trampoline
  62. *
  63. * Disable all trampoline to use exclusively another method.
  64. *
  65. * @param obj is the main structure for bpf objects.
  66. */
  67. static void ebpf_swap_disable_trampoline(struct swap_bpf *obj)
  68. {
  69. bpf_program__set_autoload(obj->progs.netdata_swap_readpage_fentry, false);
  70. bpf_program__set_autoload(obj->progs.netdata_swap_writepage_fentry, false);
  71. bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false);
  72. }
  73. /**
  74. * Set trampoline target
  75. *
  76. * Set the targets we will monitor.
  77. *
  78. * @param obj is the main structure for bpf objects.
  79. */
  80. static void ebpf_swap_set_trampoline_target(struct swap_bpf *obj)
  81. {
  82. bpf_program__set_attach_target(obj->progs.netdata_swap_readpage_fentry, 0,
  83. swap_targets[NETDATA_KEY_SWAP_READPAGE_CALL].name);
  84. bpf_program__set_attach_target(obj->progs.netdata_swap_writepage_fentry, 0,
  85. swap_targets[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name);
  86. bpf_program__set_attach_target(obj->progs.netdata_release_task_fentry, 0,
  87. EBPF_COMMON_FNCT_CLEAN_UP);
  88. }
  89. /**
  90. * Mount Attach Probe
  91. *
  92. * Attach probes to target
  93. *
  94. * @param obj is the main structure for bpf objects.
  95. *
  96. * @return It returns 0 on success and -1 otherwise.
  97. */
  98. static int ebpf_swap_attach_kprobe(struct swap_bpf *obj)
  99. {
  100. obj->links.netdata_swap_readpage_probe = bpf_program__attach_kprobe(obj->progs.netdata_swap_readpage_probe,
  101. false,
  102. swap_targets[NETDATA_KEY_SWAP_READPAGE_CALL].name);
  103. int ret = libbpf_get_error(obj->links.netdata_swap_readpage_probe);
  104. if (ret)
  105. return -1;
  106. obj->links.netdata_swap_writepage_probe = bpf_program__attach_kprobe(obj->progs.netdata_swap_writepage_probe,
  107. false,
  108. swap_targets[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name);
  109. ret = libbpf_get_error(obj->links.netdata_swap_writepage_probe);
  110. if (ret)
  111. return -1;
  112. return 0;
  113. }
  114. /**
  115. * Set hash tables
  116. *
  117. * Set the values for maps according the value given by kernel.
  118. *
  119. * @param obj is the main structure for bpf objects.
  120. */
  121. static void ebpf_swap_set_hash_tables(struct swap_bpf *obj)
  122. {
  123. swap_maps[NETDATA_PID_SWAP_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_pid_swap);
  124. swap_maps[NETDATA_SWAP_CONTROLLER].map_fd = bpf_map__fd(obj->maps.swap_ctrl);
  125. swap_maps[NETDATA_SWAP_GLOBAL_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_swap);
  126. }
  127. /**
  128. * Adjust Map
  129. *
  130. * Resize maps according input from users.
  131. *
  132. * @param obj is the main structure for bpf objects.
  133. * @param em structure with configuration
  134. */
  135. static void ebpf_swap_adjust_map(struct swap_bpf *obj, ebpf_module_t *em)
  136. {
  137. ebpf_update_map_size(obj->maps.tbl_pid_swap, &swap_maps[NETDATA_PID_SWAP_TABLE],
  138. em, bpf_map__name(obj->maps.tbl_pid_swap));
  139. ebpf_update_map_type(obj->maps.tbl_pid_swap, &swap_maps[NETDATA_PID_SWAP_TABLE]);
  140. ebpf_update_map_type(obj->maps.tbl_swap, &swap_maps[NETDATA_SWAP_GLOBAL_TABLE]);
  141. ebpf_update_map_type(obj->maps.swap_ctrl, &swap_maps[NETDATA_SWAP_CONTROLLER]);
  142. }
  143. /**
  144. * Disable Release Task
  145. *
  146. * Disable release task when apps is not enabled.
  147. *
  148. * @param obj is the main structure for bpf objects.
  149. */
  150. static void ebpf_swap_disable_release_task(struct swap_bpf *obj)
  151. {
  152. bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false);
  153. }
  154. /**
  155. * Load and attach
  156. *
  157. * Load and attach the eBPF code in kernel.
  158. *
  159. * @param obj is the main structure for bpf objects.
  160. * @param em structure with configuration
  161. *
  162. * @return it returns 0 on success and -1 otherwise
  163. */
  164. static inline int ebpf_swap_load_and_attach(struct swap_bpf *obj, ebpf_module_t *em)
  165. {
  166. netdata_ebpf_targets_t *mt = em->targets;
  167. netdata_ebpf_program_loaded_t test = mt[NETDATA_KEY_SWAP_READPAGE_CALL].mode;
  168. if (test == EBPF_LOAD_TRAMPOLINE) {
  169. ebpf_swap_disable_probe(obj);
  170. ebpf_swap_set_trampoline_target(obj);
  171. } else {
  172. ebpf_swap_disable_trampoline(obj);
  173. }
  174. ebpf_swap_adjust_map(obj, em);
  175. if (!em->apps_charts && !em->cgroup_charts)
  176. ebpf_swap_disable_release_task(obj);
  177. int ret = swap_bpf__load(obj);
  178. if (ret) {
  179. return ret;
  180. }
  181. ret = (test == EBPF_LOAD_TRAMPOLINE) ? swap_bpf__attach(obj) : ebpf_swap_attach_kprobe(obj);
  182. if (!ret) {
  183. ebpf_swap_set_hash_tables(obj);
  184. ebpf_update_controller(swap_maps[NETDATA_SWAP_CONTROLLER].map_fd, em);
  185. }
  186. return ret;
  187. }
  188. #endif
  189. /*****************************************************************
  190. *
  191. * FUNCTIONS TO CLOSE THE THREAD
  192. *
  193. *****************************************************************/
  194. static void ebpf_obsolete_specific_swap_charts(char *type, int update_every);
  195. /**
  196. * Obsolete services
  197. *
  198. * Obsolete all service charts created
  199. *
  200. * @param em a pointer to `struct ebpf_module`
  201. */
  202. static void ebpf_obsolete_swap_services(ebpf_module_t *em)
  203. {
  204. ebpf_write_chart_obsolete(NETDATA_SERVICE_FAMILY,
  205. NETDATA_MEM_SWAP_READ_CHART,
  206. "",
  207. "Calls to function swap_readpage.",
  208. EBPF_COMMON_DIMENSION_CALL,
  209. NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  210. NETDATA_EBPF_CHART_TYPE_LINE,
  211. NETDATA_CGROUP_SWAP_READ_CONTEXT,
  212. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5100,
  213. em->update_every);
  214. ebpf_write_chart_obsolete(NETDATA_SERVICE_FAMILY,
  215. NETDATA_MEM_SWAP_WRITE_CHART,
  216. "",
  217. "Calls to function swap_writepage.",
  218. EBPF_COMMON_DIMENSION_CALL,
  219. NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  220. NETDATA_EBPF_CHART_TYPE_LINE,
  221. NETDATA_CGROUP_SWAP_WRITE_CONTEXT,
  222. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5101,
  223. em->update_every);
  224. }
  225. /**
  226. * Obsolete cgroup chart
  227. *
  228. * Send obsolete for all charts created before to close.
  229. *
  230. * @param em a pointer to `struct ebpf_module`
  231. */
  232. static inline void ebpf_obsolete_swap_cgroup_charts(ebpf_module_t *em) {
  233. pthread_mutex_lock(&mutex_cgroup_shm);
  234. ebpf_obsolete_swap_services(em);
  235. ebpf_cgroup_target_t *ect;
  236. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  237. if (ect->systemd)
  238. continue;
  239. ebpf_obsolete_specific_swap_charts(ect->name, em->update_every);
  240. }
  241. pthread_mutex_unlock(&mutex_cgroup_shm);
  242. }
  243. /**
  244. * Obsolette apps charts
  245. *
  246. * Obsolete apps charts.
  247. *
  248. * @param em a pointer to the structure with the default values.
  249. */
  250. void ebpf_obsolete_swap_apps_charts(struct ebpf_module *em)
  251. {
  252. struct ebpf_target *w;
  253. int update_every = em->update_every;
  254. for (w = apps_groups_root_target; w; w = w->next) {
  255. if (unlikely(!(w->charts_created & (1<<EBPF_MODULE_SWAP_IDX))))
  256. continue;
  257. ebpf_write_chart_obsolete(NETDATA_APP_FAMILY,
  258. w->clean_name,
  259. "_ebpf_call_swap_readpage",
  260. "Calls to function swap_readpage.",
  261. EBPF_COMMON_DIMENSION_CALL,
  262. NETDATA_EBPF_MEMORY_GROUP,
  263. NETDATA_EBPF_CHART_TYPE_STACKED,
  264. "app.ebpf_call_swap_readpage",
  265. 20070,
  266. update_every);
  267. ebpf_write_chart_obsolete(NETDATA_APP_FAMILY,
  268. w->clean_name,
  269. "_ebpf_call_swap_writepage",
  270. "Calls to function swap_writepage.",
  271. EBPF_COMMON_DIMENSION_CALL,
  272. NETDATA_EBPF_MEMORY_GROUP,
  273. NETDATA_EBPF_CHART_TYPE_STACKED,
  274. "app.ebpf_call_swap_writepage",
  275. 20071,
  276. update_every);
  277. w->charts_created &= ~(1<<EBPF_MODULE_SWAP_IDX);
  278. }
  279. }
  280. /**
  281. * Obsolete global
  282. *
  283. * Obsolete global charts created by thread.
  284. *
  285. * @param em a pointer to `struct ebpf_module`
  286. */
  287. static void ebpf_obsolete_swap_global(ebpf_module_t *em)
  288. {
  289. ebpf_write_chart_obsolete(NETDATA_EBPF_MEMORY_GROUP,
  290. NETDATA_MEM_SWAP_CHART,
  291. "",
  292. "Calls to access swap memory",
  293. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_SWAP_SUBMENU,
  294. NETDATA_EBPF_CHART_TYPE_LINE,
  295. NULL,
  296. NETDATA_CHART_PRIO_MEM_SWAP_CALLS,
  297. em->update_every);
  298. }
  299. /**
  300. * Swap exit
  301. *
  302. * Cancel thread and exit.
  303. *
  304. * @param ptr thread data.
  305. */
  306. static void ebpf_swap_exit(void *ptr)
  307. {
  308. ebpf_module_t *em = (ebpf_module_t *)ptr;
  309. if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
  310. pthread_mutex_lock(&lock);
  311. if (em->cgroup_charts) {
  312. ebpf_obsolete_swap_cgroup_charts(em);
  313. fflush(stdout);
  314. }
  315. if (em->apps_charts & NETDATA_EBPF_APPS_FLAG_CHART_CREATED) {
  316. ebpf_obsolete_swap_apps_charts(em);
  317. }
  318. ebpf_obsolete_swap_global(em);
  319. fflush(stdout);
  320. pthread_mutex_unlock(&lock);
  321. }
  322. ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
  323. #ifdef LIBBPF_MAJOR_VERSION
  324. if (bpf_obj) {
  325. swap_bpf__destroy(bpf_obj);
  326. bpf_obj = NULL;
  327. }
  328. #endif
  329. if (em->objects) {
  330. ebpf_unload_legacy_code(em->objects, em->probe_links);
  331. em->objects = NULL;
  332. em->probe_links = NULL;
  333. }
  334. pthread_mutex_lock(&ebpf_exit_cleanup);
  335. em->enabled = NETDATA_THREAD_EBPF_STOPPED;
  336. ebpf_update_stats(&plugin_statistics, em);
  337. pthread_mutex_unlock(&ebpf_exit_cleanup);
  338. }
  339. /*****************************************************************
  340. *
  341. * COLLECTOR THREAD
  342. *
  343. *****************************************************************/
  344. /**
  345. * Apps Accumulator
  346. *
  347. * Sum all values read from kernel and store in the first address.
  348. *
  349. * @param out the vector with read values.
  350. * @param maps_per_core do I need to read all cores?
  351. */
  352. static void swap_apps_accumulator(netdata_publish_swap_t *out, int maps_per_core)
  353. {
  354. int i, end = (maps_per_core) ? ebpf_nprocs : 1;
  355. netdata_publish_swap_t *total = &out[0];
  356. for (i = 1; i < end; i++) {
  357. netdata_publish_swap_t *w = &out[i];
  358. total->write += w->write;
  359. total->read += w->read;
  360. }
  361. }
  362. /**
  363. * Fill PID
  364. *
  365. * Fill PID structures
  366. *
  367. * @param current_pid pid that we are collecting data
  368. * @param out values read from hash tables;
  369. */
  370. static void swap_fill_pid(uint32_t current_pid, netdata_publish_swap_t *publish)
  371. {
  372. netdata_publish_swap_t *curr = swap_pid[current_pid];
  373. if (!curr) {
  374. curr = callocz(1, sizeof(netdata_publish_swap_t));
  375. swap_pid[current_pid] = curr;
  376. }
  377. memcpy(curr, publish, sizeof(netdata_publish_swap_t));
  378. }
  379. /**
  380. * Update cgroup
  381. *
  382. * Update cgroup data based in
  383. *
  384. * @param maps_per_core do I need to read all cores?
  385. */
  386. static void ebpf_update_swap_cgroup(int maps_per_core)
  387. {
  388. ebpf_cgroup_target_t *ect ;
  389. netdata_publish_swap_t *cv = swap_vector;
  390. int fd = swap_maps[NETDATA_PID_SWAP_TABLE].map_fd;
  391. size_t length = sizeof(netdata_publish_swap_t);
  392. if (maps_per_core)
  393. length *= ebpf_nprocs;
  394. pthread_mutex_lock(&mutex_cgroup_shm);
  395. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  396. struct pid_on_target2 *pids;
  397. for (pids = ect->pids; pids; pids = pids->next) {
  398. int pid = pids->pid;
  399. netdata_publish_swap_t *out = &pids->swap;
  400. if (likely(swap_pid) && swap_pid[pid]) {
  401. netdata_publish_swap_t *in = swap_pid[pid];
  402. memcpy(out, in, sizeof(netdata_publish_swap_t));
  403. } else {
  404. memset(cv, 0, length);
  405. if (!bpf_map_lookup_elem(fd, &pid, cv)) {
  406. swap_apps_accumulator(cv, maps_per_core);
  407. memcpy(out, cv, sizeof(netdata_publish_swap_t));
  408. // We are cleaning to avoid passing data read from one process to other.
  409. memset(cv, 0, length);
  410. }
  411. }
  412. }
  413. }
  414. pthread_mutex_unlock(&mutex_cgroup_shm);
  415. }
  416. /**
  417. * Read APPS table
  418. *
  419. * Read the apps table and store data inside the structure.
  420. *
  421. * @param maps_per_core do I need to read all cores?
  422. */
  423. static void read_swap_apps_table(int maps_per_core)
  424. {
  425. netdata_publish_swap_t *cv = swap_vector;
  426. uint32_t key;
  427. struct ebpf_pid_stat *pids = ebpf_root_of_pids;
  428. int fd = swap_maps[NETDATA_PID_SWAP_TABLE].map_fd;
  429. size_t length = sizeof(netdata_publish_swap_t);
  430. if (maps_per_core)
  431. length *= ebpf_nprocs;
  432. while (pids) {
  433. key = pids->pid;
  434. if (bpf_map_lookup_elem(fd, &key, cv)) {
  435. pids = pids->next;
  436. continue;
  437. }
  438. swap_apps_accumulator(cv, maps_per_core);
  439. swap_fill_pid(key, cv);
  440. // We are cleaning to avoid passing data read from one process to other.
  441. memset(cv, 0, length);
  442. pids = pids->next;
  443. }
  444. }
  445. /**
  446. * Send global
  447. *
  448. * Send global charts to Netdata
  449. */
  450. static void swap_send_global()
  451. {
  452. write_io_chart(NETDATA_MEM_SWAP_CHART, NETDATA_EBPF_MEMORY_GROUP,
  453. swap_publish_aggregated[NETDATA_KEY_SWAP_WRITEPAGE_CALL].dimension,
  454. (long long) swap_hash_values[NETDATA_KEY_SWAP_WRITEPAGE_CALL],
  455. swap_publish_aggregated[NETDATA_KEY_SWAP_READPAGE_CALL].dimension,
  456. (long long) swap_hash_values[NETDATA_KEY_SWAP_READPAGE_CALL]);
  457. }
  458. /**
  459. * Read global counter
  460. *
  461. * Read the table with number of calls to all functions
  462. *
  463. * @param stats vector used to read data from control table.
  464. * @param maps_per_core do I need to read all cores?
  465. */
  466. static void ebpf_swap_read_global_table(netdata_idx_t *stats, int maps_per_core)
  467. {
  468. ebpf_read_global_table_stats(swap_hash_values,
  469. swap_values,
  470. swap_maps[NETDATA_SWAP_GLOBAL_TABLE].map_fd,
  471. maps_per_core,
  472. NETDATA_KEY_SWAP_READPAGE_CALL,
  473. NETDATA_SWAP_END);
  474. ebpf_read_global_table_stats(stats,
  475. swap_values,
  476. swap_maps[NETDATA_SWAP_CONTROLLER].map_fd,
  477. maps_per_core,
  478. NETDATA_CONTROLLER_PID_TABLE_ADD,
  479. NETDATA_CONTROLLER_END);
  480. }
  481. /**
  482. * Sum PIDs
  483. *
  484. * Sum values for all targets.
  485. *
  486. * @param swap
  487. * @param root
  488. */
  489. static void ebpf_swap_sum_pids(netdata_publish_swap_t *swap, struct ebpf_pid_on_target *root)
  490. {
  491. uint64_t local_read = 0;
  492. uint64_t local_write = 0;
  493. while (root) {
  494. int32_t pid = root->pid;
  495. netdata_publish_swap_t *w = swap_pid[pid];
  496. if (w) {
  497. local_write += w->write;
  498. local_read += w->read;
  499. }
  500. root = root->next;
  501. }
  502. // These conditions were added, because we are using incremental algorithm
  503. swap->write = (local_write >= swap->write) ? local_write : swap->write;
  504. swap->read = (local_read >= swap->read) ? local_read : swap->read;
  505. }
  506. /**
  507. * Send data to Netdata calling auxiliary functions.
  508. *
  509. * @param root the target list.
  510. */
  511. void ebpf_swap_send_apps_data(struct ebpf_target *root)
  512. {
  513. struct ebpf_target *w;
  514. for (w = root; w; w = w->next) {
  515. if (unlikely(!(w->charts_created & (1<<EBPF_MODULE_SWAP_IDX))))
  516. continue;
  517. ebpf_swap_sum_pids(&w->swap, w->root_pid);
  518. ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_call_swap_readpage");
  519. write_chart_dimension("calls", (long long) w->swap.read);
  520. ebpf_write_end_chart();
  521. ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_call_swap_writepage");
  522. write_chart_dimension("calls", (long long) w->swap.write);
  523. ebpf_write_end_chart();
  524. }
  525. }
  526. /**
  527. * Sum PIDs
  528. *
  529. * Sum values for all targets.
  530. *
  531. * @param swap
  532. * @param root
  533. */
  534. static void ebpf_swap_sum_cgroup_pids(netdata_publish_swap_t *swap, struct pid_on_target2 *pids)
  535. {
  536. uint64_t local_read = 0;
  537. uint64_t local_write = 0;
  538. while (pids) {
  539. netdata_publish_swap_t *w = &pids->swap;
  540. local_write += w->write;
  541. local_read += w->read;
  542. pids = pids->next;
  543. }
  544. // These conditions were added, because we are using incremental algorithm
  545. swap->write = (local_write >= swap->write) ? local_write : swap->write;
  546. swap->read = (local_read >= swap->read) ? local_read : swap->read;
  547. }
  548. /**
  549. * Send Systemd charts
  550. *
  551. * Send collected data to Netdata.
  552. */
  553. static void ebpf_send_systemd_swap_charts()
  554. {
  555. ebpf_cgroup_target_t *ect;
  556. ebpf_write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_MEM_SWAP_READ_CHART, "");
  557. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  558. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  559. write_chart_dimension(ect->name, (long long) ect->publish_systemd_swap.read);
  560. }
  561. }
  562. ebpf_write_end_chart();
  563. ebpf_write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_MEM_SWAP_WRITE_CHART, "");
  564. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  565. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  566. write_chart_dimension(ect->name, (long long) ect->publish_systemd_swap.write);
  567. }
  568. }
  569. ebpf_write_end_chart();
  570. }
  571. /**
  572. * Create specific swap charts
  573. *
  574. * Create charts for cgroup/application.
  575. *
  576. * @param type the chart type.
  577. * @param update_every value to overwrite the update frequency set by the server.
  578. */
  579. static void ebpf_create_specific_swap_charts(char *type, int update_every)
  580. {
  581. ebpf_create_chart(type, NETDATA_MEM_SWAP_READ_CHART,
  582. "Calls to function swap_readpage.",
  583. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  584. NETDATA_CGROUP_SWAP_READ_CONTEXT, NETDATA_EBPF_CHART_TYPE_LINE,
  585. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5100,
  586. ebpf_create_global_dimension,
  587. swap_publish_aggregated, 1, update_every, NETDATA_EBPF_MODULE_NAME_SWAP);
  588. ebpf_create_chart(type, NETDATA_MEM_SWAP_WRITE_CHART,
  589. "Calls to function swap_writepage.",
  590. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  591. NETDATA_CGROUP_SWAP_WRITE_CONTEXT, NETDATA_EBPF_CHART_TYPE_LINE,
  592. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5101,
  593. ebpf_create_global_dimension,
  594. &swap_publish_aggregated[NETDATA_KEY_SWAP_WRITEPAGE_CALL], 1,
  595. update_every, NETDATA_EBPF_MODULE_NAME_SWAP);
  596. }
  597. /**
  598. * Create specific swap charts
  599. *
  600. * Create charts for cgroup/application.
  601. *
  602. * @param type the chart type.
  603. * @param update_every value to overwrite the update frequency set by the server.
  604. */
  605. static void ebpf_obsolete_specific_swap_charts(char *type, int update_every)
  606. {
  607. ebpf_write_chart_obsolete(type, NETDATA_MEM_SWAP_READ_CHART, "", "Calls to function swap_readpage.",
  608. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  609. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_SWAP_READ_CONTEXT,
  610. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5100, update_every);
  611. ebpf_write_chart_obsolete(type, NETDATA_MEM_SWAP_WRITE_CHART, "", "Calls to function swap_writepage.",
  612. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  613. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_SWAP_WRITE_CONTEXT,
  614. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5101, update_every);
  615. }
  616. /*
  617. * Send Specific Swap data
  618. *
  619. * Send data for specific cgroup/apps.
  620. *
  621. * @param type chart type
  622. * @param values structure with values that will be sent to netdata
  623. */
  624. static void ebpf_send_specific_swap_data(char *type, netdata_publish_swap_t *values)
  625. {
  626. ebpf_write_begin_chart(type, NETDATA_MEM_SWAP_READ_CHART, "");
  627. write_chart_dimension(swap_publish_aggregated[NETDATA_KEY_SWAP_READPAGE_CALL].name, (long long) values->read);
  628. ebpf_write_end_chart();
  629. ebpf_write_begin_chart(type, NETDATA_MEM_SWAP_WRITE_CHART, "");
  630. write_chart_dimension(swap_publish_aggregated[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name, (long long) values->write);
  631. ebpf_write_end_chart();
  632. }
  633. /**
  634. * Create Systemd Swap Charts
  635. *
  636. * Create charts when systemd is enabled
  637. *
  638. * @param update_every value to overwrite the update frequency set by the server.
  639. **/
  640. static void ebpf_create_systemd_swap_charts(int update_every)
  641. {
  642. ebpf_create_charts_on_systemd(NETDATA_MEM_SWAP_READ_CHART,
  643. "Calls to swap_readpage.",
  644. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  645. NETDATA_EBPF_CHART_TYPE_STACKED, 20191,
  646. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX], NETDATA_SYSTEMD_SWAP_READ_CONTEXT,
  647. NETDATA_EBPF_MODULE_NAME_SWAP, update_every);
  648. ebpf_create_charts_on_systemd(NETDATA_MEM_SWAP_WRITE_CHART,
  649. "Calls to function swap_writepage.",
  650. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_CGROUP_SWAP_SUBMENU,
  651. NETDATA_EBPF_CHART_TYPE_STACKED, 20192,
  652. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX], NETDATA_SYSTEMD_SWAP_WRITE_CONTEXT,
  653. NETDATA_EBPF_MODULE_NAME_SWAP, update_every);
  654. }
  655. /**
  656. * Send data to Netdata calling auxiliary functions.
  657. *
  658. * @param update_every value to overwrite the update frequency set by the server.
  659. */
  660. void ebpf_swap_send_cgroup_data(int update_every)
  661. {
  662. if (!ebpf_cgroup_pids)
  663. return;
  664. pthread_mutex_lock(&mutex_cgroup_shm);
  665. ebpf_cgroup_target_t *ect;
  666. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  667. ebpf_swap_sum_cgroup_pids(&ect->publish_systemd_swap, ect->pids);
  668. }
  669. int has_systemd = shm_ebpf_cgroup.header->systemd_enabled;
  670. if (has_systemd) {
  671. if (send_cgroup_chart) {
  672. ebpf_create_systemd_swap_charts(update_every);
  673. fflush(stdout);
  674. }
  675. ebpf_send_systemd_swap_charts();
  676. }
  677. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  678. if (ect->systemd)
  679. continue;
  680. if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_SWAP_CHART) && ect->updated) {
  681. ebpf_create_specific_swap_charts(ect->name, update_every);
  682. ect->flags |= NETDATA_EBPF_CGROUP_HAS_SWAP_CHART;
  683. }
  684. if (ect->flags & NETDATA_EBPF_CGROUP_HAS_SWAP_CHART) {
  685. if (ect->updated) {
  686. ebpf_send_specific_swap_data(ect->name, &ect->publish_systemd_swap);
  687. } else {
  688. ebpf_obsolete_specific_swap_charts(ect->name, update_every);
  689. ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_SWAP_CHART;
  690. }
  691. }
  692. }
  693. pthread_mutex_unlock(&mutex_cgroup_shm);
  694. }
  695. /**
  696. * Main loop for this collector.
  697. */
  698. static void swap_collector(ebpf_module_t *em)
  699. {
  700. int cgroup = em->cgroup_charts;
  701. int update_every = em->update_every;
  702. heartbeat_t hb;
  703. heartbeat_init(&hb);
  704. int counter = update_every - 1;
  705. int maps_per_core = em->maps_per_core;
  706. uint32_t running_time = 0;
  707. uint32_t lifetime = em->lifetime;
  708. netdata_idx_t *stats = em->hash_table_stats;
  709. memset(stats, 0, sizeof(em->hash_table_stats));
  710. while (!ebpf_plugin_exit && running_time < lifetime) {
  711. (void)heartbeat_next(&hb, USEC_PER_SEC);
  712. if (ebpf_plugin_exit || ++counter != update_every)
  713. continue;
  714. counter = 0;
  715. netdata_apps_integration_flags_t apps = em->apps_charts;
  716. ebpf_swap_read_global_table(stats, maps_per_core);
  717. pthread_mutex_lock(&collect_data_mutex);
  718. if (apps)
  719. read_swap_apps_table(maps_per_core);
  720. if (cgroup)
  721. ebpf_update_swap_cgroup(maps_per_core);
  722. pthread_mutex_lock(&lock);
  723. swap_send_global();
  724. if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
  725. ebpf_swap_send_apps_data(apps_groups_root_target);
  726. if (cgroup)
  727. ebpf_swap_send_cgroup_data(update_every);
  728. pthread_mutex_unlock(&lock);
  729. pthread_mutex_unlock(&collect_data_mutex);
  730. pthread_mutex_lock(&ebpf_exit_cleanup);
  731. if (running_time && !em->running_time)
  732. running_time = update_every;
  733. else
  734. running_time += update_every;
  735. em->running_time = running_time;
  736. pthread_mutex_unlock(&ebpf_exit_cleanup);
  737. }
  738. }
  739. /*****************************************************************
  740. *
  741. * INITIALIZE THREAD
  742. *
  743. *****************************************************************/
  744. /**
  745. * Create apps charts
  746. *
  747. * Call ebpf_create_chart to create the charts on apps submenu.
  748. *
  749. * @param em a pointer to the structure with the default values.
  750. */
  751. void ebpf_swap_create_apps_charts(struct ebpf_module *em, void *ptr)
  752. {
  753. struct ebpf_target *root = ptr;
  754. struct ebpf_target *w;
  755. int update_every = em->update_every;
  756. for (w = root; w; w = w->next) {
  757. if (unlikely(!w->exposed))
  758. continue;
  759. ebpf_write_chart_cmd(NETDATA_APP_FAMILY,
  760. w->clean_name,
  761. "_ebpf_call_swap_readpage",
  762. "Calls to function swap_readpage.",
  763. EBPF_COMMON_DIMENSION_CALL,
  764. NETDATA_EBPF_MEMORY_GROUP,
  765. NETDATA_EBPF_CHART_TYPE_STACKED,
  766. "app.ebpf_call_swap_readpage",
  767. 20070,
  768. update_every,
  769. NETDATA_EBPF_MODULE_NAME_SWAP);
  770. ebpf_create_chart_labels("app_group", w->name, 1);
  771. ebpf_commit_label();
  772. fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
  773. ebpf_write_chart_cmd(NETDATA_APP_FAMILY,
  774. w->clean_name,
  775. "_ebpf_call_swap_writepage",
  776. "Calls to function swap_writepage.",
  777. EBPF_COMMON_DIMENSION_CALL,
  778. NETDATA_EBPF_MEMORY_GROUP,
  779. NETDATA_EBPF_CHART_TYPE_STACKED,
  780. "app.ebpf_call_swap_writepage",
  781. 20071,
  782. update_every,
  783. NETDATA_EBPF_MODULE_NAME_SWAP);
  784. ebpf_create_chart_labels("app_group", w->name, 1);
  785. ebpf_commit_label();
  786. fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
  787. w->charts_created |= 1<<EBPF_MODULE_SWAP_IDX;
  788. }
  789. em->apps_charts |= NETDATA_EBPF_APPS_FLAG_CHART_CREATED;
  790. }
  791. /**
  792. * Allocate vectors used with this thread.
  793. *
  794. * We are not testing the return, because callocz does this and shutdown the software
  795. * case it was not possible to allocate.
  796. *
  797. * @param apps is apps enabled?
  798. */
  799. static void ebpf_swap_allocate_global_vectors(int apps)
  800. {
  801. if (apps)
  802. swap_pid = callocz((size_t)pid_max, sizeof(netdata_publish_swap_t *));
  803. swap_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_publish_swap_t));
  804. swap_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
  805. memset(swap_hash_values, 0, sizeof(swap_hash_values));
  806. }
  807. /*****************************************************************
  808. *
  809. * MAIN THREAD
  810. *
  811. *****************************************************************/
  812. /**
  813. * Create global charts
  814. *
  815. * Call ebpf_create_chart to create the charts for the collector.
  816. *
  817. * @param update_every value to overwrite the update frequency set by the server.
  818. */
  819. static void ebpf_create_swap_charts(int update_every)
  820. {
  821. ebpf_create_chart(NETDATA_EBPF_MEMORY_GROUP, NETDATA_MEM_SWAP_CHART,
  822. "Calls to access swap memory",
  823. EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_SWAP_SUBMENU,
  824. NULL,
  825. NETDATA_EBPF_CHART_TYPE_LINE,
  826. NETDATA_CHART_PRIO_MEM_SWAP_CALLS,
  827. ebpf_create_global_dimension,
  828. swap_publish_aggregated, NETDATA_SWAP_END,
  829. update_every, NETDATA_EBPF_MODULE_NAME_SWAP);
  830. fflush(stdout);
  831. }
  832. /*
  833. * Load BPF
  834. *
  835. * Load BPF files.
  836. *
  837. * @param em the structure with configuration
  838. */
  839. static int ebpf_swap_load_bpf(ebpf_module_t *em)
  840. {
  841. #ifdef LIBBPF_MAJOR_VERSION
  842. ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
  843. #endif
  844. int ret = 0;
  845. ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_KEY_SWAP_READPAGE_CALL].mode);
  846. if (em->load & EBPF_LOAD_LEGACY) {
  847. em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
  848. if (!em->probe_links) {
  849. ret = -1;
  850. }
  851. }
  852. #ifdef LIBBPF_MAJOR_VERSION
  853. else {
  854. bpf_obj = swap_bpf__open();
  855. if (!bpf_obj)
  856. ret = -1;
  857. else
  858. ret = ebpf_swap_load_and_attach(bpf_obj, em);
  859. }
  860. #endif
  861. if (ret)
  862. netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
  863. return ret;
  864. }
  865. /**
  866. * SWAP thread
  867. *
  868. * Thread used to make swap thread
  869. *
  870. * @param ptr a pointer to `struct ebpf_module`
  871. *
  872. * @return It always return NULL
  873. */
  874. void *ebpf_swap_thread(void *ptr)
  875. {
  876. netdata_thread_cleanup_push(ebpf_swap_exit, ptr);
  877. ebpf_module_t *em = (ebpf_module_t *)ptr;
  878. em->maps = swap_maps;
  879. ebpf_update_pid_table(&swap_maps[NETDATA_PID_SWAP_TABLE], em);
  880. #ifdef LIBBPF_MAJOR_VERSION
  881. ebpf_adjust_thread_load(em, default_btf);
  882. #endif
  883. if (ebpf_swap_load_bpf(em)) {
  884. goto endswap;
  885. }
  886. ebpf_swap_allocate_global_vectors(em->apps_charts);
  887. int algorithms[NETDATA_SWAP_END] = { NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX };
  888. ebpf_global_labels(swap_aggregated_data, swap_publish_aggregated, swap_dimension_name, swap_dimension_name,
  889. algorithms, NETDATA_SWAP_END);
  890. pthread_mutex_lock(&lock);
  891. ebpf_create_swap_charts(em->update_every);
  892. ebpf_update_stats(&plugin_statistics, em);
  893. ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
  894. pthread_mutex_unlock(&lock);
  895. swap_collector(em);
  896. endswap:
  897. ebpf_update_disabled_plugin_stats(em);
  898. netdata_thread_cleanup_pop(1);
  899. return NULL;
  900. }