mirror of
https://github.com/torvalds/linux
synced 2024-10-11 13:55:09 +00:00
[SCSI] mpt2sas: Added support for PCIe Advanced Error Recovery.
Added support in the driver to support EEH and PCIe Advanced Error Recovery. This involves adding new pci_error_handler interface for recovering the controller from PCI Bus errors, such as SERR and PERR. Some tools are available for simulating PCI errors in order to validate this interface: http://www.kernel.org/pub/linux/utils/pci/aer-inject Signed-off-by: Kashyap Desai <kashyap.desai@lsi.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
parent
ebda4d38df
commit
ef7c80c1f1
|
@ -58,6 +58,7 @@
|
||||||
#include <linux/sort.h>
|
#include <linux/sort.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
|
#include <linux/aer.h>
|
||||||
|
|
||||||
#include "mpt2sas_base.h"
|
#include "mpt2sas_base.h"
|
||||||
|
|
||||||
|
@ -1256,6 +1257,9 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* AER (Advanced Error Reporting) hooks */
|
||||||
|
pci_enable_pcie_error_reporting(pdev);
|
||||||
|
|
||||||
pci_set_master(pdev);
|
pci_set_master(pdev);
|
||||||
|
|
||||||
if (_base_config_dma_addressing(ioc, pdev) != 0) {
|
if (_base_config_dma_addressing(ioc, pdev) != 0) {
|
||||||
|
@ -1311,6 +1315,7 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
|
||||||
ioc->chip_phys = 0;
|
ioc->chip_phys = 0;
|
||||||
ioc->pci_irq = -1;
|
ioc->pci_irq = -1;
|
||||||
pci_release_selected_regions(ioc->pdev, ioc->bars);
|
pci_release_selected_regions(ioc->pdev, ioc->bars);
|
||||||
|
pci_disable_pcie_error_reporting(pdev);
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -3547,6 +3552,7 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
|
||||||
ioc->pci_irq = -1;
|
ioc->pci_irq = -1;
|
||||||
ioc->chip_phys = 0;
|
ioc->chip_phys = 0;
|
||||||
pci_release_selected_regions(ioc->pdev, ioc->bars);
|
pci_release_selected_regions(ioc->pdev, ioc->bars);
|
||||||
|
pci_disable_pcie_error_reporting(pdev);
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/aer.h>
|
||||||
#include <linux/raid_class.h>
|
#include <linux/raid_class.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
@ -6664,6 +6665,122 @@ _scsih_resume(struct pci_dev *pdev)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _scsih_pci_error_detected - Called when a PCI error is detected.
|
||||||
|
* @pdev: PCI device struct
|
||||||
|
* @state: PCI channel state
|
||||||
|
*
|
||||||
|
* Description: Called when a PCI error is detected.
|
||||||
|
*
|
||||||
|
* Return value:
|
||||||
|
* PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT
|
||||||
|
*/
|
||||||
|
static pci_ers_result_t
|
||||||
|
_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||||
|
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
|
||||||
|
|
||||||
|
printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n",
|
||||||
|
ioc->name, state);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case pci_channel_io_normal:
|
||||||
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
||||||
|
case pci_channel_io_frozen:
|
||||||
|
scsi_block_requests(ioc->shost);
|
||||||
|
mpt2sas_base_stop_watchdog(ioc);
|
||||||
|
mpt2sas_base_free_resources(ioc);
|
||||||
|
return PCI_ERS_RESULT_NEED_RESET;
|
||||||
|
case pci_channel_io_perm_failure:
|
||||||
|
_scsih_remove(pdev);
|
||||||
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
}
|
||||||
|
return PCI_ERS_RESULT_NEED_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _scsih_pci_slot_reset - Called when PCI slot has been reset.
|
||||||
|
* @pdev: PCI device struct
|
||||||
|
*
|
||||||
|
* Description: This routine is called by the pci error recovery
|
||||||
|
* code after the PCI slot has been reset, just before we
|
||||||
|
* should resume normal operations.
|
||||||
|
*/
|
||||||
|
static pci_ers_result_t
|
||||||
|
_scsih_pci_slot_reset(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||||
|
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n",
|
||||||
|
ioc->name);
|
||||||
|
|
||||||
|
ioc->pdev = pdev;
|
||||||
|
rc = mpt2sas_base_map_resources(ioc);
|
||||||
|
if (rc)
|
||||||
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
|
||||||
|
|
||||||
|
rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
|
||||||
|
FORCE_BIG_HAMMER);
|
||||||
|
|
||||||
|
printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name,
|
||||||
|
(rc == 0) ? "success" : "failed");
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
|
else
|
||||||
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _scsih_pci_resume() - resume normal ops after PCI reset
|
||||||
|
* @pdev: pointer to PCI device
|
||||||
|
*
|
||||||
|
* Called when the error recovery driver tells us that its
|
||||||
|
* OK to resume normal operation. Use completion to allow
|
||||||
|
* halted scsi ops to resume.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
_scsih_pci_resume(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||||
|
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
|
||||||
|
|
||||||
|
printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name);
|
||||||
|
|
||||||
|
pci_cleanup_aer_uncorrect_error_status(pdev);
|
||||||
|
mpt2sas_base_start_watchdog(ioc);
|
||||||
|
scsi_unblock_requests(ioc->shost);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers
|
||||||
|
* @pdev: pointer to PCI device
|
||||||
|
*/
|
||||||
|
static pci_ers_result_t
|
||||||
|
_scsih_pci_mmio_enabled(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||||
|
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
|
||||||
|
|
||||||
|
printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n",
|
||||||
|
ioc->name);
|
||||||
|
|
||||||
|
/* TODO - dump whatever for debugging purposes */
|
||||||
|
|
||||||
|
/* Request a slot reset. */
|
||||||
|
return PCI_ERS_RESULT_NEED_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_error_handlers _scsih_err_handler = {
|
||||||
|
.error_detected = _scsih_pci_error_detected,
|
||||||
|
.mmio_enabled = _scsih_pci_mmio_enabled,
|
||||||
|
.slot_reset = _scsih_pci_slot_reset,
|
||||||
|
.resume = _scsih_pci_resume,
|
||||||
|
};
|
||||||
|
|
||||||
static struct pci_driver scsih_driver = {
|
static struct pci_driver scsih_driver = {
|
||||||
.name = MPT2SAS_DRIVER_NAME,
|
.name = MPT2SAS_DRIVER_NAME,
|
||||||
|
@ -6671,6 +6788,7 @@ static struct pci_driver scsih_driver = {
|
||||||
.probe = _scsih_probe,
|
.probe = _scsih_probe,
|
||||||
.remove = __devexit_p(_scsih_remove),
|
.remove = __devexit_p(_scsih_remove),
|
||||||
.shutdown = _scsih_shutdown,
|
.shutdown = _scsih_shutdown,
|
||||||
|
.err_handler = &_scsih_err_handler,
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
.suspend = _scsih_suspend,
|
.suspend = _scsih_suspend,
|
||||||
.resume = _scsih_resume,
|
.resume = _scsih_resume,
|
||||||
|
|
Loading…
Reference in a new issue