ebpf_filesystem.c 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  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. efp->hread.title,
  321. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  322. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hread.order, update_every);
  323. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
  324. efp->hwrite.title,
  325. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  326. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hwrite.order, update_every);
  327. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name, efp->hopen.title,
  328. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  329. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hopen.order, update_every);
  330. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hadditional.name, efp->hadditional.title,
  331. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  332. NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hadditional.order,
  333. update_every);
  334. }
  335. efp->flags = flags;
  336. }
  337. }
  338. /**
  339. * Create Filesystem chart
  340. *
  341. * Create latency charts
  342. *
  343. * @param update_every value to overwrite the update frequency set by the server.
  344. */
  345. static void ebpf_create_fs_charts(int update_every)
  346. {
  347. static int order = NETDATA_CHART_PRIO_EBPF_FILESYSTEM_CHARTS;
  348. char chart_name[64], title[256], family[64], ctx[64];
  349. int i;
  350. uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED|NETDATA_FILESYSTEM_REMOVE_CHARTS;
  351. for (i = 0; localfs[i].filesystem; i++) {
  352. ebpf_filesystem_partitions_t *efp = &localfs[i];
  353. uint32_t flags = efp->flags;
  354. if (flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION && !(flags & test)) {
  355. snprintfz(title, 255, "%s latency for each read request.", efp->filesystem);
  356. snprintfz(family, 63, "%s_latency", efp->family);
  357. snprintfz(chart_name, 63, "%s_read_latency", efp->filesystem);
  358. efp->hread.name = strdupz(chart_name);
  359. efp->hread.title = strdupz(title);
  360. efp->hread.ctx = NULL;
  361. efp->hread.order = order;
  362. efp->family_name = strdupz(family);
  363. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
  364. efp->hread.title,
  365. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  366. "filesystem.read_latency", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  367. ebpf_create_global_dimension,
  368. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  369. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  370. order++;
  371. snprintfz(title, 255, "%s latency for each write request.", efp->filesystem);
  372. snprintfz(chart_name, 63, "%s_write_latency", efp->filesystem);
  373. efp->hwrite.name = strdupz(chart_name);
  374. efp->hwrite.title = strdupz(title);
  375. efp->hwrite.ctx = NULL;
  376. efp->hwrite.order = order;
  377. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
  378. efp->hwrite.title,
  379. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  380. "filesystem.write_latency", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  381. ebpf_create_global_dimension,
  382. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  383. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  384. order++;
  385. snprintfz(title, 255, "%s latency for each open request.", efp->filesystem);
  386. snprintfz(chart_name, 63, "%s_open_latency", efp->filesystem);
  387. efp->hopen.name = strdupz(chart_name);
  388. efp->hopen.title = strdupz(title);
  389. efp->hopen.ctx = NULL;
  390. efp->hopen.order = order;
  391. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
  392. efp->hopen.title,
  393. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  394. "filesystem.open_latency", NETDATA_EBPF_CHART_TYPE_STACKED, order,
  395. ebpf_create_global_dimension,
  396. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  397. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  398. order++;
  399. char *type = (efp->flags & NETDATA_FILESYSTEM_ATTR_CHARTS) ? "attribute" : "sync";
  400. snprintfz(title, 255, "%s latency for each %s request.", efp->filesystem, type);
  401. snprintfz(chart_name, 63, "%s_%s_latency", efp->filesystem, type);
  402. snprintfz(ctx, 63, "filesystem.%s_latency", type);
  403. efp->hadditional.name = strdupz(chart_name);
  404. efp->hadditional.title = strdupz(title);
  405. efp->hadditional.ctx = strdupz(ctx);
  406. efp->hadditional.order = order;
  407. ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hadditional.name, efp->hadditional.title,
  408. EBPF_COMMON_DIMENSION_CALL, efp->family_name,
  409. ctx, NETDATA_EBPF_CHART_TYPE_STACKED, order, ebpf_create_global_dimension,
  410. filesystem_publish_aggregated, NETDATA_EBPF_HIST_MAX_BINS,
  411. update_every, NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
  412. order++;
  413. efp->flags |= NETDATA_FILESYSTEM_FLAG_CHART_CREATED;
  414. }
  415. }
  416. fflush(stdout);
  417. }
  418. /**
  419. * Initialize eBPF data
  420. *
  421. * @param em main thread structure.
  422. *
  423. * @return it returns 0 on success and -1 otherwise.
  424. */
  425. int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
  426. {
  427. pthread_mutex_lock(&lock);
  428. int i;
  429. const char *saved_name = em->info.thread_name;
  430. uint64_t kernels = em->kernels;
  431. for (i = 0; localfs[i].filesystem; i++) {
  432. ebpf_filesystem_partitions_t *efp = &localfs[i];
  433. if (!efp->probe_links && efp->flags & NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM) {
  434. em->info.thread_name = efp->filesystem;
  435. em->kernels = efp->kernels;
  436. em->maps = efp->fs_maps;
  437. #ifdef LIBBPF_MAJOR_VERSION
  438. ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
  439. #endif
  440. if (em->load & EBPF_LOAD_LEGACY) {
  441. efp->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &efp->objects);
  442. if (!efp->probe_links) {
  443. em->info.thread_name = saved_name;
  444. em->kernels = kernels;
  445. em->maps = NULL;
  446. pthread_mutex_unlock(&lock);
  447. return -1;
  448. }
  449. }
  450. #ifdef LIBBPF_MAJOR_VERSION
  451. else {
  452. efp->fs_obj = filesystem_bpf__open();
  453. if (!efp->fs_obj) {
  454. em->info.thread_name = saved_name;
  455. em->kernels = kernels;
  456. return -1;
  457. } else {
  458. if (ebpf_fs_load_and_attach(em->maps, efp->fs_obj,
  459. efp->functions, NULL))
  460. return -1;
  461. }
  462. }
  463. #endif
  464. efp->flags |= NETDATA_FILESYSTEM_FLAG_HAS_PARTITION;
  465. ebpf_update_kernel_memory(&plugin_statistics, efp->fs_maps, EBPF_ACTION_STAT_ADD);
  466. // Nedeed for filesystems like btrfs
  467. if ((efp->flags & NETDATA_FILESYSTEM_FILL_ADDRESS_TABLE) && (efp->addresses.function)) {
  468. ebpf_load_addresses(&efp->addresses, efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd);
  469. }
  470. }
  471. efp->flags &= ~NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
  472. }
  473. em->info.thread_name = saved_name;
  474. pthread_mutex_unlock(&lock);
  475. em->kernels = kernels;
  476. em->maps = NULL;
  477. if (!dimensions) {
  478. dimensions = ebpf_fill_histogram_dimension(NETDATA_EBPF_HIST_MAX_BINS);
  479. memset(filesystem_aggregated_data, 0 , NETDATA_EBPF_HIST_MAX_BINS * sizeof(netdata_syscall_stat_t));
  480. memset(filesystem_publish_aggregated, 0 , NETDATA_EBPF_HIST_MAX_BINS * sizeof(netdata_publish_syscall_t));
  481. filesystem_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
  482. }
  483. return 0;
  484. }
  485. /**
  486. * Read Local partitions
  487. *
  488. * @return the total of partitions that will be monitored
  489. */
  490. static int ebpf_read_local_partitions()
  491. {
  492. char filename[FILENAME_MAX + 1];
  493. snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", netdata_configured_host_prefix);
  494. procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
  495. if(unlikely(!ff)) {
  496. snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", netdata_configured_host_prefix);
  497. ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
  498. if(unlikely(!ff)) return 0;
  499. }
  500. ff = procfile_readall(ff);
  501. if(unlikely(!ff))
  502. return 0;
  503. int count = 0;
  504. unsigned long l, i, lines = procfile_lines(ff);
  505. for (i = 0; localfs[i].filesystem; i++) {
  506. localfs[i].flags |= NETDATA_FILESYSTEM_REMOVE_CHARTS;
  507. }
  508. for(l = 0; l < lines ; l++) {
  509. // In "normal" situation the expected value is at column 7
  510. // When `shared` options is added to mount information, the filesystem is at column 8
  511. // Finally when we have systemd starting netdata, it will be at column 9
  512. unsigned long index = procfile_linewords(ff, l) - 3;
  513. char *fs = procfile_lineword(ff, l, index);
  514. for (i = 0; localfs[i].filesystem; i++) {
  515. ebpf_filesystem_partitions_t *w = &localfs[i];
  516. if (w->enabled && (!strcmp(fs, w->filesystem) ||
  517. (w->optional_filesystem && !strcmp(fs, w->optional_filesystem)))) {
  518. localfs[i].flags |= NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
  519. localfs[i].flags &= ~NETDATA_FILESYSTEM_REMOVE_CHARTS;
  520. count++;
  521. break;
  522. }
  523. }
  524. }
  525. procfile_close(ff);
  526. return count;
  527. }
  528. /**
  529. * Update partition
  530. *
  531. * Update the partition structures before to plot
  532. *
  533. * @param em main thread structure
  534. *
  535. * @return 0 on success and -1 otherwise.
  536. */
  537. static int ebpf_update_partitions(ebpf_module_t *em)
  538. {
  539. static time_t update_every = 0;
  540. time_t curr = now_realtime_sec();
  541. if (curr < update_every)
  542. return 0;
  543. update_every = curr + 5 * em->update_every;
  544. if (!ebpf_read_local_partitions()) {
  545. em->optional = -1;
  546. return -1;
  547. }
  548. if (ebpf_filesystem_initialize_ebpf_data(em)) {
  549. return -1;
  550. }
  551. return 0;
  552. }
  553. /*****************************************************************
  554. *
  555. * CLEANUP FUNCTIONS
  556. *
  557. *****************************************************************/
  558. /*
  559. * Cleanup eBPF data
  560. */
  561. void ebpf_filesystem_cleanup_ebpf_data()
  562. {
  563. int i;
  564. for (i = 0; localfs[i].filesystem; i++) {
  565. ebpf_filesystem_partitions_t *efp = &localfs[i];
  566. if (efp->probe_links) {
  567. freez(efp->family_name);
  568. efp->family_name = NULL;
  569. freez(efp->hread.name);
  570. efp->hread.name = NULL;
  571. freez(efp->hread.title);
  572. efp->hread.title = NULL;
  573. freez(efp->hwrite.name);
  574. efp->hwrite.name = NULL;
  575. freez(efp->hwrite.title);
  576. efp->hwrite.title = NULL;
  577. freez(efp->hopen.name);
  578. efp->hopen.name = NULL;
  579. freez(efp->hopen.title);
  580. efp->hopen.title = NULL;
  581. freez(efp->hadditional.name);
  582. efp->hadditional.name = NULL;
  583. freez(efp->hadditional.title);
  584. efp->hadditional.title = NULL;
  585. freez(efp->hadditional.ctx);
  586. efp->hadditional.ctx = NULL;
  587. }
  588. }
  589. }
  590. /**
  591. * Obsolete global
  592. *
  593. * Obsolete global charts created by thread.
  594. *
  595. * @param em a pointer to `struct ebpf_module`
  596. */
  597. static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
  598. {
  599. int i;
  600. for (i = 0; localfs[i].filesystem; i++) {
  601. ebpf_filesystem_partitions_t *efp = &localfs[i];
  602. if (!efp->objects)
  603. continue;
  604. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  605. efp->hread.name,
  606. efp->hread.title,
  607. EBPF_COMMON_DIMENSION_CALL,
  608. efp->family_name,
  609. NETDATA_EBPF_CHART_TYPE_STACKED,
  610. "filesystem.read_latency",
  611. efp->hread.order,
  612. em->update_every);
  613. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  614. efp->hwrite.name,
  615. efp->hwrite.title,
  616. EBPF_COMMON_DIMENSION_CALL,
  617. efp->family_name,
  618. NETDATA_EBPF_CHART_TYPE_STACKED,
  619. "filesystem.write_latency",
  620. efp->hwrite.order,
  621. em->update_every);
  622. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  623. efp->hopen.name,
  624. efp->hopen.title,
  625. EBPF_COMMON_DIMENSION_CALL,
  626. efp->family_name,
  627. NETDATA_EBPF_CHART_TYPE_STACKED,
  628. "filesystem.open_latency",
  629. efp->hopen.order,
  630. em->update_every);
  631. ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY,
  632. efp->hadditional.name,
  633. efp->hadditional.title,
  634. EBPF_COMMON_DIMENSION_CALL,
  635. efp->family_name,
  636. NETDATA_EBPF_CHART_TYPE_STACKED,
  637. efp->hadditional.ctx,
  638. efp->hadditional.order,
  639. em->update_every);
  640. }
  641. }
  642. /**
  643. * Filesystem exit
  644. *
  645. * Cancel child thread.
  646. *
  647. * @param ptr thread data.
  648. */
  649. static void ebpf_filesystem_exit(void *ptr)
  650. {
  651. ebpf_module_t *em = (ebpf_module_t *)ptr;
  652. if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
  653. pthread_mutex_lock(&lock);
  654. ebpf_obsolete_filesystem_global(em);
  655. pthread_mutex_unlock(&lock);
  656. fflush(stdout);
  657. }
  658. ebpf_filesystem_cleanup_ebpf_data();
  659. if (dimensions) {
  660. ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  661. dimensions = NULL;
  662. }
  663. freez(filesystem_hash_values);
  664. int i;
  665. for (i = 0; localfs[i].filesystem; i++) {
  666. ebpf_filesystem_partitions_t *efp = &localfs[i];
  667. if (!efp->probe_links)
  668. continue;
  669. ebpf_unload_legacy_code(efp->objects, efp->probe_links);
  670. efp->objects = NULL;
  671. efp->probe_links = NULL;
  672. efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
  673. }
  674. pthread_mutex_lock(&ebpf_exit_cleanup);
  675. em->enabled = NETDATA_THREAD_EBPF_STOPPED;
  676. ebpf_update_stats(&plugin_statistics, em);
  677. pthread_mutex_unlock(&ebpf_exit_cleanup);
  678. }
  679. /*****************************************************************
  680. *
  681. * MAIN THREAD
  682. *
  683. *****************************************************************/
  684. /**
  685. * Select hist
  686. *
  687. * Select a histogram to store data.
  688. *
  689. * @param efp pointer for the structure with pointers.
  690. * @param id histogram selector
  691. *
  692. * @return It returns a pointer for the histogram
  693. */
  694. static inline netdata_ebpf_histogram_t *select_hist(ebpf_filesystem_partitions_t *efp, uint32_t *idx, uint32_t id)
  695. {
  696. if (id < NETDATA_KEY_CALLS_READ) {
  697. *idx = id;
  698. return &efp->hread;
  699. } else if (id < NETDATA_KEY_CALLS_WRITE) {
  700. *idx = id - NETDATA_KEY_CALLS_READ;
  701. return &efp->hwrite;
  702. } else if (id < NETDATA_KEY_CALLS_OPEN) {
  703. *idx = id - NETDATA_KEY_CALLS_WRITE;
  704. return &efp->hopen;
  705. } else if (id < NETDATA_KEY_CALLS_SYNC ){
  706. *idx = id - NETDATA_KEY_CALLS_OPEN;
  707. return &efp->hadditional;
  708. }
  709. return NULL;
  710. }
  711. /**
  712. * Read hard disk table
  713. *
  714. * @param efp structure with filesystem monitored
  715. * @param fd file descriptor to get data.
  716. * @param maps_per_core do I need to read all cores?
  717. *
  718. * Read the table with number of calls for all functions
  719. */
  720. static void read_filesystem_table(ebpf_filesystem_partitions_t *efp, int fd, int maps_per_core)
  721. {
  722. netdata_idx_t *values = filesystem_hash_values;
  723. uint32_t key;
  724. uint32_t idx;
  725. for (key = 0; key < NETDATA_KEY_CALLS_SYNC; key++) {
  726. netdata_ebpf_histogram_t *w = select_hist(efp, &idx, key);
  727. if (!w) {
  728. continue;
  729. }
  730. int test = bpf_map_lookup_elem(fd, &key, values);
  731. if (test < 0) {
  732. continue;
  733. }
  734. uint64_t total = 0;
  735. int i;
  736. int end = (maps_per_core) ? ebpf_nprocs : 1;
  737. for (i = 0; i < end; i++) {
  738. total += values[i];
  739. }
  740. if (idx >= NETDATA_EBPF_HIST_MAX_BINS)
  741. idx = NETDATA_EBPF_HIST_MAX_BINS - 1;
  742. w->histogram[idx] = total;
  743. }
  744. }
  745. /**
  746. * Read hard disk table
  747. *
  748. * Read the table with number of calls for all functions
  749. *
  750. * @param maps_per_core do I need to read all cores?
  751. */
  752. static void read_filesystem_tables(int maps_per_core)
  753. {
  754. int i;
  755. for (i = 0; localfs[i].filesystem; i++) {
  756. ebpf_filesystem_partitions_t *efp = &localfs[i];
  757. if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
  758. read_filesystem_table(efp, efp->fs_maps[NETDATA_MAIN_FS_TABLE].map_fd, maps_per_core);
  759. }
  760. }
  761. }
  762. /**
  763. * Socket read hash
  764. *
  765. * This is the thread callback.
  766. * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
  767. *
  768. * @param ptr It is a NULL value for this thread.
  769. *
  770. * @return It always returns NULL.
  771. */
  772. void ebpf_filesystem_read_hash(ebpf_module_t *em)
  773. {
  774. ebpf_obsolete_fs_charts(em->update_every);
  775. (void) ebpf_update_partitions(em);
  776. if (em->optional)
  777. return;
  778. read_filesystem_tables(em->maps_per_core);
  779. }
  780. /**
  781. * Send Hard disk data
  782. *
  783. * Send hard disk information to Netdata.
  784. */
  785. static void ebpf_histogram_send_data()
  786. {
  787. uint32_t i;
  788. uint32_t test = NETDATA_FILESYSTEM_FLAG_HAS_PARTITION | NETDATA_FILESYSTEM_REMOVE_CHARTS;
  789. for (i = 0; localfs[i].filesystem; i++) {
  790. ebpf_filesystem_partitions_t *efp = &localfs[i];
  791. if ((efp->flags & test) == NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
  792. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
  793. efp->hread.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  794. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
  795. efp->hwrite.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  796. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
  797. efp->hopen.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  798. write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hadditional.name,
  799. efp->hadditional.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
  800. }
  801. }
  802. }
  803. /**
  804. * Main loop for this collector.
  805. *
  806. * @param em main structure for this thread
  807. */
  808. static void filesystem_collector(ebpf_module_t *em)
  809. {
  810. int update_every = em->update_every;
  811. heartbeat_t hb;
  812. heartbeat_init(&hb);
  813. int counter = update_every - 1;
  814. uint32_t running_time = 0;
  815. uint32_t lifetime = em->lifetime;
  816. while (!ebpf_plugin_exit && running_time < lifetime) {
  817. (void)heartbeat_next(&hb, USEC_PER_SEC);
  818. if (ebpf_plugin_exit || ++counter != update_every)
  819. continue;
  820. counter = 0;
  821. ebpf_filesystem_read_hash(em);
  822. pthread_mutex_lock(&lock);
  823. ebpf_create_fs_charts(update_every);
  824. ebpf_histogram_send_data();
  825. pthread_mutex_unlock(&lock);
  826. pthread_mutex_lock(&ebpf_exit_cleanup);
  827. if (running_time && !em->running_time)
  828. running_time = update_every;
  829. else
  830. running_time += update_every;
  831. em->running_time = running_time;
  832. pthread_mutex_unlock(&ebpf_exit_cleanup);
  833. }
  834. }
  835. /*****************************************************************
  836. *
  837. * ENTRY THREAD
  838. *
  839. *****************************************************************/
  840. /**
  841. * Update Filesystem
  842. *
  843. * Update file system structure using values read from configuration file.
  844. */
  845. static void ebpf_update_filesystem()
  846. {
  847. char dist[NETDATA_FS_MAX_DIST_NAME + 1];
  848. int i;
  849. for (i = 0; localfs[i].filesystem; i++) {
  850. snprintfz(dist, NETDATA_FS_MAX_DIST_NAME, "%sdist", localfs[i].filesystem);
  851. localfs[i].enabled = appconfig_get_boolean(&fs_config, NETDATA_FILESYSTEM_CONFIG_NAME, dist,
  852. CONFIG_BOOLEAN_YES);
  853. }
  854. }
  855. /**
  856. * Set maps
  857. *
  858. * When thread is initialized the variable fs_maps is set as null,
  859. * this function fills the variable before to use.
  860. */
  861. static void ebpf_set_maps()
  862. {
  863. localfs[NETDATA_FS_LOCALFS_EXT4].fs_maps = ext4_maps;
  864. localfs[NETDATA_FS_LOCALFS_XFS].fs_maps = xfs_maps;
  865. localfs[NETDATA_FS_LOCALFS_NFS].fs_maps = nfs_maps;
  866. localfs[NETDATA_FS_LOCALFS_ZFS].fs_maps = zfs_maps;
  867. localfs[NETDATA_FS_LOCALFS_BTRFS].fs_maps = btrfs_maps;
  868. }
  869. /**
  870. * Filesystem thread
  871. *
  872. * Thread used to generate socket charts.
  873. *
  874. * @param ptr a pointer to `struct ebpf_module`
  875. *
  876. * @return It always return NULL
  877. */
  878. void *ebpf_filesystem_thread(void *ptr)
  879. {
  880. netdata_thread_cleanup_push(ebpf_filesystem_exit, ptr);
  881. ebpf_module_t *em = (ebpf_module_t *)ptr;
  882. ebpf_set_maps();
  883. ebpf_update_filesystem();
  884. // Initialize optional as zero, to identify when there are not partitions to monitor
  885. em->optional = 0;
  886. #ifdef LIBBPF_MAJOR_VERSION
  887. ebpf_adjust_thread_load(em, default_btf);
  888. #endif
  889. if (ebpf_update_partitions(em)) {
  890. if (em->optional)
  891. netdata_log_info("Netdata cannot monitor the filesystems used on this host.");
  892. goto endfilesystem;
  893. }
  894. int algorithms[NETDATA_EBPF_HIST_MAX_BINS];
  895. ebpf_fill_algorithms(algorithms, NETDATA_EBPF_HIST_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
  896. ebpf_global_labels(filesystem_aggregated_data, filesystem_publish_aggregated, dimensions, dimensions,
  897. algorithms, NETDATA_EBPF_HIST_MAX_BINS);
  898. pthread_mutex_lock(&lock);
  899. ebpf_create_fs_charts(em->update_every);
  900. ebpf_update_stats(&plugin_statistics, em);
  901. pthread_mutex_unlock(&lock);
  902. filesystem_collector(em);
  903. endfilesystem:
  904. ebpf_update_disabled_plugin_stats(em);
  905. netdata_thread_cleanup_pop(1);
  906. return NULL;
  907. }