master
c 356 lines 11.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 static const char *pci_aer_dirname = NULL;
6
7 typedef enum __attribute__((packed)) {
8 AER_DEV_NONFATAL = (1 << 0),
9 AER_DEV_CORRECTABLE = (1 << 1),
10 AER_DEV_FATAL = (1 << 2),
11 AER_ROOTPORT_TOTAL_ERR_COR = (1 << 3),
12 AER_ROOTPORT_TOTAL_ERR_FATAL = (1 << 4),
13 } AER_TYPE;
14
15 struct aer_value {
16 kernel_uint_t count;
17 RRDDIM *rd;
18 };
19
20 struct aer_entry {
21 bool updated;
22
23 STRING *name;
24 AER_TYPE type;
25
26 procfile *ff;
27 DICTIONARY *values;
28
29 RRDSET *st;
30 };
31
32 DICTIONARY *aer_root = NULL;
33
34 static bool aer_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
35 struct aer_value *v = old_value;
36 struct aer_value *nv = new_value;
37
38 v->count = nv->count;
39
40 return false;
41 }
42
43 static void aer_entry_free_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
44 struct aer_entry *a = value;
45 dictionary_destroy(a->values);
46 string_freez(a->name);
47 procfile_close(a->ff);
48 }
49
50 static void aer_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
51 struct aer_entry *a = value;
52 a->values = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE|DICT_OPTION_FIXED_SIZE, &dictionary_stats_category_collectors, sizeof(struct aer_value));
53 dictionary_register_conflict_callback(a->values, aer_value_conflict_callback, NULL);
54 }
55
56 static void add_pci_aer(const char *base_dir, const char *d_name, AER_TYPE type) {
57 char buffer[FILENAME_MAX + 1];
58 snprintfz(buffer, FILENAME_MAX, "%s/%s", base_dir, d_name);
59 struct aer_entry *a = dictionary_set(aer_root, buffer, NULL, sizeof(struct aer_entry));
60
61 if(!a->name)
62 a->name = string_strdupz(d_name);
63
64 a->type = type;
65 }
66
67 static bool recursively_find_pci_aer(AER_TYPE types, const char *base_dir, const char *d_name, int depth) {
68 if(depth > 100)
69 return false;
70
71 char buffer[FILENAME_MAX + 1];
72 snprintfz(buffer, FILENAME_MAX, "%s/%s", base_dir, d_name);
73 DIR *dir = opendir(buffer);
74 if(unlikely(!dir)) {
75 collector_error("Cannot read PCI_AER directory '%s'", buffer);
76 return true;
77 }
78
79 struct dirent *de = NULL;
80 while((de = readdir(dir))) {
81 if(de->d_type == DT_DIR) {
82 if(de->d_name[0] == '.')
83 continue;
84
85 recursively_find_pci_aer(types, buffer, de->d_name, depth + 1);
86 }
87 else if(de->d_type == DT_REG) {
88 if((types & AER_DEV_NONFATAL) && strcmp(de->d_name, "aer_dev_nonfatal") == 0) {
89 add_pci_aer(buffer, de->d_name, AER_DEV_NONFATAL);
90 }
91 else if((types & AER_DEV_CORRECTABLE) && strcmp(de->d_name, "aer_dev_correctable") == 0) {
92 add_pci_aer(buffer, de->d_name, AER_DEV_CORRECTABLE);
93 }
94 else if((types & AER_DEV_FATAL) && strcmp(de->d_name, "aer_dev_fatal") == 0) {
95 add_pci_aer(buffer, de->d_name, AER_DEV_FATAL);
96 }
97 else if((types & AER_ROOTPORT_TOTAL_ERR_COR) && strcmp(de->d_name, "aer_rootport_total_err_cor") == 0) {
98 add_pci_aer(buffer, de->d_name, AER_ROOTPORT_TOTAL_ERR_COR);
99 }
100 else if((types & AER_ROOTPORT_TOTAL_ERR_FATAL) && strcmp(de->d_name, "aer_rootport_total_err_fatal") == 0) {
101 add_pci_aer(buffer, de->d_name, AER_ROOTPORT_TOTAL_ERR_FATAL);
102 }
103 }
104 }
105 closedir(dir);
106 return true;
107 }
108
109 static void find_all_pci_aer(AER_TYPE types) {
110 char name[FILENAME_MAX + 1];
111 snprintfz(name, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices");
112 pci_aer_dirname = inicfg_get(&netdata_config, "plugin:proc:/sys/devices/pci/aer", "directory to monitor", name);
113
114 DIR *dir = opendir(pci_aer_dirname);
115 if(unlikely(!dir)) {
116 collector_error("Cannot read PCI_AER directory '%s'", pci_aer_dirname);
117 return;
118 }
119
120 struct dirent *de = NULL;
121 while((de = readdir(dir))) {
122 if(de->d_type == DT_DIR && de->d_name[0] == 'p' && de->d_name[1] == 'c' && de->d_name[2] == 'i' && isdigit(de->d_name[3]))
123 recursively_find_pci_aer(types, pci_aer_dirname, de->d_name, 1);
124 }
125 closedir(dir);
126 }
127
128 static void read_pci_aer_values(const char *filename, struct aer_entry *t) {
129 t->updated = false;
130
131 if(unlikely(!t->ff)) {
132 t->ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
133 if(unlikely(!t->ff))
134 return;
135 }
136
137 t->ff = procfile_readall(t->ff);
138 if(unlikely(!t->ff || procfile_lines(t->ff) < 1 || procfile_linewords(t->ff, 0) < 1))
139 return;
140
141 size_t lines = procfile_lines(t->ff);
142 for(size_t l = 0; l < lines ; l++) {
143 if(procfile_linewords(t->ff, l) != 2)
144 continue;
145
146 struct aer_value v = {
147 .count = str2ull(procfile_lineword(t->ff, l, 1), NULL)
148 };
149
150 char *key = procfile_lineword(t->ff, l, 0);
151 if(!key || !*key || (key[0] == 'T' && key[1] == 'O' && key[2] == 'T' && key[3] == 'A' && key[4] == 'L' && key[5] == '_'))
152 continue;
153
154 dictionary_set(t->values, key, &v, sizeof(v));
155 }
156
157 t->updated = true;
158 }
159
160 static void read_pci_aer_count(const char *filename, struct aer_entry *t) {
161 t->updated = false;
162
163 if(unlikely(!t->ff)) {
164 t->ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
165 if(unlikely(!t->ff))
166 return;
167 }
168
169 t->ff = procfile_readall(t->ff);
170 if(unlikely(!t->ff || procfile_lines(t->ff) < 1 || procfile_linewords(t->ff, 0) < 1))
171 return;
172
173 struct aer_value v = {
174 .count = str2ull(procfile_lineword(t->ff, 0, 0), NULL)
175 };
176 dictionary_set(t->values, "count", &v, sizeof(v));
177 t->updated = true;
178 }
179
180 static void add_label_from_link(struct aer_entry *a, const char *path, const char *link) {
181 char name[FILENAME_MAX + 1];
182 strncpyz(name, path, FILENAME_MAX);
183 char *slash = strrchr(name, '/');
184 if(slash)
185 *slash = '\0';
186
187 char name2[FILENAME_MAX + 1];
188 snprintfz(name2, FILENAME_MAX, "%s/%s", name, link);
189
190 ssize_t len = readlink(name2, name, FILENAME_MAX);
191 if(len != -1) {
192 name[len] = '\0'; // Null-terminate the string
193 slash = strrchr(name, '/');
194 if(slash) slash++;
195 else slash = name;
196 rrdlabels_add(a->st->rrdlabels, link, slash, RRDLABEL_SRC_AUTO);
197 }
198 }
199
200 // Cleanup function for the PCI AER module
201 void pci_aer_plugin_cleanup(void) {
202 if(aer_root) {
203 dictionary_destroy(aer_root);
204 aer_root = NULL;
205 }
206 }
207
208 int do_proc_sys_devices_pci_aer(int update_every, usec_t dt __maybe_unused) {
209 if(unlikely(!aer_root)) {
210 int do_root_ports = CONFIG_BOOLEAN_AUTO;
211 int do_pci_slots = CONFIG_BOOLEAN_NO;
212
213 char buffer[128];
214 rrdlabels_get_value_strcpyz(localhost->rrdlabels, buffer, sizeof(buffer), "_virtualization");
215 if(strcmp(buffer, "none") != 0) {
216 // no need to run on virtualized environments
217 do_root_ports = CONFIG_BOOLEAN_NO;
218 do_pci_slots = CONFIG_BOOLEAN_NO;
219 }
220
221 do_root_ports = inicfg_get_boolean(&netdata_config, "plugin:proc:/sys/class/pci/aer", "enable root ports", do_root_ports);
222 do_pci_slots = inicfg_get_boolean(&netdata_config, "plugin:proc:/sys/class/pci/aer", "enable pci slots", do_pci_slots);
223
224 if(!do_root_ports && !do_pci_slots)
225 return 1;
226
227 aer_root = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, &dictionary_stats_category_collectors, sizeof(struct aer_entry));
228 dictionary_register_insert_callback(aer_root, aer_insert_callback, NULL);
229 dictionary_register_delete_callback(aer_root, aer_entry_free_callback, NULL);
230
231 AER_TYPE types = ((do_root_ports) ? (AER_ROOTPORT_TOTAL_ERR_COR|AER_ROOTPORT_TOTAL_ERR_FATAL) : 0) |
232 ((do_pci_slots) ? (AER_DEV_FATAL|AER_DEV_NONFATAL|AER_DEV_CORRECTABLE) : 0);
233
234 find_all_pci_aer(types);
235
236 if(!dictionary_entries(aer_root))
237 return 1;
238 }
239
240 struct aer_entry *a;
241 dfe_start_read(aer_root, a) {
242 switch(a->type) {
243 case AER_DEV_NONFATAL:
244 case AER_DEV_FATAL:
245 case AER_DEV_CORRECTABLE:
246 read_pci_aer_values(a_dfe.name, a);
247 break;
248
249 case AER_ROOTPORT_TOTAL_ERR_COR:
250 case AER_ROOTPORT_TOTAL_ERR_FATAL:
251 read_pci_aer_count(a_dfe.name, a);
252 break;
253 }
254
255 if(!a->updated)
256 continue;
257
258 if(!a->st) {
259 const char *title = "";
260 const char *context = "";
261
262 switch(a->type) {
263 case AER_DEV_NONFATAL:
264 title = "PCI Advanced Error Reporting (AER) Non-Fatal Errors";
265 context = "pci.aer_nonfatal";
266 break;
267
268 case AER_DEV_FATAL:
269 title = "PCI Advanced Error Reporting (AER) Fatal Errors";
270 context = "pci.aer_fatal";
271 break;
272
273 case AER_DEV_CORRECTABLE:
274 title = "PCI Advanced Error Reporting (AER) Correctable Errors";
275 context = "pci.aer_correctable";
276 break;
277
278 case AER_ROOTPORT_TOTAL_ERR_COR:
279 title = "PCI Root-Port Advanced Error Reporting (AER) Correctable Errors";
280 context = "pci.rootport_aer_correctable";
281 break;
282
283 case AER_ROOTPORT_TOTAL_ERR_FATAL:
284 title = "PCI Root-Port Advanced Error Reporting (AER) Fatal Errors";
285 context = "pci.rootport_aer_fatal";
286 break;
287
288 default:
289 title = "Unknown PCI Advanced Error Reporting";
290 context = "pci.unknown_aer";
291 break;
292 }
293
294 char id[RRD_ID_LENGTH_MAX + 1];
295 char nm[RRD_ID_LENGTH_MAX + 1];
296 size_t len = strlen(pci_aer_dirname);
297
298 const char *fname = a_dfe.name;
299 if(strncmp(a_dfe.name, pci_aer_dirname, len) == 0)
300 fname = &a_dfe.name[len];
301
302 if(*fname == '/')
303 fname++;
304
305 snprintfz(id, RRD_ID_LENGTH_MAX, "%s_%s", &context[4], fname);
306 char *slash = strrchr(id, '/');
307 if(slash)
308 *slash = '\0';
309
310 netdata_fix_chart_id(id);
311
312 snprintfz(nm, RRD_ID_LENGTH_MAX, "%s", fname);
313 slash = strrchr(nm, '/');
314 if(slash)
315 *slash = '\0';
316
317 a->st = rrdset_create_localhost(
318 "pci"
319 , id
320 , NULL
321 , "aer"
322 , context
323 , title
324 , "errors/s"
325 , PLUGIN_PROC_NAME
326 , "/sys/devices/pci/aer"
327 , NETDATA_CHART_PRIO_PCI_AER
328 , update_every
329 , RRDSET_TYPE_LINE
330 );
331
332 rrdlabels_add(a->st->rrdlabels, "device", nm, RRDLABEL_SRC_AUTO);
333 add_label_from_link(a, a_dfe.name, "driver");
334
335 struct aer_value *v;
336 dfe_start_read(a->values, v) {
337 v->rd = rrddim_add(a->st, v_dfe.name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
338 }
339 dfe_done(v);
340 }
341
342 struct aer_value *v;
343 dfe_start_read(a->values, v) {
344 if(unlikely(!v->rd))
345 v->rd = rrddim_add(a->st, v_dfe.name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
346
347 rrddim_set_by_pointer(a->st, v->rd, (collected_number)v->count);
348 }
349 dfe_done(v);
350
351 rrdset_done(a->st);
352 }
353 dfe_done(a);
354
355 return 0;
356 }