cregit-Linux how code gets into the kernel

Release 4.14 arch/powerpc/kernel/eeh.c

/*
 * 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 <linas@austin.ibm.com>
 */

#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/iommu.h>
#include <linux/proc_fs.h>
#include <linux/rbtree.h>
#include <linux/reboot.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/export.h>
#include <linux/of.h>

#include <linux/atomic.h>
#include <asm/debugfs.h>
#include <asm/eeh.h>
#include <asm/eeh_event.h>
#include <asm/io.h>
#include <asm/iommu.h>
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
#include <asm/rtas.h>
#include <asm/pte-walk.h>


/** Overview:
 *  EEH, or "Enhanced 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 (5*60*1000)

/*
 * EEH probe mode support, which is part of the flags,
 * 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_subsystem_flags;

EXPORT_SYMBOL(eeh_subsystem_flags);

/*
 * EEH allowed maximal frozen times. If one particular PE's
 * frozen count in last hour exceeds this limit, the PE will
 * be forced to be offline permanently.
 */

int eeh_max_freezes = 5;

/* Platform dependent EEH operations */

struct eeh_ops *eeh_ops = NULL;

/* Lock to avoid races due to multiple reports of an error */

DEFINE_RAW_SPINLOCK(confirm_error_lock);

EXPORT_SYMBOL_GPL(confirm_error_lock);

/* Lock to protect passed flags */
static DEFINE_MUTEX(eeh_dev_mutex);

/* 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 8192

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;


static int __init eeh_setup(char *str) { if (!strcmp(str, "off")) eeh_add_flag(EEH_FORCE_DISABLED); else if (!strcmp(str, "early_log")) eeh_add_flag(EEH_EARLY_DUMP_LOG); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan45100.00%3100.00%
Total45100.00%3100.00%

__setup("eeh=", eeh_setup); /* * This routine captures assorted PCI configuration space data * for the indicated PCI device, and puts them into a buffer * for RTAS error logging. */
static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) { struct pci_dn *pdn = eeh_dev_to_pdn(edev); u32 cfg; int cap, i; int n = 0, l = 0; char buffer[128]; n += scnprintf(buf+n, len-n, "%04x:%02x:%02x.%01x\n", pdn->phb->global_number, pdn->busno, PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); pr_warn("EEH: of node=%04x:%02x:%02x.%01x\n", pdn->phb->global_number, pdn->busno, PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); eeh_ops->read_config(pdn, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); pr_warn("EEH: PCI device/vendor: %08x\n", cfg); eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); pr_warn("EEH: PCI cmd/status register: %08x\n", cfg); /* Gather bridge-specific registers */ if (edev->mode & EEH_DEV_BRIDGE) { eeh_ops->read_config(pdn, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); pr_warn("EEH: Bridge secondary status: %04x\n", cfg); eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); pr_warn("EEH: Bridge control: %04x\n", cfg); } /* Dump out the PCI-X command and status regs */ cap = edev->pcix_cap; if (cap) { eeh_ops->read_config(pdn, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); pr_warn("EEH: PCI-X cmd: %08x\n", cfg); eeh_ops->read_config(pdn, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); pr_warn("EEH: PCI-X status: %08x\n", cfg); } /* If PCI-E capable, dump PCI-E cap 10 */ cap = edev->pcie_cap; if (cap) { n += scnprintf(buf+n, len-n, "pci-e cap10:\n"); pr_warn("EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { eeh_ops->read_config(pdn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { if (i != 0) pr_warn("%s\n", buffer); l = scnprintf(buffer, sizeof(buffer), "EEH: PCI-E %02x: %08x ", 4*i, cfg); } else { l += scnprintf(buffer+l, sizeof(buffer)-l, "%08x ", cfg); } } pr_warn("%s\n", buffer); } /* If AER capable, dump it */ cap = edev->aer_cap; if (cap) { n += scnprintf(buf+n, len-n, "pci-e AER:\n"); pr_warn("EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<=13; i++) { eeh_ops->read_config(pdn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { if (i != 0) pr_warn("%s\n", buffer); l = scnprintf(buffer, sizeof(buffer), "EEH: PCI-E AER %02x: %08x ", 4*i, cfg); } else { l += scnprintf(buffer+l, sizeof(buffer)-l, "%08x ", cfg); } } pr_warn("%s\n", buffer); } return n; }

Contributors

PersonTokensPropCommitsCommitProp
Linas Vepstas42160.14%323.08%
Gavin Shan27339.00%753.85%
Alexey Kardashevskiy20.29%17.69%
Guilherme G. Piccoli20.29%17.69%
Yijing Wang20.29%17.69%
Total700100.00%13100.00%


static void *eeh_dump_pe_log(void *data, void *flag) { struct eeh_pe *pe = data; struct eeh_dev *edev, *tmp; size_t *plen = flag; eeh_pe_for_each_dev(pe, edev, tmp) *plen += eeh_dump_dev_log(edev, pci_regs_buf + *plen, EEH_PCI_REGS_LOG_LEN - *plen); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan65100.00%1100.00%
Total65100.00%1100.00%

/** * 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; /* * When the PHB is fenced or dead, it's pointless to collect * the data from PCI config space because it should return * 0xFF's. For ER, we still retrieve the data from the PCI * config space. * * For pHyp, we have to enable IO for log retrieval. Otherwise, * 0xFF's is always returned from PCI config space. * * When the @severity is EEH_LOG_PERM, the PE is going to be * removed. Prior to that, the drivers for devices included in * the PE will be closed. The drivers rely on working IO path * to bring the devices to quiet state. Otherwise, PCI traffic * from those devices after they are removed is like to cause * another unexpected EEH error. */ if (!(pe->type & EEH_PE_PHB)) { if (eeh_has_flag(EEH_ENABLE_IO_FOR_LOG) || severity == EEH_LOG_PERM) eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); /* * The config space of some PCI devices can't be accessed * when their PEs are in frozen state. Otherwise, fenced * PHB might be seen. Those PEs are identified with flag * EEH_PE_CFG_RESTRICTED, indicating EEH_PE_CFG_BLOCKED * is set automatically when the PE is put to EEH_PE_ISOLATED. * * Restoring BARs possibly triggers PCI config access in * (OPAL) firmware and then causes fenced PHB. If the * PCI config is blocked with flag EEH_PE_CFG_BLOCKED, it's * pointless to restore BARs and dump config space. */ eeh_ops->configure_bridge(pe); if (!(pe->state & EEH_PE_CFG_BLOCKED)) { eeh_pe_restore_bars(pe); pci_regs_buf[0] = 0; eeh_pe_traverse(pe, eeh_dump_pe_log, &loglen); } } eeh_ops->get_log(pe, severity, pci_regs_buf, loglen); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan6561.32%1386.67%
Linas Vepstas3533.02%16.67%
Richard A. Lary65.66%16.67%
Total106100.00%15100.00%

/** * 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; int hugepage_shift; /* * We won't find hugepages here(this is iomem). Hence we are not * worried about _PAGE_SPLITTING/collapse. Also we will not hit * page table free, because of init_mm. */ ptep = find_init_mm_pte(token, &hugepage_shift); if (!ptep) return token; WARN_ON(hugepage_shift); pa = pte_pfn(*ptep) << PAGE_SHIFT; return pa | (token & (PAGE_SIZE-1)); }

Contributors

PersonTokensPropCommitsCommitProp
Anton Blanchard3347.14%225.00%
Aneesh Kumar K.V1318.57%337.50%
Andrew Morton1318.57%112.50%
Roland Dreier1014.29%112.50%
Paul Mackerras11.43%112.50%
Total70100.00%8100.00%

/* * On PowerNV platform, we might already have fenced PHB there. * For that case, it's meaningless to recover frozen PE. Intead, * We have to handle fenced PHB firstly. */
static int eeh_phb_check_failure(struct eeh_pe *pe) { struct eeh_pe *phb_pe; unsigned long flags; int ret; if (!eeh_has_flag(EEH_PROBE_MODE_DEV)) return -EPERM; /* Find the PHB PE */ phb_pe = eeh_phb_pe_get(pe->phb); if (!phb_pe) { pr_warn("%s Can't find PE for PHB#%x\n", __func__, pe->phb->global_number); return -EEXIST; } /* If the PHB has been in problematic state */ eeh_serialize_lock(&flags); if (phb_pe->state & EEH_PE_ISOLATED) { ret = 0; goto out; } /* Check PHB state */ ret = eeh_ops->get_state(phb_pe, NULL); 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)) { ret = 0; goto out; } /* Isolate the PHB and send event */ eeh_pe_state_mark(phb_pe, EEH_PE_ISOLATED); eeh_serialize_unlock(flags); pr_err("EEH: PHB#%x failure detected, location: %s\n", phb_pe->phb->global_number, eeh_pe_loc_get(phb_pe)); dump_stack(); eeh_send_failure_event(phb_pe); return 1; out: eeh_serialize_unlock(flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan19399.48%685.71%
Russell Currey10.52%114.29%
Total194100.00%7100.00%

/** * 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; int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); unsigned long flags; struct device_node *dn; struct pci_dev *dev; struct eeh_pe *pe, *parent_pe, *phb_pe; int rc = 0; const char *location = NULL; eeh_stats.total_mmio_ffs++; if (!eeh_enabled()) return 0; if (!edev) { eeh_stats.no_dn++; return 0; } dev = eeh_dev_to_pci_dev(edev); pe = eeh_dev_to_pe(edev); /* 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\n", eeh_pci_name(dev)); return 0; } if (!pe->addr && !pe->config_addr) { eeh_stats.no_cfg_addr++; return 0; } /* * On PowerNV platform, we might already have fenced PHB * there and we need take care of that firstly. */ ret = eeh_phb_check_failure(pe); if (ret > 0) return ret; /* * If the PE isn't owned by us, we shouldn't check the * state. Instead, let the owner handle it if the PE has * been frozen. */ if (eeh_pe_passed(pe)) 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. */ eeh_serialize_lock(&flags); rc = 1; if (pe->state & EEH_PE_ISOLATED) { pe->check_count++; if (pe->check_count % EEH_MAX_FAILS == 0) { dn = pci_device_to_OF_node(dev); if (dn) 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 ? location : "unknown", 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 & active_flags) == active_flags)) { eeh_stats.false_positives++; pe->false_positives++; rc = 0; goto dn_unlock; } /* * It should be corner case that the parent PE has been * put into frozen state as well. We should take care * that at first. */ parent_pe = pe->parent; while (parent_pe) { /* Hit the ceiling ? */ if (parent_pe->type & EEH_PE_PHB) break; /* Frozen parent PE ? */ ret = eeh_ops->get_state(parent_pe, NULL); if (ret > 0 && (ret & active_flags) != active_flags) pe = parent_pe; /* Next parent level */ parent_pe = parent_pe->parent; } 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); eeh_serialize_unlock(flags); /* 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. */ phb_pe = eeh_phb_pe_get(pe->phb); pr_err("EEH: Frozen PHB#%x-PE#%x detected\n", pe->phb->global_number, pe->addr); pr_err("EEH: PE location: %s, PHB location: %s\n", eeh_pe_loc_get(pe), eeh_pe_loc_get(phb_pe)); dump_stack(); eeh_send_failure_event(pe); return 1; dn_unlock: eeh_serialize_unlock(flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan24350.94%1536.59%
Linas Vepstas10121.17%1024.39%
Paul Mackerras367.55%614.63%
Mike Mason336.92%12.44%
Anton Blanchard316.50%37.32%
Andrew Morton142.94%12.44%
Alexey Kardashevskiy71.47%12.44%
Thadeu Lima de Souza Cascardo61.26%12.44%
Wei Yang30.63%12.44%
Breno Leitão20.42%12.44%
Benjamin Herrenschmidt10.21%12.44%
Total477100.00%41100.00%

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 address * * Check for an EEH failure at the given I/O 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. */
int eeh_check_failure(const volatile void __iomem *token) { 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 0; } return eeh_dev_check_failure(edev); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras3962.90%111.11%
Gavin Shan1219.35%444.44%
Linas Vepstas58.06%111.11%
Linus Torvalds34.84%111.11%
Anton Blanchard23.23%111.11%
Roland Dreier11.61%111.11%
Total62100.00%9100.00%

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 active_flag, rc; /* * pHyp doesn't allow to enable IO or DMA on unfrozen PE. * Also, it's pointless to enable them on unfrozen PE. So * we have to check before enabling IO or DMA. */ switch (function) { case EEH_OPT_THAW_MMIO: active_flag = EEH_STATE_MMIO_ACTIVE | EEH_STATE_MMIO_ENABLED; break; case EEH_OPT_THAW_DMA: active_flag = EEH_STATE_DMA_ACTIVE; break; case EEH_OPT_DISABLE: case EEH_OPT_ENABLE: case EEH_OPT_FREEZE_PE: active_flag = 0; break; default: pr_warn("%s: Invalid function %d\n", __func__, function); return -EINVAL; } /* * Check if IO or DMA has been enabled before * enabling them. */ if (active_flag) { rc = eeh_ops->get_state(pe, NULL); if (rc < 0) return rc; /* Needn't enable it at all */ if (rc == EEH_STATE_NOT_SUPPORT) return 0; /* It's already enabled */ if (rc & active_flag) return 0; } /* Issue the request */ rc = eeh_ops->set_option(pe, function); if (rc) pr_warn("%s: Unexpected state change %d on " "PHB#%x-PE#%x, err=%d\n", __func__, function, pe->phb->global_number, pe->addr, rc); /* Check if the request is finished successfully */ if (active_flag) { rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC); if (rc < 0) return rc; if (rc & active_flag) return 0; return -EIO; } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan15075.38%866.67%
Linas Vepstas4723.62%216.67%
Russell Currey10.50%18.33%
Andrew Donnellan10.50%18.33%
Total199100.00%12100.00%


static void *eeh_disable_and_save_dev_state(void *data, void *userdata) { struct eeh_dev *edev = data; struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); struct pci_dev *dev = userdata; /* * The caller should have disabled and saved the * state for the specified device */ if (!pdev || pdev == dev) return NULL; /* Ensure we have D0 power state */ pci_set_power_state(pdev, PCI_D0); /* Save device state */ pci_save_state(pdev); /* * Disable device to avoid any DMA traffic and * interrupt from the device */ pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan79100.00%1100.00%
Total79100.00%1100.00%


static void *eeh_restore_dev_state(void *data, void *userdata) { struct eeh_dev *edev = data; struct pci_dn *pdn = eeh_dev_to_pdn(edev); struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); struct pci_dev *dev = userdata; if (!pdev) return NULL; /* Apply customization from firmware */ if (pdn && eeh_ops->restore_config) eeh_ops->restore_config(pdn); /* The caller should restore state for the specified device */ if (pdev != dev) pci_restore_state(pdev); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan8798.86%266.67%
David Gibson11.14%133.33%
Total88100.00%3100.00%

/** * pcibios_set_pcie_reset_state - 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 = eeh_dev_to_pe(edev); 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); eeh_unfreeze_pe(pe, false); if (!(pe->type & EEH_PE_VF)) eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev); eeh_pe_state_clear(pe, EEH_PE_ISOLATED); break; case pcie_hot_reset: eeh_pe_state_mark_with_cfg(pe, EEH_PE_ISOLATED); eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev); if (!(pe->type & EEH_PE_VF)) eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); eeh_ops->reset(pe, EEH_RESET_HOT); break; case pcie_warm_reset: eeh_pe_state_mark_with_cfg(pe, EEH_PE_ISOLATED); eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev); if (!(pe->type & EEH_PE_VF)) eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); break; default: eeh_pe_state_clear(pe, EEH_PE_ISOLATED | EEH_PE_CFG_BLOCKED); return -EINVAL; }; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan15161.13%770.00%
Brian King6024.29%110.00%
Wei Yang3614.57%220.00%
Total247100.00%10100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan65100.00%2100.00%
Total65100.00%2100.00%

/** * eeh_pe_reset_full - Complete a full reset process on the indicated PE * @pe: EEH PE * * This function executes a full reset procedure on a PE, including setting * the appropriate flags, performing a fundamental or hot reset, and then * deactivating the reset status. It is designed to be used within the EEH * subsystem, as opposed to eeh_pe_reset which is exported to drivers and * only performs a single operation at a time. * * This function will attempt to reset a PE three times before failing. */
int eeh_pe_reset_full(struct eeh_pe *pe) { int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); int reset_state = (EEH_PE_RESET | EEH_PE_CFG_BLOCKED); int type = EEH_RESET_HOT; unsigned int freset = 0; int i, state, ret; /* * Determine the type of reset to perform - hot or fundamental. * Hot reset is the default operation, unless any device under the * PE requires a fundamental reset. */ eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset); if (freset) type = EEH_RESET_FUNDAMENTAL; /* Mark the PE as in reset state and block config space accesses */ eeh_pe_state_mark(pe, reset_state); /* Make three attempts at resetting the bus */ for (i = 0; i < 3; i++) { ret = eeh_pe_reset(pe, type); if (ret) break; ret = eeh_pe_reset(pe, EEH_RESET_DEACTIVATE); if (ret) break; /* Wait until the PE is in a functioning state */ state = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC); if ((state & active_flags) == active_flags) break; if (state < 0) { pr_warn("%s: Unrecoverable slot failure on PHB#%x-PE#%x", __func__, pe->phb->global_number, pe->addr); ret = -ENOTRECOVERABLE; break; } /* Set error in case this is our last attempt */ ret = -EIO; pr_warn("%s: Failure %d resetting PHB#%x-PE#%x\n (%d)\n", __func__, state, pe->phb->global_number, pe->addr, (i + 1)); } eeh_pe_state_clear(pe, reset_state); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Russell Currey7033.02%212.50%
Gavin Shan7033.02%743.75%
Linas Vepstas5626.42%531.25%
Richard A. Lary125.66%16.25%
Mike Mason41.89%16.25%
Total212100.00%16100.00%

/** * 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) { struct pci_dn *pdn; int i; pdn = eeh_dev_to_pdn(edev); if (!pdn) return; for (i = 0; i < 16; i++) eeh_ops->read_config(pdn, i * 4, 4, &edev->config_space[i]); /* * For PCI bridges including root port, we need enable bus * master explicitly. Otherwise, it can't fetch IODA table * entries correctly. So we cache the bit in advance so that * we can restore it after reset, either PHB range or PE range. */ if (edev->mode & EEH_DEV_BRIDGE) edev->config_space[1] |= PCI_COMMAND_MASTER; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan4756.63%466.67%
Linas Vepstas3643.37%233.33%
Total83100.00%6100.00%

/** * 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_warn("%s: Invalid EEH ops name for %p\n", __func__, ops); return -EINVAL; } if (eeh_ops && eeh_ops != ops) { pr_warn("%s: EEH ops of platform %s already existing (%s)\n", __func__, eeh_ops->name, ops->name); return -EEXIST; } eeh_ops = ops; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan69100.00%2100.00%
Total69100.00%2100.00%

/** * 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_warn("%s: Invalid EEH ops name\n", __func__); return -EINVAL; } if (eeh_ops && !strcmp(eeh_ops->name, name)) { eeh_ops = NULL; return 0; } return -EEXIST; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan62100.00%2100.00%
Total62100.00%2100.00%


static int eeh_reboot_notifier(struct notifier_block *nb, unsigned long action, void *unused) { eeh_clear_flag(EEH_ENABLED); return NOTIFY_DONE; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan27100.00%2100.00%
Total27100.00%2100.00%

static struct notifier_block eeh_reboot_nb = { .notifier_call = eeh_reboot_notifier, }; /** * 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. */
int eeh_init(void) { struct pci_controller *hose, *tmp; struct pci_dn *pdn; static int cnt = 0; int ret = 0; /* * We have to delay the initialization on PowerNV after * the PCI hierarchy tree has been built because the PEs * are figured out based on PCI devices instead of device * tree nodes */ if (machine_is(powernv) && cnt++ <= 0) return ret; /* Register reboot notifier */ ret = register_reboot_notifier(&eeh_reboot_nb); if (ret) { pr_warn("%s: Failed to register notifier (%d)\n", __func__, ret); return ret; } /* call platform initialization function */ if (!eeh_ops) { pr_warn("%s: Platform EEH operation not found\n", __func__); return -EEXIST; } else if ((ret = eeh_ops->init())) return ret; /* Initialize PHB PEs */ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) eeh_dev_phb_init_dynamic(hose); /* Initialize EEH event */ ret = eeh_event_init(); if (ret) return ret; /* Enable EEH for all adapters */ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { pdn = hose->pci_data; traverse_pci_dn(pdn, eeh_ops->probe, NULL); } /* * Call platform post-initialization. Actually, It's good chance * to inform platform that EEH is ready to supply service if the * I/O cache stuff has been built up. */ if (eeh_ops->post_init) { ret = eeh_ops->post_init(); if (ret) return ret; } if (eeh_enabled()) pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n"); else pr_info("EEH: No capable adapters found\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan15775.12%1257.14%
Anton Blanchard2511.96%523.81%
Benjamin Herrenschmidt167.66%14.76%
Paul Mackerras73.35%14.76%
Andrew Morton41.91%29.52%
Total209100.00%21100.00%

core_initcall_sync(eeh_init); /** * eeh_add_device_early - Enable EEH for the indicated device node * @pdn: PCI 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. */
void eeh_add_device_early(struct pci_dn *pdn) { struct pci_controller *phb = pdn ? pdn->phb : NULL; struct eeh_dev *edev = pdn_to_eeh_dev(pdn); if (!edev) return; if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) return; /* USB Bus children of PCI devices will not have BUID's */ if (NULL == phb || (eeh_has_flag(EEH_PROBE_MODE_DEVTREE) && 0 == phb->buid)) return; eeh_ops->probe(pdn, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan3544.87%555.56%
Andrew Morton3241.03%222.22%
Alexey Kardashevskiy810.26%111.11%
Anton Blanchard33.85%111.11%
Total78100.00%9100.00%

/** * eeh_add_device_tree_early - Enable EEH for the indicated device * @pdn: PCI 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 pci_dn *pdn) { struct pci_dn *n; if (!pdn) return; list_for_each_entry(n, &pdn->child_list, list) eeh_add_device_tree_early(n); eeh_add_device_early(pdn); }

Contributors

PersonTokensPropCommitsCommitProp
Linas Vepstas1948.72%133.33%
Gavin Shan1743.59%133.33%
Stephen Rothwell37.69%133.33%
Total39100.00%3100.00%

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). */
void eeh_add_device_late(struct pci_dev *dev) { struct pci_dn *pdn; struct eeh_dev *edev; if (!dev || !eeh_enabled()) return; pr_debug("EEH: Adding device %s\n", pci_name(dev)); pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); edev = pdn_to_eeh_dev(pdn); if (edev->pdev == dev) { pr_debug("EEH: Already referenced !\n"); return; } /* * The EEH cache might not be removed correctly because of * unbalanced kref to the device during unplug time, which * relies on pcibios_release_device(). So we have to remove * that here explicitly. */ if (edev->pdev) { eeh_rmv_from_parent_pe(edev); eeh_addr_cache_rmv_dev(edev->pdev); eeh_sysfs_remove_device(edev->pdev); edev->mode &= ~EEH_DEV_SYSFS; /* * We definitely should have the PCI device removed * though it wasn't correctly. So we needn't call * into error handler afterwards. */ edev->mode |= EEH_DEV_NO_HANDLER; edev->pdev = NULL; dev->dev.archdata.edev = NULL; } if (eeh_has_flag(EEH_PROBE_MODE_DEV)) eeh_ops->probe(pdn, NULL); edev->pdev = dev; dev->dev.archdata.edev = edev; eeh_addr_cache_insert_dev(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan8650.29%753.85%
Andrew Morton2916.96%215.38%
Linas Vepstas2011.70%215.38%
Benjamin Herrenschmidt2011.70%17.69%
Daniel Axtens169.36%17.69%
Total171100.00%13100.00%

/** * 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); } } }

Contributors

PersonTokensPropCommitsCommitProp
Nathan Fontenot58100.00%1100.00%
Total58100.00%1100.00%

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); } } }

Contributors

PersonTokensPropCommitsCommitProp
Thadeu Lima de Souza Cascardo58100.00%1100.00%
Total58100.00%1100.00%

EXPORT_SYMBOL_GPL(eeh_add_sysfs_files); /** * eeh_remove_device - Undo EEH setup for the indicated pci device * @dev: pci device to be removed * * 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. */
void eeh_remove_device(struct pci_dev *dev) { struct eeh_dev *edev; if (!dev || !eeh_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 || !edev->pe) { pr_debug("EEH: Not referenced !\n"); return; } /* * During the hotplug for EEH error recovery, we need the EEH * device attached to the parent PE in order for BAR restore * a bit later. So we keep it for BAR restore and remove it * from the parent PE during the BAR resotre. */ edev->pdev = NULL; /* * The flag "in_error" is used to trace EEH devices for VFs * in error state or not. It's set in eeh_report_error(). If * it's not set, eeh_report_{reset,resume}() won't be called * for the VF EEH device. */ edev->in_error = false; dev->dev.archdata.edev = NULL; if (!(edev->pe->state & EEH_PE_KEEP)) eeh_rmv_from_parent_pe(edev); else edev->mode |= EEH_DEV_DISCONNECTED; /* * We're removing from the PCI subsystem, that means * the PCI device driver can't support EEH or not * well. So we rely on hotplug completely to do recovery * for the specific PCI device. */ edev->mode |= EEH_DEV_NO_HANDLER; eeh_addr_cache_rmv_dev(dev); eeh_sysfs_remove_device(dev); edev->mode &= ~EEH_DEV_SYSFS; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan7553.96%753.85%
Benjamin Herrenschmidt1812.95%17.69%
Andrew Morton139.35%17.69%
Linas Vepstas139.35%215.38%
Anton Blanchard139.35%17.69%
Wei Yang75.04%17.69%
Total139100.00%13100.00%


int eeh_unfreeze_pe(struct eeh_pe *pe, bool sw_state) { int ret; ret = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); if (ret) { pr_warn("%s: Failure %d enabling IO on PHB#%x-PE#%x\n", __func__, ret, pe->phb->global_number, pe->addr); return ret; } ret = eeh_pci_enable(pe, EEH_OPT_THAW_DMA); if (ret) { pr_warn("%s: Failure %d enabling DMA on PHB#%x-PE#%x\n", __func__, ret, pe->phb->global_number, pe->addr); return ret; } /* Clear software isolated state */ if (sw_state && (pe->state & EEH_PE_ISOLATED)) eeh_pe_state_clear(pe, EEH_PE_ISOLATED); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan113100.00%3100.00%
Total113100.00%3100.00%

static struct pci_device_id eeh_reset_ids[] = { { PCI_DEVICE(0x19a2, 0x0710) }, /* Emulex, BE */ { PCI_DEVICE(0x10df, 0xe220) }, /* Emulex, Lancer */ { PCI_DEVICE(0x14e4, 0x1657) }, /* Broadcom BCM5719 */ { 0 } };
static int eeh_pe_change_owner(struct eeh_pe *pe) { struct eeh_dev *edev, *tmp; struct pci_dev *pdev; struct pci_device_id *id; int flags, ret; /* Check PE state */ flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); ret = eeh_ops->get_state(pe, NULL); if (ret < 0 || ret == EEH_STATE_NOT_SUPPORT) return 0; /* Unfrozen PE, nothing to do */ if ((ret & flags) == flags) return 0; /* Frozen PE, check if it needs PE level reset */ eeh_pe_for_each_dev(pe, edev, tmp) { pdev = eeh_dev_to_pci_dev(edev); if (!pdev) continue; for (id = &eeh_reset_ids[0]; id->vendor != 0; id++) { if (id->vendor != PCI_ANY_ID && id->vendor != pdev->vendor) continue; if (id->device != PCI_ANY_ID && id->device != pdev->device) continue; if (id->subvendor != PCI_ANY_ID && id->subvendor != pdev->subsystem_vendor) continue; if (id->subdevice != PCI_ANY_ID && id->subdevice != pdev->subsystem_device) continue; return eeh_pe_reset_and_recover(pe); } } return eeh_unfreeze_pe(pe, true); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan208100.00%2100.00%
Total208100.00%2100.00%

/** * eeh_dev_open - Increase count of pass through devices for PE * @pdev: PCI device * * Increase count of passed through devices for the indicated * PE. In the result, the EEH errors detected on the PE won't be * reported. The PE owner will be responsible for detection * and recovery. */
int eeh_dev_open(struct pci_dev *pdev) { struct eeh_dev *edev; int ret = -ENODEV; mutex_lock(&eeh_dev_mutex); /* No PCI device ? */ if (!pdev) goto out; /* No EEH device or PE ? */ edev = pci_dev_to_eeh_dev(pdev); if (!edev || !edev->pe) goto out; /* * The PE might have been put into frozen state, but we * didn't detect that yet. The passed through PCI devices * in frozen PE won't work properly. Clear the frozen state * in advance. */ ret = eeh_pe_change_owner(edev->pe); if (ret) goto out; /* Increase PE's pass through count */ atomic_inc(&edev->pe->pass_dev_cnt); mutex_unlock(&eeh_dev_mutex); return 0; out: mutex_unlock(&eeh_dev_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan105100.00%4100.00%
Total105100.00%4100.00%

EXPORT_SYMBOL_GPL(eeh_dev_open); /** * eeh_dev_release - Decrease count of pass through devices for PE * @pdev: PCI device * * Decrease count of pass through devices for the indicated PE. If * there is no passed through device in PE, the EEH errors detected * on the PE will be reported and handled as usual. */
void eeh_dev_release(struct pci_dev *pdev) { struct eeh_dev *edev; mutex_lock(&eeh_dev_mutex); /* No PCI device ? */ if (!pdev) goto out; /* No EEH device ? */ edev = pci_dev_to_eeh_dev(pdev); if (!edev || !edev->pe || !eeh_pe_passed(edev->pe)) goto out; /* Decrease PE's pass through count */ WARN_ON(atomic_dec_if_positive(&edev->pe->pass_dev_cnt) < 0); eeh_pe_change_owner(edev->pe); out: mutex_unlock(&eeh_dev_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan90100.00%3100.00%
Total90100.00%3100.00%

EXPORT_SYMBOL(eeh_dev_release); #ifdef CONFIG_IOMMU_API
static int dev_has_iommu_table(struct device *dev, void *data) { struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev **ppdev = data; if (!dev) return 0; if (dev->iommu_group) { *ppdev = pdev; return 1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan5896.67%150.00%
Alexey Kardashevskiy23.33%150.00%
Total60100.00%2100.00%

/** * eeh_iommu_group_to_pe - Convert IOMMU group to EEH PE * @group: IOMMU group * * The routine is called to convert IOMMU group to EEH PE. */
struct eeh_pe *eeh_iommu_group_to_pe(struct iommu_group *group) { struct pci_dev *pdev = NULL; struct eeh_dev *edev; int ret; /* No IOMMU group ? */ if (!group) return NULL; ret = iommu_group_for_each_dev(group, &pdev, dev_has_iommu_table); if (!ret || !pdev) return NULL; /* No EEH device or PE ? */ edev = pci_dev_to_eeh_dev(pdev); if (!edev || !edev->pe) return NULL; return edev->pe; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan85100.00%2100.00%
Total85100.00%2100.00%

EXPORT_SYMBOL_GPL(eeh_iommu_group_to_pe); #endif /* CONFIG_IOMMU_API */ /** * eeh_pe_set_option - Set options for the indicated PE * @pe: EEH PE * @option: requested option * * The routine is called to enable or disable EEH functionality * on the indicated PE, to enable IO or DMA for the frozen PE. */
int eeh_pe_set_option(struct eeh_pe *pe, int option) { int ret = 0; /* Invalid PE ? */ if (!pe) return -ENODEV; /* * EEH functionality could possibly be disabled, just * return error for the case. And the EEH functinality * isn't expected to be disabled on one specific PE. */ switch (option) { case EEH_OPT_ENABLE: if (eeh_enabled()) { ret = eeh_pe_change_owner(pe); break; } ret = -EIO; break; case EEH_OPT_DISABLE: break; case EEH_OPT_THAW_MMIO: case EEH_OPT_THAW_DMA: case EEH_OPT_FREEZE_PE: if (!eeh_ops || !eeh_ops->set_option) { ret = -ENOENT; break; } ret = eeh_pci_enable(pe, option); break; default: pr_debug("%s: Option %d out of range (%d, %d)\n", __func__, option, EEH_OPT_DISABLE, EEH_OPT_THAW_DMA); ret = -EINVAL; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan122100.00%4100.00%
Total122100.00%4100.00%

EXPORT_SYMBOL_GPL(eeh_pe_set_option); /** * eeh_pe_get_state - Retrieve PE's state * @pe: EEH PE * * Retrieve the PE's state, which includes 3 aspects: enabled * DMA, enabled IO and asserted reset. */
int eeh_pe_get_state(struct eeh_pe *pe) { int result, ret = 0; bool rst_active, dma_en, mmio_en; /* Existing PE ? */ if (!pe) return -ENODEV; if (!eeh_ops || !eeh_ops->get_state) return -ENOENT; /* * If the parent PE is owned by the host kernel and is undergoing * error recovery, we should return the PE state as temporarily * unavailable so that the error recovery on the guest is suspended * until the recovery completes on the host. */ if (pe->parent && !(pe->state & EEH_PE_REMOVED) && (pe->parent->state & (EEH_PE_ISOLATED | EEH_PE_RECOVERING))) return EEH_PE_STATE_UNAVAIL; result = eeh_ops->get_state(pe, NULL); rst_active = !!(result & EEH_STATE_RESET_ACTIVE); dma_en = !!(result & EEH_STATE_DMA_ENABLED); mmio_en = !!(result & EEH_STATE_MMIO_ENABLED); if (rst_active) ret = EEH_PE_STATE_RESET; else if (dma_en && mmio_en) ret = EEH_PE_STATE_NORMAL; else if (!dma_en && !mmio_en) ret = EEH_PE_STATE_STOPPED_IO_DMA; else if (!dma_en && mmio_en) ret = EEH_PE_STATE_STOPPED_DMA; else ret = EEH_PE_STATE_UNAVAIL; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan174100.00%2100.00%
Total174100.00%2100.00%

EXPORT_SYMBOL_GPL(eeh_pe_get_state);
static int eeh_pe_reenable_devices(struct eeh_pe *pe) { struct eeh_dev *edev, *tmp; struct pci_dev *pdev; int ret = 0; /* Restore config space */ eeh_pe_restore_bars(pe); /* * Reenable PCI devices as the devices passed * through are always enabled before the reset. */ eeh_pe_for_each_dev(pe, edev, tmp) { pdev = eeh_dev_to_pci_dev(edev); if (!pdev) continue; ret = pci_reenable_device(pdev); if (ret) { pr_warn("%s: Failure %d reenabling %s\n", __func__, ret, pci_name(pdev)); return ret; } } /* The PE is still in frozen state */ return eeh_unfreeze_pe(pe, true); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan98100.00%2100.00%
Total98100.00%2100.00%

/** * eeh_pe_reset - Issue PE reset according to specified type * @pe: EEH PE * @option: reset type * * The routine is called to reset the specified PE with the * indicated type, either fundamental reset or hot reset. * PE reset is the most important part for error recovery. */
int eeh_pe_reset(struct eeh_pe *pe, int option) { int ret = 0; /* Invalid PE ? */ if (!pe) return -ENODEV; if (!eeh_ops || !eeh_ops->set_option || !eeh_ops->reset) return -ENOENT; switch (option) { case EEH_RESET_DEACTIVATE: ret = eeh_ops->reset(pe, option); eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); if (ret) break; ret = eeh_pe_reenable_devices(pe); break; case EEH_RESET_HOT: case EEH_RESET_FUNDAMENTAL: /* * Proactively freeze the PE to drop all MMIO access * during reset, which should be banned as it's always * cause recursive EEH error. */ eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); ret = eeh_ops->reset(pe, option); break; default: pr_debug("%s: Unsupported option %d\n", __func__, option); ret = -EINVAL; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan140100.00%5100.00%
Total140100.00%5100.00%

EXPORT_SYMBOL_GPL(eeh_pe_reset); /** * eeh_pe_configure - Configure PCI bridges after PE reset * @pe: EEH PE * * The routine is called to restore the PCI config space for * those PCI devices, especially PCI bridges affected by PE * reset issued previously. */
int eeh_pe_configure(struct eeh_pe *pe) { int ret = 0; /* Invalid PE ? */ if (!pe) return -ENODEV; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan28100.00%1100.00%
Total28100.00%1100.00%

EXPORT_SYMBOL_GPL(eeh_pe_configure); /** * eeh_pe_inject_err - Injecting the specified PCI error to the indicated PE * @pe: the indicated PE * @type: error type * @function: error function * @addr: address * @mask: address mask * * The routine is called to inject the specified PCI error, which * is determined by @type and @function, to the indicated PE for * testing purpose. */
int eeh_pe_inject_err(struct eeh_pe *pe, int type, int func, unsigned long addr, unsigned long mask) { /* Invalid PE ? */ if (!pe) return -ENODEV; /* Unsupported operation ? */ if (!eeh_ops || !eeh_ops->err_inject) return -ENOENT; /* Check on PCI error type */ if (type != EEH_ERR_TYPE_32 && type != EEH_ERR_TYPE_64) return -EINVAL; /* Check on PCI error function */ if (func < EEH_ERR_FUNC_MIN || func > EEH_ERR_FUNC_MAX) return -EINVAL; return eeh_ops->err_inject(pe, type, func, addr, mask); }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan95100.00%1100.00%
Total95100.00%1100.00%

EXPORT_SYMBOL_GPL(eeh_pe_inject_err);
static int proc_eeh_show(struct seq_file *m, void *v) { if (!eeh_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; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton3941.05%116.67%
Gavin Shan2728.42%233.33%
Anton Blanchard1717.89%116.67%
Linas Vepstas1212.63%233.33%
Total95100.00%6100.00%


static int proc_eeh_open(struct inode *inode, struct file *file) { return single_open(file, proc_eeh_show, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton26100.00%1100.00%
Total26100.00%1100.00%

static const struct file_operations proc_eeh_operations = { .open = proc_eeh_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #ifdef CONFIG_DEBUG_FS
static int eeh_enable_dbgfs_set(void *data, u64 val) { if (val) eeh_clear_flag(EEH_FORCE_DISABLED); else eeh_add_flag(EEH_FORCE_DISABLED); /* Notify the backend */ if (eeh_ops->post_init) eeh_ops->post_init(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan43100.00%2100.00%
Total43100.00%2100.00%


static int eeh_enable_dbgfs_get(void *data, u64 *val) { if (eeh_enabled()) *val = 0x1ul; else *val = 0x0ul; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan33100.00%1100.00%
Total33100.00%1100.00%


static int eeh_freeze_dbgfs_set(void *data, u64 val) { eeh_max_freezes = val; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan20100.00%1100.00%
Total20100.00%1100.00%


static int eeh_freeze_dbgfs_get(void *data, u64 *val) { *val = eeh_max_freezes; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan22100.00%1100.00%
Total22100.00%1100.00%

DEFINE_SIMPLE_ATTRIBUTE(eeh_enable_dbgfs_ops, eeh_enable_dbgfs_get, eeh_enable_dbgfs_set, "0x%llx\n"); DEFINE_SIMPLE_ATTRIBUTE(eeh_freeze_dbgfs_ops, eeh_freeze_dbgfs_get, eeh_freeze_dbgfs_set, "0x%llx\n"); #endif
static int __init eeh_init_proc(void) { if (machine_is(pseries) || machine_is(powernv)) { proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations); #ifdef CONFIG_DEBUG_FS debugfs_create_file("eeh_enable", 0600, powerpc_debugfs_root, NULL, &eeh_enable_dbgfs_ops); debugfs_create_file("eeh_max_freezes", 0600, powerpc_debugfs_root, NULL, &eeh_freeze_dbgfs_ops); #endif } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan3549.30%222.22%
Anton Blanchard1622.54%222.22%
Andrew Morton79.86%111.11%
Mike Qiu57.04%111.11%
Benjamin Herrenschmidt45.63%111.11%
Denis V. Lunev34.23%111.11%
Thadeu Lima de Souza Cascardo11.41%111.11%
Total71100.00%9100.00%

__initcall(eeh_init_proc);

Overall Contributors

PersonTokensPropCommitsCommitProp
Gavin Shan379667.44%8948.63%
Linas Vepstas81614.50%3116.94%
Andrew Morton2344.16%42.19%
Anton Blanchard1562.77%73.83%
Paul Mackerras951.69%73.83%
Russell Currey741.31%31.64%
Thadeu Lima de Souza Cascardo711.26%31.64%
Benjamin Herrenschmidt651.15%42.19%
Brian King611.08%21.09%
Nathan Fontenot591.05%10.55%
Wei Yang460.82%31.64%
Mike Mason380.68%21.09%
Alexey Kardashevskiy190.34%31.64%
Richard A. Lary180.32%21.09%
Daniel Axtens160.28%10.55%
Aneesh Kumar K.V160.28%31.64%
Roland Dreier110.20%10.55%
Stephen Rothwell60.11%10.55%
Paul Gortmaker60.11%21.09%
Mike Qiu50.09%10.55%
Denis V. Lunev30.05%10.55%
David Gibson30.05%21.09%
Linus Torvalds30.05%10.55%
Guilherme G. Piccoli20.04%10.55%
Yijing Wang20.04%10.55%
Breno Leitão20.04%10.55%
Andrew Donnellan20.04%21.09%
Michael Ellerman10.02%10.55%
Arjan van de Ven10.02%10.55%
Thomas Gleixner10.02%10.55%
Arun Sharma10.02%10.55%
Total5629100.00%183100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.