2 * collectd - src/pcie_errors.c
4 * Copyright(c) 2018 Intel Corporation. All rights reserved.
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
31 #include "utils_llist.h"
33 #include <linux/pci_regs.h>
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
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"
46 #define PCIE_DEV(x) (((x) >> 3) & 0x1f)
47 #define PCIE_FN(x) ((x)&0x07)
49 #define PCIE_ECAP_OFFSET 0x100 /* ECAP always begin at offset 0x100 */
51 typedef struct pcie_config_s {
55 char access_dir[PATH_MAX];
58 typedef struct pcie_device_s {
66 uint16_t device_status;
67 uint32_t correctable_errors;
68 uint32_t uncorrectable_errors;
71 typedef struct pcie_fops_s {
72 int (*list_devices)(llist_t *dev_list);
73 int (*open)(pcie_device_t *dev);
74 void (*close)(pcie_device_t *dev);
75 int (*read)(pcie_device_t *dev, void *buff, int size, int pos);
78 typedef struct pcie_error_s {
83 static llist_t *pcie_dev_list;
84 static pcie_config_t pcie_config = {.access_dir = "", .use_sysfs = true};
85 static pcie_fops_t pcie_fops;
87 /* Device Error Status */
88 static const pcie_error_t pcie_base_errors[] = {
89 {PCI_EXP_DEVSTA_CED, "Correctable Error"},
90 {PCI_EXP_DEVSTA_NFED, "Non-Fatal Error"},
91 {PCI_EXP_DEVSTA_FED, "Fatal Error"},
92 {PCI_EXP_DEVSTA_URD, "Unsupported Request"}};
93 static const int pcie_base_errors_num = STATIC_ARRAY_SIZE(pcie_base_errors);
95 /* Uncorrectable Error Status */
96 static const pcie_error_t pcie_aer_ues[] = {
97 #ifdef PCI_ERR_UNC_DLP
98 {PCI_ERR_UNC_DLP, "Data Link Protocol"},
100 #ifdef PCI_ERR_UNC_SURPDN
101 {PCI_ERR_UNC_SURPDN, "Surprise Down"},
103 #ifdef PCI_ERR_UNC_POISON_TLP
104 {PCI_ERR_UNC_POISON_TLP, "Poisoned TLP"},
106 #ifdef PCI_ERR_UNC_FCP
107 {PCI_ERR_UNC_FCP, "Flow Control Protocol"},
109 #ifdef PCI_ERR_UNC_COMP_TIME
110 {PCI_ERR_UNC_COMP_TIME, "Completion Timeout"},
112 #ifdef PCI_ERR_UNC_COMP_ABORT
113 {PCI_ERR_UNC_COMP_ABORT, "Completer Abort"},
115 #ifdef PCI_ERR_UNC_UNX_COMP
116 {PCI_ERR_UNC_UNX_COMP, "Unexpected Completion"},
118 #ifdef PCI_ERR_UNC_RX_OVER
119 {PCI_ERR_UNC_RX_OVER, "Receiver Overflow"},
121 #ifdef PCI_ERR_UNC_MALF_TLP
122 {PCI_ERR_UNC_MALF_TLP, "Malformed TLP"},
124 #ifdef PCI_ERR_UNC_ECRC
125 {PCI_ERR_UNC_ECRC, "ECRC Error Status"},
127 #ifdef PCI_ERR_UNC_UNSUP
128 {PCI_ERR_UNC_UNSUP, "Unsupported Request"},
130 #ifdef PCI_ERR_UNC_ACSV
131 {PCI_ERR_UNC_ACSV, "ACS Violation"},
133 #ifdef PCI_ERR_UNC_INTN
134 {PCI_ERR_UNC_INTN, "Internal"},
136 #ifdef PCI_ERR_UNC_MCBTLP
137 {PCI_ERR_UNC_MCBTLP, "MC blocked TLP"},
139 #ifdef PCI_ERR_UNC_ATOMEG
140 {PCI_ERR_UNC_ATOMEG, "Atomic egress blocked"},
142 #ifdef PCI_ERR_UNC_TLPPRE
143 {PCI_ERR_UNC_TLPPRE, "TLP prefix blocked"},
146 static const int pcie_aer_ues_num = STATIC_ARRAY_SIZE(pcie_aer_ues);
148 /* Correctable Error Status */
149 static const pcie_error_t pcie_aer_ces[] = {
150 #ifdef PCI_ERR_COR_RCVR
151 {PCI_ERR_COR_RCVR, "Receiver Error Status"},
153 #ifdef PCI_ERR_COR_BAD_TLP
154 {PCI_ERR_COR_BAD_TLP, "Bad TLP Status"},
156 #ifdef PCI_ERR_COR_BAD_DLLP
157 {PCI_ERR_COR_BAD_DLLP, "Bad DLLP Status"},
159 #ifdef PCI_ERR_COR_REP_ROLL
160 {PCI_ERR_COR_REP_ROLL, "REPLAY_NUM Rollover"},
162 #ifdef PCI_ERR_COR_REP_TIMER
163 {PCI_ERR_COR_REP_TIMER, "Replay Timer Timeout"},
165 #ifdef PCI_ERR_COR_ADV_NFAT
166 {PCI_ERR_COR_ADV_NFAT, "Advisory Non-Fatal"},
168 #ifdef PCI_ERR_COR_INTERNAL
169 {PCI_ERR_COR_INTERNAL, "Corrected Internal"},
171 #ifdef PCI_ERR_COR_LOG_OVER
172 {PCI_ERR_COR_LOG_OVER, "Header Log Overflow"},
175 static const int pcie_aer_ces_num = STATIC_ARRAY_SIZE(pcie_aer_ces);
177 static int pcie_add_device(llist_t *list, int domain, uint8_t bus,
178 uint8_t device, uint8_t fn) {
180 pcie_device_t *dev = calloc(1, sizeof(*dev));
182 ERROR(PCIE_ERRORS_PLUGIN ": Failed to allocate device");
186 dev->domain = domain;
188 dev->device = device;
192 entry = llentry_create(NULL, dev);
194 ERROR(PCIE_ERRORS_PLUGIN ": Failed to create llentry");
198 llist_append(list, entry);
200 DEBUG(PCIE_ERRORS_PLUGIN ": pci device added to list: %04x:%02x:%02x.%d",
201 domain, bus, device, fn);
205 static void pcie_clear_list(llist_t *list) {
209 for (llentry_t *e = llist_head(list); e != NULL; e = e->next)
215 static int pcie_list_devices_proc(llist_t *dev_list) {
217 char file_name[PCIE_NAME_LEN];
218 char buf[PCIE_BUFF_SIZE];
222 if (dev_list == NULL)
225 ret = snprintf(file_name, sizeof(file_name), "%s/devices",
226 pcie_config.access_dir);
227 if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
228 ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
229 pcie_config.access_dir, ret);
232 fd = fopen(file_name, "r");
234 char errbuf[PCIE_BUFF_SIZE];
235 ERROR(PCIE_ERRORS_PLUGIN ": Cannot open file %s to get devices list: %s",
236 file_name, sstrerror(errno, errbuf, sizeof(errbuf)));
240 while (fgets(buf, sizeof(buf), fd)) {
243 if (sscanf(buf, "%x", &slot) != 1) {
244 ERROR(PCIE_ERRORS_PLUGIN ": Failed to read line %u from %s", i + 1,
249 uint8_t bus = slot >> 8U;
250 uint8_t dev = PCIE_DEV(slot);
251 uint8_t fn = PCIE_FN(slot);
252 ret = pcie_add_device(dev_list, 0, bus, dev, fn);
263 static int pcie_list_devices_sysfs(llist_t *dev_list) {
266 char dir_name[PCIE_NAME_LEN];
269 if (dev_list == NULL)
272 ret = snprintf(dir_name, sizeof(dir_name), "%s/devices",
273 pcie_config.access_dir);
274 if (ret < 1 || (size_t)ret >= sizeof(dir_name)) {
275 ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
276 pcie_config.access_dir, ret);
279 dir = opendir(dir_name);
281 char errbuf[PCIE_BUFF_SIZE];
282 ERROR(PCIE_ERRORS_PLUGIN ": Cannot open dir %s to get devices list: %s",
283 dir_name, sstrerror(errno, errbuf, sizeof(errbuf)));
287 while ((item = readdir(dir))) {
288 unsigned int dom, bus, dev;
291 /* Omit special non-device entries */
292 if (item->d_name[0] == '.')
295 if (sscanf(item->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &fn) != 4) {
296 ERROR(PCIE_ERRORS_PLUGIN ": Failed to parse entry %s", item->d_name);
300 ret = pcie_add_device(dev_list, dom, bus, dev, fn);
309 static void pcie_close(pcie_device_t *dev) {
310 if (close(dev->fd) == -1) {
311 char errbuf[PCIE_BUFF_SIZE];
312 ERROR(PCIE_ERRORS_PLUGIN ": Failed to close %04x:%02x:%02x.%d, fd=%d: %s",
313 dev->domain, dev->bus, dev->device, dev->function, dev->fd,
314 sstrerror(errno, errbuf, sizeof(errbuf)));
320 static int pcie_open(pcie_device_t *dev, const char *name) {
321 dev->fd = open(name, O_RDONLY);
323 char errbuf[PCIE_BUFF_SIZE];
324 ERROR(PCIE_ERRORS_PLUGIN ": Failed to open file %s: %s", name,
325 sstrerror(errno, errbuf, sizeof(errbuf)));
332 static int pcie_open_proc(pcie_device_t *dev) {
333 char file_name[PCIE_NAME_LEN];
336 snprintf(file_name, sizeof(file_name), "%s/%02x/%02x.%d",
337 pcie_config.access_dir, dev->bus, dev->device, dev->function);
338 if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
339 ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
340 pcie_config.access_dir, ret);
344 return pcie_open(dev, file_name);
347 static int pcie_open_sysfs(pcie_device_t *dev) {
348 char file_name[PCIE_NAME_LEN];
351 snprintf(file_name, sizeof(file_name),
352 "%s/devices/%04x:%02x:%02x.%d/config", pcie_config.access_dir,
353 dev->domain, dev->bus, dev->device, dev->function);
354 if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
355 ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
356 pcie_config.access_dir, ret);
360 return pcie_open(dev, file_name);
363 static int pcie_read(pcie_device_t *dev, void *buff, int size, int pos) {
364 int len = pread(dev->fd, buff, size, pos);
369 char errbuf[PCIE_BUFF_SIZE];
370 ERROR(PCIE_ERRORS_PLUGIN ": Failed to read %04x:%02x:%02x.%d at pos %d: %s",
371 dev->domain, dev->bus, dev->device, dev->function, pos,
372 sstrerror(errno, errbuf, sizeof(errbuf)));
374 ERROR(PCIE_ERRORS_PLUGIN
375 ": %04x:%02x:%02x.%d Read only %d bytes, should be %d",
376 dev->domain, dev->bus, dev->device, dev->function, len, size);
381 static uint8_t pcie_read8(pcie_device_t *dev, int pos) {
383 if (pcie_fops.read(dev, &value, 1, pos))
388 static uint16_t pcie_read16(pcie_device_t *dev, int pos) {
390 if (pcie_fops.read(dev, &value, 2, pos))
395 static uint32_t pcie_read32(pcie_device_t *dev, int pos) {
397 if (pcie_fops.read(dev, &value, 4, pos))
402 static void pcie_dispatch_notification(pcie_device_t *dev, notification_t *n,
404 const char *type_instance) {
405 sstrncpy(n->host, hostname_g, sizeof(n->host));
406 snprintf(n->plugin_instance, sizeof(n->plugin_instance), "%04x:%02x:%02x.%d",
407 dev->domain, dev->bus, dev->device, dev->function);
408 sstrncpy(n->type, type, sizeof(n->type));
409 sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
411 plugin_dispatch_notification(n);
414 /* Report errors found in AER Correctable Error Status register */
415 static void pcie_dispatch_correctable_errors(pcie_device_t *dev,
416 uint32_t errors, uint32_t masked) {
417 for (int i = 0; i < pcie_aer_ces_num; i++) {
418 const pcie_error_t *err = pcie_aer_ces + i;
419 notification_t n = {.severity = NOTIF_WARNING,
421 .plugin = PCIE_ERRORS_PLUGIN,
424 /* If not specifically set by config option omit masked errors */
425 if (!pcie_config.notif_masked && (err->mask & masked))
428 if (err->mask & errors) {
429 /* Error already reported, notify only if persistent is set */
430 if (!pcie_config.persistent && (err->mask & dev->correctable_errors))
433 DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
434 dev->bus, dev->device, dev->function, err->desc);
435 snprintf(n.message, sizeof(n.message), "Correctable Error set: %s",
437 pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
439 } else if (err->mask & dev->correctable_errors) {
440 DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
441 dev->bus, dev->device, dev->function, err->desc);
443 n.severity = NOTIF_OKAY;
444 snprintf(n.message, sizeof(n.message), "Correctable Error cleared: %s",
446 pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
451 /* Report errors found in AER Uncorrectable Error Status register */
452 static void pcie_dispatch_uncorrectable_errors(pcie_device_t *dev,
453 uint32_t errors, uint32_t masked,
455 for (int i = 0; i < pcie_aer_ues_num; i++) {
456 const pcie_error_t *err = pcie_aer_ues + i;
457 const char *type_instance =
458 (severity & err->mask) ? PCIE_SEV_FATAL : PCIE_SEV_NOFATAL;
460 .time = cdtime(), .plugin = PCIE_ERRORS_PLUGIN, .meta = NULL};
462 /* If not specifically set by config option omit masked errors */
463 if (!pcie_config.notif_masked && (err->mask & masked))
466 if (err->mask & errors) {
467 /* Error already reported, notify only if persistent is set */
468 if (!pcie_config.persistent && (err->mask & dev->uncorrectable_errors))
471 DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) set", dev->domain,
472 dev->bus, dev->device, dev->function, err->desc, type_instance);
474 n.severity = (severity & err->mask) ? NOTIF_FAILURE : NOTIF_WARNING;
475 snprintf(n.message, sizeof(n.message), "Uncorrectable(%s) Error set: %s",
476 type_instance, err->desc);
477 pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
479 } else if (err->mask & dev->uncorrectable_errors) {
480 DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) cleared",
481 dev->domain, dev->bus, dev->device, dev->function, err->desc,
484 n.severity = NOTIF_OKAY;
485 snprintf(n.message, sizeof(n.message),
486 "Uncorrectable(%s) Error cleared: %s", type_instance, err->desc);
487 pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
492 /* Find offset of PCI Express Capability Structure
493 * in PCI configuration space.
494 * Returns offset, -1 if not found.
496 static int pcie_find_cap_exp(pcie_device_t *dev) {
497 int pos = pcie_read8(dev, PCI_CAPABILITY_LIST) & ~3;
500 uint8_t id = pcie_read8(dev, pos + PCI_CAP_LIST_ID);
504 if (id == PCI_CAP_ID_EXP)
507 pos = pcie_read8(dev, pos + PCI_CAP_LIST_NEXT) & ~3;
510 DEBUG(PCIE_ERRORS_PLUGIN ": Cannot find CAP EXP for %04x:%02x:%02x.%d",
511 dev->domain, dev->bus, dev->device, dev->function);
516 /* Find offset of Advanced Error Reporting Capability.
517 * Returns AER offset, -1 if not found.
519 static int pcie_find_ecap_aer(pcie_device_t *dev) {
520 int pos = PCIE_ECAP_OFFSET;
521 uint32_t header = pcie_read32(dev, pos);
522 int id = PCI_EXT_CAP_ID(header);
523 int next = PCI_EXT_CAP_NEXT(header);
528 if (id == PCI_EXT_CAP_ID_ERR)
532 if (next <= PCIE_ECAP_OFFSET)
535 header = pcie_read32(dev, next);
536 id = PCI_EXT_CAP_ID(header);
538 if (id == PCI_EXT_CAP_ID_ERR)
541 next = PCI_EXT_CAP_NEXT(header);
547 static void pcie_check_dev_status(pcie_device_t *dev, int pos) {
548 /* Read Device Status register with mask for errors only */
549 uint16_t new_status = pcie_read16(dev, pos + PCI_EXP_DEVSTA) & 0xf;
551 /* Check if anything new should be reported */
552 if (!(pcie_config.persistent && new_status) &&
553 (new_status == dev->device_status))
556 /* Report errors found in Device Status register */
557 for (int i = 0; i < pcie_base_errors_num; i++) {
558 const pcie_error_t *err = pcie_base_errors + i;
559 const char *type_instance = (err->mask == PCI_EXP_DEVSTA_FED)
561 : (err->mask == PCI_EXP_DEVSTA_CED)
565 (err->mask == PCI_EXP_DEVSTA_FED) ? NOTIF_FAILURE : NOTIF_WARNING;
566 notification_t n = {.severity = severity,
568 .plugin = PCIE_ERRORS_PLUGIN,
571 if (err->mask & new_status) {
572 /* Error already reported, notify only if persistent is set */
573 if (!pcie_config.persistent && (err->mask & dev->device_status))
576 DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
577 dev->bus, dev->device, dev->function, err->desc);
578 snprintf(n.message, sizeof(n.message), "Device Status Error set: %s",
580 pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
582 } else if (err->mask & dev->device_status) {
583 DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
584 dev->bus, dev->device, dev->function, err->desc);
585 n.severity = NOTIF_OKAY;
586 snprintf(n.message, sizeof(n.message), "Device Status Error cleared: %s",
588 pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
592 dev->device_status = new_status;
595 static void pcie_check_aer(pcie_device_t *dev, int pos) {
596 /* Check for AER uncorrectable errors */
597 uint32_t errors = pcie_read32(dev, pos + PCI_ERR_UNCOR_STATUS);
599 if ((pcie_config.persistent && errors) ||
600 (errors != dev->uncorrectable_errors)) {
601 uint32_t masked = pcie_read32(dev, pos + PCI_ERR_UNCOR_MASK);
602 uint32_t severity = pcie_read32(dev, pos + PCI_ERR_UNCOR_SEVER);
603 pcie_dispatch_uncorrectable_errors(dev, errors, masked, severity);
605 dev->uncorrectable_errors = errors;
607 /* Check for AER correctable errors */
608 errors = pcie_read32(dev, pos + PCI_ERR_COR_STATUS);
609 if ((pcie_config.persistent && errors) ||
610 (errors != dev->correctable_errors)) {
611 uint32_t masked = pcie_read32(dev, pos + PCI_ERR_COR_MASK);
612 pcie_dispatch_correctable_errors(dev, errors, masked);
614 dev->correctable_errors = errors;
617 static int pcie_process_devices(llist_t *devs) {
622 for (llentry_t *e = llist_head(devs); e != NULL; e = e->next) {
623 pcie_device_t *dev = e->value;
625 if (pcie_fops.open(dev) == 0) {
626 pcie_check_dev_status(dev, dev->cap_exp);
627 if (dev->ecap_aer != -1)
628 pcie_check_aer(dev, dev->ecap_aer);
630 pcie_fops.close(dev);
632 notification_t n = {.severity = NOTIF_FAILURE,
634 .message = "Failed to read device status",
635 .plugin = PCIE_ERRORS_PLUGIN,
637 pcie_dispatch_notification(dev, &n, "", "");
645 /* This function is to be called during init to filter out no pcie devices */
646 static void pcie_preprocess_devices(llist_t *devs) {
652 for (llentry_t *e = llist_head(devs); e != NULL; e = e_next) {
653 pcie_device_t *dev = e->value;
656 if (pcie_fops.open(dev) == 0) {
657 uint16_t status = pcie_read16(dev, PCI_STATUS);
658 if (status & PCI_STATUS_CAP_LIST)
659 dev->cap_exp = pcie_find_cap_exp(dev);
661 /* Every PCIe device must have Capability Structure */
662 if (dev->cap_exp == -1) {
663 DEBUG(PCIE_ERRORS_PLUGIN ": Not PCI Express device: %04x:%02x:%02x.%d",
664 dev->domain, dev->bus, dev->device, dev->function);
667 dev->ecap_aer = pcie_find_ecap_aer(dev);
668 if (dev->ecap_aer == -1)
669 INFO(PCIE_ERRORS_PLUGIN
670 ": Device is not AER capable: %04x:%02x:%02x.%d",
671 dev->domain, dev->bus, dev->device, dev->function);
674 pcie_fops.close(dev);
676 ERROR(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: failed to open",
677 dev->domain, dev->bus, dev->device, dev->function);
684 llist_remove(devs, e);
690 static int pcie_plugin_read(__attribute__((unused)) user_data_t *ud) {
692 if (pcie_process_devices(pcie_dev_list) < 0) {
693 ERROR(PCIE_ERRORS_PLUGIN ": Failed to read devices state");
699 static void pcie_access_config(void) {
700 /* Set functions for register access to
701 * use proc or sysfs depending on config. */
702 if (pcie_config.use_sysfs) {
703 pcie_fops.list_devices = pcie_list_devices_sysfs;
704 pcie_fops.open = pcie_open_sysfs;
705 if (pcie_config.access_dir[0] == '\0')
706 sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_SYSFSDIR,
707 sizeof(pcie_config.access_dir));
710 pcie_fops.list_devices = pcie_list_devices_proc;
711 pcie_fops.open = pcie_open_proc;
712 if (pcie_config.access_dir[0] == '\0')
713 sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_PROCDIR,
714 sizeof(pcie_config.access_dir));
716 /* Common functions */
717 pcie_fops.close = pcie_close;
718 pcie_fops.read = pcie_read;
721 static int pcie_plugin_config(oconfig_item_t *ci) {
724 for (int i = 0; i < ci->children_num; i++) {
725 oconfig_item_t *child = ci->children + i;
727 if (strcasecmp("Source", child->key) == 0) {
728 if ((child->values_num != 1) ||
729 (child->values[0].type != OCONFIG_TYPE_STRING)) {
731 } else if (strcasecmp("proc", child->values[0].value.string) == 0) {
732 pcie_config.use_sysfs = false;
733 } else if (strcasecmp("sysfs", child->values[0].value.string) != 0) {
734 ERROR(PCIE_ERRORS_PLUGIN ": Allowed sources are 'proc' or 'sysfs'.");
737 } else if (strcasecmp("AccessDir", child->key) == 0) {
738 status = cf_util_get_string_buffer(child, pcie_config.access_dir,
739 sizeof(pcie_config.access_dir));
740 } else if (strcasecmp("ReportMasked", child->key) == 0) {
741 status = cf_util_get_boolean(child, &pcie_config.notif_masked);
742 } else if (strcasecmp("PersistentNotifications", child->key) == 0) {
743 status = cf_util_get_boolean(child, &pcie_config.persistent);
745 ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration option \"%s\".",
752 ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration parameter \"%s\".",
761 static int pcie_shutdown(void) {
762 pcie_clear_list(pcie_dev_list);
763 pcie_dev_list = NULL;
768 static int pcie_init(void) {
770 pcie_access_config();
771 pcie_dev_list = llist_create();
772 if (pcie_fops.list_devices(pcie_dev_list) != 0) {
773 ERROR(PCIE_ERRORS_PLUGIN ": Failed to find devices.");
777 pcie_preprocess_devices(pcie_dev_list);
778 if (llist_size(pcie_dev_list) == 0) {
779 /* No any PCI Express devices were found on the system */
780 ERROR(PCIE_ERRORS_PLUGIN ": No PCIe devices found in %s",
781 pcie_config.access_dir);
789 void module_register(void) {
790 plugin_register_init(PCIE_ERRORS_PLUGIN, pcie_init);
791 plugin_register_complex_config(PCIE_ERRORS_PLUGIN, pcie_plugin_config);
792 plugin_register_complex_read(NULL, PCIE_ERRORS_PLUGIN, pcie_plugin_read, 0,
794 plugin_register_shutdown(PCIE_ERRORS_PLUGIN, pcie_shutdown);