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