ebpf_filesystem.c 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "ebpf_filesystem.h"
  3. struct config fs_config = { .first_section = NULL,
  4. .last_section = NULL,
  5. .mutex = NETDATA_MUTEX_INITIALIZER,
  6. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  7. .rwlock = AVL_LOCK_INITIALIZER } };
  8. ebpf_local_maps_t ext4_maps[] = {{.name = "tbl_ext4", .internal_input = NETDATA_KEY_CALLS_SYNC,
  9. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  10. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  11. #ifdef LIBBPF_MAJOR_VERSION
  12. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  13. #endif
  14. },
  15. {.name = "tmp_ext4", .internal_input = 4192, .user_input = 4192,
  16. .type = NETDATA_EBPF_MAP_CONTROLLER,
  17. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  18. #ifdef LIBBPF_MAJOR_VERSION
  19. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  20. #endif
  21. },
  22. {.name = NULL, .internal_input = 0, .user_input = 0,
  23. .type = NETDATA_EBPF_MAP_CONTROLLER,
  24. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  25. #ifdef LIBBPF_MAJOR_VERSION
  26. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  27. #endif
  28. }};
  29. ebpf_local_maps_t xfs_maps[] = {{.name = "tbl_xfs", .internal_input = NETDATA_KEY_CALLS_SYNC,
  30. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  31. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  32. #ifdef LIBBPF_MAJOR_VERSION
  33. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  34. #endif
  35. },
  36. {.name = "tmp_xfs", .internal_input = 4192, .user_input = 4192,
  37. .type = NETDATA_EBPF_MAP_CONTROLLER,
  38. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  39. #ifdef LIBBPF_MAJOR_VERSION
  40. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  41. #endif
  42. },
  43. {.name = NULL, .internal_input = 0, .user_input = 0,
  44. .type = NETDATA_EBPF_MAP_CONTROLLER,
  45. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  46. #ifdef LIBBPF_MAJOR_VERSION
  47. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  48. #endif
  49. }};
  50. ebpf_local_maps_t nfs_maps[] = {{.name = "tbl_nfs", .internal_input = NETDATA_KEY_CALLS_SYNC,
  51. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  52. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  53. #ifdef LIBBPF_MAJOR_VERSION
  54. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  55. #endif
  56. },
  57. {.name = "tmp_nfs", .internal_input = 4192, .user_input = 4192,
  58. .type = NETDATA_EBPF_MAP_CONTROLLER,
  59. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  60. #ifdef LIBBPF_MAJOR_VERSION
  61. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  62. #endif
  63. },
  64. {.name = NULL, .internal_input = 0, .user_input = 0,
  65. .type = NETDATA_EBPF_MAP_CONTROLLER,
  66. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  67. #ifdef LIBBPF_MAJOR_VERSION
  68. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  69. #endif
  70. }};
  71. ebpf_local_maps_t zfs_maps[] = {{.name = "tbl_zfs", .internal_input = NETDATA_KEY_CALLS_SYNC,
  72. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  73. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  74. #ifdef LIBBPF_MAJOR_VERSION
  75. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  76. #endif
  77. },
  78. {.name = "tmp_zfs", .internal_input = 4192, .user_input = 4192,
  79. .type = NETDATA_EBPF_MAP_CONTROLLER,
  80. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  81. #ifdef LIBBPF_MAJOR_VERSION
  82. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  83. #endif
  84. },
  85. {.name = NULL, .internal_input = 0, .user_input = 0,
  86. .type = NETDATA_EBPF_MAP_CONTROLLER,
  87. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  88. #ifdef LIBBPF_MAJOR_VERSION
  89. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  90. #endif
  91. }};
  92. ebpf_local_maps_t btrfs_maps[] = {{.name = "tbl_btrfs", .internal_input = NETDATA_KEY_CALLS_SYNC,
  93. .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC,
  94. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  95. #ifdef LIBBPF_MAJOR_VERSION
  96. .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
  97. #endif
  98. },
  99. {.name = "tbl_ext_addr", .internal_input = 1, .user_input = 1,
  100. .type = NETDATA_EBPF_MAP_CONTROLLER,
  101. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  102. #ifdef LIBBPF_MAJOR_VERSION
  103. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  104. #endif
  105. },
  106. {.name = "tmp_btrfs", .internal_input = 4192, .user_input = 4192,
  107. .type = NETDATA_EBPF_MAP_CONTROLLER,
  108. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  109. #ifdef LIBBPF_MAJOR_VERSION
  110. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  111. #endif
  112. },
  113. {.name = NULL, .internal_input = 0, .user_input = 0,
  114. .type = NETDATA_EBPF_MAP_CONTROLLER,
  115. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
  116. #ifdef LIBBPF_MAJOR_VERSION
  117. .map_type = BPF_MAP_TYPE_PERCPU_HASH
  118. #endif
  119. }};
  120. static netdata_syscall_stat_t filesystem_aggregated_data[NETDATA_EBPF_HIST_MAX_BINS];
  121. static netdata_publish_syscall_t filesystem_publish_aggregated[NETDATA_EBPF_HIST_MAX_BINS];
  122. char **dimensions = NULL;
  123. static netdata_idx_t *filesystem_hash_values = NULL;
  124. #ifdef LIBBPF_MAJOR_VERSION
  125. /**
  126. * FS disable kprobe
  127. *
  128. * Disable kprobes, because system will use trampolines.
  129. * We are not calling this function for while, because we are prioritizing kprobes. We opted by this road, because
  130. * distribution are still not deliverying necessary btf files per FS.
  131. *
  132. * @param obj FS object loaded.
  133. */
  134. static void ebpf_fs_disable_kprobe(struct filesystem_bpf *obj)
  135. {
  136. // kprobe
  137. bpf_program__set_autoload(obj->progs.netdata_fs_file_read_probe, false);
  138. bpf_program__set_autoload(obj->progs.netdata_fs_file_write_probe, false);
  139. bpf_program__set_autoload(obj->progs.netdata_fs_file_open_probe, false);
  140. bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_probe, false);
  141. bpf_program__set_autoload(obj->progs.netdata_fs_getattr_probe, false);
  142. // kretprobe
  143. bpf_program__set_autoload(obj->progs.netdata_fs_file_read_retprobe, false);
  144. bpf_program__set_autoload(obj->progs.netdata_fs_file_write_retprobe, false);
  145. bpf_program__set_autoload(obj->progs.netdata_fs_file_open_retprobe, false);
  146. bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_retprobe, false);
  147. bpf_program__set_autoload(obj->progs.netdata_fs_getattr_retprobe, false);
  148. }
  149. /**
  150. * Disable trampoline
  151. *
  152. * Disable trampolines to use kprobes.
  153. *
  154. * @param obj FS object loaded.
  155. */
  156. static void ebpf_fs_disable_trampoline(struct filesystem_bpf *obj)
  157. {
  158. // entry
  159. bpf_program__set_autoload(obj->progs.netdata_fs_file_read_entry, false);
  160. bpf_program__set_autoload(obj->progs.netdata_fs_file_write_entry, false);
  161. bpf_program__set_autoload(obj->progs.netdata_fs_file_open_entry, false);
  162. bpf_program__set_autoload(obj->progs.netdata_fs_getattr_entry, false);
  163. bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_entry, false);
  164. // exit
  165. bpf_program__set_autoload(obj->progs.netdata_fs_file_read_exit, false);
  166. bpf_program__set_autoload(obj->progs.netdata_fs_file_write_exit, false);
  167. bpf_program__set_autoload(obj->progs.netdata_fs_file_open_exit, false);
  168. bpf_program__set_autoload(obj->progs.netdata_fs_getattr_exit, false);
  169. bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_exit, false);
  170. }
  171. /**
  172. * Set targets
  173. *
  174. * Set targets for each objects.
  175. *
  176. * @param obj FS object loaded.
  177. * @param functions array with function names.
  178. */
  179. static void ebpf_fs_set_target(struct filesystem_bpf *obj, const char **functions)
  180. {
  181. // entry
  182. bpf_program__set_attach_target(obj->progs.netdata_fs_file_read_entry, 0,
  183. functions[NETDATA_KEY_BTF_READ]);
  184. bpf_program__set_attach_target(obj->progs.netdata_fs_file_write_entry, 0,
  185. functions[NETDATA_KEY_BTF_WRITE]);
  186. bpf_program__set_attach_target(obj->progs.netdata_fs_file_open_entry, 0,
  187. functions[NETDATA_KEY_BTF_OPEN]);
  188. bpf_program__set_attach_target(obj->progs.netdata_fs_getattr_entry, 0,
  189. functions[NETDATA_KEY_BTF_SYNC_ATTR]);
  190. // exit
  191. bpf_program__set_attach_target(obj->progs.netdata_fs_file_read_exit, 0,
  192. functions[NETDATA_KEY_BTF_READ]);
  193. bpf_program__set_attach_target(obj->progs.netdata_fs_file_write_exit, 0,
  194. functions[NETDATA_KEY_BTF_WRITE]);
  195. bpf_program__set_attach_target(obj->progs.netdata_fs_file_open_exit, 0,
  196. functions[NETDATA_KEY_BTF_OPEN]);
  197. bpf_program__set_attach_target(obj->progs.netdata_fs_getattr_exit, 0,
  198. functions[NETDATA_KEY_BTF_SYNC_ATTR]);
  199. if (functions[NETDATA_KEY_BTF_OPEN2]) {
  200. bpf_program__set_attach_target(obj->progs.netdata_fs_2nd_file_open_entry, 0,
  201. functions[NETDATA_KEY_BTF_OPEN2]);
  202. bpf_program__set_attach_target(obj->progs.netdata_fs_2nd_file_open_exit, 0,
  203. functions[NETDATA_KEY_BTF_OPEN2]);
  204. } else {
  205. bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_entry, false);
  206. bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_exit, false);
  207. }
  208. }
  209. /**
  210. * Attach Kprobe
  211. *
  212. * Attach kprobe on targets
  213. *
  214. * @param obj FS object loaded.
  215. * @param functions array with function names.
  216. */
  217. static int ebpf_fs_attach_kprobe(struct filesystem_bpf *obj, const char **functions)
  218. {
  219. // kprobe
  220. obj->links.netdata_fs_file_read_probe = bpf_program__attach_kprobe(obj->progs.netdata_fs_file_read_probe,
  221. false, functions[NETDATA_KEY_BTF_READ]);
  222. if (libbpf_get_error(obj->links.netdata_fs_file_read_probe))
  223. return -1;
  224. obj->links.netdata_fs_file_write_probe = bpf_program__attach_kprobe(obj->progs.netdata_fs_file_write_probe,
  225. false, functions[NETDATA_KEY_BTF_WRITE]);
  226. if (libbpf_get_error(obj->links.netdata_fs_file_write_probe))
  227. return -1;
  228. obj->links.netdata_fs_file_open_probe = bpf_program__attach_kprobe(obj->progs.netdata_fs_file_open_probe,
  229. false, functions[NETDATA_KEY_BTF_OPEN]);
  230. if (libbpf_get_error(obj->links.netdata_fs_file_open_probe))
  231. return -1;
  232. obj->links.netdata_fs_getattr_probe = bpf_program__attach_kprobe(obj->progs.netdata_fs_getattr_probe,
  233. false, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
  234. if (libbpf_get_error(obj->links.netdata_fs_getattr_probe))
  235. return -1;
  236. // kretprobe
  237. obj->links.netdata_fs_file_read_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_fs_file_read_retprobe,
  238. false, functions[NETDATA_KEY_BTF_READ]);
  239. if (libbpf_get_error(obj->links.netdata_fs_file_read_retprobe))
  240. return -1;
  241. obj->links.netdata_fs_file_write_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_fs_file_write_retprobe,
  242. false, functions[NETDATA_KEY_BTF_WRITE]);
  243. if (libbpf_get_error(obj->links.netdata_fs_file_write_retprobe))
  244. return -1;
  245. obj->links.netdata_fs_file_open_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_fs_file_open_retprobe,
  246. false, functions[NETDATA_KEY_BTF_OPEN]);
  247. if (libbpf_get_error(obj->links.netdata_fs_file_open_retprobe))
  248. return -1;
  249. obj->links.netdata_fs_getattr_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_fs_getattr_retprobe,
  250. false, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
  251. if (libbpf_get_error(obj->links.netdata_fs_getattr_retprobe))
  252. return -1;
  253. if (functions[NETDATA_KEY_BTF_OPEN2]) {
  254. obj->links.netdata_fs_2nd_file_open_probe = bpf_program__attach_kprobe(obj->progs.netdata_fs_2nd_file_open_probe,
  255. false, functions[NETDATA_KEY_BTF_OPEN2]);
  256. if (libbpf_get_error(obj->links.netdata_fs_2nd_file_open_probe))
  257. return -1;
  258. obj->links.netdata_fs_2nd_file_open_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_fs_2nd_file_open_retprobe,
  259. false, functions[NETDATA_KEY_BTF_OPEN2]);
  260. if (libbpf_get_error(obj->links.netdata_fs_2nd_file_open_retprobe))
  261. return -1;
  262. }
  263. return 0;
  264. }
  265. /**
  266. * Load and Attach
  267. *
  268. * Load binary and attach to targets.
  269. *
  270. * @param map Structure with information about maps.
  271. * @param obj FS object loaded.
  272. * @param functions array with function names.
  273. * @param bf sttruct with btf file loaded.
  274. */
  275. static inline int ebpf_fs_load_and_attach(ebpf_local_maps_t *map, struct filesystem_bpf *obj,
  276. const char **functions, struct btf *bf)
  277. {
  278. if (bf) {
  279. ebpf_fs_disable_kprobe(obj);
  280. ebpf_fs_set_target(obj, functions);
  281. } else {
  282. ebpf_fs_disable_trampoline(obj);
  283. }
  284. int ret = filesystem_bpf__load(obj);
  285. if (ret) {
  286. fprintf(stderr, "failed to load BPF object: %d\n", ret);
  287. return -1;
  288. }
  289. if (bf)
  290. ret = filesystem_bpf__attach(obj);
  291. else
  292. ret = ebpf_fs_attach_kprobe(obj, functions);
  293. if (!ret)
  294. map->map_fd = bpf_map__fd(obj->maps.tbl_fs);;
  295. return ret;
  296. }
  297. #endif
  298. /*****************************************************************
  299. *
  300. * COMMON FUNCTIONS
  301. *
  302. *****************************************************************/
  303. /**
  304. * Create Filesystem chart
  305. *
  306. * Create latency charts
  307. *
  308. * @param update_every value to overwrite the update frequency set by the server.
  309. */
  310. static void ebpf_obsolete_fs_charts(int update_every)
  311. {
  312. int i;
  313. uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED | NETDATA_FILESYSTEM_REMOVE_CHARTS;
  314. for (i = 0; localfs[i].filesystem; i++) {
  315. ebpf_filesystem_partitions_t *efp = &localfs[i];
  316. uint32_t flags = efp->flags;
  317. if ((flags & test) == test) {
  318. flags &= ~NETDATA_FILESYSTEM_FLAG_CHART_CREATED;
  319. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
  320. "",
  321. efp->hread.title,
  322. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  323. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hread.order, update_every);
  324. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
  325. "",
  326. efp->hwrite.title,
  327. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  328. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hwrite.order, update_every);
  329. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name, "", efp->hopen.title,
  330. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  331. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hopen.order, update_every);
  332. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hadditional.name,"", efp->hadditional.title,
  333. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  334. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hadditional.order,
  335. update_every);
  336. }
  337. efp->flags = flags;
  338. }
  339. }
  340. /**
  341. * Create Filesystem chart
  342. *
  343. * Create latency charts
  344. *
  345. * @param update_every value to overwrite the update frequency set by the server.
  346. */
  347. static void ebpf_create_fs_charts(int update_every)
  348. {
  349. static int order = NETDATA_CHART_PRIO_EBPF_FILESYSTEM_CHARTS;
  350. char chart_name[64], title[256], family[64], ctx[64];
  351. int i;
  352. uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED|NETDATA_FILESYSTEM_REMOVE_CHARTS;
  353. for (i = 0; localfs[i].filesystem; i++) {
  354. ebpf_filesystem_partitions_t *efp = &localfs[i];
  355. uint32_t flags = efp->flags;
  356. if (flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION && !(flags & test)) {
  357. snprintfz(title, sizeof(title) - 1, "%s latency for each read request.", efp->filesystem);
  358. snprintfz(family, sizeof(family) - 1, "%s_latency", efp->family);
  359. snprintfz(chart_name, sizeof(chart_name) - 1, "%s_read_latency", efp->filesystem);
  360. efp->hread.name = strdupz(chart_name);
  361. efp->hread.title = strdupz(title);
  362. efp->hread.ctx = NULL;
  363. efp->hread.order = order;
  364. efp->family_name = strdupz(family);
  365. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
  366. efp->hread.title,
  367. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  368. "filesystem.read_latency", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  369. ebpf_create_global_dimension,
  370. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  371. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  372. order++;
  373. snprintfz(title, sizeof(title) - 1, "%s latency for each write request.", efp->filesystem);
  374. snprintfz(chart_name, sizeof(chart_name) - 1, "%s_write_latency", efp->filesystem);
  375. efp->hwrite.name = strdupz(chart_name);
  376. efp->hwrite.title = strdupz(title);
  377. efp->hwrite.ctx = NULL;
  378. efp->hwrite.order = order;
  379. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
  380. efp->hwrite.title,
  381. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  382. "filesystem.write_latency", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  383. ebpf_create_global_dimension,
  384. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  385. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  386. order++;
  387. snprintfz(title, sizeof(title) - 1, "%s latency for each open request.", efp->filesystem);
  388. snprintfz(chart_name, sizeof(chart_name) - 1, "%s_open_latency", efp->filesystem);
  389. efp->hopen.name = strdupz(chart_name);
  390. efp->hopen.title = strdupz(title);
  391. efp->hopen.ctx = NULL;
  392. efp->hopen.order = order;
  393. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
  394. efp->hopen.title,
  395. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  396. "filesystem.open_latency", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  397. ebpf_create_global_dimension,
  398. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  399. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  400. order++;
  401. char *type = (efp->flags & NETDATA_FILESYSTEM_ATTR_CHARTS) ? "attribute" : "sync";
  402. snprintfz(title, sizeof(title) - 1, "%s latency for each %s request.", efp->filesystem, type);
  403. snprintfz(chart_name, sizeof(chart_name) - 1, "%s_%s_latency", efp->filesystem, type);
  404. snprintfz(ctx, sizeof(ctx) - 1, "filesystem.%s_latency", type);
  405. efp->hadditional.name = strdupz(chart_name);
  406. efp->hadditional.title = strdupz(title);
  407. efp->hadditional.ctx = strdupz(ctx);
  408. efp->hadditional.order = order;
  409. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hadditional.name, efp->hadditional.title,
  410. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  411. ctx, NETDATA_EBPF_CHART_TYPE_STACKED, order, ebpf_create_global_dimension,
  412. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  413. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  414. order++;
  415. efp->flags |= NETDATA_FILESYSTEM_FLAG_CHART_CREATED;
  416. }
  417. }
  418. fflush(stdout);
  419. }
  420. /**
  421. * Initialize eBPF data
  422. *
  423. * @param em main thread structure.
  424. *
  425. * @return it returns 0 on success and -1 otherwise.
  426. */
  427. int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
  428. {
  429. pthread_mutex_lock(&lock);
  430. int i;
  431. const char *saved_name = em->info.thread_name;
  432. uint64_t kernels = em->kernels;
  433. for (i = 0; localfs[i].filesystem; i++) {
  434. ebpf_filesystem_partitions_t *efp = &localfs[i];
  435. if (!efp->probe_links && efp->flags & NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM) {
  436. em->info.thread_name = efp->filesystem;
  437. em->kernels = efp->kernels;
  438. em->maps = efp->fs_maps;
  439. #ifdef LIBBPF_MAJOR_VERSION
  440. ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
  441. #endif
  442. if (em->load & EBPF_LOAD_LEGACY) {
  443. efp->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &efp->objects);
  444. if (!efp->probe_links) {
  445. em->info.thread_name = saved_name;
  446. em->kernels = kernels;
  447. em->maps = NULL;
  448. pthread_mutex_unlock(&lock);
  449. return -1;
  450. }
  451. }
  452. #ifdef LIBBPF_MAJOR_VERSION
  453. else {
  454. efp->fs_obj = filesystem_bpf__open();
  455. if (!efp->fs_obj) {
  456. em->info.thread_name = saved_name;
  457. em->kernels = kernels;
  458. return -1;
  459. } else {
  460. if (ebpf_fs_load_and_attach(em->maps, efp->fs_obj,
  461. efp->functions, NULL))
  462. return -1;
  463. }
  464. }
  465. #endif
  466. efp->flags |= NETDATA_FILESYSTEM_FLAG_HAS_PARTITION;
  467. ebpf_update_kernel_memory(&plugin_statistics, efp->fs_maps, EBPF_ACTION_STAT_ADD);
  468. // Nedeed for filesystems like btrfs
  469. if ((efp->flags & NETDATA_FILESYSTEM_FILL_ADDRESS_TABLE) && (efp->addresses.function)) {
  470. ebpf_load_addresses(&efp->addresses, efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd);
  471. }
  472. }
  473. efp->flags &= ~NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
  474. }
  475. em->info.thread_name = saved_name;
  476. pthread_mutex_unlock(&lock);
  477. em->kernels = kernels;
  478. em->maps = NULL;
  479. if (!dimensions) {
  480. dimensions = ebpf_fill_histogram_dimension(NETDATA_EBPF_HIST_MAX_BINS);
  481. memset(filesystem_aggregated_data, 0 , NETDATA_EBPF_HIST_MAX_BINS * sizeof(netdata_syscall_stat_t));
  482. memset(filesystem_publish_aggregated, 0 , NETDATA_EBPF_HIST_MAX_BINS * sizeof(netdata_publish_syscall_t));
  483. filesystem_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
  484. }
  485. return 0;
  486. }
  487. /**
  488. * Read Local partitions
  489. *
  490. * @return the total of partitions that will be monitored
  491. */
  492. static int ebpf_read_local_partitions()
  493. {
  494. char filename[FILENAME_MAX + 1];
  495. snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", netdata_configured_host_prefix);
  496. procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
  497. if(unlikely(!ff)) {
  498. snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", netdata_configured_host_prefix);
  499. ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
  500. if(unlikely(!ff)) return 0;
  501. }
  502. ff = procfile_readall(ff);
  503. if(unlikely(!ff))
  504. return 0;
  505. int count = 0;
  506. unsigned long l, i, lines = procfile_lines(ff);
  507. for (i = 0; localfs[i].filesystem; i++) {
  508. localfs[i].flags |= NETDATA_FILESYSTEM_REMOVE_CHARTS;
  509. }
  510. for(l = 0; l < lines ; l++) {
  511. // In "normal" situation the expected value is at column 7
  512. // When `shared` options is added to mount information, the filesystem is at column 8
  513. // Finally when we have systemd starting netdata, it will be at column 9
  514. unsigned long index = procfile_linewords(ff, l) - 3;
  515. char *fs = procfile_lineword(ff, l, index);
  516. for (i = 0; localfs[i].filesystem; i++) {
  517. ebpf_filesystem_partitions_t *w = &localfs[i];
  518. if (w->enabled && (!strcmp(fs, w->filesystem) ||
  519. (w->optional_filesystem && !strcmp(fs, w->optional_filesystem)))) {
  520. localfs[i].flags |= NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
  521. localfs[i].flags &= ~NETDATA_FILESYSTEM_REMOVE_CHARTS;
  522. count++;
  523. break;
  524. }
  525. }
  526. }
  527. procfile_close(ff);
  528. return count;
  529. }
  530. /**
  531. * Update partition
  532. *
  533. * Update the partition structures before to plot
  534. *
  535. * @param em main thread structure
  536. *
  537. * @return 0 on success and -1 otherwise.
  538. */
  539. static int ebpf_update_partitions(ebpf_module_t *em)
  540. {
  541. static time_t update_every = 0;
  542. time_t curr = now_realtime_sec();
  543. if (curr < update_every)
  544. return 0;
  545. update_every = curr + 5 * em->update_every;
  546. if (!ebpf_read_local_partitions()) {
  547. em->optional = -1;
  548. return -1;
  549. }
  550. if (ebpf_filesystem_initialize_ebpf_data(em)) {
  551. return -1;
  552. }
  553. return 0;
  554. }
  555. /*****************************************************************
  556. *
  557. * CLEANUP FUNCTIONS
  558. *
  559. *****************************************************************/
  560. /*
  561. * Cleanup eBPF data
  562. */
  563. void ebpf_filesystem_cleanup_ebpf_data()
  564. {
  565. int i;
  566. for (i = 0; localfs[i].filesystem; i++) {
  567. ebpf_filesystem_partitions_t *efp = &localfs[i];
  568. if (efp->probe_links) {
  569. freez(efp->family_name);
  570. efp->family_name = NULL;
  571. freez(efp->hread.name);
  572. efp->hread.name = NULL;
  573. freez(efp->hread.title);
  574. efp->hread.title = NULL;
  575. freez(efp->hwrite.name);
  576. efp->hwrite.name = NULL;
  577. freez(efp->hwrite.title);
  578. efp->hwrite.title = NULL;
  579. freez(efp->hopen.name);
  580. efp->hopen.name = NULL;
  581. freez(efp->hopen.title);
  582. efp->hopen.title = NULL;
  583. freez(efp->hadditional.name);
  584. efp->hadditional.name = NULL;
  585. freez(efp->hadditional.title);
  586. efp->hadditional.title = NULL;
  587. freez(efp->hadditional.ctx);
  588. efp->hadditional.ctx = NULL;
  589. }
  590. }
  591. }
  592. /**
  593. * Obsolete global
  594. *
  595. * Obsolete global charts created by thread.
  596. *
  597. * @param em a pointer to `struct ebpf_module`
  598. */
  599. static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
  600. {
  601. int i;
  602. for (i = 0; localfs[i].filesystem; i++) {
  603. ebpf_filesystem_partitions_t *efp = &localfs[i];
  604. if (!efp->objects)
  605. continue;
  606. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  607. efp->hread.name,
  608. "",
  609. efp->hread.title,
  610. EBPF_COMMON_DIMENSION_CALL,
  611. efp->family_name,
  612. NETDATA_EBPF_CHART_TYPE_STACKED,
  613. "filesystem.read_latency",
  614. efp->hread.order,
  615. em->update_every);
  616. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  617. efp->hwrite.name,
  618. "",
  619. efp->hwrite.title,
  620. EBPF_COMMON_DIMENSION_CALL,
  621. efp->family_name,
  622. NETDATA_EBPF_CHART_TYPE_STACKED,
  623. "filesystem.write_latency",
  624. efp->hwrite.order,
  625. em->update_every);
  626. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  627. efp->hopen.name,
  628. "",
  629. efp->hopen.title,
  630. EBPF_COMMON_DIMENSION_CALL,
  631. efp->family_name,
  632. NETDATA_EBPF_CHART_TYPE_STACKED,
  633. "filesystem.open_latency",
  634. efp->hopen.order,
  635. em->update_every);
  636. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  637. efp->hadditional.name,
  638. "",
  639. efp->hadditional.title,
  640. EBPF_COMMON_DIMENSION_CALL,
  641. efp->family_name,
  642. NETDATA_EBPF_CHART_TYPE_STACKED,
  643. efp->hadditional.ctx,
  644. efp->hadditional.order,
  645. em->update_every);
  646. }
  647. }
  648. /**
  649. * Filesystem exit
  650. *
  651. * Cancel child thread.
  652. *
  653. * @param ptr thread data.
  654. */
  655. static void ebpf_filesystem_exit(void *ptr)
  656. {
  657. ebpf_module_t *em = (ebpf_module_t *)ptr;
  658. if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
  659. pthread_mutex_lock(&lock);
  660. ebpf_obsolete_filesystem_global(em);
  661. pthread_mutex_unlock(&lock);
  662. fflush(stdout);
  663. }
  664. ebpf_filesystem_cleanup_ebpf_data();
  665. if (dimensions) {
  666. ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  667. dimensions = NULL;
  668. }
  669. freez(filesystem_hash_values);
  670. int i;
  671. for (i = 0; localfs[i].filesystem; i++) {
  672. ebpf_filesystem_partitions_t *efp = &localfs[i];
  673. if (!efp->probe_links)
  674. continue;
  675. ebpf_unload_legacy_code(efp->objects, efp->probe_links);
  676. efp->objects = NULL;
  677. efp->probe_links = NULL;
  678. efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
  679. }
  680. pthread_mutex_lock(&ebpf_exit_cleanup);
  681. em->enabled = NETDATA_THREAD_EBPF_STOPPED;
  682. ebpf_update_stats(&plugin_statistics, em);
  683. pthread_mutex_unlock(&ebpf_exit_cleanup);
  684. }
  685. /*****************************************************************
  686. *
  687. * MAIN THREAD
  688. *
  689. *****************************************************************/
  690. /**
  691. * Select hist
  692. *
  693. * Select a histogram to store data.
  694. *
  695. * @param efp pointer for the structure with pointers.
  696. * @param id histogram selector
  697. *
  698. * @return It returns a pointer for the histogram
  699. */
  700. static inline netdata_ebpf_histogram_t *select_hist(ebpf_filesystem_partitions_t *efp, uint32_t *idx, uint32_t id)
  701. {
  702. if (id < NETDATA_KEY_CALLS_READ) {
  703. *idx = id;
  704. return &efp->hread;
  705. } else if (id < NETDATA_KEY_CALLS_WRITE) {
  706. *idx = id - NETDATA_KEY_CALLS_READ;
  707. return &efp->hwrite;
  708. } else if (id < NETDATA_KEY_CALLS_OPEN) {
  709. *idx = id - NETDATA_KEY_CALLS_WRITE;
  710. return &efp->hopen;
  711. } else if (id < NETDATA_KEY_CALLS_SYNC ){
  712. *idx = id - NETDATA_KEY_CALLS_OPEN;
  713. return &efp->hadditional;
  714. }
  715. return NULL;
  716. }
  717. /**
  718. * Read hard disk table
  719. *
  720. * @param efp structure with filesystem monitored
  721. * @param fd file descriptor to get data.
  722. * @param maps_per_core do I need to read all cores?
  723. *
  724. * Read the table with number of calls for all functions
  725. */
  726. static void read_filesystem_table(ebpf_filesystem_partitions_t *efp, int fd, int maps_per_core)
  727. {
  728. netdata_idx_t *values = filesystem_hash_values;
  729. uint32_t key;
  730. uint32_t idx;
  731. for (key = 0; key < NETDATA_KEY_CALLS_SYNC; key++) {
  732. netdata_ebpf_histogram_t *w = select_hist(efp, &idx, key);
  733. if (!w) {
  734. continue;
  735. }
  736. int test = bpf_map_lookup_elem(fd, &key, values);
  737. if (test < 0) {
  738. continue;
  739. }
  740. uint64_t total = 0;
  741. int i;
  742. int end = (maps_per_core) ? ebpf_nprocs : 1;
  743. for (i = 0; i < end; i++) {
  744. total += values[i];
  745. }
  746. if (idx >= NETDATA_EBPF_HIST_MAX_BINS)
  747. idx = NETDATA_EBPF_HIST_MAX_BINS - 1;
  748. w->histogram[idx] = total;
  749. }
  750. }
  751. /**
  752. * Read hard disk table
  753. *
  754. * Read the table with number of calls for all functions
  755. *
  756. * @param maps_per_core do I need to read all cores?
  757. */
  758. static void read_filesystem_tables(int maps_per_core)
  759. {
  760. int i;
  761. for (i = 0; localfs[i].filesystem; i++) {
  762. ebpf_filesystem_partitions_t *efp = &localfs[i];
  763. if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
  764. read_filesystem_table(efp, efp->fs_maps[NETDATA_MAIN_FS_TABLE].map_fd, maps_per_core);
  765. }
  766. }
  767. }
  768. /**
  769. * Socket read hash
  770. *
  771. * This is the thread callback.
  772. * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
  773. *
  774. * @param ptr It is a NULL value for this thread.
  775. *
  776. * @return It always returns NULL.
  777. */
  778. void ebpf_filesystem_read_hash(ebpf_module_t *em)
  779. {
  780. ebpf_obsolete_fs_charts(em->update_every);
  781. (void) ebpf_update_partitions(em);
  782. if (em->optional)
  783. return;
  784. read_filesystem_tables(em->maps_per_core);
  785. }
  786. /**
  787. * Send Hard disk data
  788. *
  789. * Send hard disk information to Netdata.
  790. */
  791. static void ebpf_histogram_send_data()
  792. {
  793. uint32_t i;
  794. uint32_t test = NETDATA_FILESYSTEM_FLAG_HAS_PARTITION | NETDATA_FILESYSTEM_REMOVE_CHARTS;
  795. for (i = 0; localfs[i].filesystem; i++) {
  796. ebpf_filesystem_partitions_t *efp = &localfs[i];
  797. if ((efp->flags & test) == NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
  798. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
  799. efp->hread.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  800. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
  801. efp->hwrite.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  802. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
  803. efp->hopen.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  804. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hadditional.name,
  805. efp->hadditional.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  806. }
  807. }
  808. }
  809. /**
  810. * Main loop for this collector.
  811. *
  812. * @param em main structure for this thread
  813. */
  814. static void filesystem_collector(ebpf_module_t *em)
  815. {
  816. int update_every = em->update_every;
  817. heartbeat_t hb;
  818. heartbeat_init(&hb);
  819. int counter = update_every - 1;
  820. uint32_t running_time = 0;
  821. uint32_t lifetime = em->lifetime;
  822. while (!ebpf_plugin_exit && running_time < lifetime) {
  823. (void)heartbeat_next(&hb, USEC_PER_SEC);
  824. if (ebpf_plugin_exit || ++counter != update_every)
  825. continue;
  826. counter = 0;
  827. ebpf_filesystem_read_hash(em);
  828. pthread_mutex_lock(&lock);
  829. ebpf_create_fs_charts(update_every);
  830. ebpf_histogram_send_data();
  831. pthread_mutex_unlock(&lock);
  832. pthread_mutex_lock(&ebpf_exit_cleanup);
  833. if (running_time && !em->running_time)
  834. running_time = update_every;
  835. else
  836. running_time += update_every;
  837. em->running_time = running_time;
  838. pthread_mutex_unlock(&ebpf_exit_cleanup);
  839. }
  840. }
  841. /*****************************************************************
  842. *
  843. * ENTRY THREAD
  844. *
  845. *****************************************************************/
  846. /**
  847. * Update Filesystem
  848. *
  849. * Update file system structure using values read from configuration file.
  850. */
  851. static void ebpf_update_filesystem()
  852. {
  853. char dist[NETDATA_FS_MAX_DIST_NAME + 1];
  854. int i;
  855. for (i = 0; localfs[i].filesystem; i++) {
  856. snprintfz(dist, NETDATA_FS_MAX_DIST_NAME, "%sdist", localfs[i].filesystem);
  857. localfs[i].enabled = appconfig_get_boolean(&fs_config, NETDATA_FILESYSTEM_CONFIG_NAME, dist,
  858. CONFIG_BOOLEAN_YES);
  859. }
  860. }
  861. /**
  862. * Set maps
  863. *
  864. * When thread is initialized the variable fs_maps is set as null,
  865. * this function fills the variable before to use.
  866. */
  867. static void ebpf_set_maps()
  868. {
  869. localfs[NETDATA_FS_LOCALFS_EXT4].fs_maps = ext4_maps;
  870. localfs[NETDATA_FS_LOCALFS_XFS].fs_maps = xfs_maps;
  871. localfs[NETDATA_FS_LOCALFS_NFS].fs_maps = nfs_maps;
  872. localfs[NETDATA_FS_LOCALFS_ZFS].fs_maps = zfs_maps;
  873. localfs[NETDATA_FS_LOCALFS_BTRFS].fs_maps = btrfs_maps;
  874. }
  875. /**
  876. * Filesystem thread
  877. *
  878. * Thread used to generate socket charts.
  879. *
  880. * @param ptr a pointer to `struct ebpf_module`
  881. *
  882. * @return It always return NULL
  883. */
  884. void *ebpf_filesystem_thread(void *ptr)
  885. {
  886. netdata_thread_cleanup_push(ebpf_filesystem_exit, ptr);
  887. ebpf_module_t *em = (ebpf_module_t *)ptr;
  888. ebpf_set_maps();
  889. ebpf_update_filesystem();
  890. // Initialize optional as zero, to identify when there are not partitions to monitor
  891. em->optional = 0;
  892. #ifdef LIBBPF_MAJOR_VERSION
  893. ebpf_adjust_thread_load(em, default_btf);
  894. #endif
  895. if (ebpf_update_partitions(em)) {
  896. if (em->optional)
  897. netdata_log_info("Netdata cannot monitor the filesystems used on this host.");
  898. goto endfilesystem;
  899. }
  900. int algorithms[NETDATA_EBPF_HIST_MAX_BINS];
  901. ebpf_fill_algorithms(algorithms, NETDATA_EBPF_HIST_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
  902. ebpf_global_labels(filesystem_aggregated_data, filesystem_publish_aggregated, dimensions, dimensions,
  903. algorithms, NETDATA_EBPF_HIST_MAX_BINS);
  904. pthread_mutex_lock(&lock);
  905. ebpf_create_fs_charts(em->update_every);
  906. ebpf_update_stats(&plugin_statistics, em);
  907. pthread_mutex_unlock(&lock);
  908. filesystem_collector(em);
  909. endfilesystem:
  910. ebpf_update_disabled_plugin_stats(em);
  911. netdata_thread_cleanup_pop(1);
  912. return NULL;
  913. }