📄 eeh.c
字号:
/* * eeh.c * Copyright IBM Corporation 2001, 2005, 2006 * Copyright Dave Engebretsen & Todd Inglett 2001 * Copyright Linas Vepstas 2005, 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com> */#include <linux/delay.h>#include <linux/init.h>#include <linux/list.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <linux/rbtree.h>#include <linux/seq_file.h>#include <linux/spinlock.h>#include <asm/atomic.h>#include <asm/eeh.h>#include <asm/eeh_event.h>#include <asm/io.h>#include <asm/machdep.h>#include <asm/ppc-pci.h>#include <asm/rtas.h>#undef DEBUG/** Overview: * EEH, or "Extended 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 occured (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 has occurred, we assume it * is broken and panic. This sets the threshold for how many read * attempts we allow before panicking. */#define EEH_MAX_FAILS 2100000/* Time to wait for a PCI slot to report status, in milliseconds */#define PCI_BUS_RESET_WAIT_MSEC (60*1000)/* RTAS tokens */static int ibm_set_eeh_option;static int ibm_set_slot_reset;static int ibm_read_slot_reset_state;static int ibm_read_slot_reset_state2;static int ibm_slot_error_detail;static int ibm_get_config_addr_info;static int ibm_get_config_addr_info2;static int ibm_configure_bridge;int eeh_subsystem_enabled;EXPORT_SYMBOL(eeh_subsystem_enabled);/* Lock to avoid races due to multiple reports of an error */static DEFINE_SPINLOCK(confirm_error_lock);/* Buffer for reporting slot-error-detail rtas calls. Its here * in BSS, and not dynamically alloced, so that it ends up in * RMO where RTAS can access it. */static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX];static DEFINE_SPINLOCK(slot_errbuf_lock);static int eeh_error_buf_size;/* 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 4096static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];/* System monitoring statistics */static unsigned long no_device;static unsigned long no_dn;static unsigned long no_cfg_addr;static unsigned long ignored_check;static unsigned long total_mmio_ffs;static unsigned long false_positives;static unsigned long slot_resets;#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)/* --------------------------------------------------------------- *//* Below lies the EEH event infrastructure */static void rtas_slot_error_detail(struct pci_dn *pdn, int severity, char *driver_log, size_t loglen){ int config_addr; unsigned long flags; int rc; /* Log the error with the rtas logger */ spin_lock_irqsave(&slot_errbuf_lock, flags); memset(slot_errbuf, 0, eeh_error_buf_size); /* Use PE configuration address, if present */ config_addr = pdn->eeh_config_addr; if (pdn->eeh_pe_config_addr) config_addr = pdn->eeh_pe_config_addr; rc = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), virt_to_phys(driver_log), loglen, virt_to_phys(slot_errbuf), eeh_error_buf_size, severity); if (rc == 0) log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); spin_unlock_irqrestore(&slot_errbuf_lock, flags);}/** * gather_pci_data - copy assorted PCI config space registers to buff * @pdn: device to report data for * @buf: point to buffer in which to log * @len: amount of room in buffer * * This routine captures assorted PCI configuration space data, * and puts them into a buffer for RTAS error logging. */static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len){ struct device_node *dn; struct pci_dev *dev = pdn->pcidev; u32 cfg; int cap, i; int n = 0; n += scnprintf(buf+n, len-n, "%s\n", pdn->node->full_name); printk(KERN_WARNING "EEH: of node=%s\n", pdn->node->full_name); rtas_read_config(pdn, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg); rtas_read_config(pdn, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg); if (!dev) { printk(KERN_WARNING "EEH: no PCI device for this of node\n"); return n; } /* Gather bridge-specific registers */ if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) { rtas_read_config(pdn, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg); rtas_read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg); } /* Dump out the PCI-X command and status regs */ cap = pci_find_capability(dev, PCI_CAP_ID_PCIX); if (cap) { rtas_read_config(pdn, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg); rtas_read_config(pdn, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg); } /* If PCI-E capable, dump PCI-E cap 10, and the AER */ cap = pci_find_capability(dev, PCI_CAP_ID_EXP); if (cap) { n += scnprintf(buf+n, len-n, "pci-e cap10:\n"); printk(KERN_WARNING "EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { rtas_read_config(pdn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg); } cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (cap) { n += scnprintf(buf+n, len-n, "pci-e AER:\n"); printk(KERN_WARNING "EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<14; i++) { rtas_read_config(pdn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg); } } } /* Gather status on devices under the bridge */ if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) { dn = pdn->node->child; while (dn) { pdn = PCI_DN(dn); if (pdn) n += gather_pci_data(pdn, buf+n, len-n); dn = dn->sibling; } } return n;}void eeh_slot_error_detail(struct pci_dn *pdn, int severity){ size_t loglen = 0; pci_regs_buf[0] = 0; rtas_pci_enable(pdn, EEH_THAW_MMIO); loglen = gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen);}/** * read_slot_reset_state - Read the reset state of a device node's slot * @dn: device node to read * @rets: array to return results in */static int read_slot_reset_state(struct pci_dn *pdn, int rets[]){ int token, outputs; int config_addr; if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) { token = ibm_read_slot_reset_state2; outputs = 4; } else { token = ibm_read_slot_reset_state; rets[2] = 0; /* fake PE Unavailable info */ outputs = 3; } /* Use PE configuration address, if present */ config_addr = pdn->eeh_config_addr; if (pdn->eeh_pe_config_addr) config_addr = pdn->eeh_pe_config_addr; return rtas_call(token, 3, outputs, rets, config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid));}/** * eeh_wait_for_slot_status - returns error status of slot * @pdn pci device node * @max_wait_msecs maximum number to millisecs to wait * * Return negative value if a permanent error, else return * Partition Endpoint (PE) status value. * * If @max_wait_msecs is positive, then this routine will * sleep until a valid status can be obtained, or until * the max allowed wait time is exceeded, in which case * a -2 is returned. */inteeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs){ int rc; int rets[3]; int mwait; while (1) { rc = read_slot_reset_state(pdn, rets); if (rc) return rc; if (rets[1] == 0) return -1; /* EEH is not supported */ if (rets[0] != 5) return rets[0]; /* return actual status */ if (rets[2] == 0) return -1; /* permanently unavailable */ if (max_wait_msecs <= 0) break; mwait = rets[2]; if (mwait <= 0) { printk (KERN_WARNING "EEH: Firmware returned bad wait value=%d\n", mwait); mwait = 1000; } else if (mwait > 300*1000) { printk (KERN_WARNING "EEH: Firmware is taking too long, time=%d\n", mwait); mwait = 300*1000; } max_wait_msecs -= mwait; msleep (mwait); } printk(KERN_WARNING "EEH: Timed out waiting for slot status\n"); return -2;}/** * eeh_token_to_phys - convert EEH address token to phys address * @token i/o token, should be address in the form 0xA.... */static inline unsigned long eeh_token_to_phys(unsigned long token){ pte_t *ptep; unsigned long pa; ptep = find_linux_pte(init_mm.pgd, token); if (!ptep) return token; pa = pte_pfn(*ptep) << PAGE_SHIFT; return pa | (token & (PAGE_SIZE-1));}/** * Return the "partitionable endpoint" (pe) under which this device lies */struct device_node * find_device_pe(struct device_node *dn){ while ((dn->parent) && PCI_DN(dn->parent) && (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) { dn = dn->parent; } return dn;}/** Mark all devices that are peers of this device as failed. * Mark the device driver too, so that it can see the failure * immediately; this is critical, since some drivers poll * status registers in interrupts ... If a driver is polling, * and the slot is frozen, then the driver can deadlock in * an interrupt context, which is bad. */static void __eeh_mark_slot (struct device_node *dn, int mode_flag){ while (dn) { if (PCI_DN(dn)) { /* Mark the pci device driver too */ struct pci_dev *dev = PCI_DN(dn)->pcidev; PCI_DN(dn)->eeh_mode |= mode_flag; if (dev && dev->driver) dev->error_state = pci_channel_io_frozen; if (dn->child) __eeh_mark_slot (dn->child, mode_flag); } dn = dn->sibling; }}void eeh_mark_slot (struct device_node *dn, int mode_flag){ struct pci_dev *dev; dn = find_device_pe (dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) dn = dn->parent; PCI_DN(dn)->eeh_mode |= mode_flag; /* Mark the pci device too */ dev = PCI_DN(dn)->pcidev; if (dev) dev->error_state = pci_channel_io_frozen; __eeh_mark_slot (dn->child, mode_flag);}static void __eeh_clear_slot (struct device_node *dn, int mode_flag){ while (dn) { if (PCI_DN(dn)) { PCI_DN(dn)->eeh_mode &= ~mode_flag;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -