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

📄 eeh.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* Log the error with the rtas logger */	spin_lock_irqsave(&slot_errbuf_lock, flags);	memset(slot_errbuf, 0, eeh_error_buf_size);	rc = rtas_call(ibm_slot_error_detail,	               8, 1, NULL, pdn->eeh_config_addr,	               BUID_HI(pdn->phb->buid),	               BUID_LO(pdn->phb->buid), NULL, 0,	               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);}/** * 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;	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;	}	return rtas_call(token, 3, outputs, rets, pdn->eeh_config_addr,			 BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid));}/** * 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 */static 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)) {			PCI_DN(dn)->eeh_mode |= mode_flag;			if (dn->child)				__eeh_mark_slot (dn->child, mode_flag);		}		dn = dn->sibling;	}}void eeh_mark_slot (struct device_node *dn, int mode_flag){	dn = find_device_pe (dn);	PCI_DN(dn)->eeh_mode |= mode_flag;	__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;			PCI_DN(dn)->eeh_check_count = 0;			if (dn->child)				__eeh_clear_slot (dn->child, mode_flag);		}		dn = dn->sibling;	}}void eeh_clear_slot (struct device_node *dn, int mode_flag){	unsigned long flags;	spin_lock_irqsave(&confirm_error_lock, flags);	dn = find_device_pe (dn);	PCI_DN(dn)->eeh_mode &= ~mode_flag;	PCI_DN(dn)->eeh_check_count = 0;	__eeh_clear_slot (dn->child, mode_flag);	spin_unlock_irqrestore(&confirm_error_lock, flags);}/** * eeh_dn_check_failure - check if all 1's data is due to EEH slot freeze * @dn device node * @dev pci device, if known * * 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_dn_check_failure(struct device_node *dn, struct pci_dev *dev){	int ret;	int rets[3];	unsigned long flags;	struct pci_dn *pdn;	int rc = 0;	__get_cpu_var(total_mmio_ffs)++;	if (!eeh_subsystem_enabled)		return 0;	if (!dn) {		__get_cpu_var(no_dn)++;		return 0;	}	pdn = PCI_DN(dn);	/* Access to IO BARs might get this far and still not want checking. */	if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) ||	    pdn->eeh_mode & EEH_MODE_NOCHECK) {		__get_cpu_var(ignored_check)++;#ifdef DEBUG		printk ("EEH:ignored check (%x) for %s %s\n", 		        pdn->eeh_mode, pci_name (dev), dn->full_name);#endif		return 0;	}	if (!pdn->eeh_config_addr) {		__get_cpu_var(no_cfg_addr)++;		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.	 */	spin_lock_irqsave(&confirm_error_lock, flags);	rc = 1;	if (pdn->eeh_mode & EEH_MODE_ISOLATED) {		pdn->eeh_check_count ++;		if (pdn->eeh_check_count >= EEH_MAX_FAILS) {			printk (KERN_ERR "EEH: Device driver ignored %d bad reads, panicing\n",			        pdn->eeh_check_count);			dump_stack();						/* re-read the slot reset state */			if (read_slot_reset_state(pdn, rets) != 0)				rets[0] = -1;	/* reset state unknown */			/* If we are here, then we hit an infinite loop. Stop. */			panic("EEH: MMIO halt (%d) on device:%s\n", rets[0], pci_name(dev));		}		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.	 */	ret = read_slot_reset_state(pdn, rets);	/* If the call to firmware failed, punt */	if (ret != 0) {		printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n",		       ret, dn->full_name);		__get_cpu_var(false_positives)++;		rc = 0;		goto dn_unlock;	}	/* If EEH is not supported on this device, punt. */	if (rets[1] != 1) {		printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n",		       ret, dn->full_name);		__get_cpu_var(false_positives)++;		rc = 0;		goto dn_unlock;	}	/* If not the kind of error we know about, punt. */	if (rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {		__get_cpu_var(false_positives)++;		rc = 0;		goto dn_unlock;	}	/* Note that config-io to empty slots may fail;	 * we recognize empty because they don't have children. */	if ((rets[0] == 5) && (dn->child == NULL)) {		__get_cpu_var(false_positives)++;		rc = 0;		goto dn_unlock;	}	__get_cpu_var(slot_resets)++; 	/* Avoid repeated reports of this failure, including problems	 * with other functions on this device, and functions under	 * bridges. */	eeh_mark_slot (dn, EEH_MODE_ISOLATED);	spin_unlock_irqrestore(&confirm_error_lock, flags);	eeh_send_failure_event (dn, dev, rets[0], rets[2]);		/* Most EEH events are due to device driver bugs.  Having	 * a stack trace will help the device-driver authors figure	 * out what happened.  So print that out. */	if (rets[0] != 5) dump_stack();	return 1;dn_unlock:	spin_unlock_irqrestore(&confirm_error_lock, flags);	return rc;}EXPORT_SYMBOL_GPL(eeh_dn_check_failure);/** * eeh_check_failure - check if all 1's data is due to EEH slot freeze * @token i/o token, should be address in the form 0xA.... * @val value, should be all 1's (XXX why do we need this arg??) * * Check for an EEH failure at the given token address.  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 event.  This routine * will query firmware for the EEH status. * * Note this routine is safe to call in an interrupt context. */unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val){	unsigned long addr;	struct pci_dev *dev;	struct device_node *dn;	/* Finding the phys addr + pci device; this is pretty quick. */	addr = eeh_token_to_phys((unsigned long __force) token);	dev = pci_get_device_by_addr(addr);	if (!dev) {		__get_cpu_var(no_device)++;		return val;	}	dn = pci_device_to_OF_node(dev);	eeh_dn_check_failure (dn, dev);	pci_dev_put(dev);	return val;}EXPORT_SYMBOL(eeh_check_failure);/* ------------------------------------------------------------- *//* The code below deals with error recovery *//** Return negative value if a permanent error, else return * a number of milliseconds to wait until the PCI slot is * ready to be used. */static inteeh_slot_availability(struct pci_dn *pdn){	int rc;	int rets[3];	rc = read_slot_reset_state(pdn, rets);	if (rc) return rc;	if (rets[1] == 0) return -1;  /* EEH is not supported */	if (rets[0] == 0)  return 0;  /* Oll Korrect */	if (rets[0] == 5) {		if (rets[2] == 0) return -1; /* permanently unavailable */		return rets[2]; /* number of millisecs to wait */	}	return -1;}/** rtas_pci_slot_reset raises/lowers the pci #RST line *  state: 1/0 to raise/lower the #RST * * Clear the EEH-frozen condition on a slot.  This routine * asserts the PCI #RST line if the 'state' argument is '1', * and drops the #RST line if 'state is '0'.  This routine is * safe to call in an interrupt context. * */static voidrtas_pci_slot_reset(struct pci_dn *pdn, int state){	int rc;	BUG_ON (pdn==NULL); 	if (!pdn->phb) {		printk (KERN_WARNING "EEH: in slot reset, device node %s has no phb\n",		        pdn->node->full_name);		return;	}	rc = rtas_call(ibm_set_slot_reset,4,1, NULL,	               pdn->eeh_config_addr,	               BUID_HI(pdn->phb->buid),	               BUID_LO(pdn->phb->buid),	               state);	if (rc) {		printk (KERN_WARNING "EEH: Unable to reset the failed slot, (%d) #RST=%d dn=%s\n", 		        rc, state, pdn->node->full_name);		return;	}}/** rtas_set_slot_reset -- assert the pci #RST line for 1/4 second *  dn -- device node to be reset. */voidrtas_set_slot_reset(struct pci_dn *pdn){	int i, rc;	rtas_pci_slot_reset (pdn, 1);	/* The PCI bus requires that the reset be held high for at least	 * a 100 milliseconds. We wait a bit longer 'just in case'.  */#define PCI_BUS_RST_HOLD_TIME_MSEC 250	msleep (PCI_BUS_RST_HOLD_TIME_MSEC);		/* We might get hit with another EEH freeze as soon as the 	 * pci slot reset line is dropped. Make sure we don't miss	 * these, and clear the flag now. */	eeh_clear_slot (pdn->node, EEH_MODE_ISOLATED);	rtas_pci_slot_reset (pdn, 0);	/* After a PCI slot has been reset, the PCI Express spec requires	 * a 1.5 second idle time for the bus to stabilize, before starting	 * up traffic. */#define PCI_BUS_SETTLE_TIME_MSEC 1800	msleep (PCI_BUS_SETTLE_TIME_MSEC);	/* Now double check with the firmware to make sure the device is	 * ready to be used; if not, wait for recovery. */	for (i=0; i<10; i++) {		rc = eeh_slot_availability (pdn);		if (rc <= 0) break;		msleep (rc+100);	}}/* ------------------------------------------------------- *//** Save and restore of PCI BARs * * Although firmware will set up BARs during boot, it doesn't * set up device BAR's after a device reset, although it will, * if requested, set up bridge configuration. Thus, we need to * configure the PCI devices ourselves.   */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -