ebpf_disk.c 23 KB

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