ebpf_sync.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "ebpf.h"
  3. #include "ebpf_sync.h"
  4. static char *sync_counter_dimension_name[NETDATA_SYNC_IDX_END] = { "sync", "syncfs", "msync", "fsync", "fdatasync",
  5. "sync_file_range" };
  6. static netdata_syscall_stat_t sync_counter_aggregated_data[NETDATA_SYNC_IDX_END];
  7. static netdata_publish_syscall_t sync_counter_publish_aggregated[NETDATA_SYNC_IDX_END];
  8. static netdata_idx_t sync_hash_values[NETDATA_SYNC_IDX_END];
  9. static ebpf_local_maps_t sync_maps[] = {{.name = "tbl_sync", .internal_input = NETDATA_SYNC_END,
  10. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  11. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  12. {.name = "tbl_syncfs", .internal_input = NETDATA_SYNC_END,
  13. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  14. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  15. {.name = "tbl_msync", .internal_input = NETDATA_SYNC_END,
  16. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  17. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  18. {.name = "tbl_fsync", .internal_input = NETDATA_SYNC_END,
  19. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  20. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  21. {.name = "tbl_fdatasync", .internal_input = NETDATA_SYNC_END,
  22. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  23. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  24. {.name = "tbl_syncfr", .internal_input = NETDATA_SYNC_END,
  25. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  26. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  27. {.name = NULL, .internal_input = 0, .user_input = 0,
  28. .type = NETDATA_EBPF_MAP_CONTROLLER,
  29. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}};
  30. struct config sync_config = { .first_section = NULL,
  31. .last_section = NULL,
  32. .mutex = NETDATA_MUTEX_INITIALIZER,
  33. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  34. .rwlock = AVL_LOCK_INITIALIZER } };
  35. netdata_ebpf_targets_t sync_targets[] = { {.name = NETDATA_SYSCALLS_SYNC, .mode = EBPF_LOAD_TRAMPOLINE},
  36. {.name = NETDATA_SYSCALLS_SYNCFS, .mode = EBPF_LOAD_TRAMPOLINE},
  37. {.name = NETDATA_SYSCALLS_MSYNC, .mode = EBPF_LOAD_TRAMPOLINE},
  38. {.name = NETDATA_SYSCALLS_FSYNC, .mode = EBPF_LOAD_TRAMPOLINE},
  39. {.name = NETDATA_SYSCALLS_FDATASYNC, .mode = EBPF_LOAD_TRAMPOLINE},
  40. {.name = NETDATA_SYSCALLS_SYNC_FILE_RANGE, .mode = EBPF_LOAD_TRAMPOLINE},
  41. {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
  42. #ifdef LIBBPF_MAJOR_VERSION
  43. /*****************************************************************
  44. *
  45. * BTF FUNCTIONS
  46. *
  47. *****************************************************************/
  48. /**
  49. * Disable probe
  50. *
  51. * Disable kprobe to use another method.
  52. *
  53. * @param obj is the main structure for bpf objects.
  54. */
  55. static inline void ebpf_sync_disable_probe(struct sync_bpf *obj)
  56. {
  57. bpf_program__set_autoload(obj->progs.netdata_sync_kprobe, false);
  58. }
  59. /**
  60. * Disable trampoline
  61. *
  62. * Disable trampoline to use another method.
  63. *
  64. * @param obj is the main structure for bpf objects.
  65. */
  66. static inline void ebpf_sync_disable_trampoline(struct sync_bpf *obj)
  67. {
  68. bpf_program__set_autoload(obj->progs.netdata_sync_fentry, false);
  69. }
  70. /**
  71. * Disable tracepoint
  72. *
  73. * Disable tracepoints according information given.
  74. *
  75. * @param obj object loaded
  76. * @param idx Which syscall will not be disabled
  77. */
  78. void ebpf_sync_disable_tracepoints(struct sync_bpf *obj, sync_syscalls_index_t idx)
  79. {
  80. if (idx != NETDATA_SYNC_SYNC_IDX)
  81. bpf_program__set_autoload(obj->progs.netdata_sync_entry, false);
  82. if (idx != NETDATA_SYNC_SYNCFS_IDX)
  83. bpf_program__set_autoload(obj->progs.netdata_syncfs_entry, false);
  84. if (idx != NETDATA_SYNC_MSYNC_IDX)
  85. bpf_program__set_autoload(obj->progs.netdata_msync_entry, false);
  86. if (idx != NETDATA_SYNC_FSYNC_IDX)
  87. bpf_program__set_autoload(obj->progs.netdata_fsync_entry, false);
  88. if (idx != NETDATA_SYNC_FDATASYNC_IDX)
  89. bpf_program__set_autoload(obj->progs.netdata_fdatasync_entry, false);
  90. if (idx != NETDATA_SYNC_SYNC_FILE_RANGE_IDX)
  91. bpf_program__set_autoload(obj->progs.netdata_sync_file_range_entry, false);
  92. }
  93. /**
  94. * Set hash tables
  95. *
  96. * Set the values for maps according the value given by kernel.
  97. *
  98. * @param obj is the main structure for bpf objects.
  99. * @param idx the index for the main structure
  100. */
  101. static void ebpf_sync_set_hash_tables(struct sync_bpf *obj, sync_syscalls_index_t idx)
  102. {
  103. sync_maps[idx].map_fd = bpf_map__fd(obj->maps.tbl_sync);
  104. }
  105. /**
  106. * Load and attach
  107. *
  108. * Load and attach the eBPF code in kernel.
  109. *
  110. * @param obj is the main structure for bpf objects.
  111. * @param em the structure with configuration
  112. * @param target the syscall that we are attaching a tracer.
  113. * @param idx the index for the main structure
  114. *
  115. * @return it returns 0 on success and -1 otherwise
  116. */
  117. static inline int ebpf_sync_load_and_attach(struct sync_bpf *obj, ebpf_module_t *em, char *target,
  118. sync_syscalls_index_t idx)
  119. {
  120. netdata_ebpf_targets_t *synct = em->targets;
  121. netdata_ebpf_program_loaded_t test = synct[NETDATA_SYNC_SYNC_IDX].mode;
  122. if (test == EBPF_LOAD_TRAMPOLINE) {
  123. ebpf_sync_disable_probe(obj);
  124. ebpf_sync_disable_tracepoints(obj, NETDATA_SYNC_IDX_END);
  125. bpf_program__set_attach_target(obj->progs.netdata_sync_fentry, 0,
  126. target);
  127. } else if (test == EBPF_LOAD_PROBE ||
  128. test == EBPF_LOAD_RETPROBE) {
  129. ebpf_sync_disable_tracepoints(obj, NETDATA_SYNC_IDX_END);
  130. ebpf_sync_disable_trampoline(obj);
  131. } else {
  132. ebpf_sync_disable_probe(obj);
  133. ebpf_sync_disable_trampoline(obj);
  134. ebpf_sync_disable_tracepoints(obj, idx);
  135. }
  136. int ret = sync_bpf__load(obj);
  137. if (!ret) {
  138. if (test != EBPF_LOAD_PROBE && test != EBPF_LOAD_RETPROBE) {
  139. ret = sync_bpf__attach(obj);
  140. } else {
  141. obj->links.netdata_sync_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_sync_kprobe,
  142. false, target);
  143. ret = (int)libbpf_get_error(obj->links.netdata_sync_kprobe);
  144. }
  145. if (!ret)
  146. ebpf_sync_set_hash_tables(obj, idx);
  147. }
  148. return ret;
  149. }
  150. #endif
  151. /*****************************************************************
  152. *
  153. * CLEANUP THREAD
  154. *
  155. *****************************************************************/
  156. #ifdef LIBBPF_MAJOR_VERSION
  157. /**
  158. * Cleanup Objects
  159. *
  160. * Cleanup loaded objects when thread was initialized.
  161. */
  162. void ebpf_sync_cleanup_objects()
  163. {
  164. int i;
  165. for (i = 0; local_syscalls[i].syscall; i++) {
  166. ebpf_sync_syscalls_t *w = &local_syscalls[i];
  167. if (w->sync_obj)
  168. sync_bpf__destroy(w->sync_obj);
  169. }
  170. }
  171. #endif
  172. /**
  173. * Sync Free
  174. *
  175. * Cleanup variables after child threads to stop
  176. *
  177. * @param ptr thread data.
  178. */
  179. static void ebpf_sync_free(ebpf_module_t *em)
  180. {
  181. pthread_mutex_lock(&ebpf_exit_cleanup);
  182. em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING;
  183. pthread_mutex_unlock(&ebpf_exit_cleanup);
  184. #ifdef LIBBPF_MAJOR_VERSION
  185. ebpf_sync_cleanup_objects();
  186. #endif
  187. pthread_mutex_lock(&ebpf_exit_cleanup);
  188. em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED;
  189. pthread_mutex_unlock(&ebpf_exit_cleanup);
  190. }
  191. /**
  192. * Exit
  193. *
  194. * Clean up the main thread.
  195. *
  196. * @param ptr thread data.
  197. */
  198. static void ebpf_sync_exit(void *ptr)
  199. {
  200. ebpf_module_t *em = (ebpf_module_t *)ptr;
  201. ebpf_sync_free(em);
  202. }
  203. /*****************************************************************
  204. *
  205. * INITIALIZE THREAD
  206. *
  207. *****************************************************************/
  208. /**
  209. * Load Legacy
  210. *
  211. * Load legacy code.
  212. *
  213. * @param w is the sync output structure with pointers to objects loaded.
  214. * @param em is structure with configuration
  215. *
  216. * @return 0 on success and -1 otherwise.
  217. */
  218. static int ebpf_sync_load_legacy(ebpf_sync_syscalls_t *w, ebpf_module_t *em)
  219. {
  220. em->thread_name = w->syscall;
  221. if (!w->probe_links) {
  222. w->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &w->objects);
  223. if (!w->probe_links) {
  224. return -1;
  225. }
  226. }
  227. return 0;
  228. }
  229. /*
  230. * Initialize Syscalls
  231. *
  232. * Load the eBPF programs to monitor syscalls
  233. *
  234. * @return 0 on success and -1 otherwise.
  235. */
  236. static int ebpf_sync_initialize_syscall(ebpf_module_t *em)
  237. {
  238. int i;
  239. const char *saved_name = em->thread_name;
  240. int errors = 0;
  241. for (i = 0; local_syscalls[i].syscall; i++) {
  242. ebpf_sync_syscalls_t *w = &local_syscalls[i];
  243. if (w->enabled) {
  244. if (em->load & EBPF_LOAD_LEGACY) {
  245. if (ebpf_sync_load_legacy(w, em))
  246. errors++;
  247. em->thread_name = saved_name;
  248. }
  249. #ifdef LIBBPF_MAJOR_VERSION
  250. else {
  251. char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH];
  252. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, w->syscall, running_on_kernel);
  253. w->sync_obj = sync_bpf__open();
  254. if (!w->sync_obj) {
  255. errors++;
  256. } else {
  257. if (ebpf_is_function_inside_btf(default_btf, syscall)) {
  258. if (ebpf_sync_load_and_attach(w->sync_obj, em, syscall, i)) {
  259. errors++;
  260. }
  261. } else {
  262. if (ebpf_sync_load_legacy(w, em))
  263. errors++;
  264. }
  265. em->thread_name = saved_name;
  266. }
  267. }
  268. #endif
  269. }
  270. }
  271. em->thread_name = saved_name;
  272. memset(sync_counter_aggregated_data, 0 , NETDATA_SYNC_IDX_END * sizeof(netdata_syscall_stat_t));
  273. memset(sync_counter_publish_aggregated, 0 , NETDATA_SYNC_IDX_END * sizeof(netdata_publish_syscall_t));
  274. memset(sync_hash_values, 0 , NETDATA_SYNC_IDX_END * sizeof(netdata_idx_t));
  275. return (errors) ? -1 : 0;
  276. }
  277. /*****************************************************************
  278. *
  279. * DATA THREAD
  280. *
  281. *****************************************************************/
  282. /**
  283. * Read global table
  284. *
  285. * Read the table with number of calls for all functions
  286. */
  287. static void ebpf_sync_read_global_table()
  288. {
  289. netdata_idx_t stored;
  290. uint32_t idx = NETDATA_SYNC_CALL;
  291. int i;
  292. for (i = 0; local_syscalls[i].syscall; i++) {
  293. if (local_syscalls[i].enabled) {
  294. int fd = sync_maps[i].map_fd;
  295. if (!bpf_map_lookup_elem(fd, &idx, &stored)) {
  296. sync_hash_values[i] = stored;
  297. }
  298. }
  299. }
  300. }
  301. /**
  302. * Create Sync charts
  303. *
  304. * Create charts and dimensions according user input.
  305. *
  306. * @param id chart id
  307. * @param idx the first index with data.
  308. * @param end the last index with data.
  309. */
  310. static void ebpf_send_sync_chart(char *id,
  311. int idx,
  312. int end)
  313. {
  314. write_begin_chart(NETDATA_EBPF_MEMORY_GROUP, id);
  315. netdata_publish_syscall_t *move = &sync_counter_publish_aggregated[idx];
  316. while (move && idx <= end) {
  317. if (local_syscalls[idx].enabled)
  318. write_chart_dimension(move->name, sync_hash_values[idx]);
  319. move = move->next;
  320. idx++;
  321. }
  322. write_end_chart();
  323. }
  324. /**
  325. * Send data
  326. *
  327. * Send global charts to Netdata
  328. */
  329. static void sync_send_data()
  330. {
  331. if (local_syscalls[NETDATA_SYNC_FSYNC_IDX].enabled || local_syscalls[NETDATA_SYNC_FDATASYNC_IDX].enabled) {
  332. ebpf_send_sync_chart(NETDATA_EBPF_FILE_SYNC_CHART, NETDATA_SYNC_FSYNC_IDX, NETDATA_SYNC_FDATASYNC_IDX);
  333. }
  334. if (local_syscalls[NETDATA_SYNC_MSYNC_IDX].enabled)
  335. ebpf_one_dimension_write_charts(NETDATA_EBPF_MEMORY_GROUP, NETDATA_EBPF_MSYNC_CHART,
  336. sync_counter_publish_aggregated[NETDATA_SYNC_MSYNC_IDX].dimension,
  337. sync_hash_values[NETDATA_SYNC_MSYNC_IDX]);
  338. if (local_syscalls[NETDATA_SYNC_SYNC_IDX].enabled || local_syscalls[NETDATA_SYNC_SYNCFS_IDX].enabled) {
  339. ebpf_send_sync_chart(NETDATA_EBPF_SYNC_CHART, NETDATA_SYNC_SYNC_IDX, NETDATA_SYNC_SYNCFS_IDX);
  340. }
  341. if (local_syscalls[NETDATA_SYNC_SYNC_FILE_RANGE_IDX].enabled)
  342. ebpf_one_dimension_write_charts(NETDATA_EBPF_MEMORY_GROUP, NETDATA_EBPF_FILE_SEGMENT_CHART,
  343. sync_counter_publish_aggregated[NETDATA_SYNC_SYNC_FILE_RANGE_IDX].dimension,
  344. sync_hash_values[NETDATA_SYNC_SYNC_FILE_RANGE_IDX]);
  345. }
  346. /**
  347. * Main loop for this collector.
  348. */
  349. static void sync_collector(ebpf_module_t *em)
  350. {
  351. heartbeat_t hb;
  352. heartbeat_init(&hb);
  353. int update_every = em->update_every;
  354. int counter = update_every - 1;
  355. while (!ebpf_exit_plugin) {
  356. (void)heartbeat_next(&hb, USEC_PER_SEC);
  357. if (ebpf_exit_plugin || ++counter != update_every)
  358. continue;
  359. counter = 0;
  360. ebpf_sync_read_global_table();
  361. pthread_mutex_lock(&lock);
  362. sync_send_data();
  363. pthread_mutex_unlock(&lock);
  364. }
  365. }
  366. /*****************************************************************
  367. *
  368. * MAIN THREAD
  369. *
  370. *****************************************************************/
  371. /**
  372. * Create Sync charts
  373. *
  374. * Create charts and dimensions according user input.
  375. *
  376. * @param id chart id
  377. * @param title chart title
  378. * @param order order number of the specified chart
  379. * @param idx the first index with data.
  380. * @param end the last index with data.
  381. * @param update_every value to overwrite the update frequency set by the server.
  382. */
  383. static void ebpf_create_sync_chart(char *id,
  384. char *title,
  385. int order,
  386. int idx,
  387. int end,
  388. int update_every)
  389. {
  390. ebpf_write_chart_cmd(NETDATA_EBPF_MEMORY_GROUP, id, title, EBPF_COMMON_DIMENSION_CALL,
  391. NETDATA_EBPF_SYNC_SUBMENU, NETDATA_EBPF_CHART_TYPE_LINE, NULL, order,
  392. update_every,
  393. NETDATA_EBPF_MODULE_NAME_SYNC);
  394. netdata_publish_syscall_t *move = &sync_counter_publish_aggregated[idx];
  395. while (move && idx <= end) {
  396. if (local_syscalls[idx].enabled)
  397. ebpf_write_global_dimension(move->name, move->dimension, move->algorithm);
  398. move = move->next;
  399. idx++;
  400. }
  401. }
  402. /**
  403. * Create global charts
  404. *
  405. * Call ebpf_create_chart to create the charts for the collector.
  406. *
  407. * @param update_every value to overwrite the update frequency set by the server.
  408. */
  409. static void ebpf_create_sync_charts(int update_every)
  410. {
  411. if (local_syscalls[NETDATA_SYNC_FSYNC_IDX].enabled || local_syscalls[NETDATA_SYNC_FDATASYNC_IDX].enabled)
  412. ebpf_create_sync_chart(NETDATA_EBPF_FILE_SYNC_CHART,
  413. "Monitor calls for <code>fsync(2)</code> and <code>fdatasync(2)</code>.", 21300,
  414. NETDATA_SYNC_FSYNC_IDX, NETDATA_SYNC_FDATASYNC_IDX, update_every);
  415. if (local_syscalls[NETDATA_SYNC_MSYNC_IDX].enabled)
  416. ebpf_create_sync_chart(NETDATA_EBPF_MSYNC_CHART,
  417. "Monitor calls for <code>msync(2)</code>.", 21301,
  418. NETDATA_SYNC_MSYNC_IDX, NETDATA_SYNC_MSYNC_IDX, update_every);
  419. if (local_syscalls[NETDATA_SYNC_SYNC_IDX].enabled || local_syscalls[NETDATA_SYNC_SYNCFS_IDX].enabled)
  420. ebpf_create_sync_chart(NETDATA_EBPF_SYNC_CHART,
  421. "Monitor calls for <code>sync(2)</code> and <code>syncfs(2)</code>.", 21302,
  422. NETDATA_SYNC_SYNC_IDX, NETDATA_SYNC_SYNCFS_IDX, update_every);
  423. if (local_syscalls[NETDATA_SYNC_SYNC_FILE_RANGE_IDX].enabled)
  424. ebpf_create_sync_chart(NETDATA_EBPF_FILE_SEGMENT_CHART,
  425. "Monitor calls for <code>sync_file_range(2)</code>.", 21303,
  426. NETDATA_SYNC_SYNC_FILE_RANGE_IDX, NETDATA_SYNC_SYNC_FILE_RANGE_IDX, update_every);
  427. }
  428. /**
  429. * Parse Syscalls
  430. *
  431. * Parse syscall options available inside ebpf.d/sync.conf
  432. */
  433. static void ebpf_sync_parse_syscalls()
  434. {
  435. int i;
  436. for (i = 0; local_syscalls[i].syscall; i++) {
  437. local_syscalls[i].enabled = appconfig_get_boolean(&sync_config, NETDATA_SYNC_CONFIG_NAME,
  438. local_syscalls[i].syscall, CONFIG_BOOLEAN_YES);
  439. }
  440. }
  441. /**
  442. * Sync thread
  443. *
  444. * Thread used to make sync thread
  445. *
  446. * @param ptr a pointer to `struct ebpf_module`
  447. *
  448. * @return It always return NULL
  449. */
  450. void *ebpf_sync_thread(void *ptr)
  451. {
  452. netdata_thread_cleanup_push(ebpf_sync_exit, ptr);
  453. ebpf_module_t *em = (ebpf_module_t *)ptr;
  454. em->maps = sync_maps;
  455. ebpf_sync_parse_syscalls();
  456. #ifdef LIBBPF_MAJOR_VERSION
  457. ebpf_adjust_thread_load(em, default_btf);
  458. #endif
  459. if (ebpf_sync_initialize_syscall(em)) {
  460. em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED;
  461. goto endsync;
  462. }
  463. int algorithms[NETDATA_SYNC_IDX_END] = { NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX,
  464. NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX,
  465. NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX };
  466. ebpf_global_labels(sync_counter_aggregated_data, sync_counter_publish_aggregated,
  467. sync_counter_dimension_name, sync_counter_dimension_name,
  468. algorithms, NETDATA_SYNC_IDX_END);
  469. pthread_mutex_lock(&lock);
  470. ebpf_create_sync_charts(em->update_every);
  471. ebpf_update_stats(&plugin_statistics, em);
  472. pthread_mutex_unlock(&lock);
  473. sync_collector(em);
  474. endsync:
  475. ebpf_update_disabled_plugin_stats(em);
  476. netdata_thread_cleanup_pop(1);
  477. return NULL;
  478. }