Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef7c80c1 authored by Kashyap, Desai's avatar Kashyap, Desai Committed by James Bottomley
Browse files

[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: default avatarKashyap Desai <kashyap.desai@lsi.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent ebda4d38
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -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;
}
}
+118 −0
Original line number Original line Diff line number Diff line
@@ -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,