/**
* 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??)
+ * @token: I/O address
*
- * Check for an EEH failure at the given token address. Call this
+ * 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
+ * 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)
+int eeh_check_failure(const volatile void __iomem *token)
{
unsigned long addr;
struct eeh_dev *edev;
edev = eeh_addr_cache_get_dev(addr);
if (!edev) {
eeh_stats.no_device++;
- return val;
+ return 0;
}
- eeh_dev_check_failure(edev);
- return val;
+ return eeh_dev_check_failure(edev);
}
-
EXPORT_SYMBOL(eeh_check_failure);
int eeh_dev_open(struct pci_dev *pdev)
{
struct eeh_dev *edev;
+ int flag = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
+ int ret = -ENODEV;
mutex_lock(&eeh_dev_mutex);
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_ops->get_state(edev->pe, NULL);
+ if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT &&
+ (ret & flag) != flag) {
+ ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_MMIO);
+ if (ret) {
+ pr_warn("%s: Failure %d enabling MMIO "
+ "for PHB#%x-PE#%x\n",
+ __func__, ret, edev->phb->global_number,
+ edev->pe->addr);
+ goto out;
+ }
+
+ ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_DMA);
+ if (ret) {
+ pr_warn("%s: Failure %d enabling DMA "
+ "for PHB#%x-PE#%x\n",
+ __func__, ret, edev->phb->global_number,
+ edev->pe->addr);
+ goto out;
+ }
+ }
+
+ /* Clear software isolated state */
+ if (edev->pe->state & EEH_PE_ISOLATED)
+ eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED);
+
/* 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 -ENODEV;
+ return ret;
}
EXPORT_SYMBOL_GPL(eeh_dev_open);
}
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 */
+ ret = eeh_ops->set_option(pe, EEH_OPT_THAW_MMIO);
+ if (ret) {
+ pr_warn("%s: Failure %d enabling MMIO for PHB#%x-PE#%x\n",
+ __func__, ret, pe->phb->global_number, pe->addr);
+ return ret;
+ }
+
+ ret = eeh_ops->set_option(pe, EEH_OPT_THAW_DMA);
+ if (ret) {
+ pr_warn("%s: Failure %d enabling DMA for PHB#%x-PE#%x\n",
+ __func__, ret, pe->phb->global_number, pe->addr);
+ return ret;
+ }
+
+ /* Clear software isolated state */
+ eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+
+ return ret;
+}
+
/**
* eeh_pe_reset - Issue PE reset according to specified type
* @pe: EEH PE
if (ret)
break;
- /*
- * The PE is still in frozen state and we need to clear
- * that. It's good to clear frozen state after deassert
- * to avoid messy IO access during reset, which might
- * cause recursive frozen PE.
- */
- ret = eeh_ops->set_option(pe, EEH_OPT_THAW_MMIO);
- if (!ret)
- ret = eeh_ops->set_option(pe, EEH_OPT_THAW_DMA);
- if (!ret)
- eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+ 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);
+
ret = eeh_ops->reset(pe, option);
break;
default:
if (!pe)
return -ENODEV;
- /* Restore config space for the affected devices */
- eeh_pe_restore_bars(pe);
-
return ret;
}
EXPORT_SYMBOL_GPL(eeh_pe_configure);