📄 eeh.c
字号:
/* 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 + -