void eeh_sysfs_add_device(struct pci_dev *pdev) { struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); int rc=0; if (!eeh_enabled()) return; if (edev && (edev->mode & EEH_DEV_SYSFS)) return; rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode); rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_state); if (rc) pr_warn("EEH: Unable to create sysfs entries\n"); else if (edev) edev->mode |= EEH_DEV_SYSFS; }
long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, unsigned int cmd, unsigned long arg) { struct eeh_pe *pe; struct vfio_eeh_pe_op op; unsigned long minsz; long ret = -EINVAL; switch (cmd) { case VFIO_CHECK_EXTENSION: if (arg == VFIO_EEH) ret = eeh_enabled() ? 1 : 0; else ret = 0; break; case VFIO_EEH_PE_OP: pe = eeh_iommu_group_to_pe(group); if (!pe) return -ENODEV; minsz = offsetofend(struct vfio_eeh_pe_op, op); if (copy_from_user(&op, (void __user *)arg, minsz)) return -EFAULT; if (op.argsz < minsz || op.flags) return -EINVAL; switch (op.op) { case VFIO_EEH_PE_DISABLE: ret = eeh_pe_set_option(pe, EEH_OPT_DISABLE); break; case VFIO_EEH_PE_ENABLE: ret = eeh_pe_set_option(pe, EEH_OPT_ENABLE); break; case VFIO_EEH_PE_UNFREEZE_IO: ret = eeh_pe_set_option(pe, EEH_OPT_THAW_MMIO); break; case VFIO_EEH_PE_UNFREEZE_DMA: ret = eeh_pe_set_option(pe, EEH_OPT_THAW_DMA); break; case VFIO_EEH_PE_GET_STATE: ret = eeh_pe_get_state(pe); break; case VFIO_EEH_PE_RESET_DEACTIVATE: ret = eeh_pe_reset(pe, EEH_RESET_DEACTIVATE, true); break; case VFIO_EEH_PE_RESET_HOT: ret = eeh_pe_reset(pe, EEH_RESET_HOT, true); break; case VFIO_EEH_PE_RESET_FUNDAMENTAL: ret = eeh_pe_reset(pe, EEH_RESET_FUNDAMENTAL, true); break; case VFIO_EEH_PE_CONFIGURE: ret = eeh_pe_configure(pe); break; case VFIO_EEH_PE_INJECT_ERR: minsz = offsetofend(struct vfio_eeh_pe_op, err.mask); if (op.argsz < minsz) return -EINVAL; if (copy_from_user(&op, (void __user *)arg, minsz)) return -EFAULT; ret = eeh_pe_inject_err(pe, op.err.type, op.err.func, op.err.addr, op.err.mask); break; default: ret = -EINVAL; } } return ret; }
/** * 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 pci_dn *pdn; 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) { pdn = eeh_dev_to_pdn(edev); if (pdn->node) location = of_get_property(pdn->node, "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