⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 eeh.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -