pcie_errors: plugin to read PCIe errors
[collectd.git] / src / pcie_errors.c
1 /**
2  * collectd - src/pcie_errors.c
3  *
4  * Copyright(c) 2018 Intel Corporation. All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * Authors:
25  *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
26  **/
27
28 #include "collectd.h"
29
30 #include "common.h"
31 #include "utils_llist.h"
32
33 #include <linux/pci_regs.h>
34
35 #define PCIE_ERRORS_PLUGIN "pcie_errors"
36 #define PCIE_DEFAULT_PROCDIR "/proc/bus/pci"
37 #define PCIE_DEFAULT_SYSFSDIR "/sys/bus/pci"
38 #define PCIE_NAME_LEN 512
39 #define PCIE_BUFF_SIZE 1024
40
41 #define PCIE_ERROR "pcie_error"
42 #define PCIE_SEV_CE "correctable"
43 #define PCIE_SEV_FATAL "fatal"
44 #define PCIE_SEV_NOFATAL "non_fatal"
45
46 #define PCIE_DEV(x) (((x) >> 3) & 0x1f)
47 #define PCIE_FN(x) ((x)&0x07)
48
49 #define PCIE_ECAP_OFFSET 0x100 /* ECAP always begin at offset 0x100 */
50
51 typedef struct pcie_config_s {
52   _Bool use_sysfs;
53   _Bool notif_masked;
54   _Bool persistent;
55   char access_dir[PATH_MAX];
56   _Bool config_error;
57 } pcie_config_t;
58
59 typedef struct pcie_device_s {
60   int fd;
61   int domain;
62   uint8_t bus;
63   uint8_t device;
64   uint8_t function;
65   int cap_exp;
66   int ecap_aer;
67   uint16_t device_status;
68   uint32_t correctable_errors;
69   uint32_t uncorrectable_errors;
70 } pcie_device_t;
71
72 typedef struct pcie_fops_s {
73   int (*list_devices)(llist_t *dev_list);
74   int (*open)(pcie_device_t *dev);
75   void (*close)(pcie_device_t *dev);
76   int (*read)(pcie_device_t *dev, void *buff, int size, int pos);
77 } pcie_fops_t;
78
79 typedef struct pcie_error_s {
80   int mask;
81   const char *desc;
82 } pcie_error_t;
83
84 static llist_t *pcie_dev_list;
85 static pcie_config_t pcie_config = {.access_dir = "", .use_sysfs = 1};
86 static pcie_fops_t pcie_fops;
87
88 /* Device Error Status */
89 static pcie_error_t pcie_base_errors[] = {
90     {PCI_EXP_DEVSTA_CED, "Correctable Error"},
91     {PCI_EXP_DEVSTA_NFED, "Non-Fatal Error"},
92     {PCI_EXP_DEVSTA_FED, "Fatal Error"},
93     {PCI_EXP_DEVSTA_URD, "Unsupported Request"}};
94 static const int pcie_base_errors_num = STATIC_ARRAY_SIZE(pcie_base_errors);
95
96 /* Uncorrectable Error Status */
97 static pcie_error_t pcie_aer_ues[] = {
98 #ifdef PCI_ERR_UNC_DLP
99     {PCI_ERR_UNC_DLP, "Data Link Protocol"},
100 #endif
101 #ifdef PCI_ERR_UNC_SURPDN
102     {PCI_ERR_UNC_SURPDN, "Surprise Down"},
103 #endif
104 #ifdef PCI_ERR_UNC_POISON_TLP
105     {PCI_ERR_UNC_POISON_TLP, "Poisoned TLP"},
106 #endif
107 #ifdef PCI_ERR_UNC_FCP
108     {PCI_ERR_UNC_FCP, "Flow Control Protocol"},
109 #endif
110 #ifdef PCI_ERR_UNC_COMP_TIME
111     {PCI_ERR_UNC_COMP_TIME, "Completion Timeout"},
112 #endif
113 #ifdef PCI_ERR_UNC_COMP_ABORT
114     {PCI_ERR_UNC_COMP_ABORT, "Completer Abort"},
115 #endif
116 #ifdef PCI_ERR_UNC_UNX_COMP
117     {PCI_ERR_UNC_UNX_COMP, "Unexpected Completion"},
118 #endif
119 #ifdef PCI_ERR_UNC_RX_OVER
120     {PCI_ERR_UNC_RX_OVER, "Receiver Overflow"},
121 #endif
122 #ifdef PCI_ERR_UNC_MALF_TLP
123     {PCI_ERR_UNC_MALF_TLP, "Malformed TLP"},
124 #endif
125 #ifdef PCI_ERR_UNC_ECRC
126     {PCI_ERR_UNC_ECRC, "ECRC Error Status"},
127 #endif
128 #ifdef PCI_ERR_UNC_UNSUP
129     {PCI_ERR_UNC_UNSUP, "Unsupported Request"},
130 #endif
131 #ifdef PCI_ERR_UNC_ACSV
132     {PCI_ERR_UNC_ACSV, "ACS Violation"},
133 #endif
134 #ifdef PCI_ERR_UNC_INTN
135     {PCI_ERR_UNC_INTN, "Internal"},
136 #endif
137 #ifdef PCI_ERR_UNC_MCBTLP
138     {PCI_ERR_UNC_MCBTLP, "MC blocked TLP"},
139 #endif
140 #ifdef PCI_ERR_UNC_ATOMEG
141     {PCI_ERR_UNC_ATOMEG, "Atomic egress blocked"},
142 #endif
143 #ifdef PCI_ERR_UNC_TLPPRE
144     {PCI_ERR_UNC_TLPPRE, "TLP prefix blocked"},
145 #endif
146 };
147 static const int pcie_aer_ues_num = STATIC_ARRAY_SIZE(pcie_aer_ues);
148
149 /* Correctable Error Status */
150 static pcie_error_t pcie_aer_ces[] = {
151 #ifdef PCI_ERR_COR_RCVR
152     {PCI_ERR_COR_RCVR, "Receiver Error Status"},
153 #endif
154 #ifdef PCI_ERR_COR_BAD_TLP
155     {PCI_ERR_COR_BAD_TLP, "Bad TLP Status"},
156 #endif
157 #ifdef PCI_ERR_COR_BAD_DLLP
158     {PCI_ERR_COR_BAD_DLLP, "Bad DLLP Status"},
159 #endif
160 #ifdef PCI_ERR_COR_REP_ROLL
161     {PCI_ERR_COR_REP_ROLL, "REPLAY_NUM Rollover"},
162 #endif
163 #ifdef PCI_ERR_COR_REP_TIMER
164     {PCI_ERR_COR_REP_TIMER, "Replay Timer Timeout"},
165 #endif
166 #ifdef PCI_ERR_COR_ADV_NFAT
167     {PCI_ERR_COR_ADV_NFAT, "Advisory Non-Fatal"},
168 #endif
169 #ifdef PCI_ERR_COR_INTERNAL
170     {PCI_ERR_COR_INTERNAL, "Corrected Internal"},
171 #endif
172 #ifdef PCI_ERR_COR_LOG_OVER
173     {PCI_ERR_COR_LOG_OVER, "Header Log Overflow"},
174 #endif
175 };
176 static const int pcie_aer_ces_num = STATIC_ARRAY_SIZE(pcie_aer_ces);
177
178 static int pcie_add_device(llist_t *list, int domain, uint8_t bus,
179                            uint8_t device, uint8_t fn) {
180   llentry_t *entry;
181   pcie_device_t *dev = calloc(1, sizeof(*dev));
182   if (dev == NULL) {
183     ERROR(PCIE_ERRORS_PLUGIN ": Failed to allocate device");
184     return -ENOMEM;
185   }
186
187   dev->domain = domain;
188   dev->bus = bus;
189   dev->device = device;
190   dev->function = fn;
191   dev->cap_exp = -1;
192   dev->ecap_aer = -1;
193   entry = llentry_create(NULL, dev);
194   if (entry == NULL) {
195     ERROR(PCIE_ERRORS_PLUGIN ": Failed to create llentry");
196     sfree(dev);
197     return -ENOMEM;
198   }
199   llist_append(list, entry);
200
201   DEBUG(PCIE_ERRORS_PLUGIN ": pci device added to list: %04x:%02x:%02x.%d",
202         domain, bus, device, fn);
203   return 0;
204 }
205
206 static void pcie_clear_list(llist_t *list) {
207   if (list == NULL)
208     return;
209
210   for (llentry_t *e = llist_head(list); e != NULL; e = e->next)
211     sfree(e->value);
212
213   llist_destroy(list);
214 }
215
216 static int pcie_list_devices_proc(llist_t *dev_list) {
217   FILE *fd;
218   char file_name[PCIE_NAME_LEN];
219   char buf[PCIE_BUFF_SIZE];
220   unsigned int i = 0;
221   int ret = 0;
222
223   if (dev_list == NULL)
224     return -EINVAL;
225
226   snprintf(file_name, sizeof(file_name), "%s/devices", pcie_config.access_dir);
227   fd = fopen(file_name, "r");
228   if (!fd) {
229     char errbuf[PCIE_BUFF_SIZE];
230     ERROR(PCIE_ERRORS_PLUGIN ": Cannot open file %s to get devices list: %s",
231           file_name, sstrerror(errno, errbuf, sizeof(errbuf)));
232     return -ENOENT;
233   }
234
235   while (fgets(buf, sizeof(buf), fd)) {
236     unsigned int slot;
237     uint8_t bus, dev, fn;
238
239     if (sscanf(buf, "%x", &slot) != 1) {
240       ERROR(PCIE_ERRORS_PLUGIN ": Failed to read line %u from %s", i + 1,
241             file_name);
242       continue;
243     }
244
245     bus = slot >> 8U;
246     dev = PCIE_DEV(slot);
247     fn = PCIE_FN(slot);
248     ret = pcie_add_device(dev_list, 0, bus, dev, fn);
249     if (ret)
250       break;
251
252     ++i;
253   }
254
255   fclose(fd);
256   return ret;
257 }
258
259 static int pcie_list_devices_sysfs(llist_t *dev_list) {
260   DIR *dir;
261   struct dirent *item;
262   char dir_name[PCIE_NAME_LEN];
263   int ret = 0;
264
265   if (dev_list == NULL)
266     return -EINVAL;
267
268   snprintf(dir_name, sizeof(dir_name), "%s/devices", pcie_config.access_dir);
269   dir = opendir(dir_name);
270   if (!dir) {
271     char errbuf[PCIE_BUFF_SIZE];
272     ERROR(PCIE_ERRORS_PLUGIN ": Cannot open dir %s to get devices list: %s",
273           dir_name, sstrerror(errno, errbuf, sizeof(errbuf)));
274     return -ENOENT;
275   }
276
277   while ((item = readdir(dir))) {
278     unsigned int dom, bus, dev;
279     int fn;
280
281     /* Omit special non-device entries */
282     if (item->d_name[0] == '.')
283       continue;
284
285     if (sscanf(item->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &fn) != 4) {
286       ERROR(PCIE_ERRORS_PLUGIN ": Failed to parse entry %s", item->d_name);
287       continue;
288     }
289
290     ret = pcie_add_device(dev_list, dom, bus, dev, fn);
291     if (ret)
292       break;
293   }
294
295   closedir(dir);
296   return ret;
297 }
298
299 static void pcie_close(pcie_device_t *dev) {
300   if (close(dev->fd) == -1) {
301     char errbuf[PCIE_BUFF_SIZE];
302     ERROR(PCIE_ERRORS_PLUGIN ": Failed to close %04x:%02x:%02x.%d, fd=%d: %s",
303           dev->domain, dev->bus, dev->device, dev->function, dev->fd,
304           sstrerror(errno, errbuf, sizeof(errbuf)));
305   }
306
307   dev->fd = -1;
308 }
309
310 static int pcie_open(pcie_device_t *dev, const char *name) {
311   dev->fd = open(name, O_RDONLY);
312   if (dev->fd == -1) {
313     char errbuf[PCIE_BUFF_SIZE];
314     ERROR(PCIE_ERRORS_PLUGIN ": Failed to open file %s: %s", name,
315           sstrerror(errno, errbuf, sizeof(errbuf)));
316     return -ENOENT;
317   }
318
319   return 0;
320 }
321
322 static int pcie_open_proc(pcie_device_t *dev) {
323   char file_name[PCIE_NAME_LEN];
324
325   snprintf(file_name, sizeof(file_name), "%s/%02x/%02x.%d",
326            pcie_config.access_dir, dev->bus, dev->device, dev->function);
327
328   return pcie_open(dev, file_name);
329 }
330
331 static int pcie_open_sysfs(pcie_device_t *dev) {
332   char file_name[PCIE_NAME_LEN];
333
334   snprintf(file_name, sizeof(file_name), "%s/devices/%04x:%02x:%02x.%d/config",
335            pcie_config.access_dir, dev->domain, dev->bus, dev->device,
336            dev->function);
337
338   return pcie_open(dev, file_name);
339 }
340
341 static int pcie_read(pcie_device_t *dev, void *buff, int size, int pos) {
342   int len = pread(dev->fd, buff, size, pos);
343   if (len == size)
344     return 0;
345
346   if (len == -1) {
347     char errbuf[PCIE_BUFF_SIZE];
348     ERROR(PCIE_ERRORS_PLUGIN ": Failed to read %04x:%02x:%02x.%d at pos %d: %s",
349           dev->domain, dev->bus, dev->device, dev->function, pos,
350           sstrerror(errno, errbuf, sizeof(errbuf)));
351   } else {
352     ERROR(PCIE_ERRORS_PLUGIN
353           ": %04x:%02x:%02x.%d Read only %d bytes, should be %d",
354           dev->domain, dev->bus, dev->device, dev->function, len, size);
355   }
356   return -1;
357 }
358
359 static uint8_t pcie_read8(pcie_device_t *dev, int pos) {
360   uint8_t value;
361   if (pcie_fops.read(dev, &value, 1, pos))
362     return 0;
363   return value;
364 }
365
366 static uint16_t pcie_read16(pcie_device_t *dev, int pos) {
367   uint16_t value;
368   if (pcie_fops.read(dev, &value, 2, pos))
369     return 0;
370   return value;
371 }
372
373 static uint32_t pcie_read32(pcie_device_t *dev, int pos) {
374   uint32_t value;
375   if (pcie_fops.read(dev, &value, 4, pos))
376     return 0;
377   return value;
378 }
379
380 static void pcie_dispatch_notification(pcie_device_t *dev, notification_t *n,
381                                        const char *type,
382                                        const char *type_instance) {
383   sstrncpy(n->host, hostname_g, sizeof(n->host));
384   snprintf(n->plugin_instance, sizeof(n->plugin_instance), "%04x:%02x:%02x.%d",
385            dev->domain, dev->bus, dev->device, dev->function);
386   sstrncpy(n->type, type, sizeof(n->type));
387   sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
388
389   plugin_dispatch_notification(n);
390 }
391
392 /* Report errors found in AER Correctable Error Status register */
393 static void pcie_dispatch_correctable_errors(pcie_device_t *dev,
394                                              uint32_t errors, uint32_t masked) {
395   for (int i = 0; i < pcie_aer_ces_num; i++) {
396     pcie_error_t *err = pcie_aer_ces + i;
397     notification_t n = {.severity = NOTIF_WARNING,
398                         .time = cdtime(),
399                         .plugin = PCIE_ERRORS_PLUGIN,
400                         .meta = NULL};
401
402     /* If not specifically set by config option omit masked errors */
403     if (!pcie_config.notif_masked && (err->mask & masked))
404       continue;
405
406     if (err->mask & errors) {
407       /* Error already reported, notify only if persistent is set */
408       if (!pcie_config.persistent && (err->mask & dev->correctable_errors))
409         continue;
410
411       DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
412             dev->bus, dev->device, dev->function, err->desc);
413       snprintf(n.message, sizeof(n.message), "Correctable Error set: %s",
414                err->desc);
415       pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
416
417     } else if (err->mask & dev->correctable_errors) {
418       DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
419             dev->bus, dev->device, dev->function, err->desc);
420
421       n.severity = NOTIF_OKAY;
422       snprintf(n.message, sizeof(n.message), "Correctable Error cleared: %s",
423                err->desc);
424       pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
425     }
426   }
427 }
428
429 /* Report errors found in AER Uncorrectable Error Status register */
430 static void pcie_dispatch_uncorrectable_errors(pcie_device_t *dev,
431                                                uint32_t errors, uint32_t masked,
432                                                uint32_t severity) {
433   for (int i = 0; i < pcie_aer_ues_num; i++) {
434     pcie_error_t *err = pcie_aer_ues + i;
435     const char *type_instance =
436         (severity & err->mask) ? PCIE_SEV_FATAL : PCIE_SEV_NOFATAL;
437     notification_t n = {
438         .time = cdtime(), .plugin = PCIE_ERRORS_PLUGIN, .meta = NULL};
439
440     /* If not specifically set by config option omit masked errors */
441     if (!pcie_config.notif_masked && (err->mask & masked))
442       continue;
443
444     if (err->mask & errors) {
445       /* Error already reported, notify only if persistent is set */
446       if (!pcie_config.persistent && (err->mask & dev->uncorrectable_errors))
447         continue;
448
449       DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) set", dev->domain,
450             dev->bus, dev->device, dev->function, err->desc, type_instance);
451
452       n.severity = (severity & err->mask) ? NOTIF_FAILURE : NOTIF_WARNING;
453       snprintf(n.message, sizeof(n.message), "Uncorrectable(%s) Error set: %s",
454                type_instance, err->desc);
455       pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
456
457     } else if (err->mask & dev->uncorrectable_errors) {
458       DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) cleared",
459             dev->domain, dev->bus, dev->device, dev->function, err->desc,
460             type_instance);
461
462       n.severity = NOTIF_OKAY;
463       snprintf(n.message, sizeof(n.message),
464                "Uncorrectable(%s) Error cleared: %s", type_instance, err->desc);
465       pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
466     }
467   }
468 }
469
470 /* Find offset of PCI Express Capability Structure
471  * in PCI configuration space.
472  * Returns offset, -1 if not found.
473 **/
474 static int pcie_find_cap_exp(pcie_device_t *dev) {
475   int pos = pcie_read8(dev, PCI_CAPABILITY_LIST) & ~3;
476
477   while (pos) {
478     uint8_t id = pcie_read8(dev, pos + PCI_CAP_LIST_ID);
479
480     if (id == 0xff)
481       break;
482     if (id == PCI_CAP_ID_EXP)
483       return pos;
484
485     pos = pcie_read8(dev, pos + PCI_CAP_LIST_NEXT) & ~3;
486   }
487
488   DEBUG(PCIE_ERRORS_PLUGIN ": Cannot find CAP EXP for %04x:%02x:%02x.%d",
489         dev->domain, dev->bus, dev->device, dev->function);
490
491   return -1;
492 }
493
494 /* Find offset of Advanced Error Reporting Capability.
495  * Returns AER offset, -1 if not found.
496 **/
497 static int pcie_find_ecap_aer(pcie_device_t *dev) {
498   int pos = PCIE_ECAP_OFFSET;
499   uint32_t header = pcie_read32(dev, pos);
500   int id = PCI_EXT_CAP_ID(header);
501   int next = PCI_EXT_CAP_NEXT(header);
502
503   if (!id && !next)
504     return -1;
505
506   if (id == PCI_EXT_CAP_ID_ERR)
507     return pos;
508
509   while (next) {
510     if (next <= PCIE_ECAP_OFFSET)
511       break;
512
513     header = pcie_read32(dev, next);
514     id = PCI_EXT_CAP_ID(header);
515
516     if (id == PCI_EXT_CAP_ID_ERR)
517       return next;
518
519     next = PCI_EXT_CAP_NEXT(header);
520   }
521
522   return -1;
523 }
524
525 static void pcie_check_dev_status(pcie_device_t *dev, int pos) {
526   /* Read Device Status register with mask for errors only */
527   uint16_t new_status = pcie_read16(dev, pos + PCI_EXP_DEVSTA) & 0xf;
528
529   /* Check if anything new should be reported */
530   if (!(pcie_config.persistent && new_status) &&
531       (new_status == dev->device_status))
532     return;
533
534   /* Report errors found in Device Status register */
535   for (int i = 0; i < pcie_base_errors_num; i++) {
536     pcie_error_t *err = pcie_base_errors + i;
537     const char *type_instance = (err->mask == PCI_EXP_DEVSTA_FED)
538                                     ? PCIE_SEV_FATAL
539                                     : (err->mask == PCI_EXP_DEVSTA_CED)
540                                           ? PCIE_SEV_CE
541                                           : PCIE_SEV_NOFATAL;
542     const int severity =
543         (err->mask == PCI_EXP_DEVSTA_FED) ? NOTIF_FAILURE : NOTIF_WARNING;
544     notification_t n = {.severity = severity,
545                         .time = cdtime(),
546                         .plugin = PCIE_ERRORS_PLUGIN,
547                         .meta = NULL};
548
549     if (err->mask & new_status) {
550       /* Error already reported, notify only if persistent is set */
551       if (!pcie_config.persistent && (err->mask & dev->device_status))
552         continue;
553
554       DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
555             dev->bus, dev->device, dev->function, err->desc);
556       snprintf(n.message, sizeof(n.message), "Device Status Error set: %s",
557                err->desc);
558       pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
559
560     } else if (err->mask & dev->device_status) {
561       DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
562             dev->bus, dev->device, dev->function, err->desc);
563       n.severity = NOTIF_OKAY;
564       snprintf(n.message, sizeof(n.message), "Device Status Error cleared: %s",
565                err->desc);
566       pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
567     }
568   }
569
570   dev->device_status = new_status;
571 }
572
573 static void pcie_check_aer(pcie_device_t *dev, int pos) {
574   /* Check for AER uncorrectable errors */
575   uint32_t errors = pcie_read32(dev, pos + PCI_ERR_UNCOR_STATUS);
576
577   if ((pcie_config.persistent && errors) ||
578       (errors != dev->uncorrectable_errors)) {
579     uint32_t masked = pcie_read32(dev, pos + PCI_ERR_UNCOR_MASK);
580     uint32_t severity = pcie_read32(dev, pos + PCI_ERR_UNCOR_SEVER);
581     pcie_dispatch_uncorrectable_errors(dev, errors, masked, severity);
582   }
583   dev->uncorrectable_errors = errors;
584
585   /* Check for AER correctable errors */
586   errors = pcie_read32(dev, pos + PCI_ERR_COR_STATUS);
587   if ((pcie_config.persistent && errors) ||
588       (errors != dev->correctable_errors)) {
589     uint32_t masked = pcie_read32(dev, pos + PCI_ERR_COR_MASK);
590     pcie_dispatch_correctable_errors(dev, errors, masked);
591   }
592   dev->correctable_errors = errors;
593 }
594
595 static int pcie_process_devices(llist_t *devs) {
596   int ret = 0;
597   if (devs == NULL)
598     return -1;
599
600   for (llentry_t *e = llist_head(devs); e != NULL; e = e->next) {
601     pcie_device_t *dev = e->value;
602
603     if (pcie_fops.open(dev) == 0) {
604       pcie_check_dev_status(dev, dev->cap_exp);
605       if (dev->ecap_aer != -1)
606         pcie_check_aer(dev, dev->ecap_aer);
607
608       pcie_fops.close(dev);
609     } else {
610       notification_t n = {.severity = NOTIF_FAILURE,
611                           .time = cdtime(),
612                           .message = "Failed to read device status",
613                           .plugin = PCIE_ERRORS_PLUGIN,
614                           .meta = NULL};
615       pcie_dispatch_notification(dev, &n, "", "");
616       ret = -1;
617     }
618   }
619
620   return ret;
621 }
622
623 /* This function is to be called during init to filter out no pcie devices */
624 static void pcie_preprocess_devices(llist_t *devs) {
625   llentry_t *e_next;
626
627   if (devs == NULL)
628     return;
629
630   for (llentry_t *e = llist_head(devs); e != NULL; e = e_next) {
631     pcie_device_t *dev = e->value;
632     _Bool del = 0;
633
634     if (pcie_fops.open(dev) == 0) {
635       uint16_t status = pcie_read16(dev, PCI_STATUS);
636       if (status & PCI_STATUS_CAP_LIST)
637         dev->cap_exp = pcie_find_cap_exp(dev);
638
639       /* Every PCIe device must have Capability Structure */
640       if (dev->cap_exp == -1) {
641         DEBUG(PCIE_ERRORS_PLUGIN ": Not PCI Express device: %04x:%02x:%02x.%d",
642               dev->domain, dev->bus, dev->device, dev->function);
643         del = 1;
644       } else {
645         dev->ecap_aer = pcie_find_ecap_aer(dev);
646         if (dev->ecap_aer == -1)
647           INFO(PCIE_ERRORS_PLUGIN
648                ": Device is not AER capable: %04x:%02x:%02x.%d",
649                dev->domain, dev->bus, dev->device, dev->function);
650       }
651
652       pcie_fops.close(dev);
653     } else {
654       ERROR(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: failed to open",
655             dev->domain, dev->bus, dev->device, dev->function);
656       del = 1;
657     }
658
659     e_next = e->next;
660     if (del) {
661       sfree(dev);
662       llist_remove(devs, e);
663       llentry_destroy(e);
664     }
665   }
666 }
667
668 static int pcie_plugin_read(__attribute__((unused)) user_data_t *ud) {
669
670   if (pcie_process_devices(pcie_dev_list) < 0) {
671     ERROR(PCIE_ERRORS_PLUGIN ": Failed to read devices state");
672     return -1;
673   }
674   return 0;
675 }
676
677 static void pcie_access_config(void) {
678   /* Set functions for register access to
679    * use proc or sysfs depending on config. */
680   if (pcie_config.use_sysfs) {
681     pcie_fops.list_devices = pcie_list_devices_sysfs;
682     pcie_fops.open = pcie_open_sysfs;
683     if (pcie_config.access_dir[0] == '\0')
684       sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_SYSFSDIR,
685                sizeof(pcie_config.access_dir));
686   } else {
687     /* use proc */
688     pcie_fops.list_devices = pcie_list_devices_proc;
689     pcie_fops.open = pcie_open_proc;
690     if (pcie_config.access_dir[0] == '\0')
691       sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_PROCDIR,
692                sizeof(pcie_config.access_dir));
693   }
694   /* Common functions */
695   pcie_fops.close = pcie_close;
696   pcie_fops.read = pcie_read;
697 }
698
699 static int pcie_plugin_config(oconfig_item_t *ci) {
700
701   for (int i = 0; i < ci->children_num; i++) {
702     oconfig_item_t *child = ci->children + i;
703     int status = 0;
704
705     if (strcasecmp("Source", child->key) == 0) {
706       if ((child->values_num != 1) ||
707           (child->values[0].type != OCONFIG_TYPE_STRING)) {
708         status = -1;
709       } else if (strcasecmp("proc", child->values[0].value.string) == 0) {
710         pcie_config.use_sysfs = 0;
711       } else if (strcasecmp("sysfs", child->values[0].value.string) != 0) {
712         ERROR(PCIE_ERRORS_PLUGIN ": Allowed sources are 'proc' or 'sysfs'.");
713         status = -1;
714       }
715     } else if (strcasecmp("AccessDir", child->key) == 0) {
716       status = cf_util_get_string_buffer(child, pcie_config.access_dir,
717                                          sizeof(pcie_config.access_dir));
718     } else if (strcasecmp("ReportMasked", child->key) == 0) {
719       status = cf_util_get_boolean(child, &pcie_config.notif_masked);
720     } else if (strcasecmp("PersistentNotifications", child->key) == 0) {
721       status = cf_util_get_boolean(child, &pcie_config.persistent);
722     } else {
723       ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration option \"%s\".",
724             child->key);
725       pcie_config.config_error = 1;
726       break;
727     }
728
729     if (status) {
730       ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration parameter \"%s\".",
731             child->key);
732       pcie_config.config_error = 1;
733       break;
734     }
735   }
736
737   return 0;
738 }
739
740 static int pcie_shutdown(void) {
741   pcie_clear_list(pcie_dev_list);
742   pcie_dev_list = NULL;
743
744   return 0;
745 }
746
747 static int pcie_init(void) {
748   if (pcie_config.config_error) {
749     ERROR(PCIE_ERRORS_PLUGIN
750           ": Error in configuration, failed to init plugin.");
751     return -1;
752   }
753
754   pcie_access_config();
755   pcie_dev_list = llist_create();
756   if (pcie_fops.list_devices(pcie_dev_list) != 0) {
757     ERROR(PCIE_ERRORS_PLUGIN ": Failed to find devices.");
758     pcie_shutdown();
759     return -1;
760   }
761   pcie_preprocess_devices(pcie_dev_list);
762   if (llist_size(pcie_dev_list) == 0) {
763     /* No any PCI Express devices were found on the system */
764     ERROR(PCIE_ERRORS_PLUGIN ": No PCIe devices found in %s",
765           pcie_config.access_dir);
766     pcie_shutdown();
767     return -1;
768   }
769
770   return 0;
771 }
772
773 void module_register(void) {
774   plugin_register_init(PCIE_ERRORS_PLUGIN, pcie_init);
775   plugin_register_complex_config(PCIE_ERRORS_PLUGIN, pcie_plugin_config);
776   plugin_register_complex_read(NULL, PCIE_ERRORS_PLUGIN, pcie_plugin_read, 0,
777                                NULL);
778   plugin_register_shutdown(PCIE_ERRORS_PLUGIN, pcie_shutdown);
779 }