debugfs_zswap.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "debugfs_plugin.h"
  3. static long system_page_size = 4096;
  4. static collected_number pages_to_bytes(collected_number value)
  5. {
  6. return value * system_page_size;
  7. }
  8. struct netdata_zswap_metric {
  9. const char *filename;
  10. const char *chart_id;
  11. const char *title;
  12. const char *units;
  13. RRDSET_TYPE charttype;
  14. int prio;
  15. const char *dimension;
  16. RRD_ALGORITHM algorithm;
  17. int divisor;
  18. int enabled;
  19. int chart_created;
  20. collected_number value;
  21. collected_number (*convertv)(collected_number v);
  22. };
  23. static struct netdata_zswap_metric zswap_calculated_metrics[] = {
  24. {.filename = "",
  25. .chart_id = "pool_compression_ratio",
  26. .dimension = "compression_ratio",
  27. .units = "ratio",
  28. .title = "Zswap compression ratio",
  29. .algorithm = RRD_ALGORITHM_ABSOLUTE,
  30. .charttype = RRDSET_TYPE_LINE,
  31. .enabled = CONFIG_BOOLEAN_YES,
  32. .chart_created = CONFIG_BOOLEAN_NO,
  33. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_COMPRESS_RATIO,
  34. .divisor = 100,
  35. .convertv = NULL,
  36. .value = -1},
  37. };
  38. enum netdata_zswap_calculated {
  39. NETDATA_ZSWAP_COMPRESSION_RATIO_CHART,
  40. };
  41. enum netdata_zwap_independent {
  42. NETDATA_ZSWAP_POOL_TOTAL_SIZE,
  43. NETDATA_ZSWAP_STORED_PAGES,
  44. NETDATA_ZSWAP_POOL_LIMIT_HIT,
  45. NETDATA_ZSWAP_WRITTEN_BACK_PAGES,
  46. NETDATA_ZSWAP_SAME_FILLED_PAGES,
  47. NETDATA_ZSWAP_DUPLICATE_ENTRY,
  48. // Terminator
  49. NETDATA_ZSWAP_SITE_END
  50. };
  51. static struct netdata_zswap_metric zswap_independent_metrics[] = {
  52. // https://elixir.bootlin.com/linux/latest/source/mm/zswap.c
  53. {.filename = "/sys/kernel/debug/zswap/pool_total_size",
  54. .chart_id = "pool_compressed_size",
  55. .dimension = "compressed_size",
  56. .units = "bytes",
  57. .title = "Zswap compressed bytes currently stored",
  58. .algorithm = RRD_ALGORITHM_ABSOLUTE,
  59. .charttype = RRDSET_TYPE_AREA,
  60. .enabled = CONFIG_BOOLEAN_YES,
  61. .chart_created = CONFIG_BOOLEAN_NO,
  62. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_POOL_TOT_SIZE,
  63. .divisor = 1,
  64. .convertv = NULL,
  65. .value = -1},
  66. {.filename = "/sys/kernel/debug/zswap/stored_pages",
  67. .chart_id = "pool_raw_size",
  68. .dimension = "uncompressed_size",
  69. .units = "bytes",
  70. .title = "Zswap uncompressed bytes currently stored",
  71. .algorithm = RRD_ALGORITHM_ABSOLUTE,
  72. .charttype = RRDSET_TYPE_AREA,
  73. .enabled = CONFIG_BOOLEAN_YES,
  74. .chart_created = CONFIG_BOOLEAN_NO,
  75. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_STORED_PAGE,
  76. .divisor = 1,
  77. .convertv = pages_to_bytes,
  78. .value = -1},
  79. {.filename = "/sys/kernel/debug/zswap/pool_limit_hit",
  80. .chart_id = "pool_limit_hit",
  81. .dimension = "limit",
  82. .units = "events/s",
  83. .title = "Zswap pool limit was reached",
  84. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  85. .charttype = RRDSET_TYPE_LINE,
  86. .enabled = CONFIG_BOOLEAN_YES,
  87. .chart_created = CONFIG_BOOLEAN_NO,
  88. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_POOL_LIM_HIT,
  89. .divisor = 1,
  90. .convertv = NULL,
  91. .value = -1},
  92. {.filename = "/sys/kernel/debug/zswap/written_back_pages",
  93. .chart_id = "written_back_raw_bytes",
  94. .dimension = "written_back",
  95. .units = "bytes/s",
  96. .title = "Zswap uncomressed bytes written back when pool limit was reached",
  97. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  98. .charttype = RRDSET_TYPE_AREA,
  99. .enabled = CONFIG_BOOLEAN_YES,
  100. .chart_created = CONFIG_BOOLEAN_NO,
  101. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_WRT_BACK_PAGES,
  102. .divisor = 1,
  103. .convertv = pages_to_bytes,
  104. .value = -1},
  105. {.filename = "/sys/kernel/debug/zswap/same_filled_pages",
  106. .chart_id = "same_filled_raw_size",
  107. .dimension = "same_filled",
  108. .units = "bytes",
  109. .title = "Zswap same-value filled uncompressed bytes currently stored",
  110. .algorithm = RRD_ALGORITHM_ABSOLUTE,
  111. .charttype = RRDSET_TYPE_AREA,
  112. .enabled = CONFIG_BOOLEAN_YES,
  113. .chart_created = CONFIG_BOOLEAN_NO,
  114. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_SAME_FILL_PAGE,
  115. .divisor = 1,
  116. .convertv = pages_to_bytes,
  117. .value = -1},
  118. {.filename = "/sys/kernel/debug/zswap/duplicate_entry",
  119. .chart_id = "duplicate_entry",
  120. .dimension = "duplicate",
  121. .units = "entries/s",
  122. .title = "Zswap duplicate store was encountered",
  123. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  124. .charttype = RRDSET_TYPE_LINE,
  125. .enabled = CONFIG_BOOLEAN_YES,
  126. .chart_created = CONFIG_BOOLEAN_NO,
  127. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_DUPP_ENTRY,
  128. .divisor = 1,
  129. .convertv = NULL,
  130. .value = -1},
  131. // The terminator
  132. {.filename = NULL,
  133. .chart_id = NULL,
  134. .dimension = NULL,
  135. .units = NULL,
  136. .title = NULL,
  137. .algorithm = RRD_ALGORITHM_ABSOLUTE,
  138. .charttype = RRDSET_TYPE_LINE,
  139. .enabled = CONFIG_BOOLEAN_NO,
  140. .chart_created = CONFIG_BOOLEAN_NO,
  141. .prio = -1,
  142. .value = -1}};
  143. enum netdata_zswap_rejected {
  144. NETDATA_ZSWAP_REJECTED_CHART,
  145. NETDATA_ZSWAP_REJECTED_COMPRESS_POOR,
  146. NETDATA_ZSWAP_REJECTED_KMEM_FAIL,
  147. NETDATA_ZSWAP_REJECTED_RALLOC_FAIL,
  148. NETDATA_ZSWAP_REJECTED_RRECLAIM_FAIL,
  149. // Terminator
  150. NETDATA_ZSWAP_REJECTED_END
  151. };
  152. static struct netdata_zswap_metric zswap_rejected_metrics[] = {
  153. {.filename = "/sys/kernel/debug/zswap/",
  154. .chart_id = "rejections",
  155. .dimension = NULL,
  156. .units = "rejections/s",
  157. .title = "Zswap rejections",
  158. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  159. .charttype = RRDSET_TYPE_STACKED,
  160. .enabled = CONFIG_BOOLEAN_YES,
  161. .chart_created = CONFIG_BOOLEAN_NO,
  162. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_REJECTS,
  163. .divisor = 1,
  164. .convertv = NULL,
  165. .value = -1},
  166. {.filename = "/sys/kernel/debug/zswap/reject_compress_poor",
  167. .chart_id = "reject_compress_poor",
  168. .dimension = "compress_poor",
  169. .units = NULL,
  170. .title = NULL,
  171. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  172. .charttype = RRDSET_TYPE_STACKED,
  173. .enabled = CONFIG_BOOLEAN_YES,
  174. .chart_created = CONFIG_BOOLEAN_NO,
  175. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_REJECTS,
  176. .divisor = 1,
  177. .convertv = NULL,
  178. .value = -1},
  179. {.filename = "/sys/kernel/debug/zswap/reject_kmemcache_fail",
  180. .chart_id = "reject_kmemcache_fail",
  181. .dimension = "kmemcache_fail",
  182. .units = NULL,
  183. .title = NULL,
  184. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  185. .charttype = RRDSET_TYPE_STACKED,
  186. .enabled = CONFIG_BOOLEAN_YES,
  187. .chart_created = CONFIG_BOOLEAN_NO,
  188. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_REJECTS,
  189. .divisor = 1,
  190. .convertv = NULL,
  191. .value = -1},
  192. {.filename = "/sys/kernel/debug/zswap/reject_alloc_fail",
  193. .chart_id = "reject_alloc_fail",
  194. .dimension = "alloc_fail",
  195. .units = NULL,
  196. .title = NULL,
  197. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  198. .charttype = RRDSET_TYPE_STACKED,
  199. .enabled = CONFIG_BOOLEAN_YES,
  200. .chart_created = CONFIG_BOOLEAN_NO,
  201. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_REJECTS,
  202. .divisor = 1,
  203. .convertv = NULL,
  204. .value = -1},
  205. {.filename = "/sys/kernel/debug/zswap/reject_reclaim_fail",
  206. .chart_id = "reject_reclaim_fail",
  207. .dimension = "reclaim_fail",
  208. .units = NULL,
  209. .title = NULL,
  210. .algorithm = RRD_ALGORITHM_INCREMENTAL,
  211. .charttype = RRDSET_TYPE_STACKED,
  212. .enabled = CONFIG_BOOLEAN_YES,
  213. .chart_created = CONFIG_BOOLEAN_NO,
  214. .prio = NETDATA_CHART_PRIO_SYSTEM_ZSWAP_REJECTS,
  215. .divisor = 1,
  216. .convertv = NULL,
  217. .value = -1},
  218. // The terminator
  219. {.filename = NULL,
  220. .chart_id = NULL,
  221. .dimension = NULL,
  222. .units = NULL,
  223. .title = NULL,
  224. .algorithm = RRD_ALGORITHM_ABSOLUTE,
  225. .charttype = RRDSET_TYPE_STACKED,
  226. .enabled = CONFIG_BOOLEAN_NO,
  227. .chart_created = CONFIG_BOOLEAN_NO,
  228. .prio = -1,
  229. .value = -1}};
  230. int zswap_collect_data(struct netdata_zswap_metric *metric)
  231. {
  232. char filename[FILENAME_MAX + 1];
  233. snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, metric->filename);
  234. if (read_single_number_file(filename, (unsigned long long *)&metric->value)) {
  235. netdata_log_error("Cannot read file %s", filename);
  236. return 1;
  237. }
  238. if (metric->convertv)
  239. metric->value = metric->convertv(metric->value);
  240. return 0;
  241. }
  242. static void
  243. zswap_send_chart(struct netdata_zswap_metric *metric, int update_every, const char *name, const char *option)
  244. {
  245. fprintf(
  246. stdout,
  247. "CHART system.zswap_%s '' '%s' '%s' 'zswap' '' '%s' %d %d '%s' 'debugfs.plugin' '%s'\n",
  248. metric->chart_id,
  249. metric->title,
  250. metric->units,
  251. debugfs_rrdset_type_name(metric->charttype),
  252. metric->prio,
  253. update_every,
  254. (!option) ? "" : option,
  255. name);
  256. }
  257. static void zswap_send_dimension(struct netdata_zswap_metric *metric)
  258. {
  259. int div = metric->divisor > 0 ? metric->divisor : 1;
  260. fprintf(
  261. stdout,
  262. "DIMENSION '%s' '%s' %s 1 %d ''\n",
  263. metric->dimension,
  264. metric->dimension,
  265. debugfs_rrd_algorithm_name(metric->algorithm),
  266. div);
  267. }
  268. static void zswap_send_begin(struct netdata_zswap_metric *metric)
  269. {
  270. fprintf(stdout, "BEGIN system.zswap_%s\n", metric->chart_id);
  271. }
  272. static void zswap_send_set(struct netdata_zswap_metric *metric)
  273. {
  274. fprintf(stdout, "SET %s = %lld\n", metric->dimension, metric->value);
  275. }
  276. static void zswap_send_end_and_flush()
  277. {
  278. fprintf(stdout, "END\n");
  279. fflush(stdout);
  280. }
  281. static void zswap_independent_chart(struct netdata_zswap_metric *metric, int update_every, const char *name)
  282. {
  283. if (unlikely(!metric->chart_created)) {
  284. metric->chart_created = CONFIG_BOOLEAN_YES;
  285. zswap_send_chart(metric, update_every, name, NULL);
  286. zswap_send_dimension(metric);
  287. }
  288. zswap_send_begin(metric);
  289. zswap_send_set(metric);
  290. zswap_send_end_and_flush();
  291. }
  292. void zswap_reject_chart(int update_every, const char *name)
  293. {
  294. struct netdata_zswap_metric *metric = &zswap_rejected_metrics[NETDATA_ZSWAP_REJECTED_CHART];
  295. if (unlikely(!metric->chart_created)) {
  296. metric->chart_created = CONFIG_BOOLEAN_YES;
  297. zswap_send_chart(metric, update_every, name, NULL);
  298. for (int i = NETDATA_ZSWAP_REJECTED_COMPRESS_POOR; zswap_rejected_metrics[i].filename; i++) {
  299. metric = &zswap_rejected_metrics[i];
  300. if (likely(metric->enabled))
  301. zswap_send_dimension(metric);
  302. }
  303. }
  304. metric = &zswap_rejected_metrics[NETDATA_ZSWAP_REJECTED_CHART];
  305. zswap_send_begin(metric);
  306. for (int i = NETDATA_ZSWAP_REJECTED_COMPRESS_POOR; zswap_rejected_metrics[i].filename; i++) {
  307. metric = &zswap_rejected_metrics[i];
  308. if (likely(metric->enabled))
  309. zswap_send_set(metric);
  310. }
  311. zswap_send_end_and_flush();
  312. }
  313. static void zswap_obsolete_charts(int update_every, const char *name)
  314. {
  315. struct netdata_zswap_metric *metric = NULL;
  316. for (int i = 0; zswap_independent_metrics[i].filename; i++) {
  317. metric = &zswap_independent_metrics[i];
  318. if (likely(metric->chart_created))
  319. zswap_send_chart(metric, update_every, name, "obsolete");
  320. }
  321. metric = &zswap_rejected_metrics[NETDATA_ZSWAP_REJECTED_CHART];
  322. if (likely(metric->chart_created))
  323. zswap_send_chart(metric, update_every, name, "obsolete");
  324. metric = &zswap_calculated_metrics[NETDATA_ZSWAP_COMPRESSION_RATIO_CHART];
  325. if (likely(metric->chart_created))
  326. zswap_send_chart(metric, update_every, name, "obsolete");
  327. }
  328. #define ZSWAP_STATE_SIZE 1 // Y or N
  329. static int debugfs_is_zswap_enabled()
  330. {
  331. char filename[FILENAME_MAX + 1];
  332. snprintfz(filename, FILENAME_MAX, "/sys/module/zswap/parameters/enabled"); // host prefix is not needed here
  333. char state[ZSWAP_STATE_SIZE + 1];
  334. int ret = read_file(filename, state, ZSWAP_STATE_SIZE);
  335. if (unlikely(!ret && !strcmp(state, "Y"))) {
  336. return 0;
  337. }
  338. return 1;
  339. }
  340. int do_debugfs_zswap(int update_every, const char *name)
  341. {
  342. static int check_if_enabled = 1;
  343. if (likely(check_if_enabled && debugfs_is_zswap_enabled())) {
  344. netdata_log_info("Zswap is disabled");
  345. return 1;
  346. }
  347. check_if_enabled = 0;
  348. system_page_size = sysconf(_SC_PAGESIZE);
  349. struct netdata_zswap_metric *metric = NULL;
  350. int enabled = 0;
  351. for (int i = 0; zswap_independent_metrics[i].filename; i++) {
  352. metric = &zswap_independent_metrics[i];
  353. if (unlikely(!metric->enabled))
  354. continue;
  355. if (unlikely(!(metric->enabled = !zswap_collect_data(metric))))
  356. continue;
  357. zswap_independent_chart(metric, update_every, name);
  358. enabled++;
  359. }
  360. struct netdata_zswap_metric *metric_size = &zswap_independent_metrics[NETDATA_ZSWAP_POOL_TOTAL_SIZE];
  361. struct netdata_zswap_metric *metric_raw_size = &zswap_independent_metrics[NETDATA_ZSWAP_STORED_PAGES];
  362. if (metric_size->enabled && metric_raw_size->enabled) {
  363. metric = &zswap_calculated_metrics[NETDATA_ZSWAP_COMPRESSION_RATIO_CHART];
  364. metric->value = 0;
  365. if (metric_size->value > 0)
  366. metric->value =
  367. (collected_number)((NETDATA_DOUBLE)metric_raw_size->value / (NETDATA_DOUBLE)metric_size->value * 100);
  368. zswap_independent_chart(metric, update_every, name);
  369. }
  370. int enabled_rejected = 0;
  371. for (int i = NETDATA_ZSWAP_REJECTED_COMPRESS_POOR; zswap_rejected_metrics[i].filename; i++) {
  372. metric = &zswap_rejected_metrics[i];
  373. if (unlikely(!metric->enabled))
  374. continue;
  375. if (unlikely(!(metric->enabled = !zswap_collect_data(metric))))
  376. continue;
  377. enabled++;
  378. enabled_rejected++;
  379. }
  380. if (likely(enabled_rejected > 0))
  381. zswap_reject_chart(update_every, name);
  382. if (unlikely(!enabled)) {
  383. zswap_obsolete_charts(update_every, name);
  384. return 1;
  385. }
  386. return 0;
  387. }