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

📄 eeh.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 3 页
字号:
			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);		/* 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;	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;	total_mmio_ffs++;	if (!eeh_subsystem_enabled)		return 0;	if (!dn) {		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) {		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 && !pdn->eeh_pe_config_addr) {		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();			msleep(5000);						/* 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);		false_positives++;		pdn->eeh_false_positives ++;		rc = 0;		goto dn_unlock;	}	/* Note that config-io to empty slots may fail;	 * they are empty when they don't have children. */	if ((rets[0] == 5) && (dn->child == NULL)) {		false_positives++;		pdn->eeh_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);		false_positives++;		pdn->eeh_false_positives ++;		rc = 0;		goto dn_unlock;	}	/* If not the kind of error we know about, punt. */	if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {		false_positives++;		pdn->eeh_false_positives ++;		rc = 0;		goto dn_unlock;	}	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);	/* 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. */	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) {		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 *//** * rtas_pci_enable - enable MMIO or DMA transfers for this slot * @pdn pci device node */intrtas_pci_enable(struct pci_dn *pdn, int function){	int config_addr;	int rc;	/* 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_set_eeh_option, 4, 1, NULL,	               config_addr,	               BUID_HI(pdn->phb->buid),	               BUID_LO(pdn->phb->buid),		            function);	if (rc)		printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n",		        function, rc, pdn->node->full_name);	rc = eeh_wait_for_slot_status (pdn, PCI_BUS_RESET_WAIT_MSEC);	if ((rc == 4) && (function == EEH_THAW_MMIO))		return 0;	return rc;}/** * rtas_pci_slot_reset - raises/lowers the pci #RST line * @pdn pci device node * @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 config_addr;	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;	}	/* 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_set_slot_reset,4,1, NULL,	               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);}/** * pcibios_set_pcie_slot_reset - Set PCI-E reset state * @dev:	pci device struct * @state:	reset state to enter * * Return value: * 	0 if success **/int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state){	struct device_node *dn = pci_device_to_OF_node(dev);	struct pci_dn *pdn = PCI_DN(dn);	switch (state) {	case pcie_deassert_reset:		rtas_pci_slot_reset(pdn, 0);		break;	case pcie_hot_reset:		rtas_pci_slot_reset(pdn, 1);		break;	case pcie_warm_reset:		rtas_pci_slot_reset(pdn, 3);		break;	default:		return -EINVAL;	};	return 0;}/** * rtas_set_slot_reset -- assert the pci #RST line for 1/4 second * @pdn: pci device node to be reset. * *  Return 0 if success, else a non-zero value. */static void __rtas_set_slot_reset(struct pci_dn *pdn){	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);}int rtas_set_slot_reset(struct pci_dn *pdn){	int i, rc;	/* Take three shots at resetting the bus */	for (i=0; i<3; i++) {		__rtas_set_slot_reset(pdn);		rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);		if (rc == 0)			return 0;		if (rc < 0) {			printk(KERN_ERR "EEH: unrecoverable slot failure %s\n",			       pdn->node->full_name);			return -1;		}		printk(KERN_ERR "EEH: bus reset %d failed on slot %s, rc=%d\n",		       i+1, pdn->node->full_name, rc);	}	return -1;}/* ------------------------------------------------------- *//** 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.   *//** * __restore_bars - Restore the Base Address Registers * @pdn: pci device node * * Loads the PCI configuration space base address registers, * the expansion ROM base address, the latency timer, and etc. * from the saved values in the device node. */static inline void __restore_bars (struct pci_dn *pdn){	int i;	if (NULL==pdn->phb) return;	for (i=4; i<10; i++) {		rtas_write_config(pdn, i*4, 4, pdn->config_space[i]);	}	/* 12 == Expansion ROM Address */	rtas_write_config(pdn, 12*4, 4, pdn->config_space[12]);#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))#define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)])	rtas_write_config (pdn, PCI_CACHE_LINE_SIZE, 1,	            SAVED_BYTE(PCI_CACHE_LINE_SIZE));	rtas_write_config (pdn, PCI_LATENCY_TIMER, 1,	            SAVED_BYTE(PCI_LATENCY_TIMER));	/* max latency, min grant, interrupt pin and line */	rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]);}/** * eeh_restore_bars - restore the PCI config space info * * This routine performs a recursive walk to the children * of this device as well. */void eeh_restore_bars(struct pci_dn *pdn){	struct device_node *dn;	if (!pdn) 		return;		if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code))		__restore_bars (pdn);

⌨️ 快捷键说明

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