ebpf_disk.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include <sys/resource.h>
  3. #include <stdlib.h>
  4. #include "ebpf.h"
  5. #include "ebpf_disk.h"
  6. struct config disk_config = { .first_section = NULL,
  7. .last_section = NULL,
  8. .mutex = NETDATA_MUTEX_INITIALIZER,
  9. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  10. .rwlock = AVL_LOCK_INITIALIZER } };
  11. static ebpf_local_maps_t disk_maps[] = {{.name = "tbl_disk_iocall", .internal_input = NETDATA_DISK_HISTOGRAM_LENGTH,
  12. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  13. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  14. #ifdef LIBBPF_MAJOR_VERSION
  15. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  16. #endif
  17. },
  18. {.name = "tmp_disk_tp_stat", .internal_input = 8192, .user_input = 8192,
  19. .type = NETDATA_EBPF_MAP_STATIC,
  20. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  21. #ifdef LIBBPF_MAJOR_VERSION
  22. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  23. #endif
  24. },
  25. {.name = NULL, .internal_input = 0, .user_input = 0,
  26. .type = NETDATA_EBPF_MAP_CONTROLLER,
  27. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  28. #ifdef LIBBPF_MAJOR_VERSION
  29. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  30. #endif
  31. }};
  32. static avl_tree_lock disk_tree;
  33. netdata_ebpf_disks_t *disk_list = NULL;
  34. char *tracepoint_block_type = { "block"} ;
  35. char *tracepoint_block_issue = { "block_rq_issue" };
  36. char *tracepoint_block_rq_complete = { "block_rq_complete" };
  37. static int was_block_issue_enabled = 0;
  38. static int was_block_rq_complete_enabled = 0;
  39. static char **dimensions = NULL;
  40. static netdata_syscall_stat_t disk_aggregated_data[NETDATA_EBPF_HIST_MAX_BINS];
  41. static netdata_publish_syscall_t disk_publish_aggregated[NETDATA_EBPF_HIST_MAX_BINS];
  42. static netdata_idx_t *disk_hash_values = NULL;
  43. ebpf_publish_disk_t *plot_disks = NULL;
  44. pthread_mutex_t plot_mutex;
  45. /*****************************************************************
  46. *
  47. * FUNCTIONS TO MANIPULATE HARD DISKS
  48. *
  49. *****************************************************************/
  50. /**
  51. * Parse start
  52. *
  53. * Parse start address of disk
  54. *
  55. * @param w structure where data is stored
  56. * @param filename variable used to store value
  57. *
  58. * @return It returns 0 on success and -1 otherwise
  59. */
  60. static inline int ebpf_disk_parse_start(netdata_ebpf_disks_t *w, char *filename)
  61. {
  62. char content[FILENAME_MAX + 1];
  63. int fd = open(filename, O_RDONLY, 0);
  64. if (fd < 0) {
  65. return -1;
  66. }
  67. ssize_t file_length = read(fd, content, 4095);
  68. if (file_length > 0) {
  69. if (file_length > FILENAME_MAX)
  70. file_length = FILENAME_MAX;
  71. content[file_length] = '\0';
  72. w->start = strtoul(content, NULL, 10);
  73. }
  74. close(fd);
  75. return 0;
  76. }
  77. /**
  78. * Parse uevent
  79. *
  80. * Parse uevent file
  81. *
  82. * @param w structure where data is stored
  83. * @param filename variable used to store value
  84. *
  85. * @return It returns 0 on success and -1 otherwise
  86. */
  87. static inline int ebpf_parse_uevent(netdata_ebpf_disks_t *w, char *filename)
  88. {
  89. char content[FILENAME_MAX + 1];
  90. int fd = open(filename, O_RDONLY, 0);
  91. if (fd < 0) {
  92. return -1;
  93. }
  94. ssize_t file_length = read(fd, content, FILENAME_MAX);
  95. if (file_length > 0) {
  96. if (file_length > FILENAME_MAX)
  97. file_length = FILENAME_MAX;
  98. content[file_length] = '\0';
  99. char *s = strstr(content, "PARTNAME=EFI");
  100. if (s) {
  101. w->main->boot_partition = w;
  102. w->flags |= NETDATA_DISK_HAS_EFI;
  103. w->boot_chart = strdupz("disk_bootsector");
  104. }
  105. }
  106. close(fd);
  107. return 0;
  108. }
  109. /**
  110. * Parse Size
  111. *
  112. * @param w structure where data is stored
  113. * @param filename variable used to store value
  114. *
  115. * @return It returns 0 on success and -1 otherwise
  116. */
  117. static inline int ebpf_parse_size(netdata_ebpf_disks_t *w, char *filename)
  118. {
  119. char content[FILENAME_MAX + 1];
  120. int fd = open(filename, O_RDONLY, 0);
  121. if (fd < 0) {
  122. return -1;
  123. }
  124. ssize_t file_length = read(fd, content, FILENAME_MAX);
  125. if (file_length > 0) {
  126. if (file_length > FILENAME_MAX)
  127. file_length = FILENAME_MAX;
  128. content[file_length] = '\0';
  129. w->end = w->start + strtoul(content, NULL, 10) -1;
  130. }
  131. close(fd);
  132. return 0;
  133. }
  134. /**
  135. * Read Disk information
  136. *
  137. * Read disk information from /sys/block
  138. *
  139. * @param w structure where data is stored
  140. * @param name disk name
  141. */
  142. static void ebpf_read_disk_info(netdata_ebpf_disks_t *w, char *name)
  143. {
  144. static netdata_ebpf_disks_t *main_disk = NULL;
  145. static uint32_t key = 0;
  146. char *path = { "/sys/block" };
  147. char disk[NETDATA_DISK_NAME_LEN + 1];
  148. char filename[FILENAME_MAX + 1];
  149. snprintfz(disk, NETDATA_DISK_NAME_LEN, "%s", name);
  150. size_t length = strlen(disk);
  151. if (!length) {
  152. return;
  153. }
  154. length--;
  155. size_t curr = length;
  156. while (isdigit((int)disk[length])) {
  157. disk[length--] = '\0';
  158. }
  159. // We are looking for partition information, if it is a device we will ignore it.
  160. if (curr == length) {
  161. main_disk = w;
  162. key = MKDEV(w->major, w->minor);
  163. w->bootsector_key = key;
  164. return;
  165. }
  166. w->bootsector_key = key;
  167. w->main = main_disk;
  168. snprintfz(filename, FILENAME_MAX, "%s/%s/%s/uevent", path, disk, name);
  169. if (ebpf_parse_uevent(w, filename))
  170. return;
  171. snprintfz(filename, FILENAME_MAX, "%s/%s/%s/start", path, disk, name);
  172. if (ebpf_disk_parse_start(w, filename))
  173. return;
  174. snprintfz(filename, FILENAME_MAX, "%s/%s/%s/size", path, disk, name);
  175. ebpf_parse_size(w, filename);
  176. }
  177. /**
  178. * New encode dev
  179. *
  180. * New encode algorithm extracted from https://elixir.bootlin.com/linux/v5.10.8/source/include/linux/kdev_t.h#L39
  181. *
  182. * @param major driver major number
  183. * @param minor driver minor number
  184. *
  185. * @return
  186. */
  187. static inline uint32_t netdata_new_encode_dev(uint32_t major, uint32_t minor) {
  188. return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
  189. }
  190. /**
  191. * Compare disks
  192. *
  193. * Compare major and minor values to add disks to tree.
  194. *
  195. * @param a pointer to netdata_ebpf_disks
  196. * @param b pointer to netdata_ebpf_disks
  197. *
  198. * @return It returns 0 case the values are equal, 1 case a is bigger than b and -1 case a is smaller than b.
  199. */
  200. static int ebpf_compare_disks(void *a, void *b)
  201. {
  202. netdata_ebpf_disks_t *ptr1 = a;
  203. netdata_ebpf_disks_t *ptr2 = b;
  204. if (ptr1->dev > ptr2->dev)
  205. return 1;
  206. if (ptr1->dev < ptr2->dev)
  207. return -1;
  208. return 0;
  209. }
  210. /**
  211. * Update listen table
  212. *
  213. * Update link list when it is necessary.
  214. *
  215. * @param name disk name
  216. * @param major major disk identifier
  217. * @param minor minor disk identifier
  218. * @param current_time current timestamp
  219. */
  220. static void update_disk_table(char *name, int major, int minor, time_t current_time)
  221. {
  222. netdata_ebpf_disks_t find;
  223. netdata_ebpf_disks_t *w;
  224. size_t length;
  225. uint32_t dev = netdata_new_encode_dev(major, minor);
  226. find.dev = dev;
  227. netdata_ebpf_disks_t *ret = (netdata_ebpf_disks_t *) avl_search_lock(&disk_tree, (avl_t *)&find);
  228. if (ret) { // Disk is already present
  229. ret->flags |= NETDATA_DISK_IS_HERE;
  230. ret->last_update = current_time;
  231. return;
  232. }
  233. netdata_ebpf_disks_t *update_next = disk_list;
  234. if (likely(disk_list)) {
  235. netdata_ebpf_disks_t *move = disk_list;
  236. while (move) {
  237. if (dev == move->dev)
  238. return;
  239. update_next = move;
  240. move = move->next;
  241. }
  242. w = callocz(1, sizeof(netdata_ebpf_disks_t));
  243. length = strlen(name);
  244. if (length >= NETDATA_DISK_NAME_LEN)
  245. length = NETDATA_DISK_NAME_LEN;
  246. memcpy(w->family, name, length);
  247. w->family[length] = '\0';
  248. w->major = major;
  249. w->minor = minor;
  250. w->dev = netdata_new_encode_dev(major, minor);
  251. update_next->next = w;
  252. } else {
  253. disk_list = callocz(1, sizeof(netdata_ebpf_disks_t));
  254. length = strlen(name);
  255. if (length >= NETDATA_DISK_NAME_LEN)
  256. length = NETDATA_DISK_NAME_LEN;
  257. memcpy(disk_list->family, name, length);
  258. disk_list->family[length] = '\0';
  259. disk_list->major = major;
  260. disk_list->minor = minor;
  261. disk_list->dev = netdata_new_encode_dev(major, minor);
  262. w = disk_list;
  263. }
  264. ebpf_read_disk_info(w, name);
  265. netdata_ebpf_disks_t *check;
  266. check = (netdata_ebpf_disks_t *) avl_insert_lock(&disk_tree, (avl_t *)w);
  267. if (check != w)
  268. error("Internal error, cannot insert the AVL tree.");
  269. #ifdef NETDATA_INTERNAL_CHECKS
  270. info("The Latency is monitoring the hard disk %s (Major = %d, Minor = %d, Device = %u)", name, major, minor,w->dev);
  271. #endif
  272. w->flags |= NETDATA_DISK_IS_HERE;
  273. }
  274. /**
  275. * Read Local Disks
  276. *
  277. * Parse /proc/partitions to get block disks used to measure latency.
  278. *
  279. * @return It returns 0 on success and -1 otherwise
  280. */
  281. static int read_local_disks()
  282. {
  283. char filename[FILENAME_MAX + 1];
  284. snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, NETDATA_EBPF_PROC_PARTITIONS);
  285. procfile *ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
  286. if (!ff)
  287. return -1;
  288. ff = procfile_readall(ff);
  289. if (!ff)
  290. return -1;
  291. size_t lines = procfile_lines(ff), l;
  292. time_t current_time = now_realtime_sec();
  293. for(l = 2; l < lines ;l++) {
  294. size_t words = procfile_linewords(ff, l);
  295. // This is header or end of file
  296. if (unlikely(words < 4))
  297. continue;
  298. int major = (int)strtol(procfile_lineword(ff, l, 0), NULL, 10);
  299. // The main goal of this thread is to measure block devices, so any block device with major number
  300. // smaller than 7 according /proc/devices is not "important".
  301. if (major > 7) {
  302. int minor = (int)strtol(procfile_lineword(ff, l, 1), NULL, 10);
  303. update_disk_table(procfile_lineword(ff, l, 3), major, minor, current_time);
  304. }
  305. }
  306. procfile_close(ff);
  307. return 0;
  308. }
  309. /**
  310. * Update disks
  311. *
  312. * @param em main thread structure
  313. */
  314. void ebpf_update_disks(ebpf_module_t *em)
  315. {
  316. static time_t update_every = 0;
  317. time_t curr = now_realtime_sec();
  318. if (curr < update_every)
  319. return;
  320. update_every = curr + 5 * em->update_every;
  321. (void)read_local_disks();
  322. }
  323. /*****************************************************************
  324. *
  325. * FUNCTIONS TO CLOSE THE THREAD
  326. *
  327. *****************************************************************/
  328. /**
  329. * Disk disable tracepoints
  330. *
  331. * Disable tracepoints when the plugin was responsible to enable it.
  332. */
  333. static void ebpf_disk_disable_tracepoints()
  334. {
  335. char *default_message = { "Cannot disable the tracepoint" };
  336. if (!was_block_issue_enabled) {
  337. if (ebpf_disable_tracing_values(tracepoint_block_type, tracepoint_block_issue))
  338. error("%s %s/%s.", default_message, tracepoint_block_type, tracepoint_block_issue);
  339. }
  340. if (!was_block_rq_complete_enabled) {
  341. if (ebpf_disable_tracing_values(tracepoint_block_type, tracepoint_block_rq_complete))
  342. error("%s %s/%s.", default_message, tracepoint_block_type, tracepoint_block_rq_complete);
  343. }
  344. }
  345. /**
  346. * Cleanup plot disks
  347. *
  348. * Clean disk list
  349. */
  350. static void ebpf_cleanup_plot_disks()
  351. {
  352. ebpf_publish_disk_t *move = plot_disks, *next;
  353. while (move) {
  354. next = move->next;
  355. freez(move);
  356. move = next;
  357. }
  358. }
  359. /**
  360. * Cleanup Disk List
  361. */
  362. static void ebpf_cleanup_disk_list()
  363. {
  364. netdata_ebpf_disks_t *move = disk_list;
  365. while (move) {
  366. netdata_ebpf_disks_t *next = move->next;
  367. freez(move->histogram.name);
  368. freez(move->boot_chart);
  369. freez(move);
  370. move = next;
  371. }
  372. }
  373. /**
  374. * Disk exit.
  375. *
  376. * Cancel child and exit.
  377. *
  378. * @param ptr thread data.
  379. */
  380. static void ebpf_disk_exit(void *ptr)
  381. {
  382. ebpf_module_t *em = (ebpf_module_t *)ptr;
  383. if (em->objects)
  384. ebpf_unload_legacy_code(em->objects, em->probe_links);
  385. ebpf_disk_disable_tracepoints();
  386. if (dimensions)
  387. ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  388. freez(disk_hash_values);
  389. pthread_mutex_destroy(&plot_mutex);
  390. ebpf_cleanup_plot_disks();
  391. ebpf_cleanup_disk_list();
  392. pthread_mutex_lock(&ebpf_exit_cleanup);
  393. em->enabled = NETDATA_THREAD_EBPF_STOPPED;
  394. pthread_mutex_unlock(&ebpf_exit_cleanup);
  395. }
  396. /*****************************************************************
  397. *
  398. * MAIN LOOP
  399. *
  400. *****************************************************************/
  401. /**
  402. * Fill Plot list
  403. *
  404. * @param ptr a pointer for current disk
  405. */
  406. static void ebpf_fill_plot_disks(netdata_ebpf_disks_t *ptr)
  407. {
  408. pthread_mutex_lock(&plot_mutex);
  409. ebpf_publish_disk_t *w;
  410. if (likely(plot_disks)) {
  411. ebpf_publish_disk_t *move = plot_disks, *store = plot_disks;
  412. while (move) {
  413. if (move->plot == ptr) {
  414. pthread_mutex_unlock(&plot_mutex);
  415. return;
  416. }
  417. store = move;
  418. move = move->next;
  419. }
  420. w = callocz(1, sizeof(ebpf_publish_disk_t));
  421. w->plot = ptr;
  422. store->next = w;
  423. } else {
  424. plot_disks = callocz(1, sizeof(ebpf_publish_disk_t));
  425. plot_disks->plot = ptr;
  426. }
  427. pthread_mutex_unlock(&plot_mutex);
  428. ptr->flags |= NETDATA_DISK_ADDED_TO_PLOT_LIST;
  429. }
  430. /**
  431. * Read hard disk table
  432. *
  433. * Read the table with number of calls for all functions
  434. *
  435. * @param table file descriptor for table
  436. * @param maps_per_core do I need to read all cores?
  437. */
  438. static void read_hard_disk_tables(int table, int maps_per_core)
  439. {
  440. netdata_idx_t *values = disk_hash_values;
  441. block_key_t key = {};
  442. block_key_t next_key = {};
  443. netdata_ebpf_disks_t *ret = NULL;
  444. while (bpf_map_get_next_key(table, &key, &next_key) == 0) {
  445. int test = bpf_map_lookup_elem(table, &key, values);
  446. if (test < 0) {
  447. key = next_key;
  448. continue;
  449. }
  450. netdata_ebpf_disks_t find;
  451. find.dev = key.dev;
  452. if (likely(ret)) {
  453. if (find.dev != ret->dev)
  454. ret = (netdata_ebpf_disks_t *)avl_search_lock(&disk_tree, (avl_t *)&find);
  455. } else
  456. ret = (netdata_ebpf_disks_t *)avl_search_lock(&disk_tree, (avl_t *)&find);
  457. // Disk was inserted after we parse /proc/partitions
  458. if (!ret) {
  459. if (read_local_disks()) {
  460. key = next_key;
  461. continue;
  462. }
  463. ret = (netdata_ebpf_disks_t *)avl_search_lock(&disk_tree, (avl_t *)&find);
  464. if (!ret) {
  465. // We should never reach this point, but we are adding it to keep a safe code
  466. key = next_key;
  467. continue;
  468. }
  469. }
  470. uint64_t total = 0;
  471. int i;
  472. int end = (maps_per_core) ? 1 : ebpf_nprocs;
  473. for (i = 0; i < end; i++) {
  474. total += values[i];
  475. }
  476. ret->histogram.histogram[key.bin] = total;
  477. if (!(ret->flags & NETDATA_DISK_ADDED_TO_PLOT_LIST))
  478. ebpf_fill_plot_disks(ret);
  479. key = next_key;
  480. }
  481. }
  482. /**
  483. * Obsolete Hard Disk charts
  484. *
  485. * Make Hard disk charts and fill chart name
  486. *
  487. * @param w the structure with necessary information to create the chart
  488. * @param update_every value to overwrite the update frequency set by the server.
  489. */
  490. static void ebpf_obsolete_hd_charts(netdata_ebpf_disks_t *w, int update_every)
  491. {
  492. ebpf_write_chart_obsolete(w->histogram.name, w->family, w->histogram.title, EBPF_COMMON_DIMENSION_CALL,
  493. w->family, NETDATA_EBPF_CHART_TYPE_STACKED, "disk.latency_io",
  494. w->histogram.order, update_every);
  495. w->flags = 0;
  496. }
  497. /**
  498. * Create Hard Disk charts
  499. *
  500. * Make Hard disk charts and fill chart name
  501. *
  502. * @param w the structure with necessary information to create the chart
  503. * @param update_every value to overwrite the update frequency set by the server.
  504. */
  505. static void ebpf_create_hd_charts(netdata_ebpf_disks_t *w, int update_every)
  506. {
  507. int order = NETDATA_CHART_PRIO_DISK_LATENCY;
  508. char *family = w->family;
  509. w->histogram.name = strdupz("disk_latency_io");
  510. w->histogram.title = NULL;
  511. w->histogram.order = order;
  512. ebpf_create_chart(w->histogram.name, family, "Disk latency", EBPF_COMMON_DIMENSION_CALL,
  513. family, "disk.latency_io", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  514. ebpf_create_global_dimension, disk_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  515. update_every, NETDATA_EBPF_MODULE_NAME_DISK);
  516. order++;
  517. w->flags |= NETDATA_DISK_CHART_CREATED;
  518. }
  519. /**
  520. * Remove pointer from plot
  521. *
  522. * Remove pointer from plot list when the disk is not present.
  523. */
  524. static void ebpf_remove_pointer_from_plot_disk(ebpf_module_t *em)
  525. {
  526. time_t current_time = now_realtime_sec();
  527. time_t limit = 10 * em->update_every;
  528. pthread_mutex_lock(&plot_mutex);
  529. ebpf_publish_disk_t *move = plot_disks, *prev = plot_disks;
  530. int update_every = em->update_every;
  531. while (move) {
  532. netdata_ebpf_disks_t *ned = move->plot;
  533. uint32_t flags = ned->flags;
  534. if (!(flags & NETDATA_DISK_IS_HERE) && ((current_time - ned->last_update) > limit)) {
  535. ebpf_obsolete_hd_charts(ned, update_every);
  536. avl_t *ret = (avl_t *)avl_remove_lock(&disk_tree, (avl_t *)ned);
  537. UNUSED(ret);
  538. if (move == plot_disks) {
  539. freez(move);
  540. plot_disks = NULL;
  541. break;
  542. } else {
  543. prev->next = move->next;
  544. ebpf_publish_disk_t *clean = move;
  545. move = move->next;
  546. freez(clean);
  547. continue;
  548. }
  549. }
  550. prev = move;
  551. move = move->next;
  552. }
  553. pthread_mutex_unlock(&plot_mutex);
  554. }
  555. /**
  556. * Send Hard disk data
  557. *
  558. * Send hard disk information to Netdata.
  559. *
  560. * @param update_every value to overwrite the update frequency set by the server.
  561. */
  562. static void ebpf_latency_send_hd_data(int update_every)
  563. {
  564. pthread_mutex_lock(&plot_mutex);
  565. if (!plot_disks) {
  566. pthread_mutex_unlock(&plot_mutex);
  567. return;
  568. }
  569. ebpf_publish_disk_t *move = plot_disks;
  570. while (move) {
  571. netdata_ebpf_disks_t *ned = move->plot;
  572. uint32_t flags = ned->flags;
  573. if (!(flags & NETDATA_DISK_CHART_CREATED)) {
  574. ebpf_create_hd_charts(ned, update_every);
  575. }
  576. if ((flags & NETDATA_DISK_CHART_CREATED)) {
  577. write_histogram_chart(ned->histogram.name, ned->family,
  578. ned->histogram.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  579. }
  580. ned->flags &= ~NETDATA_DISK_IS_HERE;
  581. move = move->next;
  582. }
  583. pthread_mutex_unlock(&plot_mutex);
  584. }
  585. /**
  586. * Main loop for this collector.
  587. */
  588. static void disk_collector(ebpf_module_t *em)
  589. {
  590. disk_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
  591. int update_every = em->update_every;
  592. heartbeat_t hb;
  593. heartbeat_init(&hb);
  594. int counter = update_every - 1;
  595. int maps_per_core = em->maps_per_core;
  596. while (!ebpf_exit_plugin) {
  597. (void)heartbeat_next(&hb, USEC_PER_SEC);
  598. if (ebpf_exit_plugin || ++counter != update_every)
  599. continue;
  600. counter = 0;
  601. read_hard_disk_tables(disk_maps[NETDATA_DISK_READ].map_fd, maps_per_core);
  602. pthread_mutex_lock(&lock);
  603. ebpf_remove_pointer_from_plot_disk(em);
  604. ebpf_latency_send_hd_data(update_every);
  605. pthread_mutex_unlock(&lock);
  606. ebpf_update_disks(em);
  607. }
  608. }
  609. /*****************************************************************
  610. *
  611. * EBPF DISK THREAD
  612. *
  613. *****************************************************************/
  614. /**
  615. * Enable tracepoints
  616. *
  617. * Enable necessary tracepoints for thread.
  618. *
  619. * @return It returns 0 on success and -1 otherwise
  620. */
  621. static int ebpf_disk_enable_tracepoints()
  622. {
  623. int test = ebpf_is_tracepoint_enabled(tracepoint_block_type, tracepoint_block_issue);
  624. if (test == -1)
  625. return -1;
  626. else if (!test) {
  627. if (ebpf_enable_tracing_values(tracepoint_block_type, tracepoint_block_issue))
  628. return -1;
  629. }
  630. was_block_issue_enabled = test;
  631. test = ebpf_is_tracepoint_enabled(tracepoint_block_type, tracepoint_block_rq_complete);
  632. if (test == -1)
  633. return -1;
  634. else if (!test) {
  635. if (ebpf_enable_tracing_values(tracepoint_block_type, tracepoint_block_rq_complete))
  636. return -1;
  637. }
  638. was_block_rq_complete_enabled = test;
  639. return 0;
  640. }
  641. /**
  642. * Disk thread
  643. *
  644. * Thread used to generate disk charts.
  645. *
  646. * @param ptr a pointer to `struct ebpf_module`
  647. *
  648. * @return It always return NULL
  649. */
  650. void *ebpf_disk_thread(void *ptr)
  651. {
  652. netdata_thread_cleanup_push(ebpf_disk_exit, ptr);
  653. ebpf_module_t *em = (ebpf_module_t *)ptr;
  654. em->maps = disk_maps;
  655. if (ebpf_disk_enable_tracepoints()) {
  656. goto enddisk;
  657. }
  658. avl_init_lock(&disk_tree, ebpf_compare_disks);
  659. if (read_local_disks()) {
  660. goto enddisk;
  661. }
  662. if (pthread_mutex_init(&plot_mutex, NULL)) {
  663. error("Cannot initialize local mutex");
  664. goto enddisk;
  665. }
  666. #ifdef LIBBPF_MAJOR_VERSION
  667. ebpf_define_map_type(disk_maps, em->maps_per_core, running_on_kernel);
  668. #endif
  669. em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
  670. if (!em->probe_links) {
  671. goto enddisk;
  672. }
  673. int algorithms[NETDATA_EBPF_HIST_MAX_BINS];
  674. ebpf_fill_algorithms(algorithms, NETDATA_EBPF_HIST_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
  675. dimensions = ebpf_fill_histogram_dimension(NETDATA_EBPF_HIST_MAX_BINS);
  676. ebpf_global_labels(disk_aggregated_data, disk_publish_aggregated, dimensions, dimensions, algorithms,
  677. NETDATA_EBPF_HIST_MAX_BINS);
  678. pthread_mutex_lock(&lock);
  679. ebpf_update_stats(&plugin_statistics, em);
  680. ebpf_update_kernel_memory_with_vector(&plugin_statistics, disk_maps);
  681. pthread_mutex_unlock(&lock);
  682. disk_collector(em);
  683. enddisk:
  684. ebpf_update_disabled_plugin_stats(em);
  685. netdata_thread_cleanup_pop(1);
  686. return NULL;
  687. }