From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost.localdomain (localhost [127.0.0.1]) by ozlabs.org (Postfix) with ESMTP id 52EC22C0322 for ; Wed, 19 Jun 2013 13:58:06 +1000 (EST) From: Michael Neuling To: Gavin Shan Subject: Re: [PATCH 01/31] powerpc/eeh: Move common part to kernel directory In-reply-to: <1371544435-4943-2-git-send-email-shangw@linux.vnet.ibm.com> References: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com> <1371544435-4943-2-git-send-email-shangw@linux.vnet.ibm.com> Date: Wed, 19 Jun 2013 13:58:06 +1000 Message-ID: <10320.1371614286@ale.ozlabs.ibm.com> Cc: linuxppc-dev@lists.ozlabs.org List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Bunch of whitespace issues here: % git am ~/Mail/linuxppc/31202 Applying: powerpc/eeh: Move common part to kernel directory /home/mikey/src/powerpc-test/.git/rebase-apply/patch:437: trailing whitespace. /home/mikey/src/powerpc-test/.git/rebase-apply/patch:594: space before tab in indent. */ /home/mikey/src/powerpc-test/.git/rebase-apply/patch:607: trailing whitespace. /home/mikey/src/powerpc-test/.git/rebase-apply/patch:608: trailing whitespace. /* We might get hit with another EEH freeze as soon as the /home/mikey/src/powerpc-test/.git/rebase-apply/patch:673: trailing whitespace. error: patch failed: arch/powerpc/platforms/pseries/eeh_pe.c:1 error: arch/powerpc/platforms/pseries/eeh_pe.c: patch does not apply Patch failed at 0001 powerpc/eeh: Move common part to kernel directory When you have resolved this problem run "git am --resolved". If you would prefer to skip this patch, instead run "git am --skip". To restore the original branch and stop patching run "git am --abort". Mikey Gavin Shan wrote: > The patch moves the common part of EEH core into arch/powerpc/kernel > directory so that we needn't PPC_PSERIES while compiling POWERNV > platform: > > * Move the EEH common part into arch/powerpc/kernel > * Move the functions for PCI hotplug from pSeries platform to > arch/powerpc/kernel/pci_hotplug.c > * Move CONFIG_EEH from arch/powerpc/platforms/pseries/Kconfig to > arch/powerpc/platforms/Kconfig > * Adjust makefile accordingly > > Signed-off-by: Gavin Shan > --- > arch/powerpc/kernel/Makefile | 4 +- > arch/powerpc/kernel/eeh.c | 942 +++++++++++++++++++++++++++ > arch/powerpc/kernel/eeh_cache.c | 319 +++++++++ > arch/powerpc/kernel/eeh_dev.c | 112 ++++ > arch/powerpc/kernel/eeh_driver.c | 552 ++++++++++++++++ > arch/powerpc/kernel/eeh_event.c | 142 ++++ > arch/powerpc/kernel/eeh_pe.c | 653 +++++++++++++++++++ > arch/powerpc/kernel/eeh_sysfs.c | 75 +++ > arch/powerpc/kernel/pci_hotplug.c | 111 ++++ > arch/powerpc/platforms/Kconfig | 5 + > arch/powerpc/platforms/pseries/Kconfig | 5 - > arch/powerpc/platforms/pseries/Makefile | 4 +- > arch/powerpc/platforms/pseries/eeh.c | 942 --------------------------- > arch/powerpc/platforms/pseries/eeh_cache.c | 319 --------- > arch/powerpc/platforms/pseries/eeh_dev.c | 112 ---- > arch/powerpc/platforms/pseries/eeh_driver.c | 552 ---------------- > arch/powerpc/platforms/pseries/eeh_event.c | 142 ---- > arch/powerpc/platforms/pseries/eeh_pe.c | 653 ------------------- > arch/powerpc/platforms/pseries/eeh_sysfs.c | 75 --- > arch/powerpc/platforms/pseries/pci_dlpar.c | 85 --- > 20 files changed, 2915 insertions(+), 2889 deletions(-) > create mode 100644 arch/powerpc/kernel/eeh.c > create mode 100644 arch/powerpc/kernel/eeh_cache.c > create mode 100644 arch/powerpc/kernel/eeh_dev.c > create mode 100644 arch/powerpc/kernel/eeh_driver.c > create mode 100644 arch/powerpc/kernel/eeh_event.c > create mode 100644 arch/powerpc/kernel/eeh_pe.c > create mode 100644 arch/powerpc/kernel/eeh_sysfs.c > create mode 100644 arch/powerpc/kernel/pci_hotplug.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh_cache.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh_dev.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh_driver.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh_event.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh_pe.c > delete mode 100644 arch/powerpc/platforms/pseries/eeh_sysfs.c > > diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile > index f960a79..5826906 100644 > --- a/arch/powerpc/kernel/Makefile > +++ b/arch/powerpc/kernel/Makefile > @@ -58,6 +58,8 @@ obj-$(CONFIG_RTAS_PROC) += rtas-proc.o > obj-$(CONFIG_LPARCFG) += lparcfg.o > obj-$(CONFIG_IBMVIO) += vio.o > obj-$(CONFIG_IBMEBUS) += ibmebus.o > +obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \ > + eeh_driver.o eeh_event.o eeh_sysfs.o > obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o > obj-$(CONFIG_CRASH_DUMP) += crash_dump.o > obj-$(CONFIG_FA_DUMP) += fadump.o > @@ -100,7 +102,7 @@ obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o > obj-$(CONFIG_STACKTRACE) += stacktrace.o > obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o > > -pci64-$(CONFIG_PPC64) += pci_dn.o isa-bridge.o > +pci64-$(CONFIG_PPC64) += pci_hotplug.o pci_dn.o isa-bridge.o > obj-$(CONFIG_PCI) += pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \ > pci-common.o pci_of_scan.o > obj-$(CONFIG_PCI_MSI) += msi.o > diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c > new file mode 100644 > index 0000000..6b73d6c > --- /dev/null > +++ b/arch/powerpc/kernel/eeh.c > @@ -0,0 +1,942 @@ > +/* > + * Copyright IBM Corporation 2001, 2005, 2006 > + * Copyright Dave Engebretsen & Todd Inglett 2001 > + * Copyright Linas Vepstas 2005, 2006 > + * Copyright 2001-2012 IBM Corporation. > + * > + * 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > + > +/** 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 occurred (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, it might be broken. > + * This sets the threshold for how many read attempts we allow > + * before printing an error message. > + */ > +#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) > + > +/* Platform dependent EEH operations */ > +struct eeh_ops *eeh_ops = NULL; > + > +int eeh_subsystem_enabled; > +EXPORT_SYMBOL(eeh_subsystem_enabled); > + > +/* > + * EEH probe mode support. The intention is to support multiple > + * platforms for EEH. Some platforms like pSeries do PCI emunation > + * based on device tree. However, other platforms like powernv probe > + * PCI devices from hardware. The flag is used to distinguish that. > + * In addition, struct eeh_ops::probe would be invoked for particular > + * OF node or PCI device so that the corresponding PE would be created > + * there. > + */ > +int eeh_probe_mode; > + > +/* Global EEH mutex */ > +DEFINE_MUTEX(eeh_mutex); > + > +/* Lock to avoid races due to multiple reports of an error */ > +static DEFINE_RAW_SPINLOCK(confirm_error_lock); > + > +/* 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 4096 > +static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN]; > + > +/* > + * The struct is used to maintain the EEH global statistic > + * information. Besides, the EEH global statistics will be > + * exported to user space through procfs > + */ > +struct eeh_stats { > + u64 no_device; /* PCI device not found */ > + u64 no_dn; /* OF node not found */ > + u64 no_cfg_addr; /* Config address not found */ > + u64 ignored_check; /* EEH check skipped */ > + u64 total_mmio_ffs; /* Total EEH checks */ > + u64 false_positives; /* Unnecessary EEH checks */ > + u64 slot_resets; /* PE reset */ > +}; > + > +static struct eeh_stats eeh_stats; > + > +#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) > + > +/** > + * eeh_gather_pci_data - Copy assorted PCI config space registers to buff > + * @edev: 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 eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) > +{ > + struct device_node *dn = eeh_dev_to_of_node(edev); > + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > + u32 cfg; > + int cap, i; > + int n = 0; > + > + n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); > + printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name); > + > + eeh_ops->read_config(dn, 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); > + > + eeh_ops->read_config(dn, 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) { > + eeh_ops->read_config(dn, 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); > + > + eeh_ops->read_config(dn, 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) { > + eeh_ops->read_config(dn, cap, 4, &cfg); > + n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); > + printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg); > + > + eeh_ops->read_config(dn, 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++) { > + eeh_ops->read_config(dn, 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++) { > + eeh_ops->read_config(dn, 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); > + } > + } > + } > + > + return n; > +} > + > +/** > + * eeh_slot_error_detail - Generate combined log including driver log and error log > + * @pe: EEH PE > + * @severity: temporary or permanent error log > + * > + * This routine should be called to generate the combined log, which > + * is comprised of driver log and error log. The driver log is figured > + * out from the config space of the corresponding PCI device, while > + * the error log is fetched through platform dependent function call. > + */ > +void eeh_slot_error_detail(struct eeh_pe *pe, int severity) > +{ > + size_t loglen = 0; > + struct eeh_dev *edev; > + > + eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); > + eeh_ops->configure_bridge(pe); > + eeh_pe_restore_bars(pe); > + > + pci_regs_buf[0] = 0; > + eeh_pe_for_each_dev(pe, edev) { > + loglen += eeh_gather_pci_data(edev, pci_regs_buf, > + EEH_PCI_REGS_LOG_LEN); > + } > + > + eeh_ops->get_log(pe, severity, pci_regs_buf, loglen); > +} > + > +/** > + * eeh_token_to_phys - Convert EEH address token to phys address > + * @token: I/O token, should be address in the form 0xA.... > + * > + * This routine should be called to convert virtual I/O address > + * to physical one. > + */ > +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)); > +} > + > +/** > + * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze > + * @edev: eeh device > + * > + * 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_dev_check_failure(struct eeh_dev *edev) > +{ > + int ret; > + unsigned long flags; > + struct device_node *dn; > + struct pci_dev *dev; > + struct eeh_pe *pe; > + int rc = 0; > + const char *location; > + > + eeh_stats.total_mmio_ffs++; > + > + if (!eeh_subsystem_enabled) > + return 0; > + > + if (!edev) { > + eeh_stats.no_dn++; > + return 0; > + } > + dn = eeh_dev_to_of_node(edev); > + dev = eeh_dev_to_pci_dev(edev); > + pe = edev->pe; > + > + /* Access to IO BARs might get this far and still not want checking. */ > + if (!pe) { > + eeh_stats.ignored_check++; > + pr_debug("EEH: Ignored check for %s %s\n", > + eeh_pci_name(dev), dn->full_name); > + return 0; > + } > + > + if (!pe->addr && !pe->config_addr) { > + eeh_stats.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. > + */ > + raw_spin_lock_irqsave(&confirm_error_lock, flags); > + rc = 1; > + if (pe->state & EEH_PE_ISOLATED) { > + pe->check_count++; > + if (pe->check_count % EEH_MAX_FAILS == 0) { > + location = of_get_property(dn, "ibm,loc-code", NULL); > + printk(KERN_ERR "EEH: %d reads ignored for recovering device at " > + "location=%s driver=%s pci addr=%s\n", > + pe->check_count, location, > + eeh_driver_name(dev), eeh_pci_name(dev)); > + printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n", > + eeh_driver_name(dev)); > + dump_stack(); > + } > + 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 = eeh_ops->get_state(pe, NULL); > + > + /* Note that config-io to empty slots may fail; > + * they are empty when they don't have children. > + * We will punt with the following conditions: Failure to get > + * PE's state, EEH not support and Permanently unavailable > + * state, PE is in good state. > + */ > + if ((ret < 0) || > + (ret == EEH_STATE_NOT_SUPPORT) || > + (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) == > + (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) { > + eeh_stats.false_positives++; > + pe->false_positives++; > + rc = 0; > + goto dn_unlock; > + } > + > + eeh_stats.slot_resets++; > + > + /* Avoid repeated reports of this failure, including problems > + * with other functions on this device, and functions under > + * bridges. > + */ > + eeh_pe_state_mark(pe, EEH_PE_ISOLATED); > + raw_spin_unlock_irqrestore(&confirm_error_lock, flags); > + > + eeh_send_failure_event(pe); > + > + /* 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. > + */ > + WARN(1, "EEH: failure detected\n"); > + return 1; > + > +dn_unlock: > + raw_spin_unlock_irqrestore(&confirm_error_lock, flags); > + return rc; > +} > + > +EXPORT_SYMBOL_GPL(eeh_dev_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 eeh_dev *edev; > + > + /* Finding the phys addr + pci device; this is pretty quick. */ > + addr = eeh_token_to_phys((unsigned long __force) token); > + edev = eeh_addr_cache_get_dev(addr); > + if (!edev) { > + eeh_stats.no_device++; > + return val; > + } > + > + eeh_dev_check_failure(edev); > + > + pci_dev_put(eeh_dev_to_pci_dev(edev)); > + return val; > +} > + > +EXPORT_SYMBOL(eeh_check_failure); > + > + > +/** > + * eeh_pci_enable - Enable MMIO or DMA transfers for this slot > + * @pe: EEH PE > + * > + * This routine should be called to reenable frozen MMIO or DMA > + * so that it would work correctly again. It's useful while doing > + * recovery or log collection on the indicated device. > + */ > +int eeh_pci_enable(struct eeh_pe *pe, int function) > +{ > + int rc; > + > + rc = eeh_ops->set_option(pe, function); > + if (rc) > + pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n", > + __func__, function, pe->phb->global_number, pe->addr, rc); > + > + rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC); > + if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) && > + (function == EEH_OPT_THAW_MMIO)) > + return 0; > + > + return rc; > +} > + > +/** > + * 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 eeh_dev *edev = pci_dev_to_eeh_dev(dev); > + struct eeh_pe *pe = edev->pe; > + > + if (!pe) { > + pr_err("%s: No PE found on PCI device %s\n", > + __func__, pci_name(dev)); > + return -EINVAL; > + } > + > + switch (state) { > + case pcie_deassert_reset: > + eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); > + break; > + case pcie_hot_reset: > + eeh_ops->reset(pe, EEH_RESET_HOT); > + break; > + case pcie_warm_reset: > + eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); > + break; > + default: > + return -EINVAL; > + }; > + > + return 0; > +} > + > +/** > + * eeh_set_pe_freset - Check the required reset for the indicated device > + * @data: EEH device > + * @flag: return value > + * > + * Each device might have its preferred reset type: fundamental or > + * hot reset. The routine is used to collected the information for > + * the indicated device and its children so that the bunch of the > + * devices could be reset properly. > + */ > +static void *eeh_set_dev_freset(void *data, void *flag) > +{ > + struct pci_dev *dev; > + unsigned int *freset = (unsigned int *)flag; > + struct eeh_dev *edev = (struct eeh_dev *)data; > + > + dev = eeh_dev_to_pci_dev(edev); > + if (dev) > + *freset |= dev->needs_freset; > + > + return NULL; > +} > + > +/** > + * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second > + * @pe: EEH PE > + * > + * Assert the PCI #RST line for 1/4 second. > + */ > +static void eeh_reset_pe_once(struct eeh_pe *pe) > +{ > + unsigned int freset = 0; > + > + /* Determine type of EEH reset required for > + * Partitionable Endpoint, a hot-reset (1) > + * or a fundamental reset (3). > + * A fundamental reset required by any device under > + * Partitionable Endpoint trumps hot-reset. > + */ > + eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset); > + > + if (freset) > + eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); > + else > + eeh_ops->reset(pe, EEH_RESET_HOT); > + > + /* 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_pe_state_clear(pe, EEH_PE_ISOLATED); > + > + eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); > + > + /* 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); > +} > + > +/** > + * eeh_reset_pe - Reset the indicated PE > + * @pe: EEH PE > + * > + * This routine should be called to reset indicated device, including > + * PE. A PE might include multiple PCI devices and sometimes PCI bridges > + * might be involved as well. > + */ > +int eeh_reset_pe(struct eeh_pe *pe) > +{ > + int i, rc; > + > + /* Take three shots at resetting the bus */ > + for (i=0; i<3; i++) { > + eeh_reset_pe_once(pe); > + > + rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC); > + if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) > + return 0; > + > + if (rc < 0) { > + pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x", > + __func__, pe->phb->global_number, pe->addr); > + return -1; > + } > + pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n", > + i+1, pe->phb->global_number, pe->addr, rc); > + } > + > + return -1; > +} > + > +/** > + * eeh_save_bars - Save device bars > + * @edev: PCI device associated EEH device > + * > + * Save the values of the device bars. Unlike the restore > + * routine, this routine is *not* recursive. This is because > + * PCI devices are added individually; but, for the restore, > + * an entire slot is reset at a time. > + */ > +void eeh_save_bars(struct eeh_dev *edev) > +{ > + int i; > + struct device_node *dn; > + > + if (!edev) > + return; > + dn = eeh_dev_to_of_node(edev); > + > + for (i = 0; i < 16; i++) > + eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]); > +} > + > +/** > + * eeh_ops_register - Register platform dependent EEH operations > + * @ops: platform dependent EEH operations > + * > + * Register the platform dependent EEH operation callback > + * functions. The platform should call this function before > + * any other EEH operations. > + */ > +int __init eeh_ops_register(struct eeh_ops *ops) > +{ > + if (!ops->name) { > + pr_warning("%s: Invalid EEH ops name for %p\n", > + __func__, ops); > + return -EINVAL; > + } > + > + if (eeh_ops && eeh_ops != ops) { > + pr_warning("%s: EEH ops of platform %s already existing (%s)\n", > + __func__, eeh_ops->name, ops->name); > + return -EEXIST; > + } > + > + eeh_ops = ops; > + > + return 0; > +} > + > +/** > + * eeh_ops_unregister - Unreigster platform dependent EEH operations > + * @name: name of EEH platform operations > + * > + * Unregister the platform dependent EEH operation callback > + * functions. > + */ > +int __exit eeh_ops_unregister(const char *name) > +{ > + if (!name || !strlen(name)) { > + pr_warning("%s: Invalid EEH ops name\n", > + __func__); > + return -EINVAL; > + } > + > + if (eeh_ops && !strcmp(eeh_ops->name, name)) { > + eeh_ops = NULL; > + return 0; > + } > + > + return -EEXIST; > +} > + > +/** > + * eeh_init - EEH initialization > + * > + * Initialize EEH by trying to enable it for all of the adapters in the system. > + * As a side effect we can determine here if eeh is supported at all. > + * Note that we leave EEH on so failed config cycles won't cause a machine > + * check. If a user turns off EEH for a particular adapter they are really > + * telling Linux to ignore errors. Some hardware (e.g. POWER5) won't > + * grant access to a slot if EEH isn't enabled, and so we always enable > + * EEH for all slots/all devices. > + * > + * The eeh-force-off option disables EEH checking globally, for all slots. > + * Even if force-off is set, the EEH hardware is still enabled, so that > + * newer systems can boot. > + */ > +static int __init eeh_init(void) > +{ > + struct pci_controller *hose, *tmp; > + struct device_node *phb; > + int ret; > + > + /* call platform initialization function */ > + if (!eeh_ops) { > + pr_warning("%s: Platform EEH operation not found\n", > + __func__); > + return -EEXIST; > + } else if ((ret = eeh_ops->init())) { > + pr_warning("%s: Failed to call platform init function (%d)\n", > + __func__, ret); > + return ret; > + } > + > + raw_spin_lock_init(&confirm_error_lock); > + > + /* Enable EEH for all adapters */ > + if (eeh_probe_mode_devtree()) { > + list_for_each_entry_safe(hose, tmp, > + &hose_list, list_node) { > + phb = hose->dn; > + traverse_pci_devices(phb, eeh_ops->of_probe, NULL); > + } > + } > + > + if (eeh_subsystem_enabled) > + pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n"); > + else > + pr_warning("EEH: No capable adapters found\n"); > + > + return ret; > +} > + > +core_initcall_sync(eeh_init); > + > +/** > + * eeh_add_device_early - Enable EEH for the indicated device_node > + * @dn: device node for which to set up EEH > + * > + * This routine must be used to perform EEH initialization for PCI > + * devices that were added after system boot (e.g. hotplug, dlpar). > + * This routine must be called before any i/o is performed to the > + * adapter (inluding any config-space i/o). > + * Whether this actually enables EEH or not for this device depends > + * on the CEC architecture, type of the device, on earlier boot > + * command-line arguments & etc. > + */ > +static void eeh_add_device_early(struct device_node *dn) > +{ > + struct pci_controller *phb; > + > + if (!of_node_to_eeh_dev(dn)) > + return; > + phb = of_node_to_eeh_dev(dn)->phb; > + > + /* USB Bus children of PCI devices will not have BUID's */ > + if (NULL == phb || 0 == phb->buid) > + return; > + > + /* FIXME: hotplug support on POWERNV */ > + eeh_ops->of_probe(dn, NULL); > +} > + > +/** > + * eeh_add_device_tree_early - Enable EEH for the indicated device > + * @dn: device node > + * > + * This routine must be used to perform EEH initialization for the > + * indicated PCI device that was added after system boot (e.g. > + * hotplug, dlpar). > + */ > +void eeh_add_device_tree_early(struct device_node *dn) > +{ > + struct device_node *sib; > + > + for_each_child_of_node(dn, sib) > + eeh_add_device_tree_early(sib); > + eeh_add_device_early(dn); > +} > +EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); > + > +/** > + * eeh_add_device_late - Perform EEH initialization for the indicated pci device > + * @dev: pci device for which to set up EEH > + * > + * This routine must be used to complete EEH initialization for PCI > + * devices that were added after system boot (e.g. hotplug, dlpar). > + */ > +static void eeh_add_device_late(struct pci_dev *dev) > +{ > + struct device_node *dn; > + struct eeh_dev *edev; > + > + if (!dev || !eeh_subsystem_enabled) > + return; > + > + pr_debug("EEH: Adding device %s\n", pci_name(dev)); > + > + dn = pci_device_to_OF_node(dev); > + edev = of_node_to_eeh_dev(dn); > + if (edev->pdev == dev) { > + pr_debug("EEH: Already referenced !\n"); > + return; > + } > + WARN_ON(edev->pdev); > + > + pci_dev_get(dev); > + edev->pdev = dev; > + dev->dev.archdata.edev = edev; > + > + eeh_addr_cache_insert_dev(dev); > +} > + > +/** > + * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus > + * @bus: PCI bus > + * > + * This routine must be used to perform EEH initialization for PCI > + * devices which are attached to the indicated PCI bus. The PCI bus > + * is added after system boot through hotplug or dlpar. > + */ > +void eeh_add_device_tree_late(struct pci_bus *bus) > +{ > + struct pci_dev *dev; > + > + list_for_each_entry(dev, &bus->devices, bus_list) { > + eeh_add_device_late(dev); > + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { > + struct pci_bus *subbus = dev->subordinate; > + if (subbus) > + eeh_add_device_tree_late(subbus); > + } > + } > +} > +EXPORT_SYMBOL_GPL(eeh_add_device_tree_late); > + > +/** > + * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus > + * @bus: PCI bus > + * > + * This routine must be used to add EEH sysfs files for PCI > + * devices which are attached to the indicated PCI bus. The PCI bus > + * is added after system boot through hotplug or dlpar. > + */ > +void eeh_add_sysfs_files(struct pci_bus *bus) > +{ > + struct pci_dev *dev; > + > + list_for_each_entry(dev, &bus->devices, bus_list) { > + eeh_sysfs_add_device(dev); > + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { > + struct pci_bus *subbus = dev->subordinate; > + if (subbus) > + eeh_add_sysfs_files(subbus); > + } > + } > +} > +EXPORT_SYMBOL_GPL(eeh_add_sysfs_files); > + > +/** > + * eeh_remove_device - Undo EEH setup for the indicated pci device > + * @dev: pci device to be removed > + * @purge_pe: remove the PE or not > + * > + * This routine should be called when a device is removed from > + * a running system (e.g. by hotplug or dlpar). It unregisters > + * the PCI device from the EEH subsystem. I/O errors affecting > + * this device will no longer be detected after this call; thus, > + * i/o errors affecting this slot may leave this device unusable. > + */ > +static void eeh_remove_device(struct pci_dev *dev, int purge_pe) > +{ > + struct eeh_dev *edev; > + > + if (!dev || !eeh_subsystem_enabled) > + return; > + edev = pci_dev_to_eeh_dev(dev); > + > + /* Unregister the device with the EEH/PCI address search system */ > + pr_debug("EEH: Removing device %s\n", pci_name(dev)); > + > + if (!edev || !edev->pdev) { > + pr_debug("EEH: Not referenced !\n"); > + return; > + } > + edev->pdev = NULL; > + dev->dev.archdata.edev = NULL; > + pci_dev_put(dev); > + > + eeh_rmv_from_parent_pe(edev, purge_pe); > + eeh_addr_cache_rmv_dev(dev); > + eeh_sysfs_remove_device(dev); > +} > + > +/** > + * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device > + * @dev: PCI device > + * @purge_pe: remove the corresponding PE or not > + * > + * This routine must be called when a device is removed from the > + * running system through hotplug or dlpar. The corresponding > + * PCI address cache will be removed. > + */ > +void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) > +{ > + struct pci_bus *bus = dev->subordinate; > + struct pci_dev *child, *tmp; > + > + eeh_remove_device(dev, purge_pe); > + > + if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { > + list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) > + eeh_remove_bus_device(child, purge_pe); > + } > +} > +EXPORT_SYMBOL_GPL(eeh_remove_bus_device); > + > +static int proc_eeh_show(struct seq_file *m, void *v) > +{ > + if (0 == eeh_subsystem_enabled) { > + seq_printf(m, "EEH Subsystem is globally disabled\n"); > + seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs); > + } else { > + seq_printf(m, "EEH Subsystem is enabled\n"); > + seq_printf(m, > + "no device=%llu\n" > + "no device node=%llu\n" > + "no config address=%llu\n" > + "check not wanted=%llu\n" > + "eeh_total_mmio_ffs=%llu\n" > + "eeh_false_positives=%llu\n" > + "eeh_slot_resets=%llu\n", > + eeh_stats.no_device, > + eeh_stats.no_dn, > + eeh_stats.no_cfg_addr, > + eeh_stats.ignored_check, > + eeh_stats.total_mmio_ffs, > + eeh_stats.false_positives, > + eeh_stats.slot_resets); > + } > + > + return 0; > +} > + > +static int proc_eeh_open(struct inode *inode, struct file *file) > +{ > + return single_open(file, proc_eeh_show, NULL); > +} > + > +static const struct file_operations proc_eeh_operations = { > + .open = proc_eeh_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static int __init eeh_init_proc(void) > +{ > + if (machine_is(pseries)) > + proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations); > + return 0; > +} > +__initcall(eeh_init_proc); > diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c > new file mode 100644 > index 0000000..5a4c879 > --- /dev/null > +++ b/arch/powerpc/kernel/eeh_cache.c > @@ -0,0 +1,319 @@ > +/* > + * PCI address cache; allows the lookup of PCI devices based on I/O address > + * > + * Copyright IBM Corporation 2004 > + * Copyright Linas Vepstas 2004 > + * > + * 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > + > +/** > + * The pci address cache subsystem. This subsystem places > + * PCI device address resources into a red-black tree, sorted > + * according to the address range, so that given only an i/o > + * address, the corresponding PCI device can be **quickly** > + * found. It is safe to perform an address lookup in an interrupt > + * context; this ability is an important feature. > + * > + * Currently, the only customer of this code is the EEH subsystem; > + * thus, this code has been somewhat tailored to suit EEH better. > + * In particular, the cache does *not* hold the addresses of devices > + * for which EEH is not enabled. > + * > + * (Implementation Note: The RB tree seems to be better/faster > + * than any hash algo I could think of for this problem, even > + * with the penalty of slow pointer chases for d-cache misses). > + */ > +struct pci_io_addr_range { > + struct rb_node rb_node; > + unsigned long addr_lo; > + unsigned long addr_hi; > + struct eeh_dev *edev; > + struct pci_dev *pcidev; > + unsigned int flags; > +}; > + > +static struct pci_io_addr_cache { > + struct rb_root rb_root; > + spinlock_t piar_lock; > +} pci_io_addr_cache_root; > + > +static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr) > +{ > + struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node; > + > + while (n) { > + struct pci_io_addr_range *piar; > + piar = rb_entry(n, struct pci_io_addr_range, rb_node); > + > + if (addr < piar->addr_lo) { > + n = n->rb_left; > + } else { > + if (addr > piar->addr_hi) { > + n = n->rb_right; > + } else { > + pci_dev_get(piar->pcidev); > + return piar->edev; > + } > + } > + } > + > + return NULL; > +} > + > +/** > + * eeh_addr_cache_get_dev - Get device, given only address > + * @addr: mmio (PIO) phys address or i/o port number > + * > + * Given an mmio phys address, or a port number, find a pci device > + * that implements this address. Be sure to pci_dev_put the device > + * when finished. I/O port numbers are assumed to be offset > + * from zero (that is, they do *not* have pci_io_addr added in). > + * It is safe to call this function within an interrupt. > + */ > +struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr) > +{ > + struct eeh_dev *edev; > + unsigned long flags; > + > + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); > + edev = __eeh_addr_cache_get_device(addr); > + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); > + return edev; > +} > + > +#ifdef DEBUG > +/* > + * Handy-dandy debug print routine, does nothing more > + * than print out the contents of our addr cache. > + */ > +static void eeh_addr_cache_print(struct pci_io_addr_cache *cache) > +{ > + struct rb_node *n; > + int cnt = 0; > + > + n = rb_first(&cache->rb_root); > + while (n) { > + struct pci_io_addr_range *piar; > + piar = rb_entry(n, struct pci_io_addr_range, rb_node); > + pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n", > + (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt, > + piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev)); > + cnt++; > + n = rb_next(n); > + } > +} > +#endif > + > +/* Insert address range into the rb tree. */ > +static struct pci_io_addr_range * > +eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo, > + unsigned long ahi, unsigned int flags) > +{ > + struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node; > + struct rb_node *parent = NULL; > + struct pci_io_addr_range *piar; > + > + /* Walk tree, find a place to insert into tree */ > + while (*p) { > + parent = *p; > + piar = rb_entry(parent, struct pci_io_addr_range, rb_node); > + if (ahi < piar->addr_lo) { > + p = &parent->rb_left; > + } else if (alo > piar->addr_hi) { > + p = &parent->rb_right; > + } else { > + if (dev != piar->pcidev || > + alo != piar->addr_lo || ahi != piar->addr_hi) { > + pr_warning("PIAR: overlapping address range\n"); > + } > + return piar; > + } > + } > + piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC); > + if (!piar) > + return NULL; > + > + pci_dev_get(dev); > + piar->addr_lo = alo; > + piar->addr_hi = ahi; > + piar->edev = pci_dev_to_eeh_dev(dev); > + piar->pcidev = dev; > + piar->flags = flags; > + > +#ifdef DEBUG > + pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n", > + alo, ahi, pci_name(dev)); > +#endif > + > + rb_link_node(&piar->rb_node, parent, p); > + rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root); > + > + return piar; > +} > + > +static void __eeh_addr_cache_insert_dev(struct pci_dev *dev) > +{ > + struct device_node *dn; > + struct eeh_dev *edev; > + int i; > + > + dn = pci_device_to_OF_node(dev); > + if (!dn) { > + pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev)); > + return; > + } > + > + edev = of_node_to_eeh_dev(dn); > + if (!edev) { > + pr_warning("PCI: no EEH dev found for dn=%s\n", > + dn->full_name); > + return; > + } > + > + /* Skip any devices for which EEH is not enabled. */ > + if (!edev->pe) { > +#ifdef DEBUG > + pr_info("PCI: skip building address cache for=%s - %s\n", > + pci_name(dev), dn->full_name); > +#endif > + return; > + } > + > + /* Walk resources on this device, poke them into the tree */ > + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { > + unsigned long start = pci_resource_start(dev,i); > + unsigned long end = pci_resource_end(dev,i); > + unsigned int flags = pci_resource_flags(dev,i); > + > + /* We are interested only bus addresses, not dma or other stuff */ > + if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM))) > + continue; > + if (start == 0 || ~start == 0 || end == 0 || ~end == 0) > + continue; > + eeh_addr_cache_insert(dev, start, end, flags); > + } > +} > + > +/** > + * eeh_addr_cache_insert_dev - Add a device to the address cache > + * @dev: PCI device whose I/O addresses we are interested in. > + * > + * In order to support the fast lookup of devices based on addresses, > + * we maintain a cache of devices that can be quickly searched. > + * This routine adds a device to that cache. > + */ > +void eeh_addr_cache_insert_dev(struct pci_dev *dev) > +{ > + unsigned long flags; > + > + /* Ignore PCI bridges */ > + if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) > + return; > + > + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); > + __eeh_addr_cache_insert_dev(dev); > + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); > +} > + > +static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev) > +{ > + struct rb_node *n; > + > +restart: > + n = rb_first(&pci_io_addr_cache_root.rb_root); > + while (n) { > + struct pci_io_addr_range *piar; > + piar = rb_entry(n, struct pci_io_addr_range, rb_node); > + > + if (piar->pcidev == dev) { > + rb_erase(n, &pci_io_addr_cache_root.rb_root); > + pci_dev_put(piar->pcidev); > + kfree(piar); > + goto restart; > + } > + n = rb_next(n); > + } > +} > + > +/** > + * eeh_addr_cache_rmv_dev - remove pci device from addr cache > + * @dev: device to remove > + * > + * Remove a device from the addr-cache tree. > + * This is potentially expensive, since it will walk > + * the tree multiple times (once per resource). > + * But so what; device removal doesn't need to be that fast. > + */ > +void eeh_addr_cache_rmv_dev(struct pci_dev *dev) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); > + __eeh_addr_cache_rmv_dev(dev); > + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); > +} > + > +/** > + * eeh_addr_cache_build - Build a cache of I/O addresses > + * > + * Build a cache of pci i/o addresses. This cache will be used to > + * find the pci device that corresponds to a given address. > + * This routine scans all pci busses to build the cache. > + * Must be run late in boot process, after the pci controllers > + * have been scanned for devices (after all device resources are known). > + */ > +void __init eeh_addr_cache_build(void) > +{ > + struct device_node *dn; > + struct eeh_dev *edev; > + struct pci_dev *dev = NULL; > + > + spin_lock_init(&pci_io_addr_cache_root.piar_lock); > + > + for_each_pci_dev(dev) { > + eeh_addr_cache_insert_dev(dev); > + > + dn = pci_device_to_OF_node(dev); > + if (!dn) > + continue; > + > + edev = of_node_to_eeh_dev(dn); > + if (!edev) > + continue; > + > + pci_dev_get(dev); /* matching put is in eeh_remove_device() */ > + dev->dev.archdata.edev = edev; > + edev->pdev = dev; > + > + eeh_sysfs_add_device(dev); > + } > + > +#ifdef DEBUG > + /* Verify tree built up above, echo back the list of addrs. */ > + eeh_addr_cache_print(&pci_io_addr_cache_root); > +#endif > +} > + > diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c > new file mode 100644 > index 0000000..1efa28f > --- /dev/null > +++ b/arch/powerpc/kernel/eeh_dev.c > @@ -0,0 +1,112 @@ > +/* > + * The file intends to implement dynamic creation of EEH device, which will > + * be bound with OF node and PCI device simutaneously. The EEH devices would > + * be foundamental information for EEH core components to work proerly. Besides, > + * We have to support multiple situations where dynamic creation of EEH device > + * is required: > + * > + * 1) Before PCI emunation starts, we need create EEH devices according to the > + * PCI sensitive OF nodes. > + * 2) When PCI emunation is done, we need do the binding between PCI device and > + * the associated EEH device. > + * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device > + * will be created while PCI sensitive OF node is detected from DR. > + * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If > + * PHB is newly inserted, we also need create EEH devices accordingly. > + * > + * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. > + * > + * 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +/** > + * eeh_dev_init - Create EEH device according to OF node > + * @dn: device node > + * @data: PHB > + * > + * It will create EEH device according to the given OF node. The function > + * might be called by PCI emunation, DR, PHB hotplug. > + */ > +void *eeh_dev_init(struct device_node *dn, void *data) > +{ > + struct pci_controller *phb = data; > + struct eeh_dev *edev; > + > + /* Allocate EEH device */ > + edev = kzalloc(sizeof(*edev), GFP_KERNEL); > + if (!edev) { > + pr_warning("%s: out of memory\n", __func__); > + return NULL; > + } > + > + /* Associate EEH device with OF node */ > + PCI_DN(dn)->edev = edev; > + edev->dn = dn; > + edev->phb = phb; > + INIT_LIST_HEAD(&edev->list); > + > + return NULL; > +} > + > +/** > + * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB > + * @phb: PHB > + * > + * Scan the PHB OF node and its child association, then create the > + * EEH devices accordingly > + */ > +void eeh_dev_phb_init_dynamic(struct pci_controller *phb) > +{ > + struct device_node *dn = phb->dn; > + > + /* EEH PE for PHB */ > + eeh_phb_pe_create(phb); > + > + /* EEH device for PHB */ > + eeh_dev_init(dn, phb); > + > + /* EEH devices for children OF nodes */ > + traverse_pci_devices(dn, eeh_dev_init, phb); > +} > + > +/** > + * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs > + * > + * Scan all the existing PHBs and create EEH devices for their OF > + * nodes and their children OF nodes > + */ > +static int __init eeh_dev_phb_init(void) > +{ > + struct pci_controller *phb, *tmp; > + > + list_for_each_entry_safe(phb, tmp, &hose_list, list_node) > + eeh_dev_phb_init_dynamic(phb); > + > + pr_info("EEH: devices created\n"); > + > + return 0; > +} > + > +core_initcall(eeh_dev_phb_init); > diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c > new file mode 100644 > index 0000000..a3fefb6 > --- /dev/null > +++ b/arch/powerpc/kernel/eeh_driver.c > @@ -0,0 +1,552 @@ > +/* > + * PCI Error Recovery Driver for RPA-compliant PPC64 platform. > + * Copyright IBM Corp. 2004 2005 > + * Copyright Linas Vepstas 2004, 2005 > + * > + * All rights reserved. > + * > + * 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, GOOD TITLE or > + * NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA. > + * > + * Send comments and feedback to Linas Vepstas > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/** > + * eeh_pcid_name - Retrieve name of PCI device driver > + * @pdev: PCI device > + * > + * This routine is used to retrieve the name of PCI device driver > + * if that's valid. > + */ > +static inline const char *eeh_pcid_name(struct pci_dev *pdev) > +{ > + if (pdev && pdev->dev.driver) > + return pdev->dev.driver->name; > + return ""; > +} > + > +/** > + * eeh_pcid_get - Get the PCI device driver > + * @pdev: PCI device > + * > + * The function is used to retrieve the PCI device driver for > + * the indicated PCI device. Besides, we will increase the reference > + * of the PCI device driver to prevent that being unloaded on > + * the fly. Otherwise, kernel crash would be seen. > + */ > +static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev) > +{ > + if (!pdev || !pdev->driver) > + return NULL; > + > + if (!try_module_get(pdev->driver->driver.owner)) > + return NULL; > + > + return pdev->driver; > +} > + > +/** > + * eeh_pcid_put - Dereference on the PCI device driver > + * @pdev: PCI device > + * > + * The function is called to do dereference on the PCI device > + * driver of the indicated PCI device. > + */ > +static inline void eeh_pcid_put(struct pci_dev *pdev) > +{ > + if (!pdev || !pdev->driver) > + return; > + > + module_put(pdev->driver->driver.owner); > +} > + > +#if 0 > +static void print_device_node_tree(struct pci_dn *pdn, int dent) > +{ > + int i; > + struct device_node *pc; > + > + if (!pdn) > + return; > + for (i = 0; i < dent; i++) > + printk(" "); > + printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n", > + pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr, > + pdn->eeh_pe_config_addr, pdn->node->full_name); > + dent += 3; > + pc = pdn->node->child; > + while (pc) { > + print_device_node_tree(PCI_DN(pc), dent); > + pc = pc->sibling; > + } > +} > +#endif > + > +/** > + * eeh_disable_irq - Disable interrupt for the recovering device > + * @dev: PCI device > + * > + * This routine must be called when reporting temporary or permanent > + * error to the particular PCI device to disable interrupt of that > + * device. If the device has enabled MSI or MSI-X interrupt, we needn't > + * do real work because EEH should freeze DMA transfers for those PCI > + * devices encountering EEH errors, which includes MSI or MSI-X. > + */ > +static void eeh_disable_irq(struct pci_dev *dev) > +{ > + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev); > + > + /* Don't disable MSI and MSI-X interrupts. They are > + * effectively disabled by the DMA Stopped state > + * when an EEH error occurs. > + */ > + if (dev->msi_enabled || dev->msix_enabled) > + return; > + > + if (!irq_has_action(dev->irq)) > + return; > + > + edev->mode |= EEH_DEV_IRQ_DISABLED; > + disable_irq_nosync(dev->irq); > +} > + > +/** > + * eeh_enable_irq - Enable interrupt for the recovering device > + * @dev: PCI device > + * > + * This routine must be called to enable interrupt while failed > + * device could be resumed. > + */ > +static void eeh_enable_irq(struct pci_dev *dev) > +{ > + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev); > + > + if ((edev->mode) & EEH_DEV_IRQ_DISABLED) { > + edev->mode &= ~EEH_DEV_IRQ_DISABLED; > + enable_irq(dev->irq); > + } > +} > + > +/** > + * eeh_report_error - Report pci error to each device driver > + * @data: eeh device > + * @userdata: return value > + * > + * Report an EEH error to each device driver, collect up and > + * merge the device driver responses. Cumulative response > + * passed back in "userdata". > + */ > +static void *eeh_report_error(void *data, void *userdata) > +{ > + struct eeh_dev *edev = (struct eeh_dev *)data; > + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > + enum pci_ers_result rc, *res = userdata; > + struct pci_driver *driver; > + > + /* We might not have the associated PCI device, > + * then we should continue for next one. > + */ > + if (!dev) return NULL; > + dev->error_state = pci_channel_io_frozen; > + > + driver = eeh_pcid_get(dev); > + if (!driver) return NULL; > + > + eeh_disable_irq(dev); > + > + if (!driver->err_handler || > + !driver->err_handler->error_detected) { > + eeh_pcid_put(dev); > + return NULL; > + } > + > + rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen); > + > + /* A driver that needs a reset trumps all others */ > + if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; > + if (*res == PCI_ERS_RESULT_NONE) *res = rc; > + > + eeh_pcid_put(dev); > + return NULL; > +} > + > +/** > + * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled > + * @data: eeh device > + * @userdata: return value > + * > + * Tells each device driver that IO ports, MMIO and config space I/O > + * are now enabled. Collects up and merges the device driver responses. > + * Cumulative response passed back in "userdata". > + */ > +static void *eeh_report_mmio_enabled(void *data, void *userdata) > +{ > + struct eeh_dev *edev = (struct eeh_dev *)data; > + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > + enum pci_ers_result rc, *res = userdata; > + struct pci_driver *driver; > + > + driver = eeh_pcid_get(dev); > + if (!driver) return NULL; > + > + if (!driver->err_handler || > + !driver->err_handler->mmio_enabled) { > + eeh_pcid_put(dev); > + return NULL; > + } > + > + rc = driver->err_handler->mmio_enabled(dev); > + > + /* A driver that needs a reset trumps all others */ > + if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; > + if (*res == PCI_ERS_RESULT_NONE) *res = rc; > + > + eeh_pcid_put(dev); > + return NULL; > +} > + > +/** > + * eeh_report_reset - Tell device that slot has been reset > + * @data: eeh device > + * @userdata: return value > + * > + * This routine must be called while EEH tries to reset particular > + * PCI device so that the associated PCI device driver could take > + * some actions, usually to save data the driver needs so that the > + * driver can work again while the device is recovered. > + */ > +static void *eeh_report_reset(void *data, void *userdata) > +{ > + struct eeh_dev *edev = (struct eeh_dev *)data; > + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > + enum pci_ers_result rc, *res = userdata; > + struct pci_driver *driver; > + > + if (!dev) return NULL; > + dev->error_state = pci_channel_io_normal; > + > + driver = eeh_pcid_get(dev); > + if (!driver) return NULL; > + > + eeh_enable_irq(dev); > + > + if (!driver->err_handler || > + !driver->err_handler->slot_reset) { > + eeh_pcid_put(dev); > + return NULL; > + } > + > + rc = driver->err_handler->slot_reset(dev); > + if ((*res == PCI_ERS_RESULT_NONE) || > + (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc; > + if (*res == PCI_ERS_RESULT_DISCONNECT && > + rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; > + > + eeh_pcid_put(dev); > + return NULL; > +} > + > +/** > + * eeh_report_resume - Tell device to resume normal operations > + * @data: eeh device > + * @userdata: return value > + * > + * This routine must be called to notify the device driver that it > + * could resume so that the device driver can do some initialization > + * to make the recovered device work again. > + */ > +static void *eeh_report_resume(void *data, void *userdata) > +{ > + struct eeh_dev *edev = (struct eeh_dev *)data; > + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > + struct pci_driver *driver; > + > + if (!dev) return NULL; > + dev->error_state = pci_channel_io_normal; > + > + driver = eeh_pcid_get(dev); > + if (!driver) return NULL; > + > + eeh_enable_irq(dev); > + > + if (!driver->err_handler || > + !driver->err_handler->resume) { > + eeh_pcid_put(dev); > + return NULL; > + } > + > + driver->err_handler->resume(dev); > + > + eeh_pcid_put(dev); > + return NULL; > +} > + > +/** > + * eeh_report_failure - Tell device driver that device is dead. > + * @data: eeh device > + * @userdata: return value > + * > + * This informs the device driver that the device is permanently > + * dead, and that no further recovery attempts will be made on it. > + */ > +static void *eeh_report_failure(void *data, void *userdata) > +{ > + struct eeh_dev *edev = (struct eeh_dev *)data; > + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > + struct pci_driver *driver; > + > + if (!dev) return NULL; > + dev->error_state = pci_channel_io_perm_failure; > + > + driver = eeh_pcid_get(dev); > + if (!driver) return NULL; > + > + eeh_disable_irq(dev); > + > + if (!driver->err_handler || > + !driver->err_handler->error_detected) { > + eeh_pcid_put(dev); > + return NULL; > + } > + > + driver->err_handler->error_detected(dev, pci_channel_io_perm_failure); > + > + eeh_pcid_put(dev); > + return NULL; > +} > + > +/** > + * eeh_reset_device - Perform actual reset of a pci slot > + * @pe: EEH PE > + * @bus: PCI bus corresponding to the isolcated slot > + * > + * This routine must be called to do reset on the indicated PE. > + * During the reset, udev might be invoked because those affected > + * PCI devices will be removed and then added. > + */ > +static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus) > +{ > + int cnt, rc; > + > + /* pcibios will clear the counter; save the value */ > + cnt = pe->freeze_count; > + > + /* > + * We don't remove the corresponding PE instances because > + * we need the information afterwords. The attached EEH > + * devices are expected to be attached soon when calling > + * into pcibios_add_pci_devices(). > + */ > + if (bus) > + __pcibios_remove_pci_devices(bus, 0); > + > + /* Reset the pci controller. (Asserts RST#; resets config space). > + * Reconfigure bridges and devices. Don't try to bring the system > + * up if the reset failed for some reason. > + */ > + rc = eeh_reset_pe(pe); > + if (rc) > + return rc; > + > + /* Restore PE */ > + eeh_ops->configure_bridge(pe); > + eeh_pe_restore_bars(pe); > + > + /* Give the system 5 seconds to finish running the user-space > + * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes, > + * this is a hack, but if we don't do this, and try to bring > + * the device up before the scripts have taken it down, > + * potentially weird things happen. > + */ > + if (bus) { > + ssleep(5); > + pcibios_add_pci_devices(bus); > + } > + pe->freeze_count = cnt; > + > + return 0; > +} > + > +/* The longest amount of time to wait for a pci device > + * to come back on line, in seconds. > + */ > +#define MAX_WAIT_FOR_RECOVERY 150 > + > +/** > + * eeh_handle_event - Reset a PCI device after hard lockup. > + * @pe: EEH PE > + * > + * While PHB detects address or data parity errors on particular PCI > + * slot, the associated PE will be frozen. Besides, DMA's occurring > + * to wild addresses (which usually happen due to bugs in device > + * drivers or in PCI adapter firmware) can cause EEH error. #SERR, > + * #PERR or other misc PCI-related errors also can trigger EEH errors. > + * > + * Recovery process consists of unplugging the device driver (which > + * generated hotplug events to userspace), then issuing a PCI #RST to > + * the device, then reconfiguring the PCI config space for all bridges > + * & devices under this slot, and then finally restarting the device > + * drivers (which cause a second set of hotplug events to go out to > + * userspace). > + */ > +void eeh_handle_event(struct eeh_pe *pe) > +{ > + struct pci_bus *frozen_bus; > + int rc = 0; > + enum pci_ers_result result = PCI_ERS_RESULT_NONE; > + > + frozen_bus = eeh_pe_bus_get(pe); > + if (!frozen_bus) { > + pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n", > + __func__, pe->phb->global_number, pe->addr); > + return; > + } > + > + pe->freeze_count++; > + if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES) > + goto excess_failures; > + pr_warning("EEH: This PCI device has failed %d times in the last hour\n", > + pe->freeze_count); > + > + /* Walk the various device drivers attached to this slot through > + * a reset sequence, giving each an opportunity to do what it needs > + * to accomplish the reset. Each child gets a report of the > + * status ... if any child can't handle the reset, then the entire > + * slot is dlpar removed and added. > + */ > + eeh_pe_dev_traverse(pe, eeh_report_error, &result); > + > + /* Get the current PCI slot state. This can take a long time, > + * sometimes over 3 seconds for certain systems. > + */ > + rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000); > + if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { > + printk(KERN_WARNING "EEH: Permanent failure\n"); > + goto hard_fail; > + } > + > + /* Since rtas may enable MMIO when posting the error log, > + * don't post the error log until after all dev drivers > + * have been informed. > + */ > + eeh_slot_error_detail(pe, EEH_LOG_TEMP); > + > + /* If all device drivers were EEH-unaware, then shut > + * down all of the device drivers, and hope they > + * go down willingly, without panicing the system. > + */ > + if (result == PCI_ERS_RESULT_NONE) { > + rc = eeh_reset_device(pe, frozen_bus); > + if (rc) { > + printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc); > + goto hard_fail; > + } > + } > + > + /* If all devices reported they can proceed, then re-enable MMIO */ > + if (result == PCI_ERS_RESULT_CAN_RECOVER) { > + rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); > + > + if (rc < 0) > + goto hard_fail; > + if (rc) { > + result = PCI_ERS_RESULT_NEED_RESET; > + } else { > + result = PCI_ERS_RESULT_NONE; > + eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result); > + } > + } > + > + /* If all devices reported they can proceed, then re-enable DMA */ > + if (result == PCI_ERS_RESULT_CAN_RECOVER) { > + rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA); > + > + if (rc < 0) > + goto hard_fail; > + if (rc) > + result = PCI_ERS_RESULT_NEED_RESET; > + else > + result = PCI_ERS_RESULT_RECOVERED; > + } > + > + /* If any device has a hard failure, then shut off everything. */ > + if (result == PCI_ERS_RESULT_DISCONNECT) { > + printk(KERN_WARNING "EEH: Device driver gave up\n"); > + goto hard_fail; > + } > + > + /* If any device called out for a reset, then reset the slot */ > + if (result == PCI_ERS_RESULT_NEED_RESET) { > + rc = eeh_reset_device(pe, NULL); > + if (rc) { > + printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc); > + goto hard_fail; > + } > + result = PCI_ERS_RESULT_NONE; > + eeh_pe_dev_traverse(pe, eeh_report_reset, &result); > + } > + > + /* All devices should claim they have recovered by now. */ > + if ((result != PCI_ERS_RESULT_RECOVERED) && > + (result != PCI_ERS_RESULT_NONE)) { > + printk(KERN_WARNING "EEH: Not recovered\n"); > + goto hard_fail; > + } > + > + /* Tell all device drivers that they can resume operations */ > + eeh_pe_dev_traverse(pe, eeh_report_resume, NULL); > + > + return; > + > +excess_failures: > + /* > + * About 90% of all real-life EEH failures in the field > + * are due to poorly seated PCI cards. Only 10% or so are > + * due to actual, failed cards. > + */ > + pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n" > + "last hour and has been permanently disabled.\n" > + "Please try reseating or replacing it.\n", > + pe->phb->global_number, pe->addr, > + pe->freeze_count); > + goto perm_error; > + > +hard_fail: > + pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n" > + "Please try reseating or replacing it\n", > + pe->phb->global_number, pe->addr); > + > +perm_error: > + eeh_slot_error_detail(pe, EEH_LOG_PERM); > + > + /* Notify all devices that they're about to go down. */ > + eeh_pe_dev_traverse(pe, eeh_report_failure, NULL); > + > + /* Shut down the device drivers for good. */ > + if (frozen_bus) > + pcibios_remove_pci_devices(frozen_bus); > +} > + > diff --git a/arch/powerpc/kernel/eeh_event.c b/arch/powerpc/kernel/eeh_event.c > new file mode 100644 > index 0000000..185bedd > --- /dev/null > +++ b/arch/powerpc/kernel/eeh_event.c > @@ -0,0 +1,142 @@ > +/* > + * 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 > + * > + * Copyright (c) 2005 Linas Vepstas > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/** Overview: > + * EEH error states may be detected within exception handlers; > + * however, the recovery processing needs to occur asynchronously > + * in a normal kernel context and not an interrupt context. > + * This pair of routines creates an event and queues it onto a > + * work-queue, where a worker thread can drive recovery. > + */ > + > +/* EEH event workqueue setup. */ > +static DEFINE_SPINLOCK(eeh_eventlist_lock); > +LIST_HEAD(eeh_eventlist); > +static void eeh_thread_launcher(struct work_struct *); > +DECLARE_WORK(eeh_event_wq, eeh_thread_launcher); > + > +/* Serialize reset sequences for a given pci device */ > +DEFINE_MUTEX(eeh_event_mutex); > + > +/** > + * eeh_event_handler - Dispatch EEH events. > + * @dummy - unused > + * > + * The detection of a frozen slot can occur inside an interrupt, > + * where it can be hard to do anything about it. The goal of this > + * routine is to pull these detection events out of the context > + * of the interrupt handler, and re-dispatch them for processing > + * at a later time in a normal context. > + */ > +static int eeh_event_handler(void * dummy) > +{ > + unsigned long flags; > + struct eeh_event *event; > + struct eeh_pe *pe; > + > + spin_lock_irqsave(&eeh_eventlist_lock, flags); > + event = NULL; > + > + /* Unqueue the event, get ready to process. */ > + if (!list_empty(&eeh_eventlist)) { > + event = list_entry(eeh_eventlist.next, struct eeh_event, list); > + list_del(&event->list); > + } > + spin_unlock_irqrestore(&eeh_eventlist_lock, flags); > + > + if (event == NULL) > + return 0; > + > + /* Serialize processing of EEH events */ > + mutex_lock(&eeh_event_mutex); > + pe = event->pe; > + eeh_pe_state_mark(pe, EEH_PE_RECOVERING); > + pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n", > + pe->phb->global_number, pe->addr); > + > + set_current_state(TASK_INTERRUPTIBLE); /* Don't add to load average */ > + eeh_handle_event(pe); > + eeh_pe_state_clear(pe, EEH_PE_RECOVERING); > + > + kfree(event); > + mutex_unlock(&eeh_event_mutex); > + > + /* If there are no new errors after an hour, clear the counter. */ > + if (pe && pe->freeze_count > 0) { > + msleep_interruptible(3600*1000); > + if (pe->freeze_count > 0) > + pe->freeze_count--; > + > + } > + > + return 0; > +} > + > +/** > + * eeh_thread_launcher - Start kernel thread to handle EEH events > + * @dummy - unused > + * > + * This routine is called to start the kernel thread for processing > + * EEH event. > + */ > +static void eeh_thread_launcher(struct work_struct *dummy) > +{ > + if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd"))) > + printk(KERN_ERR "Failed to start EEH daemon\n"); > +} > + > +/** > + * eeh_send_failure_event - Generate a PCI error event > + * @pe: EEH PE > + * > + * This routine can be called within an interrupt context; > + * the actual event will be delivered in a normal context > + * (from a workqueue). > + */ > +int eeh_send_failure_event(struct eeh_pe *pe) > +{ > + unsigned long flags; > + struct eeh_event *event; > + > + event = kzalloc(sizeof(*event), GFP_ATOMIC); > + if (!event) { > + pr_err("EEH: out of memory, event not handled\n"); > + return -ENOMEM; > + } > + event->pe = pe; > + > + /* We may or may not be called in an interrupt context */ > + spin_lock_irqsave(&eeh_eventlist_lock, flags); > + list_add(&event->list, &eeh_eventlist); > + spin_unlock_irqrestore(&eeh_eventlist_lock, flags); > + > + schedule_work(&eeh_event_wq); > + > + return 0; > +} > diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c > new file mode 100644 > index 0000000..9d4a9e8 > --- /dev/null > +++ b/arch/powerpc/kernel/eeh_pe.c > @@ -0,0 +1,653 @@ > +/* > + * The file intends to implement PE based on the information from > + * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device. > + * All the PEs should be organized as hierarchy tree. The first level > + * of the tree will be associated to existing PHBs since the particular > + * PE is only meaningful in one PHB domain. > + * > + * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. > + * > + * 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +static LIST_HEAD(eeh_phb_pe); > + > +/** > + * eeh_pe_alloc - Allocate PE > + * @phb: PCI controller > + * @type: PE type > + * > + * Allocate PE instance dynamically. > + */ > +static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type) > +{ > + struct eeh_pe *pe; > + > + /* Allocate PHB PE */ > + pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL); > + if (!pe) return NULL; > + > + /* Initialize PHB PE */ > + pe->type = type; > + pe->phb = phb; > + INIT_LIST_HEAD(&pe->child_list); > + INIT_LIST_HEAD(&pe->child); > + INIT_LIST_HEAD(&pe->edevs); > + > + return pe; > +} > + > +/** > + * eeh_phb_pe_create - Create PHB PE > + * @phb: PCI controller > + * > + * The function should be called while the PHB is detected during > + * system boot or PCI hotplug in order to create PHB PE. > + */ > +int eeh_phb_pe_create(struct pci_controller *phb) > +{ > + struct eeh_pe *pe; > + > + /* Allocate PHB PE */ > + pe = eeh_pe_alloc(phb, EEH_PE_PHB); > + if (!pe) { > + pr_err("%s: out of memory!\n", __func__); > + return -ENOMEM; > + } > + > + /* Put it into the list */ > + eeh_lock(); > + list_add_tail(&pe->child, &eeh_phb_pe); > + eeh_unlock(); > + > + pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number); > + > + return 0; > +} > + > +/** > + * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB > + * @phb: PCI controller > + * > + * The overall PEs form hierarchy tree. The first layer of the > + * hierarchy tree is composed of PHB PEs. The function is used > + * to retrieve the corresponding PHB PE according to the given PHB. > + */ > +static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) > +{ > + struct eeh_pe *pe; > + > + list_for_each_entry(pe, &eeh_phb_pe, child) { > + /* > + * Actually, we needn't check the type since > + * the PE for PHB has been determined when that > + * was created. > + */ > + if ((pe->type & EEH_PE_PHB) && pe->phb == phb) > + return pe; > + } > + > + return NULL; > +} > + > +/** > + * eeh_pe_next - Retrieve the next PE in the tree > + * @pe: current PE > + * @root: root PE > + * > + * The function is used to retrieve the next PE in the > + * hierarchy PE tree. > + */ > +static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, > + struct eeh_pe *root) > +{ > + struct list_head *next = pe->child_list.next; > + > + if (next == &pe->child_list) { > + while (1) { > + if (pe == root) > + return NULL; > + next = pe->child.next; > + if (next != &pe->parent->child_list) > + break; > + pe = pe->parent; > + } > + } > + > + return list_entry(next, struct eeh_pe, child); > +} > + > +/** > + * eeh_pe_traverse - Traverse PEs in the specified PHB > + * @root: root PE > + * @fn: callback > + * @flag: extra parameter to callback > + * > + * The function is used to traverse the specified PE and its > + * child PEs. The traversing is to be terminated once the > + * callback returns something other than NULL, or no more PEs > + * to be traversed. > + */ > +static void *eeh_pe_traverse(struct eeh_pe *root, > + eeh_traverse_func fn, void *flag) > +{ > + struct eeh_pe *pe; > + void *ret; > + > + for (pe = root; pe; pe = eeh_pe_next(pe, root)) { > + ret = fn(pe, flag); > + if (ret) return ret; > + } > + > + return NULL; > +} > + > +/** > + * eeh_pe_dev_traverse - Traverse the devices from the PE > + * @root: EEH PE > + * @fn: function callback > + * @flag: extra parameter to callback > + * > + * The function is used to traverse the devices of the specified > + * PE and its child PEs. > + */ > +void *eeh_pe_dev_traverse(struct eeh_pe *root, > + eeh_traverse_func fn, void *flag) > +{ > + struct eeh_pe *pe; > + struct eeh_dev *edev; > + void *ret; > + > + if (!root) { > + pr_warning("%s: Invalid PE %p\n", __func__, root); > + return NULL; > + } > + > + eeh_lock(); > + > + /* Traverse root PE */ > + for (pe = root; pe; pe = eeh_pe_next(pe, root)) { > + eeh_pe_for_each_dev(pe, edev) { > + ret = fn(edev, flag); > + if (ret) { > + eeh_unlock(); > + return ret; > + } > + } > + } > + > + eeh_unlock(); > + > + return NULL; > +} > + > +/** > + * __eeh_pe_get - Check the PE address > + * @data: EEH PE > + * @flag: EEH device > + * > + * For one particular PE, it can be identified by PE address > + * or tranditional BDF address. BDF address is composed of > + * Bus/Device/Function number. The extra data referred by flag > + * indicates which type of address should be used. > + */ > +static void *__eeh_pe_get(void *data, void *flag) > +{ > + struct eeh_pe *pe = (struct eeh_pe *)data; > + struct eeh_dev *edev = (struct eeh_dev *)flag; > + > + /* Unexpected PHB PE */ > + if (pe->type & EEH_PE_PHB) > + return NULL; > + > + /* We prefer PE address */ > + if (edev->pe_config_addr && > + (edev->pe_config_addr == pe->addr)) > + return pe; > + > + /* Try BDF address */ > + if (edev->pe_config_addr && > + (edev->config_addr == pe->config_addr)) > + return pe; > + > + return NULL; > +} > + > +/** > + * eeh_pe_get - Search PE based on the given address > + * @edev: EEH device > + * > + * Search the corresponding PE based on the specified address which > + * is included in the eeh device. The function is used to check if > + * the associated PE has been created against the PE address. It's > + * notable that the PE address has 2 format: traditional PE address > + * which is composed of PCI bus/device/function number, or unified > + * PE address. > + */ > +static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) > +{ > + struct eeh_pe *root = eeh_phb_pe_get(edev->phb); > + struct eeh_pe *pe; > + > + pe = eeh_pe_traverse(root, __eeh_pe_get, edev); > + > + return pe; > +} > + > +/** > + * eeh_pe_get_parent - Retrieve the parent PE > + * @edev: EEH device > + * > + * The whole PEs existing in the system are organized as hierarchy > + * tree. The function is used to retrieve the parent PE according > + * to the parent EEH device. > + */ > +static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) > +{ > + struct device_node *dn; > + struct eeh_dev *parent; > + > + /* > + * It might have the case for the indirect parent > + * EEH device already having associated PE, but > + * the direct parent EEH device doesn't have yet. > + */ > + dn = edev->dn->parent; > + while (dn) { > + /* We're poking out of PCI territory */ > + if (!PCI_DN(dn)) return NULL; > + > + parent = of_node_to_eeh_dev(dn); > + /* We're poking out of PCI territory */ > + if (!parent) return NULL; > + > + if (parent->pe) > + return parent->pe; > + > + dn = dn->parent; > + } > + > + return NULL; > +} > + > +/** > + * eeh_add_to_parent_pe - Add EEH device to parent PE > + * @edev: EEH device > + * > + * Add EEH device to the parent PE. If the parent PE already > + * exists, the PE type will be changed to EEH_PE_BUS. Otherwise, > + * we have to create new PE to hold the EEH device and the new > + * PE will be linked to its parent PE as well. > + */ > +int eeh_add_to_parent_pe(struct eeh_dev *edev) > +{ > + struct eeh_pe *pe, *parent; > + > + eeh_lock(); > + > + /* > + * Search the PE has been existing or not according > + * to the PE address. If that has been existing, the > + * PE should be composed of PCI bus and its subordinate > + * components. > + */ > + pe = eeh_pe_get(edev); > + if (pe && !(pe->type & EEH_PE_INVALID)) { > + if (!edev->pe_config_addr) { > + eeh_unlock(); > + pr_err("%s: PE with addr 0x%x already exists\n", > + __func__, edev->config_addr); > + return -EEXIST; > + } > + > + /* Mark the PE as type of PCI bus */ > + pe->type = EEH_PE_BUS; > + edev->pe = pe; > + > + /* Put the edev to PE */ > + list_add_tail(&edev->list, &pe->edevs); > + eeh_unlock(); > + pr_debug("EEH: Add %s to Bus PE#%x\n", > + edev->dn->full_name, pe->addr); > + > + return 0; > + } else if (pe && (pe->type & EEH_PE_INVALID)) { > + list_add_tail(&edev->list, &pe->edevs); > + edev->pe = pe; > + /* > + * We're running to here because of PCI hotplug caused by > + * EEH recovery. We need clear EEH_PE_INVALID until the top. > + */ > + parent = pe; > + while (parent) { > + if (!(parent->type & EEH_PE_INVALID)) > + break; > + parent->type &= ~EEH_PE_INVALID; > + parent = parent->parent; > + } > + eeh_unlock(); > + pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", > + edev->dn->full_name, pe->addr, pe->parent->addr); > + > + return 0; > + } > + > + /* Create a new EEH PE */ > + pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE); > + if (!pe) { > + eeh_unlock(); > + pr_err("%s: out of memory!\n", __func__); > + return -ENOMEM; > + } > + pe->addr = edev->pe_config_addr; > + pe->config_addr = edev->config_addr; > + > + /* > + * Put the new EEH PE into hierarchy tree. If the parent > + * can't be found, the newly created PE will be attached > + * to PHB directly. Otherwise, we have to associate the > + * PE with its parent. > + */ > + parent = eeh_pe_get_parent(edev); > + if (!parent) { > + parent = eeh_phb_pe_get(edev->phb); > + if (!parent) { > + eeh_unlock(); > + pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", > + __func__, edev->phb->global_number); > + edev->pe = NULL; > + kfree(pe); > + return -EEXIST; > + } > + } > + pe->parent = parent; > + > + /* > + * Put the newly created PE into the child list and > + * link the EEH device accordingly. > + */ > + list_add_tail(&pe->child, &parent->child_list); > + list_add_tail(&edev->list, &pe->edevs); > + edev->pe = pe; > + eeh_unlock(); > + pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", > + edev->dn->full_name, pe->addr, pe->parent->addr); > + > + return 0; > +} > + > +/** > + * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE > + * @edev: EEH device > + * @purge_pe: remove PE or not > + * > + * The PE hierarchy tree might be changed when doing PCI hotplug. > + * Also, the PCI devices or buses could be removed from the system > + * during EEH recovery. So we have to call the function remove the > + * corresponding PE accordingly if necessary. > + */ > +int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) > +{ > + struct eeh_pe *pe, *parent, *child; > + int cnt; > + > + if (!edev->pe) { > + pr_warning("%s: No PE found for EEH device %s\n", > + __func__, edev->dn->full_name); > + return -EEXIST; > + } > + > + eeh_lock(); > + > + /* Remove the EEH device */ > + pe = edev->pe; > + edev->pe = NULL; > + list_del(&edev->list); > + > + /* > + * Check if the parent PE includes any EEH devices. > + * If not, we should delete that. Also, we should > + * delete the parent PE if it doesn't have associated > + * child PEs and EEH devices. > + */ > + while (1) { > + parent = pe->parent; > + if (pe->type & EEH_PE_PHB) > + break; > + > + if (purge_pe) { > + if (list_empty(&pe->edevs) && > + list_empty(&pe->child_list)) { > + list_del(&pe->child); > + kfree(pe); > + } else { > + break; > + } > + } else { > + if (list_empty(&pe->edevs)) { > + cnt = 0; > + list_for_each_entry(child, &pe->child_list, child) { > + if (!(child->type & EEH_PE_INVALID)) { > + cnt++; > + break; > + } > + } > + > + if (!cnt) > + pe->type |= EEH_PE_INVALID; > + else > + break; > + } > + } > + > + pe = parent; > + } > + > + eeh_unlock(); > + > + return 0; > +} > + > +/** > + * __eeh_pe_state_mark - Mark the state for the PE > + * @data: EEH PE > + * @flag: state > + * > + * The function is used to mark the indicated state for the given > + * PE. Also, the associated PCI devices will be put into IO frozen > + * state as well. > + */ > +static void *__eeh_pe_state_mark(void *data, void *flag) > +{ > + struct eeh_pe *pe = (struct eeh_pe *)data; > + int state = *((int *)flag); > + struct eeh_dev *tmp; > + struct pci_dev *pdev; > + > + /* > + * Mark the PE with the indicated state. Also, > + * the associated PCI device will be put into > + * I/O frozen state to avoid I/O accesses from > + * the PCI device driver. > + */ > + pe->state |= state; > + eeh_pe_for_each_dev(pe, tmp) { > + pdev = eeh_dev_to_pci_dev(tmp); > + if (pdev) > + pdev->error_state = pci_channel_io_frozen; > + } > + > + return NULL; > +} > + > +/** > + * eeh_pe_state_mark - Mark specified state for PE and its associated device > + * @pe: EEH PE > + * > + * EEH error affects the current PE and its child PEs. The function > + * is used to mark appropriate state for the affected PEs and the > + * associated devices. > + */ > +void eeh_pe_state_mark(struct eeh_pe *pe, int state) > +{ > + eeh_lock(); > + eeh_pe_traverse(pe, __eeh_pe_state_mark, &state); > + eeh_unlock(); > +} > + > +/** > + * __eeh_pe_state_clear - Clear state for the PE > + * @data: EEH PE > + * @flag: state > + * > + * The function is used to clear the indicated state from the > + * given PE. Besides, we also clear the check count of the PE > + * as well. > + */ > +static void *__eeh_pe_state_clear(void *data, void *flag) > +{ > + struct eeh_pe *pe = (struct eeh_pe *)data; > + int state = *((int *)flag); > + > + pe->state &= ~state; > + pe->check_count = 0; > + > + return NULL; > +} > + > +/** > + * eeh_pe_state_clear - Clear state for the PE and its children > + * @pe: PE > + * @state: state to be cleared > + * > + * When the PE and its children has been recovered from error, > + * we need clear the error state for that. The function is used > + * for the purpose. > + */ > +void eeh_pe_state_clear(struct eeh_pe *pe, int state) > +{ > + eeh_lock(); > + eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); > + eeh_unlock(); > +} > + > +/** > + * eeh_restore_one_device_bars - Restore the Base Address Registers for one device > + * @data: EEH device > + * @flag: Unused > + * > + * 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 void *eeh_restore_one_device_bars(void *data, void *flag) > +{ > + int i; > + u32 cmd; > + struct eeh_dev *edev = (struct eeh_dev *)data; > + struct device_node *dn = eeh_dev_to_of_node(edev); > + > + for (i = 4; i < 10; i++) > + eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); > + /* 12 == Expansion ROM Address */ > + eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); > + > +#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) > +#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) > + > + eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, > + SAVED_BYTE(PCI_CACHE_LINE_SIZE)); > + eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, > + SAVED_BYTE(PCI_LATENCY_TIMER)); > + > + /* max latency, min grant, interrupt pin and line */ > + eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); > + > + /* > + * Restore PERR & SERR bits, some devices require it, > + * don't touch the other command bits > + */ > + eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); > + if (edev->config_space[1] & PCI_COMMAND_PARITY) > + cmd |= PCI_COMMAND_PARITY; > + else > + cmd &= ~PCI_COMMAND_PARITY; > + if (edev->config_space[1] & PCI_COMMAND_SERR) > + cmd |= PCI_COMMAND_SERR; > + else > + cmd &= ~PCI_COMMAND_SERR; > + eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); > + > + return NULL; > +} > + > +/** > + * eeh_pe_restore_bars - Restore the PCI config space info > + * @pe: EEH PE > + * > + * This routine performs a recursive walk to the children > + * of this device as well. > + */ > +void eeh_pe_restore_bars(struct eeh_pe *pe) > +{ > + /* > + * We needn't take the EEH lock since eeh_pe_dev_traverse() > + * will take that. > + */ > + eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL); > +} > + > +/** > + * eeh_pe_bus_get - Retrieve PCI bus according to the given PE > + * @pe: EEH PE > + * > + * Retrieve the PCI bus according to the given PE. Basically, > + * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the > + * primary PCI bus will be retrieved. The parent bus will be > + * returned for BUS PE. However, we don't have associated PCI > + * bus for DEVICE PE. > + */ > +struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe) > +{ > + struct pci_bus *bus = NULL; > + struct eeh_dev *edev; > + struct pci_dev *pdev; > + > + eeh_lock(); > + > + if (pe->type & EEH_PE_PHB) { > + bus = pe->phb->bus; > + } else if (pe->type & EEH_PE_BUS || > + pe->type & EEH_PE_DEVICE) { > + edev = list_first_entry(&pe->edevs, struct eeh_dev, list); > + pdev = eeh_dev_to_pci_dev(edev); > + if (pdev) > + bus = pdev->bus; > + } > + > + eeh_unlock(); > + > + return bus; > +} > diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c > new file mode 100644 > index 0000000..d377083 > --- /dev/null > +++ b/arch/powerpc/kernel/eeh_sysfs.c > @@ -0,0 +1,75 @@ > +/* > + * Sysfs entries for PCI Error Recovery for PAPR-compliant platform. > + * Copyright IBM Corporation 2007 > + * Copyright Linas Vepstas 2007 > + * > + * All rights reserved. > + * > + * 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, GOOD TITLE or > + * NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA. > + * > + * Send comments and feedback to Linas Vepstas > + */ > +#include > +#include > +#include > +#include > + > +/** > + * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic > + * @_name: name of file in sysfs directory > + * @_memb: name of member in struct pci_dn to access > + * @_format: printf format for display > + * > + * All of the attributes look very similar, so just > + * auto-gen a cut-n-paste routine to display them. > + */ > +#define EEH_SHOW_ATTR(_name,_memb,_format) \ > +static ssize_t eeh_show_##_name(struct device *dev, \ > + struct device_attribute *attr, char *buf) \ > +{ \ > + struct pci_dev *pdev = to_pci_dev(dev); \ > + struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \ > + \ > + if (!edev) \ > + return 0; \ > + \ > + return sprintf(buf, _format "\n", edev->_memb); \ > +} \ > +static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL); > + > +EEH_SHOW_ATTR(eeh_mode, mode, "0x%x"); > +EEH_SHOW_ATTR(eeh_config_addr, config_addr, "0x%x"); > +EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x"); > + > +void eeh_sysfs_add_device(struct pci_dev *pdev) > +{ > + int rc=0; > + > + rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode); > + rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr); > + rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); > + > + if (rc) > + printk(KERN_WARNING "EEH: Unable to create sysfs entries\n"); > +} > + > +void eeh_sysfs_remove_device(struct pci_dev *pdev) > +{ > + device_remove_file(&pdev->dev, &dev_attr_eeh_mode); > + device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr); > + device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); > +} > + > diff --git a/arch/powerpc/kernel/pci_hotplug.c b/arch/powerpc/kernel/pci_hotplug.c > new file mode 100644 > index 0000000..3f60880 > --- /dev/null > +++ b/arch/powerpc/kernel/pci_hotplug.c > @@ -0,0 +1,111 @@ > +/* > + * Derived from "arch/powerpc/platforms/pseries/pci_dlpar.c" > + * > + * Copyright (C) 2003 Linda Xie > + * Copyright (C) 2005 International Business Machines > + * > + * Updates, 2005, John Rose > + * Updates, 2005, Linas Vepstas > + * Updates, 2013, Gavin Shan > + * > + * 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. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +/** > + * __pcibios_remove_pci_devices - remove all devices under this bus > + * @bus: the indicated PCI bus > + * @purge_pe: destroy the PE on removal of PCI devices > + * > + * Remove all of the PCI devices under this bus both from the > + * linux pci device tree, and from the powerpc EEH address cache. > + * By default, the corresponding PE will be destroied during the > + * normal PCI hotplug path. For PCI hotplug during EEH recovery, > + * the corresponding PE won't be destroied and deallocated. > + */ > +void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe) > +{ > + struct pci_dev *dev, *tmp; > + struct pci_bus *child_bus; > + > + /* First go down child busses */ > + list_for_each_entry(child_bus, &bus->children, node) > + __pcibios_remove_pci_devices(child_bus, purge_pe); > + > + pr_debug("PCI: Removing devices on bus %04x:%02x\n", > + pci_domain_nr(bus), bus->number); > + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { > + pr_debug(" * Removing %s...\n", pci_name(dev)); > + eeh_remove_bus_device(dev, purge_pe); > + pci_stop_and_remove_bus_device(dev); > + } > +} > + > +/** > + * pcibios_remove_pci_devices - remove all devices under this bus > + * @bus: the indicated PCI bus > + * > + * Remove all of the PCI devices under this bus both from the > + * linux pci device tree, and from the powerpc EEH address cache. > + */ > +void pcibios_remove_pci_devices(struct pci_bus *bus) > +{ > + __pcibios_remove_pci_devices(bus, 1); > +} > +EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); > + > +/** > + * pcibios_add_pci_devices - adds new pci devices to bus > + * @bus: the indicated PCI bus > + * > + * This routine will find and fixup new pci devices under > + * the indicated bus. This routine presumes that there > + * might already be some devices under this bridge, so > + * it carefully tries to add only new devices. (And that > + * is how this routine differs from other, similar pcibios > + * routines.) > + */ > +void pcibios_add_pci_devices(struct pci_bus * bus) > +{ > + int slotno, num, mode, pass, max; > + struct pci_dev *dev; > + struct device_node *dn = pci_bus_to_OF_node(bus); > + > + eeh_add_device_tree_early(dn); > + > + mode = PCI_PROBE_NORMAL; > + if (ppc_md.pci_probe_mode) > + mode = ppc_md.pci_probe_mode(bus); > + > + if (mode == PCI_PROBE_DEVTREE) { > + /* use ofdt-based probe */ > + of_rescan_bus(dn, bus); > + } else if (mode == PCI_PROBE_NORMAL) { > + /* use legacy probe */ > + slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); > + num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); > + if (!num) > + return; > + pcibios_setup_bus_devices(bus); > + max = bus->busn_res.start; > + for (pass = 0; pass < 2; pass++) { > + list_for_each_entry(dev, &bus->devices, bus_list) { > + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || > + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) > + max = pci_scan_bridge(bus, dev, > + max, pass); > + } > + } > + } > + pcibios_finish_adding_to_bus(bus); > +} > +EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); > diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig > index b62aab3..bed8c60 100644 > --- a/arch/powerpc/platforms/Kconfig > +++ b/arch/powerpc/platforms/Kconfig > @@ -164,6 +164,11 @@ config IBMEBUS > help > Bus device driver for GX bus based adapters. > > +config EEH > + bool > + depends on (PPC_POWERNV || PPC_PSERIES) && PCI > + default y > + > config PPC_MPC106 > bool > default n > diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig > index 4459eff..1bd3399 100644 > --- a/arch/powerpc/platforms/pseries/Kconfig > +++ b/arch/powerpc/platforms/pseries/Kconfig > @@ -33,11 +33,6 @@ config PPC_SPLPAR > processors, that is, which share physical processors between > two or more partitions. > > -config EEH > - bool > - depends on PPC_PSERIES && PCI > - default y > - > config PSERIES_MSI > bool > depends on PCI_MSI && EEH > diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile > index 53866e5..8ae0103 100644 > --- a/arch/powerpc/platforms/pseries/Makefile > +++ b/arch/powerpc/platforms/pseries/Makefile > @@ -6,9 +6,7 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ > firmware.o power.o dlpar.o mobility.o > obj-$(CONFIG_SMP) += smp.o > obj-$(CONFIG_SCANLOG) += scanlog.o > -obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \ > - eeh_driver.o eeh_event.o eeh_sysfs.o \ > - eeh_pseries.o > +obj-$(CONFIG_EEH) += eeh_pseries.o > obj-$(CONFIG_KEXEC) += kexec.o > obj-$(CONFIG_PCI) += pci.o pci_dlpar.o > obj-$(CONFIG_PSERIES_MSI) += msi.o > diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c > deleted file mode 100644 > index 6b73d6c..0000000 > --- a/arch/powerpc/platforms/pseries/eeh.c > +++ /dev/null > @@ -1,942 +0,0 @@ > -/* > - * Copyright IBM Corporation 2001, 2005, 2006 > - * Copyright Dave Engebretsen & Todd Inglett 2001 > - * Copyright Linas Vepstas 2005, 2006 > - * Copyright 2001-2012 IBM Corporation. > - * > - * 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 > - */ > - > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > - > -#include > -#include > -#include > -#include > -#include > -#include > -#include > - > - > -/** 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 occurred (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, it might be broken. > - * This sets the threshold for how many read attempts we allow > - * before printing an error message. > - */ > -#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) > - > -/* Platform dependent EEH operations */ > -struct eeh_ops *eeh_ops = NULL; > - > -int eeh_subsystem_enabled; > -EXPORT_SYMBOL(eeh_subsystem_enabled); > - > -/* > - * EEH probe mode support. The intention is to support multiple > - * platforms for EEH. Some platforms like pSeries do PCI emunation > - * based on device tree. However, other platforms like powernv probe > - * PCI devices from hardware. The flag is used to distinguish that. > - * In addition, struct eeh_ops::probe would be invoked for particular > - * OF node or PCI device so that the corresponding PE would be created > - * there. > - */ > -int eeh_probe_mode; > - > -/* Global EEH mutex */ > -DEFINE_MUTEX(eeh_mutex); > - > -/* Lock to avoid races due to multiple reports of an error */ > -static DEFINE_RAW_SPINLOCK(confirm_error_lock); > - > -/* 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 4096 > -static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN]; > - > -/* > - * The struct is used to maintain the EEH global statistic > - * information. Besides, the EEH global statistics will be > - * exported to user space through procfs > - */ > -struct eeh_stats { > - u64 no_device; /* PCI device not found */ > - u64 no_dn; /* OF node not found */ > - u64 no_cfg_addr; /* Config address not found */ > - u64 ignored_check; /* EEH check skipped */ > - u64 total_mmio_ffs; /* Total EEH checks */ > - u64 false_positives; /* Unnecessary EEH checks */ > - u64 slot_resets; /* PE reset */ > -}; > - > -static struct eeh_stats eeh_stats; > - > -#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) > - > -/** > - * eeh_gather_pci_data - Copy assorted PCI config space registers to buff > - * @edev: 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 eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) > -{ > - struct device_node *dn = eeh_dev_to_of_node(edev); > - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > - u32 cfg; > - int cap, i; > - int n = 0; > - > - n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); > - printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name); > - > - eeh_ops->read_config(dn, 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); > - > - eeh_ops->read_config(dn, 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) { > - eeh_ops->read_config(dn, 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); > - > - eeh_ops->read_config(dn, 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) { > - eeh_ops->read_config(dn, cap, 4, &cfg); > - n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); > - printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg); > - > - eeh_ops->read_config(dn, 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++) { > - eeh_ops->read_config(dn, 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++) { > - eeh_ops->read_config(dn, 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); > - } > - } > - } > - > - return n; > -} > - > -/** > - * eeh_slot_error_detail - Generate combined log including driver log and error log > - * @pe: EEH PE > - * @severity: temporary or permanent error log > - * > - * This routine should be called to generate the combined log, which > - * is comprised of driver log and error log. The driver log is figured > - * out from the config space of the corresponding PCI device, while > - * the error log is fetched through platform dependent function call. > - */ > -void eeh_slot_error_detail(struct eeh_pe *pe, int severity) > -{ > - size_t loglen = 0; > - struct eeh_dev *edev; > - > - eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); > - eeh_ops->configure_bridge(pe); > - eeh_pe_restore_bars(pe); > - > - pci_regs_buf[0] = 0; > - eeh_pe_for_each_dev(pe, edev) { > - loglen += eeh_gather_pci_data(edev, pci_regs_buf, > - EEH_PCI_REGS_LOG_LEN); > - } > - > - eeh_ops->get_log(pe, severity, pci_regs_buf, loglen); > -} > - > -/** > - * eeh_token_to_phys - Convert EEH address token to phys address > - * @token: I/O token, should be address in the form 0xA.... > - * > - * This routine should be called to convert virtual I/O address > - * to physical one. > - */ > -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)); > -} > - > -/** > - * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze > - * @edev: eeh device > - * > - * 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_dev_check_failure(struct eeh_dev *edev) > -{ > - int ret; > - unsigned long flags; > - struct device_node *dn; > - struct pci_dev *dev; > - struct eeh_pe *pe; > - int rc = 0; > - const char *location; > - > - eeh_stats.total_mmio_ffs++; > - > - if (!eeh_subsystem_enabled) > - return 0; > - > - if (!edev) { > - eeh_stats.no_dn++; > - return 0; > - } > - dn = eeh_dev_to_of_node(edev); > - dev = eeh_dev_to_pci_dev(edev); > - pe = edev->pe; > - > - /* Access to IO BARs might get this far and still not want checking. */ > - if (!pe) { > - eeh_stats.ignored_check++; > - pr_debug("EEH: Ignored check for %s %s\n", > - eeh_pci_name(dev), dn->full_name); > - return 0; > - } > - > - if (!pe->addr && !pe->config_addr) { > - eeh_stats.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. > - */ > - raw_spin_lock_irqsave(&confirm_error_lock, flags); > - rc = 1; > - if (pe->state & EEH_PE_ISOLATED) { > - pe->check_count++; > - if (pe->check_count % EEH_MAX_FAILS == 0) { > - location = of_get_property(dn, "ibm,loc-code", NULL); > - printk(KERN_ERR "EEH: %d reads ignored for recovering device at " > - "location=%s driver=%s pci addr=%s\n", > - pe->check_count, location, > - eeh_driver_name(dev), eeh_pci_name(dev)); > - printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n", > - eeh_driver_name(dev)); > - dump_stack(); > - } > - 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 = eeh_ops->get_state(pe, NULL); > - > - /* Note that config-io to empty slots may fail; > - * they are empty when they don't have children. > - * We will punt with the following conditions: Failure to get > - * PE's state, EEH not support and Permanently unavailable > - * state, PE is in good state. > - */ > - if ((ret < 0) || > - (ret == EEH_STATE_NOT_SUPPORT) || > - (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) == > - (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) { > - eeh_stats.false_positives++; > - pe->false_positives++; > - rc = 0; > - goto dn_unlock; > - } > - > - eeh_stats.slot_resets++; > - > - /* Avoid repeated reports of this failure, including problems > - * with other functions on this device, and functions under > - * bridges. > - */ > - eeh_pe_state_mark(pe, EEH_PE_ISOLATED); > - raw_spin_unlock_irqrestore(&confirm_error_lock, flags); > - > - eeh_send_failure_event(pe); > - > - /* 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. > - */ > - WARN(1, "EEH: failure detected\n"); > - return 1; > - > -dn_unlock: > - raw_spin_unlock_irqrestore(&confirm_error_lock, flags); > - return rc; > -} > - > -EXPORT_SYMBOL_GPL(eeh_dev_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 eeh_dev *edev; > - > - /* Finding the phys addr + pci device; this is pretty quick. */ > - addr = eeh_token_to_phys((unsigned long __force) token); > - edev = eeh_addr_cache_get_dev(addr); > - if (!edev) { > - eeh_stats.no_device++; > - return val; > - } > - > - eeh_dev_check_failure(edev); > - > - pci_dev_put(eeh_dev_to_pci_dev(edev)); > - return val; > -} > - > -EXPORT_SYMBOL(eeh_check_failure); > - > - > -/** > - * eeh_pci_enable - Enable MMIO or DMA transfers for this slot > - * @pe: EEH PE > - * > - * This routine should be called to reenable frozen MMIO or DMA > - * so that it would work correctly again. It's useful while doing > - * recovery or log collection on the indicated device. > - */ > -int eeh_pci_enable(struct eeh_pe *pe, int function) > -{ > - int rc; > - > - rc = eeh_ops->set_option(pe, function); > - if (rc) > - pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n", > - __func__, function, pe->phb->global_number, pe->addr, rc); > - > - rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC); > - if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) && > - (function == EEH_OPT_THAW_MMIO)) > - return 0; > - > - return rc; > -} > - > -/** > - * 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 eeh_dev *edev = pci_dev_to_eeh_dev(dev); > - struct eeh_pe *pe = edev->pe; > - > - if (!pe) { > - pr_err("%s: No PE found on PCI device %s\n", > - __func__, pci_name(dev)); > - return -EINVAL; > - } > - > - switch (state) { > - case pcie_deassert_reset: > - eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); > - break; > - case pcie_hot_reset: > - eeh_ops->reset(pe, EEH_RESET_HOT); > - break; > - case pcie_warm_reset: > - eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); > - break; > - default: > - return -EINVAL; > - }; > - > - return 0; > -} > - > -/** > - * eeh_set_pe_freset - Check the required reset for the indicated device > - * @data: EEH device > - * @flag: return value > - * > - * Each device might have its preferred reset type: fundamental or > - * hot reset. The routine is used to collected the information for > - * the indicated device and its children so that the bunch of the > - * devices could be reset properly. > - */ > -static void *eeh_set_dev_freset(void *data, void *flag) > -{ > - struct pci_dev *dev; > - unsigned int *freset = (unsigned int *)flag; > - struct eeh_dev *edev = (struct eeh_dev *)data; > - > - dev = eeh_dev_to_pci_dev(edev); > - if (dev) > - *freset |= dev->needs_freset; > - > - return NULL; > -} > - > -/** > - * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second > - * @pe: EEH PE > - * > - * Assert the PCI #RST line for 1/4 second. > - */ > -static void eeh_reset_pe_once(struct eeh_pe *pe) > -{ > - unsigned int freset = 0; > - > - /* Determine type of EEH reset required for > - * Partitionable Endpoint, a hot-reset (1) > - * or a fundamental reset (3). > - * A fundamental reset required by any device under > - * Partitionable Endpoint trumps hot-reset. > - */ > - eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset); > - > - if (freset) > - eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); > - else > - eeh_ops->reset(pe, EEH_RESET_HOT); > - > - /* 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_pe_state_clear(pe, EEH_PE_ISOLATED); > - > - eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); > - > - /* 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); > -} > - > -/** > - * eeh_reset_pe - Reset the indicated PE > - * @pe: EEH PE > - * > - * This routine should be called to reset indicated device, including > - * PE. A PE might include multiple PCI devices and sometimes PCI bridges > - * might be involved as well. > - */ > -int eeh_reset_pe(struct eeh_pe *pe) > -{ > - int i, rc; > - > - /* Take three shots at resetting the bus */ > - for (i=0; i<3; i++) { > - eeh_reset_pe_once(pe); > - > - rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC); > - if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) > - return 0; > - > - if (rc < 0) { > - pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x", > - __func__, pe->phb->global_number, pe->addr); > - return -1; > - } > - pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n", > - i+1, pe->phb->global_number, pe->addr, rc); > - } > - > - return -1; > -} > - > -/** > - * eeh_save_bars - Save device bars > - * @edev: PCI device associated EEH device > - * > - * Save the values of the device bars. Unlike the restore > - * routine, this routine is *not* recursive. This is because > - * PCI devices are added individually; but, for the restore, > - * an entire slot is reset at a time. > - */ > -void eeh_save_bars(struct eeh_dev *edev) > -{ > - int i; > - struct device_node *dn; > - > - if (!edev) > - return; > - dn = eeh_dev_to_of_node(edev); > - > - for (i = 0; i < 16; i++) > - eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]); > -} > - > -/** > - * eeh_ops_register - Register platform dependent EEH operations > - * @ops: platform dependent EEH operations > - * > - * Register the platform dependent EEH operation callback > - * functions. The platform should call this function before > - * any other EEH operations. > - */ > -int __init eeh_ops_register(struct eeh_ops *ops) > -{ > - if (!ops->name) { > - pr_warning("%s: Invalid EEH ops name for %p\n", > - __func__, ops); > - return -EINVAL; > - } > - > - if (eeh_ops && eeh_ops != ops) { > - pr_warning("%s: EEH ops of platform %s already existing (%s)\n", > - __func__, eeh_ops->name, ops->name); > - return -EEXIST; > - } > - > - eeh_ops = ops; > - > - return 0; > -} > - > -/** > - * eeh_ops_unregister - Unreigster platform dependent EEH operations > - * @name: name of EEH platform operations > - * > - * Unregister the platform dependent EEH operation callback > - * functions. > - */ > -int __exit eeh_ops_unregister(const char *name) > -{ > - if (!name || !strlen(name)) { > - pr_warning("%s: Invalid EEH ops name\n", > - __func__); > - return -EINVAL; > - } > - > - if (eeh_ops && !strcmp(eeh_ops->name, name)) { > - eeh_ops = NULL; > - return 0; > - } > - > - return -EEXIST; > -} > - > -/** > - * eeh_init - EEH initialization > - * > - * Initialize EEH by trying to enable it for all of the adapters in the system. > - * As a side effect we can determine here if eeh is supported at all. > - * Note that we leave EEH on so failed config cycles won't cause a machine > - * check. If a user turns off EEH for a particular adapter they are really > - * telling Linux to ignore errors. Some hardware (e.g. POWER5) won't > - * grant access to a slot if EEH isn't enabled, and so we always enable > - * EEH for all slots/all devices. > - * > - * The eeh-force-off option disables EEH checking globally, for all slots. > - * Even if force-off is set, the EEH hardware is still enabled, so that > - * newer systems can boot. > - */ > -static int __init eeh_init(void) > -{ > - struct pci_controller *hose, *tmp; > - struct device_node *phb; > - int ret; > - > - /* call platform initialization function */ > - if (!eeh_ops) { > - pr_warning("%s: Platform EEH operation not found\n", > - __func__); > - return -EEXIST; > - } else if ((ret = eeh_ops->init())) { > - pr_warning("%s: Failed to call platform init function (%d)\n", > - __func__, ret); > - return ret; > - } > - > - raw_spin_lock_init(&confirm_error_lock); > - > - /* Enable EEH for all adapters */ > - if (eeh_probe_mode_devtree()) { > - list_for_each_entry_safe(hose, tmp, > - &hose_list, list_node) { > - phb = hose->dn; > - traverse_pci_devices(phb, eeh_ops->of_probe, NULL); > - } > - } > - > - if (eeh_subsystem_enabled) > - pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n"); > - else > - pr_warning("EEH: No capable adapters found\n"); > - > - return ret; > -} > - > -core_initcall_sync(eeh_init); > - > -/** > - * eeh_add_device_early - Enable EEH for the indicated device_node > - * @dn: device node for which to set up EEH > - * > - * This routine must be used to perform EEH initialization for PCI > - * devices that were added after system boot (e.g. hotplug, dlpar). > - * This routine must be called before any i/o is performed to the > - * adapter (inluding any config-space i/o). > - * Whether this actually enables EEH or not for this device depends > - * on the CEC architecture, type of the device, on earlier boot > - * command-line arguments & etc. > - */ > -static void eeh_add_device_early(struct device_node *dn) > -{ > - struct pci_controller *phb; > - > - if (!of_node_to_eeh_dev(dn)) > - return; > - phb = of_node_to_eeh_dev(dn)->phb; > - > - /* USB Bus children of PCI devices will not have BUID's */ > - if (NULL == phb || 0 == phb->buid) > - return; > - > - /* FIXME: hotplug support on POWERNV */ > - eeh_ops->of_probe(dn, NULL); > -} > - > -/** > - * eeh_add_device_tree_early - Enable EEH for the indicated device > - * @dn: device node > - * > - * This routine must be used to perform EEH initialization for the > - * indicated PCI device that was added after system boot (e.g. > - * hotplug, dlpar). > - */ > -void eeh_add_device_tree_early(struct device_node *dn) > -{ > - struct device_node *sib; > - > - for_each_child_of_node(dn, sib) > - eeh_add_device_tree_early(sib); > - eeh_add_device_early(dn); > -} > -EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); > - > -/** > - * eeh_add_device_late - Perform EEH initialization for the indicated pci device > - * @dev: pci device for which to set up EEH > - * > - * This routine must be used to complete EEH initialization for PCI > - * devices that were added after system boot (e.g. hotplug, dlpar). > - */ > -static void eeh_add_device_late(struct pci_dev *dev) > -{ > - struct device_node *dn; > - struct eeh_dev *edev; > - > - if (!dev || !eeh_subsystem_enabled) > - return; > - > - pr_debug("EEH: Adding device %s\n", pci_name(dev)); > - > - dn = pci_device_to_OF_node(dev); > - edev = of_node_to_eeh_dev(dn); > - if (edev->pdev == dev) { > - pr_debug("EEH: Already referenced !\n"); > - return; > - } > - WARN_ON(edev->pdev); > - > - pci_dev_get(dev); > - edev->pdev = dev; > - dev->dev.archdata.edev = edev; > - > - eeh_addr_cache_insert_dev(dev); > -} > - > -/** > - * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus > - * @bus: PCI bus > - * > - * This routine must be used to perform EEH initialization for PCI > - * devices which are attached to the indicated PCI bus. The PCI bus > - * is added after system boot through hotplug or dlpar. > - */ > -void eeh_add_device_tree_late(struct pci_bus *bus) > -{ > - struct pci_dev *dev; > - > - list_for_each_entry(dev, &bus->devices, bus_list) { > - eeh_add_device_late(dev); > - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { > - struct pci_bus *subbus = dev->subordinate; > - if (subbus) > - eeh_add_device_tree_late(subbus); > - } > - } > -} > -EXPORT_SYMBOL_GPL(eeh_add_device_tree_late); > - > -/** > - * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus > - * @bus: PCI bus > - * > - * This routine must be used to add EEH sysfs files for PCI > - * devices which are attached to the indicated PCI bus. The PCI bus > - * is added after system boot through hotplug or dlpar. > - */ > -void eeh_add_sysfs_files(struct pci_bus *bus) > -{ > - struct pci_dev *dev; > - > - list_for_each_entry(dev, &bus->devices, bus_list) { > - eeh_sysfs_add_device(dev); > - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { > - struct pci_bus *subbus = dev->subordinate; > - if (subbus) > - eeh_add_sysfs_files(subbus); > - } > - } > -} > -EXPORT_SYMBOL_GPL(eeh_add_sysfs_files); > - > -/** > - * eeh_remove_device - Undo EEH setup for the indicated pci device > - * @dev: pci device to be removed > - * @purge_pe: remove the PE or not > - * > - * This routine should be called when a device is removed from > - * a running system (e.g. by hotplug or dlpar). It unregisters > - * the PCI device from the EEH subsystem. I/O errors affecting > - * this device will no longer be detected after this call; thus, > - * i/o errors affecting this slot may leave this device unusable. > - */ > -static void eeh_remove_device(struct pci_dev *dev, int purge_pe) > -{ > - struct eeh_dev *edev; > - > - if (!dev || !eeh_subsystem_enabled) > - return; > - edev = pci_dev_to_eeh_dev(dev); > - > - /* Unregister the device with the EEH/PCI address search system */ > - pr_debug("EEH: Removing device %s\n", pci_name(dev)); > - > - if (!edev || !edev->pdev) { > - pr_debug("EEH: Not referenced !\n"); > - return; > - } > - edev->pdev = NULL; > - dev->dev.archdata.edev = NULL; > - pci_dev_put(dev); > - > - eeh_rmv_from_parent_pe(edev, purge_pe); > - eeh_addr_cache_rmv_dev(dev); > - eeh_sysfs_remove_device(dev); > -} > - > -/** > - * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device > - * @dev: PCI device > - * @purge_pe: remove the corresponding PE or not > - * > - * This routine must be called when a device is removed from the > - * running system through hotplug or dlpar. The corresponding > - * PCI address cache will be removed. > - */ > -void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) > -{ > - struct pci_bus *bus = dev->subordinate; > - struct pci_dev *child, *tmp; > - > - eeh_remove_device(dev, purge_pe); > - > - if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { > - list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) > - eeh_remove_bus_device(child, purge_pe); > - } > -} > -EXPORT_SYMBOL_GPL(eeh_remove_bus_device); > - > -static int proc_eeh_show(struct seq_file *m, void *v) > -{ > - if (0 == eeh_subsystem_enabled) { > - seq_printf(m, "EEH Subsystem is globally disabled\n"); > - seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs); > - } else { > - seq_printf(m, "EEH Subsystem is enabled\n"); > - seq_printf(m, > - "no device=%llu\n" > - "no device node=%llu\n" > - "no config address=%llu\n" > - "check not wanted=%llu\n" > - "eeh_total_mmio_ffs=%llu\n" > - "eeh_false_positives=%llu\n" > - "eeh_slot_resets=%llu\n", > - eeh_stats.no_device, > - eeh_stats.no_dn, > - eeh_stats.no_cfg_addr, > - eeh_stats.ignored_check, > - eeh_stats.total_mmio_ffs, > - eeh_stats.false_positives, > - eeh_stats.slot_resets); > - } > - > - return 0; > -} > - > -static int proc_eeh_open(struct inode *inode, struct file *file) > -{ > - return single_open(file, proc_eeh_show, NULL); > -} > - > -static const struct file_operations proc_eeh_operations = { > - .open = proc_eeh_open, > - .read = seq_read, > - .llseek = seq_lseek, > - .release = single_release, > -}; > - > -static int __init eeh_init_proc(void) > -{ > - if (machine_is(pseries)) > - proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations); > - return 0; > -} > -__initcall(eeh_init_proc); > diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c > deleted file mode 100644 > index 5a4c879..0000000 > --- a/arch/powerpc/platforms/pseries/eeh_cache.c > +++ /dev/null > @@ -1,319 +0,0 @@ > -/* > - * PCI address cache; allows the lookup of PCI devices based on I/O address > - * > - * Copyright IBM Corporation 2004 > - * Copyright Linas Vepstas 2004 > - * > - * 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 > - */ > - > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > - > - > -/** > - * The pci address cache subsystem. This subsystem places > - * PCI device address resources into a red-black tree, sorted > - * according to the address range, so that given only an i/o > - * address, the corresponding PCI device can be **quickly** > - * found. It is safe to perform an address lookup in an interrupt > - * context; this ability is an important feature. > - * > - * Currently, the only customer of this code is the EEH subsystem; > - * thus, this code has been somewhat tailored to suit EEH better. > - * In particular, the cache does *not* hold the addresses of devices > - * for which EEH is not enabled. > - * > - * (Implementation Note: The RB tree seems to be better/faster > - * than any hash algo I could think of for this problem, even > - * with the penalty of slow pointer chases for d-cache misses). > - */ > -struct pci_io_addr_range { > - struct rb_node rb_node; > - unsigned long addr_lo; > - unsigned long addr_hi; > - struct eeh_dev *edev; > - struct pci_dev *pcidev; > - unsigned int flags; > -}; > - > -static struct pci_io_addr_cache { > - struct rb_root rb_root; > - spinlock_t piar_lock; > -} pci_io_addr_cache_root; > - > -static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr) > -{ > - struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node; > - > - while (n) { > - struct pci_io_addr_range *piar; > - piar = rb_entry(n, struct pci_io_addr_range, rb_node); > - > - if (addr < piar->addr_lo) { > - n = n->rb_left; > - } else { > - if (addr > piar->addr_hi) { > - n = n->rb_right; > - } else { > - pci_dev_get(piar->pcidev); > - return piar->edev; > - } > - } > - } > - > - return NULL; > -} > - > -/** > - * eeh_addr_cache_get_dev - Get device, given only address > - * @addr: mmio (PIO) phys address or i/o port number > - * > - * Given an mmio phys address, or a port number, find a pci device > - * that implements this address. Be sure to pci_dev_put the device > - * when finished. I/O port numbers are assumed to be offset > - * from zero (that is, they do *not* have pci_io_addr added in). > - * It is safe to call this function within an interrupt. > - */ > -struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr) > -{ > - struct eeh_dev *edev; > - unsigned long flags; > - > - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); > - edev = __eeh_addr_cache_get_device(addr); > - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); > - return edev; > -} > - > -#ifdef DEBUG > -/* > - * Handy-dandy debug print routine, does nothing more > - * than print out the contents of our addr cache. > - */ > -static void eeh_addr_cache_print(struct pci_io_addr_cache *cache) > -{ > - struct rb_node *n; > - int cnt = 0; > - > - n = rb_first(&cache->rb_root); > - while (n) { > - struct pci_io_addr_range *piar; > - piar = rb_entry(n, struct pci_io_addr_range, rb_node); > - pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n", > - (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt, > - piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev)); > - cnt++; > - n = rb_next(n); > - } > -} > -#endif > - > -/* Insert address range into the rb tree. */ > -static struct pci_io_addr_range * > -eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo, > - unsigned long ahi, unsigned int flags) > -{ > - struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node; > - struct rb_node *parent = NULL; > - struct pci_io_addr_range *piar; > - > - /* Walk tree, find a place to insert into tree */ > - while (*p) { > - parent = *p; > - piar = rb_entry(parent, struct pci_io_addr_range, rb_node); > - if (ahi < piar->addr_lo) { > - p = &parent->rb_left; > - } else if (alo > piar->addr_hi) { > - p = &parent->rb_right; > - } else { > - if (dev != piar->pcidev || > - alo != piar->addr_lo || ahi != piar->addr_hi) { > - pr_warning("PIAR: overlapping address range\n"); > - } > - return piar; > - } > - } > - piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC); > - if (!piar) > - return NULL; > - > - pci_dev_get(dev); > - piar->addr_lo = alo; > - piar->addr_hi = ahi; > - piar->edev = pci_dev_to_eeh_dev(dev); > - piar->pcidev = dev; > - piar->flags = flags; > - > -#ifdef DEBUG > - pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n", > - alo, ahi, pci_name(dev)); > -#endif > - > - rb_link_node(&piar->rb_node, parent, p); > - rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root); > - > - return piar; > -} > - > -static void __eeh_addr_cache_insert_dev(struct pci_dev *dev) > -{ > - struct device_node *dn; > - struct eeh_dev *edev; > - int i; > - > - dn = pci_device_to_OF_node(dev); > - if (!dn) { > - pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev)); > - return; > - } > - > - edev = of_node_to_eeh_dev(dn); > - if (!edev) { > - pr_warning("PCI: no EEH dev found for dn=%s\n", > - dn->full_name); > - return; > - } > - > - /* Skip any devices for which EEH is not enabled. */ > - if (!edev->pe) { > -#ifdef DEBUG > - pr_info("PCI: skip building address cache for=%s - %s\n", > - pci_name(dev), dn->full_name); > -#endif > - return; > - } > - > - /* Walk resources on this device, poke them into the tree */ > - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { > - unsigned long start = pci_resource_start(dev,i); > - unsigned long end = pci_resource_end(dev,i); > - unsigned int flags = pci_resource_flags(dev,i); > - > - /* We are interested only bus addresses, not dma or other stuff */ > - if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM))) > - continue; > - if (start == 0 || ~start == 0 || end == 0 || ~end == 0) > - continue; > - eeh_addr_cache_insert(dev, start, end, flags); > - } > -} > - > -/** > - * eeh_addr_cache_insert_dev - Add a device to the address cache > - * @dev: PCI device whose I/O addresses we are interested in. > - * > - * In order to support the fast lookup of devices based on addresses, > - * we maintain a cache of devices that can be quickly searched. > - * This routine adds a device to that cache. > - */ > -void eeh_addr_cache_insert_dev(struct pci_dev *dev) > -{ > - unsigned long flags; > - > - /* Ignore PCI bridges */ > - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) > - return; > - > - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); > - __eeh_addr_cache_insert_dev(dev); > - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); > -} > - > -static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev) > -{ > - struct rb_node *n; > - > -restart: > - n = rb_first(&pci_io_addr_cache_root.rb_root); > - while (n) { > - struct pci_io_addr_range *piar; > - piar = rb_entry(n, struct pci_io_addr_range, rb_node); > - > - if (piar->pcidev == dev) { > - rb_erase(n, &pci_io_addr_cache_root.rb_root); > - pci_dev_put(piar->pcidev); > - kfree(piar); > - goto restart; > - } > - n = rb_next(n); > - } > -} > - > -/** > - * eeh_addr_cache_rmv_dev - remove pci device from addr cache > - * @dev: device to remove > - * > - * Remove a device from the addr-cache tree. > - * This is potentially expensive, since it will walk > - * the tree multiple times (once per resource). > - * But so what; device removal doesn't need to be that fast. > - */ > -void eeh_addr_cache_rmv_dev(struct pci_dev *dev) > -{ > - unsigned long flags; > - > - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); > - __eeh_addr_cache_rmv_dev(dev); > - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); > -} > - > -/** > - * eeh_addr_cache_build - Build a cache of I/O addresses > - * > - * Build a cache of pci i/o addresses. This cache will be used to > - * find the pci device that corresponds to a given address. > - * This routine scans all pci busses to build the cache. > - * Must be run late in boot process, after the pci controllers > - * have been scanned for devices (after all device resources are known). > - */ > -void __init eeh_addr_cache_build(void) > -{ > - struct device_node *dn; > - struct eeh_dev *edev; > - struct pci_dev *dev = NULL; > - > - spin_lock_init(&pci_io_addr_cache_root.piar_lock); > - > - for_each_pci_dev(dev) { > - eeh_addr_cache_insert_dev(dev); > - > - dn = pci_device_to_OF_node(dev); > - if (!dn) > - continue; > - > - edev = of_node_to_eeh_dev(dn); > - if (!edev) > - continue; > - > - pci_dev_get(dev); /* matching put is in eeh_remove_device() */ > - dev->dev.archdata.edev = edev; > - edev->pdev = dev; > - > - eeh_sysfs_add_device(dev); > - } > - > -#ifdef DEBUG > - /* Verify tree built up above, echo back the list of addrs. */ > - eeh_addr_cache_print(&pci_io_addr_cache_root); > -#endif > -} > - > diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c > deleted file mode 100644 > index 1efa28f..0000000 > --- a/arch/powerpc/platforms/pseries/eeh_dev.c > +++ /dev/null > @@ -1,112 +0,0 @@ > -/* > - * The file intends to implement dynamic creation of EEH device, which will > - * be bound with OF node and PCI device simutaneously. The EEH devices would > - * be foundamental information for EEH core components to work proerly. Besides, > - * We have to support multiple situations where dynamic creation of EEH device > - * is required: > - * > - * 1) Before PCI emunation starts, we need create EEH devices according to the > - * PCI sensitive OF nodes. > - * 2) When PCI emunation is done, we need do the binding between PCI device and > - * the associated EEH device. > - * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device > - * will be created while PCI sensitive OF node is detected from DR. > - * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If > - * PHB is newly inserted, we also need create EEH devices accordingly. > - * > - * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. > - * > - * 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 > - */ > - > -#include > -#include > -#include > -#include > -#include > -#include > - > -#include > -#include > - > -/** > - * eeh_dev_init - Create EEH device according to OF node > - * @dn: device node > - * @data: PHB > - * > - * It will create EEH device according to the given OF node. The function > - * might be called by PCI emunation, DR, PHB hotplug. > - */ > -void *eeh_dev_init(struct device_node *dn, void *data) > -{ > - struct pci_controller *phb = data; > - struct eeh_dev *edev; > - > - /* Allocate EEH device */ > - edev = kzalloc(sizeof(*edev), GFP_KERNEL); > - if (!edev) { > - pr_warning("%s: out of memory\n", __func__); > - return NULL; > - } > - > - /* Associate EEH device with OF node */ > - PCI_DN(dn)->edev = edev; > - edev->dn = dn; > - edev->phb = phb; > - INIT_LIST_HEAD(&edev->list); > - > - return NULL; > -} > - > -/** > - * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB > - * @phb: PHB > - * > - * Scan the PHB OF node and its child association, then create the > - * EEH devices accordingly > - */ > -void eeh_dev_phb_init_dynamic(struct pci_controller *phb) > -{ > - struct device_node *dn = phb->dn; > - > - /* EEH PE for PHB */ > - eeh_phb_pe_create(phb); > - > - /* EEH device for PHB */ > - eeh_dev_init(dn, phb); > - > - /* EEH devices for children OF nodes */ > - traverse_pci_devices(dn, eeh_dev_init, phb); > -} > - > -/** > - * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs > - * > - * Scan all the existing PHBs and create EEH devices for their OF > - * nodes and their children OF nodes > - */ > -static int __init eeh_dev_phb_init(void) > -{ > - struct pci_controller *phb, *tmp; > - > - list_for_each_entry_safe(phb, tmp, &hose_list, list_node) > - eeh_dev_phb_init_dynamic(phb); > - > - pr_info("EEH: devices created\n"); > - > - return 0; > -} > - > -core_initcall(eeh_dev_phb_init); > diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c > deleted file mode 100644 > index a3fefb6..0000000 > --- a/arch/powerpc/platforms/pseries/eeh_driver.c > +++ /dev/null > @@ -1,552 +0,0 @@ > -/* > - * PCI Error Recovery Driver for RPA-compliant PPC64 platform. > - * Copyright IBM Corp. 2004 2005 > - * Copyright Linas Vepstas 2004, 2005 > - * > - * All rights reserved. > - * > - * 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, GOOD TITLE or > - * NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA. > - * > - * Send comments and feedback to Linas Vepstas > - */ > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > - > -/** > - * eeh_pcid_name - Retrieve name of PCI device driver > - * @pdev: PCI device > - * > - * This routine is used to retrieve the name of PCI device driver > - * if that's valid. > - */ > -static inline const char *eeh_pcid_name(struct pci_dev *pdev) > -{ > - if (pdev && pdev->dev.driver) > - return pdev->dev.driver->name; > - return ""; > -} > - > -/** > - * eeh_pcid_get - Get the PCI device driver > - * @pdev: PCI device > - * > - * The function is used to retrieve the PCI device driver for > - * the indicated PCI device. Besides, we will increase the reference > - * of the PCI device driver to prevent that being unloaded on > - * the fly. Otherwise, kernel crash would be seen. > - */ > -static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev) > -{ > - if (!pdev || !pdev->driver) > - return NULL; > - > - if (!try_module_get(pdev->driver->driver.owner)) > - return NULL; > - > - return pdev->driver; > -} > - > -/** > - * eeh_pcid_put - Dereference on the PCI device driver > - * @pdev: PCI device > - * > - * The function is called to do dereference on the PCI device > - * driver of the indicated PCI device. > - */ > -static inline void eeh_pcid_put(struct pci_dev *pdev) > -{ > - if (!pdev || !pdev->driver) > - return; > - > - module_put(pdev->driver->driver.owner); > -} > - > -#if 0 > -static void print_device_node_tree(struct pci_dn *pdn, int dent) > -{ > - int i; > - struct device_node *pc; > - > - if (!pdn) > - return; > - for (i = 0; i < dent; i++) > - printk(" "); > - printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n", > - pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr, > - pdn->eeh_pe_config_addr, pdn->node->full_name); > - dent += 3; > - pc = pdn->node->child; > - while (pc) { > - print_device_node_tree(PCI_DN(pc), dent); > - pc = pc->sibling; > - } > -} > -#endif > - > -/** > - * eeh_disable_irq - Disable interrupt for the recovering device > - * @dev: PCI device > - * > - * This routine must be called when reporting temporary or permanent > - * error to the particular PCI device to disable interrupt of that > - * device. If the device has enabled MSI or MSI-X interrupt, we needn't > - * do real work because EEH should freeze DMA transfers for those PCI > - * devices encountering EEH errors, which includes MSI or MSI-X. > - */ > -static void eeh_disable_irq(struct pci_dev *dev) > -{ > - struct eeh_dev *edev = pci_dev_to_eeh_dev(dev); > - > - /* Don't disable MSI and MSI-X interrupts. They are > - * effectively disabled by the DMA Stopped state > - * when an EEH error occurs. > - */ > - if (dev->msi_enabled || dev->msix_enabled) > - return; > - > - if (!irq_has_action(dev->irq)) > - return; > - > - edev->mode |= EEH_DEV_IRQ_DISABLED; > - disable_irq_nosync(dev->irq); > -} > - > -/** > - * eeh_enable_irq - Enable interrupt for the recovering device > - * @dev: PCI device > - * > - * This routine must be called to enable interrupt while failed > - * device could be resumed. > - */ > -static void eeh_enable_irq(struct pci_dev *dev) > -{ > - struct eeh_dev *edev = pci_dev_to_eeh_dev(dev); > - > - if ((edev->mode) & EEH_DEV_IRQ_DISABLED) { > - edev->mode &= ~EEH_DEV_IRQ_DISABLED; > - enable_irq(dev->irq); > - } > -} > - > -/** > - * eeh_report_error - Report pci error to each device driver > - * @data: eeh device > - * @userdata: return value > - * > - * Report an EEH error to each device driver, collect up and > - * merge the device driver responses. Cumulative response > - * passed back in "userdata". > - */ > -static void *eeh_report_error(void *data, void *userdata) > -{ > - struct eeh_dev *edev = (struct eeh_dev *)data; > - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > - enum pci_ers_result rc, *res = userdata; > - struct pci_driver *driver; > - > - /* We might not have the associated PCI device, > - * then we should continue for next one. > - */ > - if (!dev) return NULL; > - dev->error_state = pci_channel_io_frozen; > - > - driver = eeh_pcid_get(dev); > - if (!driver) return NULL; > - > - eeh_disable_irq(dev); > - > - if (!driver->err_handler || > - !driver->err_handler->error_detected) { > - eeh_pcid_put(dev); > - return NULL; > - } > - > - rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen); > - > - /* A driver that needs a reset trumps all others */ > - if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; > - if (*res == PCI_ERS_RESULT_NONE) *res = rc; > - > - eeh_pcid_put(dev); > - return NULL; > -} > - > -/** > - * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled > - * @data: eeh device > - * @userdata: return value > - * > - * Tells each device driver that IO ports, MMIO and config space I/O > - * are now enabled. Collects up and merges the device driver responses. > - * Cumulative response passed back in "userdata". > - */ > -static void *eeh_report_mmio_enabled(void *data, void *userdata) > -{ > - struct eeh_dev *edev = (struct eeh_dev *)data; > - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > - enum pci_ers_result rc, *res = userdata; > - struct pci_driver *driver; > - > - driver = eeh_pcid_get(dev); > - if (!driver) return NULL; > - > - if (!driver->err_handler || > - !driver->err_handler->mmio_enabled) { > - eeh_pcid_put(dev); > - return NULL; > - } > - > - rc = driver->err_handler->mmio_enabled(dev); > - > - /* A driver that needs a reset trumps all others */ > - if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; > - if (*res == PCI_ERS_RESULT_NONE) *res = rc; > - > - eeh_pcid_put(dev); > - return NULL; > -} > - > -/** > - * eeh_report_reset - Tell device that slot has been reset > - * @data: eeh device > - * @userdata: return value > - * > - * This routine must be called while EEH tries to reset particular > - * PCI device so that the associated PCI device driver could take > - * some actions, usually to save data the driver needs so that the > - * driver can work again while the device is recovered. > - */ > -static void *eeh_report_reset(void *data, void *userdata) > -{ > - struct eeh_dev *edev = (struct eeh_dev *)data; > - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > - enum pci_ers_result rc, *res = userdata; > - struct pci_driver *driver; > - > - if (!dev) return NULL; > - dev->error_state = pci_channel_io_normal; > - > - driver = eeh_pcid_get(dev); > - if (!driver) return NULL; > - > - eeh_enable_irq(dev); > - > - if (!driver->err_handler || > - !driver->err_handler->slot_reset) { > - eeh_pcid_put(dev); > - return NULL; > - } > - > - rc = driver->err_handler->slot_reset(dev); > - if ((*res == PCI_ERS_RESULT_NONE) || > - (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc; > - if (*res == PCI_ERS_RESULT_DISCONNECT && > - rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; > - > - eeh_pcid_put(dev); > - return NULL; > -} > - > -/** > - * eeh_report_resume - Tell device to resume normal operations > - * @data: eeh device > - * @userdata: return value > - * > - * This routine must be called to notify the device driver that it > - * could resume so that the device driver can do some initialization > - * to make the recovered device work again. > - */ > -static void *eeh_report_resume(void *data, void *userdata) > -{ > - struct eeh_dev *edev = (struct eeh_dev *)data; > - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > - struct pci_driver *driver; > - > - if (!dev) return NULL; > - dev->error_state = pci_channel_io_normal; > - > - driver = eeh_pcid_get(dev); > - if (!driver) return NULL; > - > - eeh_enable_irq(dev); > - > - if (!driver->err_handler || > - !driver->err_handler->resume) { > - eeh_pcid_put(dev); > - return NULL; > - } > - > - driver->err_handler->resume(dev); > - > - eeh_pcid_put(dev); > - return NULL; > -} > - > -/** > - * eeh_report_failure - Tell device driver that device is dead. > - * @data: eeh device > - * @userdata: return value > - * > - * This informs the device driver that the device is permanently > - * dead, and that no further recovery attempts will be made on it. > - */ > -static void *eeh_report_failure(void *data, void *userdata) > -{ > - struct eeh_dev *edev = (struct eeh_dev *)data; > - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); > - struct pci_driver *driver; > - > - if (!dev) return NULL; > - dev->error_state = pci_channel_io_perm_failure; > - > - driver = eeh_pcid_get(dev); > - if (!driver) return NULL; > - > - eeh_disable_irq(dev); > - > - if (!driver->err_handler || > - !driver->err_handler->error_detected) { > - eeh_pcid_put(dev); > - return NULL; > - } > - > - driver->err_handler->error_detected(dev, pci_channel_io_perm_failure); > - > - eeh_pcid_put(dev); > - return NULL; > -} > - > -/** > - * eeh_reset_device - Perform actual reset of a pci slot > - * @pe: EEH PE > - * @bus: PCI bus corresponding to the isolcated slot > - * > - * This routine must be called to do reset on the indicated PE. > - * During the reset, udev might be invoked because those affected > - * PCI devices will be removed and then added. > - */ > -static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus) > -{ > - int cnt, rc; > - > - /* pcibios will clear the counter; save the value */ > - cnt = pe->freeze_count; > - > - /* > - * We don't remove the corresponding PE instances because > - * we need the information afterwords. The attached EEH > - * devices are expected to be attached soon when calling > - * into pcibios_add_pci_devices(). > - */ > - if (bus) > - __pcibios_remove_pci_devices(bus, 0); > - > - /* Reset the pci controller. (Asserts RST#; resets config space). > - * Reconfigure bridges and devices. Don't try to bring the system > - * up if the reset failed for some reason. > - */ > - rc = eeh_reset_pe(pe); > - if (rc) > - return rc; > - > - /* Restore PE */ > - eeh_ops->configure_bridge(pe); > - eeh_pe_restore_bars(pe); > - > - /* Give the system 5 seconds to finish running the user-space > - * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes, > - * this is a hack, but if we don't do this, and try to bring > - * the device up before the scripts have taken it down, > - * potentially weird things happen. > - */ > - if (bus) { > - ssleep(5); > - pcibios_add_pci_devices(bus); > - } > - pe->freeze_count = cnt; > - > - return 0; > -} > - > -/* The longest amount of time to wait for a pci device > - * to come back on line, in seconds. > - */ > -#define MAX_WAIT_FOR_RECOVERY 150 > - > -/** > - * eeh_handle_event - Reset a PCI device after hard lockup. > - * @pe: EEH PE > - * > - * While PHB detects address or data parity errors on particular PCI > - * slot, the associated PE will be frozen. Besides, DMA's occurring > - * to wild addresses (which usually happen due to bugs in device > - * drivers or in PCI adapter firmware) can cause EEH error. #SERR, > - * #PERR or other misc PCI-related errors also can trigger EEH errors. > - * > - * Recovery process consists of unplugging the device driver (which > - * generated hotplug events to userspace), then issuing a PCI #RST to > - * the device, then reconfiguring the PCI config space for all bridges > - * & devices under this slot, and then finally restarting the device > - * drivers (which cause a second set of hotplug events to go out to > - * userspace). > - */ > -void eeh_handle_event(struct eeh_pe *pe) > -{ > - struct pci_bus *frozen_bus; > - int rc = 0; > - enum pci_ers_result result = PCI_ERS_RESULT_NONE; > - > - frozen_bus = eeh_pe_bus_get(pe); > - if (!frozen_bus) { > - pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n", > - __func__, pe->phb->global_number, pe->addr); > - return; > - } > - > - pe->freeze_count++; > - if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES) > - goto excess_failures; > - pr_warning("EEH: This PCI device has failed %d times in the last hour\n", > - pe->freeze_count); > - > - /* Walk the various device drivers attached to this slot through > - * a reset sequence, giving each an opportunity to do what it needs > - * to accomplish the reset. Each child gets a report of the > - * status ... if any child can't handle the reset, then the entire > - * slot is dlpar removed and added. > - */ > - eeh_pe_dev_traverse(pe, eeh_report_error, &result); > - > - /* Get the current PCI slot state. This can take a long time, > - * sometimes over 3 seconds for certain systems. > - */ > - rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000); > - if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { > - printk(KERN_WARNING "EEH: Permanent failure\n"); > - goto hard_fail; > - } > - > - /* Since rtas may enable MMIO when posting the error log, > - * don't post the error log until after all dev drivers > - * have been informed. > - */ > - eeh_slot_error_detail(pe, EEH_LOG_TEMP); > - > - /* If all device drivers were EEH-unaware, then shut > - * down all of the device drivers, and hope they > - * go down willingly, without panicing the system. > - */ > - if (result == PCI_ERS_RESULT_NONE) { > - rc = eeh_reset_device(pe, frozen_bus); > - if (rc) { > - printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc); > - goto hard_fail; > - } > - } > - > - /* If all devices reported they can proceed, then re-enable MMIO */ > - if (result == PCI_ERS_RESULT_CAN_RECOVER) { > - rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); > - > - if (rc < 0) > - goto hard_fail; > - if (rc) { > - result = PCI_ERS_RESULT_NEED_RESET; > - } else { > - result = PCI_ERS_RESULT_NONE; > - eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result); > - } > - } > - > - /* If all devices reported they can proceed, then re-enable DMA */ > - if (result == PCI_ERS_RESULT_CAN_RECOVER) { > - rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA); > - > - if (rc < 0) > - goto hard_fail; > - if (rc) > - result = PCI_ERS_RESULT_NEED_RESET; > - else > - result = PCI_ERS_RESULT_RECOVERED; > - } > - > - /* If any device has a hard failure, then shut off everything. */ > - if (result == PCI_ERS_RESULT_DISCONNECT) { > - printk(KERN_WARNING "EEH: Device driver gave up\n"); > - goto hard_fail; > - } > - > - /* If any device called out for a reset, then reset the slot */ > - if (result == PCI_ERS_RESULT_NEED_RESET) { > - rc = eeh_reset_device(pe, NULL); > - if (rc) { > - printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc); > - goto hard_fail; > - } > - result = PCI_ERS_RESULT_NONE; > - eeh_pe_dev_traverse(pe, eeh_report_reset, &result); > - } > - > - /* All devices should claim they have recovered by now. */ > - if ((result != PCI_ERS_RESULT_RECOVERED) && > - (result != PCI_ERS_RESULT_NONE)) { > - printk(KERN_WARNING "EEH: Not recovered\n"); > - goto hard_fail; > - } > - > - /* Tell all device drivers that they can resume operations */ > - eeh_pe_dev_traverse(pe, eeh_report_resume, NULL); > - > - return; > - > -excess_failures: > - /* > - * About 90% of all real-life EEH failures in the field > - * are due to poorly seated PCI cards. Only 10% or so are > - * due to actual, failed cards. > - */ > - pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n" > - "last hour and has been permanently disabled.\n" > - "Please try reseating or replacing it.\n", > - pe->phb->global_number, pe->addr, > - pe->freeze_count); > - goto perm_error; > - > -hard_fail: > - pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n" > - "Please try reseating or replacing it\n", > - pe->phb->global_number, pe->addr); > - > -perm_error: > - eeh_slot_error_detail(pe, EEH_LOG_PERM); > - > - /* Notify all devices that they're about to go down. */ > - eeh_pe_dev_traverse(pe, eeh_report_failure, NULL); > - > - /* Shut down the device drivers for good. */ > - if (frozen_bus) > - pcibios_remove_pci_devices(frozen_bus); > -} > - > diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c > deleted file mode 100644 > index 185bedd..0000000 > --- a/arch/powerpc/platforms/pseries/eeh_event.c > +++ /dev/null > @@ -1,142 +0,0 @@ > -/* > - * 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 > - * > - * Copyright (c) 2005 Linas Vepstas > - */ > - > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > - > -/** Overview: > - * EEH error states may be detected within exception handlers; > - * however, the recovery processing needs to occur asynchronously > - * in a normal kernel context and not an interrupt context. > - * This pair of routines creates an event and queues it onto a > - * work-queue, where a worker thread can drive recovery. > - */ > - > -/* EEH event workqueue setup. */ > -static DEFINE_SPINLOCK(eeh_eventlist_lock); > -LIST_HEAD(eeh_eventlist); > -static void eeh_thread_launcher(struct work_struct *); > -DECLARE_WORK(eeh_event_wq, eeh_thread_launcher); > - > -/* Serialize reset sequences for a given pci device */ > -DEFINE_MUTEX(eeh_event_mutex); > - > -/** > - * eeh_event_handler - Dispatch EEH events. > - * @dummy - unused > - * > - * The detection of a frozen slot can occur inside an interrupt, > - * where it can be hard to do anything about it. The goal of this > - * routine is to pull these detection events out of the context > - * of the interrupt handler, and re-dispatch them for processing > - * at a later time in a normal context. > - */ > -static int eeh_event_handler(void * dummy) > -{ > - unsigned long flags; > - struct eeh_event *event; > - struct eeh_pe *pe; > - > - spin_lock_irqsave(&eeh_eventlist_lock, flags); > - event = NULL; > - > - /* Unqueue the event, get ready to process. */ > - if (!list_empty(&eeh_eventlist)) { > - event = list_entry(eeh_eventlist.next, struct eeh_event, list); > - list_del(&event->list); > - } > - spin_unlock_irqrestore(&eeh_eventlist_lock, flags); > - > - if (event == NULL) > - return 0; > - > - /* Serialize processing of EEH events */ > - mutex_lock(&eeh_event_mutex); > - pe = event->pe; > - eeh_pe_state_mark(pe, EEH_PE_RECOVERING); > - pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n", > - pe->phb->global_number, pe->addr); > - > - set_current_state(TASK_INTERRUPTIBLE); /* Don't add to load average */ > - eeh_handle_event(pe); > - eeh_pe_state_clear(pe, EEH_PE_RECOVERING); > - > - kfree(event); > - mutex_unlock(&eeh_event_mutex); > - > - /* If there are no new errors after an hour, clear the counter. */ > - if (pe && pe->freeze_count > 0) { > - msleep_interruptible(3600*1000); > - if (pe->freeze_count > 0) > - pe->freeze_count--; > - > - } > - > - return 0; > -} > - > -/** > - * eeh_thread_launcher - Start kernel thread to handle EEH events > - * @dummy - unused > - * > - * This routine is called to start the kernel thread for processing > - * EEH event. > - */ > -static void eeh_thread_launcher(struct work_struct *dummy) > -{ > - if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd"))) > - printk(KERN_ERR "Failed to start EEH daemon\n"); > -} > - > -/** > - * eeh_send_failure_event - Generate a PCI error event > - * @pe: EEH PE > - * > - * This routine can be called within an interrupt context; > - * the actual event will be delivered in a normal context > - * (from a workqueue). > - */ > -int eeh_send_failure_event(struct eeh_pe *pe) > -{ > - unsigned long flags; > - struct eeh_event *event; > - > - event = kzalloc(sizeof(*event), GFP_ATOMIC); > - if (!event) { > - pr_err("EEH: out of memory, event not handled\n"); > - return -ENOMEM; > - } > - event->pe = pe; > - > - /* We may or may not be called in an interrupt context */ > - spin_lock_irqsave(&eeh_eventlist_lock, flags); > - list_add(&event->list, &eeh_eventlist); > - spin_unlock_irqrestore(&eeh_eventlist_lock, flags); > - > - schedule_work(&eeh_event_wq); > - > - return 0; > -} > diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c > deleted file mode 100644 > index 9d4a9e8..0000000 > --- a/arch/powerpc/platforms/pseries/eeh_pe.c > +++ /dev/null > @@ -1,653 +0,0 @@ > -/* > - * The file intends to implement PE based on the information from > - * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device. > - * All the PEs should be organized as hierarchy tree. The first level > - * of the tree will be associated to existing PHBs since the particular > - * PE is only meaningful in one PHB domain. > - * > - * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. > - * > - * 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 > - */ > - > -#include > -#include > -#include > -#include > -#include > -#include > - > -#include > -#include > - > -static LIST_HEAD(eeh_phb_pe); > - > -/** > - * eeh_pe_alloc - Allocate PE > - * @phb: PCI controller > - * @type: PE type > - * > - * Allocate PE instance dynamically. > - */ > -static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type) > -{ > - struct eeh_pe *pe; > - > - /* Allocate PHB PE */ > - pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL); > - if (!pe) return NULL; > - > - /* Initialize PHB PE */ > - pe->type = type; > - pe->phb = phb; > - INIT_LIST_HEAD(&pe->child_list); > - INIT_LIST_HEAD(&pe->child); > - INIT_LIST_HEAD(&pe->edevs); > - > - return pe; > -} > - > -/** > - * eeh_phb_pe_create - Create PHB PE > - * @phb: PCI controller > - * > - * The function should be called while the PHB is detected during > - * system boot or PCI hotplug in order to create PHB PE. > - */ > -int eeh_phb_pe_create(struct pci_controller *phb) > -{ > - struct eeh_pe *pe; > - > - /* Allocate PHB PE */ > - pe = eeh_pe_alloc(phb, EEH_PE_PHB); > - if (!pe) { > - pr_err("%s: out of memory!\n", __func__); > - return -ENOMEM; > - } > - > - /* Put it into the list */ > - eeh_lock(); > - list_add_tail(&pe->child, &eeh_phb_pe); > - eeh_unlock(); > - > - pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number); > - > - return 0; > -} > - > -/** > - * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB > - * @phb: PCI controller > - * > - * The overall PEs form hierarchy tree. The first layer of the > - * hierarchy tree is composed of PHB PEs. The function is used > - * to retrieve the corresponding PHB PE according to the given PHB. > - */ > -static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) > -{ > - struct eeh_pe *pe; > - > - list_for_each_entry(pe, &eeh_phb_pe, child) { > - /* > - * Actually, we needn't check the type since > - * the PE for PHB has been determined when that > - * was created. > - */ > - if ((pe->type & EEH_PE_PHB) && pe->phb == phb) > - return pe; > - } > - > - return NULL; > -} > - > -/** > - * eeh_pe_next - Retrieve the next PE in the tree > - * @pe: current PE > - * @root: root PE > - * > - * The function is used to retrieve the next PE in the > - * hierarchy PE tree. > - */ > -static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, > - struct eeh_pe *root) > -{ > - struct list_head *next = pe->child_list.next; > - > - if (next == &pe->child_list) { > - while (1) { > - if (pe == root) > - return NULL; > - next = pe->child.next; > - if (next != &pe->parent->child_list) > - break; > - pe = pe->parent; > - } > - } > - > - return list_entry(next, struct eeh_pe, child); > -} > - > -/** > - * eeh_pe_traverse - Traverse PEs in the specified PHB > - * @root: root PE > - * @fn: callback > - * @flag: extra parameter to callback > - * > - * The function is used to traverse the specified PE and its > - * child PEs. The traversing is to be terminated once the > - * callback returns something other than NULL, or no more PEs > - * to be traversed. > - */ > -static void *eeh_pe_traverse(struct eeh_pe *root, > - eeh_traverse_func fn, void *flag) > -{ > - struct eeh_pe *pe; > - void *ret; > - > - for (pe = root; pe; pe = eeh_pe_next(pe, root)) { > - ret = fn(pe, flag); > - if (ret) return ret; > - } > - > - return NULL; > -} > - > -/** > - * eeh_pe_dev_traverse - Traverse the devices from the PE > - * @root: EEH PE > - * @fn: function callback > - * @flag: extra parameter to callback > - * > - * The function is used to traverse the devices of the specified > - * PE and its child PEs. > - */ > -void *eeh_pe_dev_traverse(struct eeh_pe *root, > - eeh_traverse_func fn, void *flag) > -{ > - struct eeh_pe *pe; > - struct eeh_dev *edev; > - void *ret; > - > - if (!root) { > - pr_warning("%s: Invalid PE %p\n", __func__, root); > - return NULL; > - } > - > - eeh_lock(); > - > - /* Traverse root PE */ > - for (pe = root; pe; pe = eeh_pe_next(pe, root)) { > - eeh_pe_for_each_dev(pe, edev) { > - ret = fn(edev, flag); > - if (ret) { > - eeh_unlock(); > - return ret; > - } > - } > - } > - > - eeh_unlock(); > - > - return NULL; > -} > - > -/** > - * __eeh_pe_get - Check the PE address > - * @data: EEH PE > - * @flag: EEH device > - * > - * For one particular PE, it can be identified by PE address > - * or tranditional BDF address. BDF address is composed of > - * Bus/Device/Function number. The extra data referred by flag > - * indicates which type of address should be used. > - */ > -static void *__eeh_pe_get(void *data, void *flag) > -{ > - struct eeh_pe *pe = (struct eeh_pe *)data; > - struct eeh_dev *edev = (struct eeh_dev *)flag; > - > - /* Unexpected PHB PE */ > - if (pe->type & EEH_PE_PHB) > - return NULL; > - > - /* We prefer PE address */ > - if (edev->pe_config_addr && > - (edev->pe_config_addr == pe->addr)) > - return pe; > - > - /* Try BDF address */ > - if (edev->pe_config_addr && > - (edev->config_addr == pe->config_addr)) > - return pe; > - > - return NULL; > -} > - > -/** > - * eeh_pe_get - Search PE based on the given address > - * @edev: EEH device > - * > - * Search the corresponding PE based on the specified address which > - * is included in the eeh device. The function is used to check if > - * the associated PE has been created against the PE address. It's > - * notable that the PE address has 2 format: traditional PE address > - * which is composed of PCI bus/device/function number, or unified > - * PE address. > - */ > -static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) > -{ > - struct eeh_pe *root = eeh_phb_pe_get(edev->phb); > - struct eeh_pe *pe; > - > - pe = eeh_pe_traverse(root, __eeh_pe_get, edev); > - > - return pe; > -} > - > -/** > - * eeh_pe_get_parent - Retrieve the parent PE > - * @edev: EEH device > - * > - * The whole PEs existing in the system are organized as hierarchy > - * tree. The function is used to retrieve the parent PE according > - * to the parent EEH device. > - */ > -static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) > -{ > - struct device_node *dn; > - struct eeh_dev *parent; > - > - /* > - * It might have the case for the indirect parent > - * EEH device already having associated PE, but > - * the direct parent EEH device doesn't have yet. > - */ > - dn = edev->dn->parent; > - while (dn) { > - /* We're poking out of PCI territory */ > - if (!PCI_DN(dn)) return NULL; > - > - parent = of_node_to_eeh_dev(dn); > - /* We're poking out of PCI territory */ > - if (!parent) return NULL; > - > - if (parent->pe) > - return parent->pe; > - > - dn = dn->parent; > - } > - > - return NULL; > -} > - > -/** > - * eeh_add_to_parent_pe - Add EEH device to parent PE > - * @edev: EEH device > - * > - * Add EEH device to the parent PE. If the parent PE already > - * exists, the PE type will be changed to EEH_PE_BUS. Otherwise, > - * we have to create new PE to hold the EEH device and the new > - * PE will be linked to its parent PE as well. > - */ > -int eeh_add_to_parent_pe(struct eeh_dev *edev) > -{ > - struct eeh_pe *pe, *parent; > - > - eeh_lock(); > - > - /* > - * Search the PE has been existing or not according > - * to the PE address. If that has been existing, the > - * PE should be composed of PCI bus and its subordinate > - * components. > - */ > - pe = eeh_pe_get(edev); > - if (pe && !(pe->type & EEH_PE_INVALID)) { > - if (!edev->pe_config_addr) { > - eeh_unlock(); > - pr_err("%s: PE with addr 0x%x already exists\n", > - __func__, edev->config_addr); > - return -EEXIST; > - } > - > - /* Mark the PE as type of PCI bus */ > - pe->type = EEH_PE_BUS; > - edev->pe = pe; > - > - /* Put the edev to PE */ > - list_add_tail(&edev->list, &pe->edevs); > - eeh_unlock(); > - pr_debug("EEH: Add %s to Bus PE#%x\n", > - edev->dn->full_name, pe->addr); > - > - return 0; > - } else if (pe && (pe->type & EEH_PE_INVALID)) { > - list_add_tail(&edev->list, &pe->edevs); > - edev->pe = pe; > - /* > - * We're running to here because of PCI hotplug caused by > - * EEH recovery. We need clear EEH_PE_INVALID until the top. > - */ > - parent = pe; > - while (parent) { > - if (!(parent->type & EEH_PE_INVALID)) > - break; > - parent->type &= ~EEH_PE_INVALID; > - parent = parent->parent; > - } > - eeh_unlock(); > - pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", > - edev->dn->full_name, pe->addr, pe->parent->addr); > - > - return 0; > - } > - > - /* Create a new EEH PE */ > - pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE); > - if (!pe) { > - eeh_unlock(); > - pr_err("%s: out of memory!\n", __func__); > - return -ENOMEM; > - } > - pe->addr = edev->pe_config_addr; > - pe->config_addr = edev->config_addr; > - > - /* > - * Put the new EEH PE into hierarchy tree. If the parent > - * can't be found, the newly created PE will be attached > - * to PHB directly. Otherwise, we have to associate the > - * PE with its parent. > - */ > - parent = eeh_pe_get_parent(edev); > - if (!parent) { > - parent = eeh_phb_pe_get(edev->phb); > - if (!parent) { > - eeh_unlock(); > - pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", > - __func__, edev->phb->global_number); > - edev->pe = NULL; > - kfree(pe); > - return -EEXIST; > - } > - } > - pe->parent = parent; > - > - /* > - * Put the newly created PE into the child list and > - * link the EEH device accordingly. > - */ > - list_add_tail(&pe->child, &parent->child_list); > - list_add_tail(&edev->list, &pe->edevs); > - edev->pe = pe; > - eeh_unlock(); > - pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", > - edev->dn->full_name, pe->addr, pe->parent->addr); > - > - return 0; > -} > - > -/** > - * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE > - * @edev: EEH device > - * @purge_pe: remove PE or not > - * > - * The PE hierarchy tree might be changed when doing PCI hotplug. > - * Also, the PCI devices or buses could be removed from the system > - * during EEH recovery. So we have to call the function remove the > - * corresponding PE accordingly if necessary. > - */ > -int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) > -{ > - struct eeh_pe *pe, *parent, *child; > - int cnt; > - > - if (!edev->pe) { > - pr_warning("%s: No PE found for EEH device %s\n", > - __func__, edev->dn->full_name); > - return -EEXIST; > - } > - > - eeh_lock(); > - > - /* Remove the EEH device */ > - pe = edev->pe; > - edev->pe = NULL; > - list_del(&edev->list); > - > - /* > - * Check if the parent PE includes any EEH devices. > - * If not, we should delete that. Also, we should > - * delete the parent PE if it doesn't have associated > - * child PEs and EEH devices. > - */ > - while (1) { > - parent = pe->parent; > - if (pe->type & EEH_PE_PHB) > - break; > - > - if (purge_pe) { > - if (list_empty(&pe->edevs) && > - list_empty(&pe->child_list)) { > - list_del(&pe->child); > - kfree(pe); > - } else { > - break; > - } > - } else { > - if (list_empty(&pe->edevs)) { > - cnt = 0; > - list_for_each_entry(child, &pe->child_list, child) { > - if (!(child->type & EEH_PE_INVALID)) { > - cnt++; > - break; > - } > - } > - > - if (!cnt) > - pe->type |= EEH_PE_INVALID; > - else > - break; > - } > - } > - > - pe = parent; > - } > - > - eeh_unlock(); > - > - return 0; > -} > - > -/** > - * __eeh_pe_state_mark - Mark the state for the PE > - * @data: EEH PE > - * @flag: state > - * > - * The function is used to mark the indicated state for the given > - * PE. Also, the associated PCI devices will be put into IO frozen > - * state as well. > - */ > -static void *__eeh_pe_state_mark(void *data, void *flag) > -{ > - struct eeh_pe *pe = (struct eeh_pe *)data; > - int state = *((int *)flag); > - struct eeh_dev *tmp; > - struct pci_dev *pdev; > - > - /* > - * Mark the PE with the indicated state. Also, > - * the associated PCI device will be put into > - * I/O frozen state to avoid I/O accesses from > - * the PCI device driver. > - */ > - pe->state |= state; > - eeh_pe_for_each_dev(pe, tmp) { > - pdev = eeh_dev_to_pci_dev(tmp); > - if (pdev) > - pdev->error_state = pci_channel_io_frozen; > - } > - > - return NULL; > -} > - > -/** > - * eeh_pe_state_mark - Mark specified state for PE and its associated device > - * @pe: EEH PE > - * > - * EEH error affects the current PE and its child PEs. The function > - * is used to mark appropriate state for the affected PEs and the > - * associated devices. > - */ > -void eeh_pe_state_mark(struct eeh_pe *pe, int state) > -{ > - eeh_lock(); > - eeh_pe_traverse(pe, __eeh_pe_state_mark, &state); > - eeh_unlock(); > -} > - > -/** > - * __eeh_pe_state_clear - Clear state for the PE > - * @data: EEH PE > - * @flag: state > - * > - * The function is used to clear the indicated state from the > - * given PE. Besides, we also clear the check count of the PE > - * as well. > - */ > -static void *__eeh_pe_state_clear(void *data, void *flag) > -{ > - struct eeh_pe *pe = (struct eeh_pe *)data; > - int state = *((int *)flag); > - > - pe->state &= ~state; > - pe->check_count = 0; > - > - return NULL; > -} > - > -/** > - * eeh_pe_state_clear - Clear state for the PE and its children > - * @pe: PE > - * @state: state to be cleared > - * > - * When the PE and its children has been recovered from error, > - * we need clear the error state for that. The function is used > - * for the purpose. > - */ > -void eeh_pe_state_clear(struct eeh_pe *pe, int state) > -{ > - eeh_lock(); > - eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); > - eeh_unlock(); > -} > - > -/** > - * eeh_restore_one_device_bars - Restore the Base Address Registers for one device > - * @data: EEH device > - * @flag: Unused > - * > - * 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 void *eeh_restore_one_device_bars(void *data, void *flag) > -{ > - int i; > - u32 cmd; > - struct eeh_dev *edev = (struct eeh_dev *)data; > - struct device_node *dn = eeh_dev_to_of_node(edev); > - > - for (i = 4; i < 10; i++) > - eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); > - /* 12 == Expansion ROM Address */ > - eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); > - > -#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) > -#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) > - > - eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, > - SAVED_BYTE(PCI_CACHE_LINE_SIZE)); > - eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, > - SAVED_BYTE(PCI_LATENCY_TIMER)); > - > - /* max latency, min grant, interrupt pin and line */ > - eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); > - > - /* > - * Restore PERR & SERR bits, some devices require it, > - * don't touch the other command bits > - */ > - eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); > - if (edev->config_space[1] & PCI_COMMAND_PARITY) > - cmd |= PCI_COMMAND_PARITY; > - else > - cmd &= ~PCI_COMMAND_PARITY; > - if (edev->config_space[1] & PCI_COMMAND_SERR) > - cmd |= PCI_COMMAND_SERR; > - else > - cmd &= ~PCI_COMMAND_SERR; > - eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); > - > - return NULL; > -} > - > -/** > - * eeh_pe_restore_bars - Restore the PCI config space info > - * @pe: EEH PE > - * > - * This routine performs a recursive walk to the children > - * of this device as well. > - */ > -void eeh_pe_restore_bars(struct eeh_pe *pe) > -{ > - /* > - * We needn't take the EEH lock since eeh_pe_dev_traverse() > - * will take that. > - */ > - eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL); > -} > - > -/** > - * eeh_pe_bus_get - Retrieve PCI bus according to the given PE > - * @pe: EEH PE > - * > - * Retrieve the PCI bus according to the given PE. Basically, > - * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the > - * primary PCI bus will be retrieved. The parent bus will be > - * returned for BUS PE. However, we don't have associated PCI > - * bus for DEVICE PE. > - */ > -struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe) > -{ > - struct pci_bus *bus = NULL; > - struct eeh_dev *edev; > - struct pci_dev *pdev; > - > - eeh_lock(); > - > - if (pe->type & EEH_PE_PHB) { > - bus = pe->phb->bus; > - } else if (pe->type & EEH_PE_BUS || > - pe->type & EEH_PE_DEVICE) { > - edev = list_first_entry(&pe->edevs, struct eeh_dev, list); > - pdev = eeh_dev_to_pci_dev(edev); > - if (pdev) > - bus = pdev->bus; > - } > - > - eeh_unlock(); > - > - return bus; > -} > diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c > deleted file mode 100644 > index d377083..0000000 > --- a/arch/powerpc/platforms/pseries/eeh_sysfs.c > +++ /dev/null > @@ -1,75 +0,0 @@ > -/* > - * Sysfs entries for PCI Error Recovery for PAPR-compliant platform. > - * Copyright IBM Corporation 2007 > - * Copyright Linas Vepstas 2007 > - * > - * All rights reserved. > - * > - * 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, GOOD TITLE or > - * NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA. > - * > - * Send comments and feedback to Linas Vepstas > - */ > -#include > -#include > -#include > -#include > - > -/** > - * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic > - * @_name: name of file in sysfs directory > - * @_memb: name of member in struct pci_dn to access > - * @_format: printf format for display > - * > - * All of the attributes look very similar, so just > - * auto-gen a cut-n-paste routine to display them. > - */ > -#define EEH_SHOW_ATTR(_name,_memb,_format) \ > -static ssize_t eeh_show_##_name(struct device *dev, \ > - struct device_attribute *attr, char *buf) \ > -{ \ > - struct pci_dev *pdev = to_pci_dev(dev); \ > - struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \ > - \ > - if (!edev) \ > - return 0; \ > - \ > - return sprintf(buf, _format "\n", edev->_memb); \ > -} \ > -static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL); > - > -EEH_SHOW_ATTR(eeh_mode, mode, "0x%x"); > -EEH_SHOW_ATTR(eeh_config_addr, config_addr, "0x%x"); > -EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x"); > - > -void eeh_sysfs_add_device(struct pci_dev *pdev) > -{ > - int rc=0; > - > - rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode); > - rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr); > - rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); > - > - if (rc) > - printk(KERN_WARNING "EEH: Unable to create sysfs entries\n"); > -} > - > -void eeh_sysfs_remove_device(struct pci_dev *pdev) > -{ > - device_remove_file(&pdev->dev, &dev_attr_eeh_mode); > - device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr); > - device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); > -} > - > diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c > index c91b22b..efe6137 100644 > --- a/arch/powerpc/platforms/pseries/pci_dlpar.c > +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c > @@ -64,91 +64,6 @@ pcibios_find_pci_bus(struct device_node *dn) > } > EXPORT_SYMBOL_GPL(pcibios_find_pci_bus); > > -/** > - * __pcibios_remove_pci_devices - remove all devices under this bus > - * @bus: the indicated PCI bus > - * @purge_pe: destroy the PE on removal of PCI devices > - * > - * Remove all of the PCI devices under this bus both from the > - * linux pci device tree, and from the powerpc EEH address cache. > - * By default, the corresponding PE will be destroied during the > - * normal PCI hotplug path. For PCI hotplug during EEH recovery, > - * the corresponding PE won't be destroied and deallocated. > - */ > -void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe) > -{ > - struct pci_dev *dev, *tmp; > - struct pci_bus *child_bus; > - > - /* First go down child busses */ > - list_for_each_entry(child_bus, &bus->children, node) > - __pcibios_remove_pci_devices(child_bus, purge_pe); > - > - pr_debug("PCI: Removing devices on bus %04x:%02x\n", > - pci_domain_nr(bus), bus->number); > - list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { > - pr_debug(" * Removing %s...\n", pci_name(dev)); > - eeh_remove_bus_device(dev, purge_pe); > - pci_stop_and_remove_bus_device(dev); > - } > -} > - > -/** > - * pcibios_remove_pci_devices - remove all devices under this bus > - * > - * Remove all of the PCI devices under this bus both from the > - * linux pci device tree, and from the powerpc EEH address cache. > - */ > -void pcibios_remove_pci_devices(struct pci_bus *bus) > -{ > - __pcibios_remove_pci_devices(bus, 1); > -} > -EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); > - > -/** > - * pcibios_add_pci_devices - adds new pci devices to bus > - * > - * This routine will find and fixup new pci devices under > - * the indicated bus. This routine presumes that there > - * might already be some devices under this bridge, so > - * it carefully tries to add only new devices. (And that > - * is how this routine differs from other, similar pcibios > - * routines.) > - */ > -void pcibios_add_pci_devices(struct pci_bus * bus) > -{ > - int slotno, num, mode, pass, max; > - struct pci_dev *dev; > - struct device_node *dn = pci_bus_to_OF_node(bus); > - > - eeh_add_device_tree_early(dn); > - > - mode = PCI_PROBE_NORMAL; > - if (ppc_md.pci_probe_mode) > - mode = ppc_md.pci_probe_mode(bus); > - > - if (mode == PCI_PROBE_DEVTREE) { > - /* use ofdt-based probe */ > - of_rescan_bus(dn, bus); > - } else if (mode == PCI_PROBE_NORMAL) { > - /* use legacy probe */ > - slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); > - num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); > - if (!num) > - return; > - pcibios_setup_bus_devices(bus); > - max = bus->busn_res.start; > - for (pass=0; pass < 2; pass++) > - list_for_each_entry(dev, &bus->devices, bus_list) { > - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || > - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) > - max = pci_scan_bridge(bus, dev, max, pass); > - } > - } > - pcibios_finish_adding_to_bus(bus); > -} > -EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); > - > struct pci_controller *init_phb_dynamic(struct device_node *dn) > { > struct pci_controller *phb; > -- > 1.7.5.4 > > _______________________________________________ > Linuxppc-dev mailing list > Linuxppc-dev@lists.ozlabs.org > https://lists.ozlabs.org/listinfo/linuxppc-dev >