in platforms/powernv/eeh-powernv.c [1406:1601]
static int pnv_eeh_next_error(struct eeh_pe **pe)
{
struct pci_controller *hose;
struct pnv_phb *phb;
struct eeh_pe *phb_pe, *parent_pe;
__be64 frozen_pe_no;
__be16 err_type, severity;
long rc;
int state, ret = EEH_NEXT_ERR_NONE;
/*
* While running here, it's safe to purge the event queue. The
* event should still be masked.
*/
eeh_remove_event(NULL, false);
list_for_each_entry(hose, &hose_list, list_node) {
/*
* If the subordinate PCI buses of the PHB has been
* removed or is exactly under error recovery, we
* needn't take care of it any more.
*/
phb = hose->private_data;
phb_pe = eeh_phb_pe_get(hose);
if (!phb_pe || (phb_pe->state & EEH_PE_ISOLATED))
continue;
rc = opal_pci_next_error(phb->opal_id,
&frozen_pe_no, &err_type, &severity);
if (rc != OPAL_SUCCESS) {
pr_devel("%s: Invalid return value on "
"PHB#%x (0x%lx) from opal_pci_next_error",
__func__, hose->global_number, rc);
continue;
}
/* If the PHB doesn't have error, stop processing */
if (be16_to_cpu(err_type) == OPAL_EEH_NO_ERROR ||
be16_to_cpu(severity) == OPAL_EEH_SEV_NO_ERROR) {
pr_devel("%s: No error found on PHB#%x\n",
__func__, hose->global_number);
continue;
}
/*
* Processing the error. We're expecting the error with
* highest priority reported upon multiple errors on the
* specific PHB.
*/
pr_devel("%s: Error (%d, %d, %llu) on PHB#%x\n",
__func__, be16_to_cpu(err_type),
be16_to_cpu(severity), be64_to_cpu(frozen_pe_no),
hose->global_number);
switch (be16_to_cpu(err_type)) {
case OPAL_EEH_IOC_ERROR:
if (be16_to_cpu(severity) == OPAL_EEH_SEV_IOC_DEAD) {
pr_err("EEH: dead IOC detected\n");
ret = EEH_NEXT_ERR_DEAD_IOC;
} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
pr_info("EEH: IOC informative error "
"detected\n");
pnv_eeh_get_and_dump_hub_diag(hose);
ret = EEH_NEXT_ERR_NONE;
}
break;
case OPAL_EEH_PHB_ERROR:
if (be16_to_cpu(severity) == OPAL_EEH_SEV_PHB_DEAD) {
*pe = phb_pe;
pr_err("EEH: dead PHB#%x detected, "
"location: %s\n",
hose->global_number,
eeh_pe_loc_get(phb_pe));
ret = EEH_NEXT_ERR_DEAD_PHB;
} else if (be16_to_cpu(severity) ==
OPAL_EEH_SEV_PHB_FENCED) {
*pe = phb_pe;
pr_err("EEH: Fenced PHB#%x detected, "
"location: %s\n",
hose->global_number,
eeh_pe_loc_get(phb_pe));
ret = EEH_NEXT_ERR_FENCED_PHB;
} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
pr_info("EEH: PHB#%x informative error "
"detected, location: %s\n",
hose->global_number,
eeh_pe_loc_get(phb_pe));
pnv_eeh_get_phb_diag(phb_pe);
pnv_pci_dump_phb_diag_data(hose, phb_pe->data);
ret = EEH_NEXT_ERR_NONE;
}
break;
case OPAL_EEH_PE_ERROR:
/*
* If we can't find the corresponding PE, we
* just try to unfreeze.
*/
if (pnv_eeh_get_pe(hose,
be64_to_cpu(frozen_pe_no), pe)) {
pr_info("EEH: Clear non-existing PHB#%x-PE#%llx\n",
hose->global_number, be64_to_cpu(frozen_pe_no));
pr_info("EEH: PHB location: %s\n",
eeh_pe_loc_get(phb_pe));
/* Dump PHB diag-data */
rc = opal_pci_get_phb_diag_data2(phb->opal_id,
phb->diag_data, phb->diag_data_size);
if (rc == OPAL_SUCCESS)
pnv_pci_dump_phb_diag_data(hose,
phb->diag_data);
/* Try best to clear it */
opal_pci_eeh_freeze_clear(phb->opal_id,
be64_to_cpu(frozen_pe_no),
OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
ret = EEH_NEXT_ERR_NONE;
} else if ((*pe)->state & EEH_PE_ISOLATED ||
eeh_pe_passed(*pe)) {
ret = EEH_NEXT_ERR_NONE;
} else {
pr_err("EEH: Frozen PE#%x "
"on PHB#%x detected\n",
(*pe)->addr,
(*pe)->phb->global_number);
pr_err("EEH: PE location: %s, "
"PHB location: %s\n",
eeh_pe_loc_get(*pe),
eeh_pe_loc_get(phb_pe));
ret = EEH_NEXT_ERR_FROZEN_PE;
}
break;
default:
pr_warn("%s: Unexpected error type %d\n",
__func__, be16_to_cpu(err_type));
}
/*
* EEH core will try recover from fenced PHB or
* frozen PE. In the time for frozen PE, EEH core
* enable IO path for that before collecting logs,
* but it ruins the site. So we have to dump the
* log in advance here.
*/
if ((ret == EEH_NEXT_ERR_FROZEN_PE ||
ret == EEH_NEXT_ERR_FENCED_PHB) &&
!((*pe)->state & EEH_PE_ISOLATED)) {
eeh_pe_mark_isolated(*pe);
pnv_eeh_get_phb_diag(*pe);
if (eeh_has_flag(EEH_EARLY_DUMP_LOG))
pnv_pci_dump_phb_diag_data((*pe)->phb,
(*pe)->data);
}
/*
* We probably have the frozen parent PE out there and
* we need have to handle frozen parent PE firstly.
*/
if (ret == EEH_NEXT_ERR_FROZEN_PE) {
parent_pe = (*pe)->parent;
while (parent_pe) {
/* Hit the ceiling ? */
if (parent_pe->type & EEH_PE_PHB)
break;
/* Frozen parent PE ? */
state = eeh_ops->get_state(parent_pe, NULL);
if (state > 0 && !eeh_state_active(state))
*pe = parent_pe;
/* Next parent level */
parent_pe = parent_pe->parent;
}
/* We possibly migrate to another PE */
eeh_pe_mark_isolated(*pe);
}
/*
* If we have no errors on the specific PHB or only
* informative error there, we continue poking it.
* Otherwise, we need actions to be taken by upper
* layer.
*/
if (ret > EEH_NEXT_ERR_INF)
break;
}
/* Unmask the event */
if (ret == EEH_NEXT_ERR_NONE && eeh_enabled())
enable_irq(eeh_event_irq);
return ret;
}