// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright IBM Corporation 2001, 2005, 2006 * Copyright Dave Engebretsen & Todd Inglett 2001 * Copyright Linas Vepstas 2005, 2006 * Copyright 2001-2012 IBM Corporation. * * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com> */ #include <linux/delay.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/list.h>#include <linux/pci.h>#include <linux/iommu.h>#include <linux/proc_fs.h>#include <linux/rbtree.h>#include <linux/reboot.h>#include <linux/seq_file.h>#include <linux/spinlock.h>#include <linux/export.h>#include <linux/of.h>#include <linux/debugfs.h> #include <linux/atomic.h>#include <asm/eeh.h>#include <asm/eeh_event.h>#include <asm/io.h>#include <asm/iommu.h>#include <asm/machdep.h>#include <asm/ppc-pci.h>#include <asm/rtas.h>#include <asm/pte-walk.h> /** Overview: * EEH, or "Enhanced Error Handling" is a PCI bridge technology for * dealing with PCI bus errors that can't be dealt with within the * usual PCI framework, except by check-stopping the CPU. Systems * that are designed for high-availability/reliability cannot afford * to crash due to a "mere" PCI error, thus the need for EEH. * An EEH-capable bridge operates by converting a detected error * into a "slot freeze", taking the PCI adapter off-line, making * the slot behave, from the OS'es point of view, as if the slot * were "empty": all reads return 0xff's and all writes are silently * ignored. EEH slot isolation events can be triggered by parity * errors on the address or data busses (e.g. during posted writes), * which in turn might be caused by low voltage on the bus, dust, * vibration, humidity, radioactivity or plain-old failed hardware. * * Note, however, that one of the leading causes of EEH slot * freeze events are buggy device drivers, buggy device microcode, * or buggy device hardware. This is because any attempt by the * device to bus-master data to a memory address that is not * assigned to the device will trigger a slot freeze. (The idea * is to prevent devices-gone-wild from corrupting system memory). * Buggy hardware/drivers will have a miserable time co-existing * with EEH. * * Ideally, a PCI device driver, when suspecting that an isolation * event has occurred (e.g. by reading 0xff's), will then ask EEH * whether this is the case, and then take appropriate steps to * reset the PCI slot, the PCI device, and then resume operations. * However, until that day, the checking is done here, with the * eeh_check_failure() routine embedded in the MMIO macros. If * the slot is found to be isolated, an "EEH Event" is synthesized * and sent out for processing. */ /* If a device driver keeps reading an MMIO register in an interrupt * handler after a slot isolation event, it might be broken. * This sets the threshold for how many read attempts we allow * before printing an error message. */#define EEH_MAX_FAILS 2100000 /* Time to wait for a PCI slot to report status, in milliseconds */#define PCI_BUS_RESET_WAIT_MSEC (5*60*1000) /* * EEH probe mode support, which is part of the flags, * is to support multiple platforms for EEH. Some platforms * like pSeries do PCI emunation based on device tree. * However, other platforms like powernv probe PCI devices * from hardware. The flag is used to distinguish that. * In addition, struct eeh_ops::probe would be invoked for * particular OF node or PCI device so that the corresponding * PE would be created there. */int eeh_subsystem_flags;EXPORT_SYMBOL(eeh_subsystem_flags); /* * EEH allowed maximal frozen times. If one particular PE's * frozen count in last hour exceeds this limit, the PE will * be forced to be offline permanently. */u32 eeh_max_freezes = 5; /* * Controls whether a recovery event should be scheduled when an * isolated device is discovered. This is only really useful for * debugging problems with the EEH core. */bool eeh_debugfs_no_recover; /* Platform dependent EEH operations */struct eeh_ops *eeh_ops = NULL; /* Lock to avoid races due to multiple reports of an error */DEFINE_RAW_SPINLOCK(confirm_error_lock);EXPORT_SYMBOL_GPL(confirm_error_lock); /* Lock to protect passed flags */static DEFINE_MUTEX(eeh_dev_mutex); /* Buffer for reporting pci register dumps. Its here in BSS, and * not dynamically alloced, so that it ends up in RMO where RTAS * can access it. */#define EEH_PCI_REGS_LOG_LEN 8192static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN]; /* * The struct is used to maintain the EEH global statistic * information. Besides, the EEH global statistics will be * exported to user space through procfs */struct eeh_stats { u64 no_device; /* PCI device not found */ u64 no_dn; /* OF node not found */ u64 no_cfg_addr; /* Config address not found */ u64 ignored_check; /* EEH check skipped */ u64 total_mmio_ffs; /* Total EEH checks */ u64 false_positives; /* Unnecessary EEH checks */ u64 slot_resets; /* PE reset */}; static struct eeh_stats eeh_stats; static int __init eeh_setup(char *str){ if (!strcmp(str, "off")) eeh_add_flag(EEH_FORCE_DISABLED); else if (!strcmp(str, "early_log")) eeh_add_flag(EEH_EARLY_DUMP_LOG); return 1;}__setup("eeh=", eeh_setup); void eeh_show_enabled(void){ if (eeh_has_flag(EEH_FORCE_DISABLED)) pr_info("EEH: Recovery disabled by kernel parameter.\n"); else if (eeh_has_flag(EEH_ENABLED)) pr_info("EEH: Capable adapter found: recovery enabled.\n"); else pr_info("EEH: No capable adapters found: recovery disabled.\n");} /* * This routine captures assorted PCI configuration space data * for the indicated PCI device, and puts them into a buffer * for RTAS error logging. */static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len){ u32 cfg; int cap, i; int n = 0, l = 0; char buffer[128]; n += scnprintf(buf+n, len-n, "%04x:%02x:%02x.%01x\n", edev->pe->phb->global_number, edev->bdfn >> 8, PCI_SLOT(edev->bdfn), PCI_FUNC(edev->bdfn)); pr_warn("EEH: of node=%04x:%02x:%02x.%01x\n", edev->pe->phb->global_number, edev->bdfn >> 8, PCI_SLOT(edev->bdfn), PCI_FUNC(edev->bdfn)); eeh_ops->read_config(edev, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); pr_warn("EEH: PCI device/vendor: %08x\n", cfg); eeh_ops->read_config(edev, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); pr_warn("EEH: PCI cmd/status register: %08x\n", cfg); /* Gather bridge-specific registers */ if (edev->mode & EEH_DEV_BRIDGE) { eeh_ops->read_config(edev, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); pr_warn("EEH: Bridge secondary status: %04x\n", cfg); eeh_ops->read_config(edev, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); pr_warn("EEH: Bridge control: %04x\n", cfg); } /* Dump out the PCI-X command and status regs */ cap = edev->pcix_cap; if (cap) { eeh_ops->read_config(edev, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); pr_warn("EEH: PCI-X cmd: %08x\n", cfg); eeh_ops->read_config(edev, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); pr_warn("EEH: PCI-X status: %08x\n", cfg); } /* If PCI-E capable, dump PCI-E cap 10 */ cap = edev->pcie_cap; if (cap) { n += scnprintf(buf+n, len-n, "pci-e cap10:\n"); pr_warn("EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { eeh_ops->read_config(edev, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { if (i != 0) pr_warn("%s\n", buffer); l = scnprintf(buffer, sizeof(buffer), "EEH: PCI-E %02x: %08x ", 4*i, cfg); } else { l += scnprintf(buffer+l, sizeof(buffer)-l, "%08x ", cfg); } } pr_warn("%s\n", buffer); } /* If AER capable, dump it */ cap = edev->aer_cap; if (cap) { n += scnprintf(buf+n, len-n, "pci-e AER:\n"); pr_warn("EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<=13; i++) { eeh_ops->read_config(edev, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { if (i != 0) pr_warn("%s\n", buffer); l = scnprintf(buffer, sizeof(buffer), "EEH: PCI-E AER %02x: %08x ", 4*i, cfg); } else { l += scnprintf(buffer+l, sizeof(buffer)-l, "%08x ", cfg); } } pr_warn("%s\n", buffer); } return n;} static void *eeh_dump_pe_log(struct eeh_pe *pe, void *flag){ struct eeh_dev *edev, *tmp; size_t *plen = flag; eeh_pe_for_each_dev(pe, edev, tmp) *plen += eeh_dump_dev_log(edev, pci_regs_buf + *plen, EEH_PCI_REGS_LOG_LEN - *plen); return NULL;} /** * eeh_slot_error_detail - Generate combined log including driver log and error log * @pe: EEH PE * @severity: temporary or permanent error log * * This routine should be called to generate the combined log, which * is comprised of driver log and error log. The driver log is figured * out from the config space of the corresponding PCI device, while * the error log is fetched through platform dependent function call. */void eeh_slot_error_detail(struct eeh_pe *pe, int severity){ size_t loglen = 0; /* * When the PHB is fenced or dead, it's pointless to collect * the data from PCI config space because it should return * 0xFF's. For ER, we still retrieve the data from the PCI * config space. * * For pHyp, we have to enable IO for log retrieval. Otherwise, * 0xFF's is always returned from PCI config space. * * When the @severity is EEH_LOG_PERM, the PE is going to be * removed. Prior to that, the drivers for devices included in * the PE will be closed. The drivers rely on working IO path * to bring the devices to quiet state. Otherwise, PCI traffic * from those devices after they are removed is like to cause * another unexpected EEH error. */ if (!(pe->type & EEH_PE_PHB)) { if (eeh_has_flag(EEH_ENABLE_IO_FOR_LOG) || severity == EEH_LOG_PERM) eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); /* * The config space of some PCI devices can't be accessed * when their PEs are in frozen state. Otherwise, fenced * PHB might be seen. Those PEs are identified with flag * EEH_PE_CFG_RESTRICTED, indicating EEH_PE_CFG_BLOCKED * is set automatically when the PE is put to EEH_PE_ISOLATED. * * Restoring BARs possibly triggers PCI config access in * (OPAL) firmware and then causes fenced PHB. If the * PCI config is blocked with flag EEH_PE_CFG_BLOCKED, it's * pointless to restore BARs and dump config space. */ eeh_ops->configure_bridge(pe); if (!(pe->state & EEH_PE_CFG_BLOCKED)) { eeh_pe_restore_bars(pe); pci_regs_buf[0] = 0; eeh_pe_traverse(pe, eeh_dump_pe_log, &loglen); } } eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);} /** * eeh_token_to_phys - Convert EEH address token to phys address * @token: I/O token, should be address in the form 0xA.... * * This routine should be called to convert virtual I/O address * to physical one. */static inline unsigned long eeh_token_to_phys(unsigned long token){ return ppc_find_vmap_phys(token);} /* * On PowerNV platform, we might already have fenced PHB there. * For that case, it's meaningless to recover frozen PE. Intead, * We have to handle fenced PHB firstly. */static int eeh_phb_check_failure(struct eeh_pe *pe){ struct eeh_pe *phb_pe; unsigned long flags; int ret; if (!eeh_has_flag(EEH_PROBE_MODE_DEV)) return -EPERM; /* Find the PHB PE */ phb_pe = eeh_phb_pe_get(pe->phb); if (!phb_pe) { pr_warn("%s Can't find PE for PHB#%x\n", __func__, pe->phb->global_number); return -EEXIST; } /* If the PHB has been in problematic state */ eeh_serialize_lock(&flags); if (phb_pe->state & EEH_PE_ISOLATED) { ret = 0; goto out; } /* Check PHB state */ ret = eeh_ops->get_state(phb_pe, NULL); if ((ret < 0) || (ret == EEH_STATE_NOT_SUPPORT) || eeh_state_active(ret)) { ret = 0; goto out; } /* Isolate the PHB and send event */ eeh_pe_mark_isolated(phb_pe); eeh_serialize_unlock(flags); pr_debug("EEH: PHB#%x failure detected, location: %s\n", phb_pe->phb->global_number, eeh_pe_loc_get(phb_pe)); eeh_send_failure_event(phb_pe); return 1;out: eeh_serialize_unlock(flags); return ret;} static inline const char *eeh_driver_name(struct pci_dev *pdev){ if (pdev) return dev_driver_string(&pdev->dev); return "<null>";} /** * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze * @edev: eeh device * * Check for an EEH failure for the given device node. Call this * routine if the result of a read was all 0xff's and you want to * find out if this is due to an EEH slot freeze. This routine * will query firmware for the EEH status. * * Returns 0 if there has not been an EEH error; otherwise returns * a non-zero value and queues up a slot isolation event notification. * * It is safe to call this routine in an interrupt context. */int eeh_dev_check_failure(struct eeh_dev *edev){ int ret; unsigned long flags; struct device_node *dn; struct pci_dev *dev; struct eeh_pe *pe, *parent_pe; int rc = 0; const char *location = NULL; eeh_stats.total_mmio_ffs++; if (!eeh_enabled()) return 0; if (!edev) { eeh_stats.no_dn++; return 0; } dev = eeh_dev_to_pci_dev(edev); pe = eeh_dev_to_pe(edev); /* Access to IO BARs might get this far and still not want checking. */ if (!pe) { eeh_stats.ignored_check++; eeh_edev_dbg(edev, "Ignored check\n"); return 0; } /* * On PowerNV platform, we might already have fenced PHB * there and we need take care of that firstly. */ ret = eeh_phb_check_failure(pe); if (ret > 0) return ret; /* * If the PE isn't owned by us, we shouldn't check the * state. Instead, let the owner handle it if the PE has * been frozen. */ if (eeh_pe_passed(pe)) return 0; /* If we already have a pending isolation event for this * slot, we know it's bad already, we don't need to check. * Do this checking under a lock; as multiple PCI devices * in one slot might report errors simultaneously, and we * only want one error recovery routine running. */ eeh_serialize_lock(&flags); rc = 1; if (pe->state & EEH_PE_ISOLATED) { pe->check_count++; if (pe->check_count == EEH_MAX_FAILS) { dn = pci_device_to_OF_node(dev); if (dn) location = of_get_property(dn, "ibm,loc-code", NULL); eeh_edev_err(edev, "%d reads ignored for recovering device at location=%s driver=%s\n", pe->check_count, location ? location : "unknown", eeh_driver_name(dev)); eeh_edev_err(edev, "Might be infinite loop in %s driver\n", eeh_driver_name(dev)); dump_stack(); } goto dn_unlock; } /* * Now test for an EEH failure. This is VERY expensive. * Note that the eeh_config_addr may be a parent device * in the case of a device behind a bridge, or it may be * function zero of a multi-function device. * In any case they must share a common PHB.Lines 1–500 of 1928. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright IBM Corporation 2001, 2005, 2006 * Copyright Dave Engebretsen & Todd Inglett 2001 * Copyright Linas Vepstas 2005, 2006 * Copyright 2001-2012 IBM Corporation. * * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com> */ #include <linux/delay.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/list.h> #include <linux/pci.h> #include <linux/iommu.h> #include <linux/proc_fs.h> #include <linux/rbtree.h> #include <linux/reboot.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/export.h> #include <linux/of.h> #include <linux/debugfs.h> #include <linux/atomic.h> #include <asm/eeh.h> #include <asm/eeh_event.h> #include <asm/io.h> #include <asm/iommu.h> #include <asm/machdep.h> #include <asm/ppc-pci.h> #include <asm/rtas.h> #include <asm/pte-walk.h> /** Overview: * EEH, or "Enhanced Error Handling" is a PCI bridge technology for * dealing with PCI bus errors that can't be dealt with within the * usual PCI framework, except by check-stopping the CPU. Systems * that are designed for high-availability/reliability cannot afford * to crash due to a "mere" PCI error, thus the need for EEH. * An EEH-capable bridge operates by converting a detected error * into a "slot freeze", taking the PCI adapter off-line, making * the slot behave, from the OS'es point of view, as if the slot * were "empty": all reads return 0xff's and all writes are silently * ignored. EEH slot isolation events can be triggered by parity * errors on the address or data busses (e.g. during posted writes), * which in turn might be caused by low voltage on the bus, dust, * vibration, humidity, radioactivity or plain-old failed hardware. * * Note, however, that one of the leading causes of EEH slot * freeze events are buggy device drivers, buggy device microcode, * or buggy device hardware. This is because any attempt by the * device to bus-master data to a memory address that is not * assigned to the device will trigger a slot freeze. (The idea * is to prevent devices-gone-wild from corrupting system memory). * Buggy hardware/drivers will have a miserable time co-existing * with EEH. * * Ideally, a PCI device driver, when suspecting that an isolation * event has occurred (e.g. by reading 0xff's), will then ask EEH * whether this is the case, and then take appropriate steps to * reset the PCI slot, the PCI device, and then resume operations. * However, until that day, the checking is done here, with the * eeh_check_failure() routine embedded in the MMIO macros. If * the slot is found to be isolated, an "EEH Event" is synthesized * and sent out for processing. */ /* If a device driver keeps reading an MMIO register in an interrupt * handler after a slot isolation event, it might be broken. * This sets the threshold for how many read attempts we allow * before printing an error message. */ #define EEH_MAX_FAILS 2100000 /* Time to wait for a PCI slot to report status, in milliseconds */ #define PCI_BUS_RESET_WAIT_MSEC (5*60*1000) /* * EEH probe mode support, which is part of the flags, * is to support multiple platforms for EEH. Some platforms * like pSeries do PCI emunation based on device tree. * However, other platforms like powernv probe PCI devices * from hardware. The flag is used to distinguish that. * In addition, struct eeh_ops::probe would be invoked for * particular OF node or PCI device so that the corresponding * PE would be created there. */ int eeh_subsystem_flags; EXPORT_SYMBOL(eeh_subsystem_flags); /* * EEH allowed maximal frozen times. If one particular PE's * frozen count in last hour exceeds this limit, the PE will * be forced to be offline permanently. */ u32 eeh_max_freezes = 5; /* * Controls whether a recovery event should be scheduled when an * isolated device is discovered. This is only really useful for * debugging problems with the EEH core. */ bool eeh_debugfs_no_recover; /* Platform dependent EEH operations */ struct eeh_ops *eeh_ops = NULL; /* Lock to avoid races due to multiple reports of an error */ DEFINE_RAW_SPINLOCK(confirm_error_lock); EXPORT_SYMBOL_GPL(confirm_error_lock); /* Lock to protect passed flags */ static DEFINE_MUTEX(eeh_dev_mutex); /* Buffer for reporting pci register dumps. Its here in BSS, and * not dynamically alloced, so that it ends up in RMO where RTAS * can access it. */ #define EEH_PCI_REGS_LOG_LEN 8192 static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN]; /* * The struct is used to maintain the EEH global statistic * information. Besides, the EEH global statistics will be * exported to user space through procfs */ struct eeh_stats { u64 no_device; /* PCI device not found */ u64 no_dn; /* OF node not found */ u64 no_cfg_addr; /* Config address not found */ u64 ignored_check; /* EEH check skipped */ u64 total_mmio_ffs; /* Total EEH checks */ u64 false_positives; /* Unnecessary EEH checks */ u64 slot_resets; /* PE reset */ }; static struct eeh_stats eeh_stats; static int __init eeh_setup(char *str) { if (!strcmp(str, "off")) eeh_add_flag(EEH_FORCE_DISABLED); else if (!strcmp(str, "early_log")) eeh_add_flag(EEH_EARLY_DUMP_LOG); return 1; } __setup("eeh=", eeh_setup); void eeh_show_enabled(void) { if (eeh_has_flag(EEH_FORCE_DISABLED)) pr_info("EEH: Recovery disabled by kernel parameter.\n"); else if (eeh_has_flag(EEH_ENABLED)) pr_info("EEH: Capable adapter found: recovery enabled.\n"); else pr_info("EEH: No capable adapters found: recovery disabled.\n"); } /* * This routine captures assorted PCI configuration space data * for the indicated PCI device, and puts them into a buffer * for RTAS error logging. */ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) { u32 cfg; int cap, i; int n = 0, l = 0; char buffer[128]; n += scnprintf(buf+n, len-n, "%04x:%02x:%02x.%01x\n", edev->pe->phb->global_number, edev->bdfn >> 8, PCI_SLOT(edev->bdfn), PCI_FUNC(edev->bdfn)); pr_warn("EEH: of node=%04x:%02x:%02x.%01x\n", edev->pe->phb->global_number, edev->bdfn >> 8, PCI_SLOT(edev->bdfn), PCI_FUNC(edev->bdfn)); eeh_ops->read_config(edev, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); pr_warn("EEH: PCI device/vendor: %08x\n", cfg); eeh_ops->read_config(edev, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); pr_warn("EEH: PCI cmd/status register: %08x\n", cfg); /* Gather bridge-specific registers */ if (edev->mode & EEH_DEV_BRIDGE) { eeh_ops->read_config(edev, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); pr_warn("EEH: Bridge secondary status: %04x\n", cfg); eeh_ops->read_config(edev, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); pr_warn("EEH: Bridge control: %04x\n", cfg); } /* Dump out the PCI-X command and status regs */ cap = edev->pcix_cap; if (cap) { eeh_ops->read_config(edev, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); pr_warn("EEH: PCI-X cmd: %08x\n", cfg); eeh_ops->read_config(edev, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); pr_warn("EEH: PCI-X status: %08x\n", cfg); } /* If PCI-E capable, dump PCI-E cap 10 */ cap = edev->pcie_cap; if (cap) { n += scnprintf(buf+n, len-n, "pci-e cap10:\n"); pr_warn("EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { eeh_ops->read_config(edev, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { if (i != 0) pr_warn("%s\n", buffer); l = scnprintf(buffer, sizeof(buffer), "EEH: PCI-E %02x: %08x ", 4*i, cfg); } else { l += scnprintf(buffer+l, sizeof(buffer)-l, "%08x ", cfg); } } pr_warn("%s\n", buffer); } /* If AER capable, dump it */ cap = edev->aer_cap; if (cap) { n += scnprintf(buf+n, len-n, "pci-e AER:\n"); pr_warn("EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<=13; i++) { eeh_ops->read_config(edev, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { if (i != 0) pr_warn("%s\n", buffer); l = scnprintf(buffer, sizeof(buffer), "EEH: PCI-E AER %02x: %08x ", 4*i, cfg); } else { l += scnprintf(buffer+l, sizeof(buffer)-l, "%08x ", cfg); } } pr_warn("%s\n", buffer); } return n; } static void *eeh_dump_pe_log(struct eeh_pe *pe, void *flag) { struct eeh_dev *edev, *tmp; size_t *plen = flag; eeh_pe_for_each_dev(pe, edev, tmp) *plen += eeh_dump_dev_log(edev, pci_regs_buf + *plen, EEH_PCI_REGS_LOG_LEN - *plen); return NULL; } /** * eeh_slot_error_detail - Generate combined log including driver log and error log * @pe: EEH PE * @severity: temporary or permanent error log * * This routine should be called to generate the combined log, which * is comprised of driver log and error log. The driver log is figured * out from the config space of the corresponding PCI device, while * the error log is fetched through platform dependent function call. */ void eeh_slot_error_detail(struct eeh_pe *pe, int severity) { size_t loglen = 0; /* * When the PHB is fenced or dead, it's pointless to collect * the data from PCI config space because it should return * 0xFF's. For ER, we still retrieve the data from the PCI * config space. * * For pHyp, we have to enable IO for log retrieval. Otherwise, * 0xFF's is always returned from PCI config space. * * When the @severity is EEH_LOG_PERM, the PE is going to be * removed. Prior to that, the drivers for devices included in * the PE will be closed. The drivers rely on working IO path * to bring the devices to quiet state. Otherwise, PCI traffic * from those devices after they are removed is like to cause * another unexpected EEH error. */ if (!(pe->type & EEH_PE_PHB)) { if (eeh_has_flag(EEH_ENABLE_IO_FOR_LOG) || severity == EEH_LOG_PERM) eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); /* * The config space of some PCI devices can't be accessed * when their PEs are in frozen state. Otherwise, fenced * PHB might be seen. Those PEs are identified with flag * EEH_PE_CFG_RESTRICTED, indicating EEH_PE_CFG_BLOCKED * is set automatically when the PE is put to EEH_PE_ISOLATED. * * Restoring BARs possibly triggers PCI config access in * (OPAL) firmware and then causes fenced PHB. If the * PCI config is blocked with flag EEH_PE_CFG_BLOCKED, it's * pointless to restore BARs and dump config space. */ eeh_ops->configure_bridge(pe); if (!(pe->state & EEH_PE_CFG_BLOCKED)) { eeh_pe_restore_bars(pe); pci_regs_buf[0] = 0; eeh_pe_traverse(pe, eeh_dump_pe_log, &loglen); } } eeh_ops->get_log(pe, severity, pci_regs_buf, loglen); } /** * eeh_token_to_phys - Convert EEH address token to phys address * @token: I/O token, should be address in the form 0xA.... * * This routine should be called to convert virtual I/O address * to physical one. */ static inline unsigned long eeh_token_to_phys(unsigned long token) { return ppc_find_vmap_phys(token); } /* * On PowerNV platform, we might already have fenced PHB there. * For that case, it's meaningless to recover frozen PE. Intead, * We have to handle fenced PHB firstly. */ static int eeh_phb_check_failure(struct eeh_pe *pe) { struct eeh_pe *phb_pe; unsigned long flags; int ret; if (!eeh_has_flag(EEH_PROBE_MODE_DEV)) return -EPERM; /* Find the PHB PE */ phb_pe = eeh_phb_pe_get(pe->phb); if (!phb_pe) { pr_warn("%s Can't find PE for PHB#%x\n", __func__, pe->phb->global_number); return -EEXIST; } /* If the PHB has been in problematic state */ eeh_serialize_lock(&flags); if (phb_pe->state & EEH_PE_ISOLATED) { ret = 0; goto out; } /* Check PHB state */ ret = eeh_ops->get_state(phb_pe, NULL); if ((ret < 0) || (ret == EEH_STATE_NOT_SUPPORT) || eeh_state_active(ret)) { ret = 0; goto out; } /* Isolate the PHB and send event */ eeh_pe_mark_isolated(phb_pe); eeh_serialize_unlock(flags); pr_debug("EEH: PHB#%x failure detected, location: %s\n", phb_pe->phb->global_number, eeh_pe_loc_get(phb_pe)); eeh_send_failure_event(phb_pe); return 1; out: eeh_serialize_unlock(flags); return ret; } static inline const char *eeh_driver_name(struct pci_dev *pdev) { if (pdev) return dev_driver_string(&pdev->dev); return "<null>"; } /** * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze * @edev: eeh device * * Check for an EEH failure for the given device node. Call this * routine if the result of a read was all 0xff's and you want to * find out if this is due to an EEH slot freeze. This routine * will query firmware for the EEH status. * * Returns 0 if there has not been an EEH error; otherwise returns * a non-zero value and queues up a slot isolation event notification. * * It is safe to call this routine in an interrupt context. */ int eeh_dev_check_failure(struct eeh_dev *edev) { int ret; unsigned long flags; struct device_node *dn; struct pci_dev *dev; struct eeh_pe *pe, *parent_pe; int rc = 0; const char *location = NULL; eeh_stats.total_mmio_ffs++; if (!eeh_enabled()) return 0; if (!edev) { eeh_stats.no_dn++; return 0; } dev = eeh_dev_to_pci_dev(edev); pe = eeh_dev_to_pe(edev); /* Access to IO BARs might get this far and still not want checking. */ if (!pe) { eeh_stats.ignored_check++; eeh_edev_dbg(edev, "Ignored check\n"); return 0; } /* * On PowerNV platform, we might already have fenced PHB * there and we need take care of that firstly. */ ret = eeh_phb_check_failure(pe); if (ret > 0) return ret; /* * If the PE isn't owned by us, we shouldn't check the * state. Instead, let the owner handle it if the PE has * been frozen. */ if (eeh_pe_passed(pe)) return 0; /* If we already have a pending isolation event for this * slot, we know it's bad already, we don't need to check. * Do this checking under a lock; as multiple PCI devices * in one slot might report errors simultaneously, and we * only want one error recovery routine running. */ eeh_serialize_lock(&flags); rc = 1; if (pe->state & EEH_PE_ISOLATED) { pe->check_count++; if (pe->check_count == EEH_MAX_FAILS) { dn = pci_device_to_OF_node(dev); if (dn) location = of_get_property(dn, "ibm,loc-code", NULL); eeh_edev_err(edev, "%d reads ignored for recovering device at location=%s driver=%s\n", pe->check_count, location ? location : "unknown", eeh_driver_name(dev)); eeh_edev_err(edev, "Might be infinite loop in %s driver\n", eeh_driver_name(dev)); dump_stack(); } goto dn_unlock; } /* * Now test for an EEH failure. This is VERY expensive. * Note that the eeh_config_addr may be a parent device * in the case of a device behind a bridge, or it may be * function zero of a multi-function device. * In any case they must share a common PHB.