[SCSI] lpfc 8.3.5: Add AER support
Signed-off-by: James Smart <james.smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
parent
6a9c52cf22
commit
0d87841997
4 changed files with 278 additions and 24 deletions
|
@ -534,6 +534,7 @@ struct lpfc_hba {
|
|||
#define ASYNC_EVENT 0x80
|
||||
#define LINK_DISABLED 0x100 /* Link disabled by user */
|
||||
#define FCF_DISC_INPROGRESS 0x200 /* FCF discovery in progress */
|
||||
#define HBA_AER_ENABLED 0x800 /* AER enabled with HBA */
|
||||
struct lpfc_dmabuf slim2p;
|
||||
|
||||
MAILBOX_t *mbox;
|
||||
|
@ -607,6 +608,7 @@ struct lpfc_hba {
|
|||
uint32_t cfg_enable_bg;
|
||||
uint32_t cfg_enable_fip;
|
||||
uint32_t cfg_log_verbose;
|
||||
uint32_t cfg_aer_support;
|
||||
|
||||
lpfc_vpd_t vpd; /* vital product data */
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/aer.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
|
@ -2765,6 +2766,179 @@ lpfc_link_speed_init(struct lpfc_hba *phba, int val)
|
|||
static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
|
||||
lpfc_link_speed_show, lpfc_link_speed_store);
|
||||
|
||||
/*
|
||||
# lpfc_aer_support: Support PCIe device Advanced Error Reporting (AER)
|
||||
# 0 = aer disabled or not supported
|
||||
# 1 = aer supported and enabled (default)
|
||||
# Value range is [0,1]. Default value is 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* lpfc_aer_support_store - Set the adapter for aer support
|
||||
*
|
||||
* @dev: class device that is converted into a Scsi_host.
|
||||
* @attr: device attribute, not used.
|
||||
* @buf: containing the string "selective".
|
||||
* @count: unused variable.
|
||||
*
|
||||
* Description:
|
||||
* If the val is 1 and currently the device's AER capability was not
|
||||
* enabled, invoke the kernel's enable AER helper routine, trying to
|
||||
* enable the device's AER capability. If the helper routine enabling
|
||||
* AER returns success, update the device's cfg_aer_support flag to
|
||||
* indicate AER is supported by the device; otherwise, if the device
|
||||
* AER capability is already enabled to support AER, then do nothing.
|
||||
*
|
||||
* If the val is 0 and currently the device's AER support was enabled,
|
||||
* invoke the kernel's disable AER helper routine. After that, update
|
||||
* the device's cfg_aer_support flag to indicate AER is not supported
|
||||
* by the device; otherwise, if the device AER capability is already
|
||||
* disabled from supporting AER, then do nothing.
|
||||
*
|
||||
* Returns:
|
||||
* length of the buf on success if val is in range the intended mode
|
||||
* is supported.
|
||||
* -EINVAL if val out of range or intended mode is not supported.
|
||||
**/
|
||||
static ssize_t
|
||||
lpfc_aer_support_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct Scsi_Host *shost = class_to_shost(dev);
|
||||
struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
|
||||
struct lpfc_hba *phba = vport->phba;
|
||||
int val = 0, rc = -EINVAL;
|
||||
|
||||
if (!isdigit(buf[0]))
|
||||
return -EINVAL;
|
||||
if (sscanf(buf, "%i", &val) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
if (phba->hba_flag & HBA_AER_ENABLED) {
|
||||
rc = pci_disable_pcie_error_reporting(phba->pcidev);
|
||||
if (!rc) {
|
||||
spin_lock_irq(&phba->hbalock);
|
||||
phba->hba_flag &= ~HBA_AER_ENABLED;
|
||||
spin_unlock_irq(&phba->hbalock);
|
||||
phba->cfg_aer_support = 0;
|
||||
rc = strlen(buf);
|
||||
} else
|
||||
rc = -EINVAL;
|
||||
} else
|
||||
phba->cfg_aer_support = 0;
|
||||
rc = strlen(buf);
|
||||
break;
|
||||
case 1:
|
||||
if (!(phba->hba_flag & HBA_AER_ENABLED)) {
|
||||
rc = pci_enable_pcie_error_reporting(phba->pcidev);
|
||||
if (!rc) {
|
||||
spin_lock_irq(&phba->hbalock);
|
||||
phba->hba_flag |= HBA_AER_ENABLED;
|
||||
spin_unlock_irq(&phba->hbalock);
|
||||
phba->cfg_aer_support = 1;
|
||||
rc = strlen(buf);
|
||||
} else
|
||||
rc = -EINVAL;
|
||||
} else
|
||||
phba->cfg_aer_support = 1;
|
||||
rc = strlen(buf);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int lpfc_aer_support = 1;
|
||||
module_param(lpfc_aer_support, int, 1);
|
||||
MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
|
||||
lpfc_param_show(aer_support)
|
||||
|
||||
/**
|
||||
* lpfc_aer_support_init - Set the initial adapters aer support flag
|
||||
* @phba: lpfc_hba pointer.
|
||||
* @val: link speed value.
|
||||
*
|
||||
* Description:
|
||||
* If val is in a valid range [0,1], then set the adapter's initial
|
||||
* cfg_aer_support field. It will be up to the driver's probe_one
|
||||
* routine to determine whether the device's AER support can be set
|
||||
* or not.
|
||||
*
|
||||
* Notes:
|
||||
* If the value is not in range log a kernel error message, and
|
||||
* choose the default value of setting AER support and return.
|
||||
*
|
||||
* Returns:
|
||||
* zero if val saved.
|
||||
* -EINVAL val out of range
|
||||
**/
|
||||
static int
|
||||
lpfc_aer_support_init(struct lpfc_hba *phba, int val)
|
||||
{
|
||||
if (val == 0 || val == 1) {
|
||||
phba->cfg_aer_support = val;
|
||||
return 0;
|
||||
}
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"2712 lpfc_aer_support attribute value %d out "
|
||||
"of range, allowed values are 0|1, setting it "
|
||||
"to default value of 1\n", val);
|
||||
phba->cfg_aer_support = 1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
|
||||
lpfc_aer_support_show, lpfc_aer_support_store);
|
||||
|
||||
/**
|
||||
* lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device
|
||||
* @dev: class device that is converted into a Scsi_host.
|
||||
* @attr: device attribute, not used.
|
||||
* @buf: containing the string "selective".
|
||||
* @count: unused variable.
|
||||
*
|
||||
* Description:
|
||||
* If the @buf contains 1 and the device currently has the AER support
|
||||
* enabled, then invokes the kernel AER helper routine
|
||||
* pci_cleanup_aer_uncorrect_error_status to clean up the uncorrectable
|
||||
* error status register.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* Returns:
|
||||
* -EINVAL if the buf does not contain the 1 or the device is not currently
|
||||
* enabled with the AER support.
|
||||
**/
|
||||
static ssize_t
|
||||
lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct Scsi_Host *shost = class_to_shost(dev);
|
||||
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
|
||||
struct lpfc_hba *phba = vport->phba;
|
||||
int val, rc = -1;
|
||||
|
||||
if (!isdigit(buf[0]))
|
||||
return -EINVAL;
|
||||
if (sscanf(buf, "%i", &val) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (val == 1 && phba->hba_flag & HBA_AER_ENABLED)
|
||||
rc = pci_cleanup_aer_uncorrect_error_status(phba->pcidev);
|
||||
|
||||
if (rc == 0)
|
||||
return strlen(buf);
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL,
|
||||
lpfc_aer_cleanup_state);
|
||||
|
||||
/*
|
||||
# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
|
||||
# Value range is [2,3]. Default value is 3.
|
||||
|
@ -3068,6 +3242,8 @@ struct device_attribute *lpfc_hba_attrs[] = {
|
|||
&dev_attr_lpfc_max_scsicmpl_time,
|
||||
&dev_attr_lpfc_stat_data_ctrl,
|
||||
&dev_attr_lpfc_prot_sg_seg_cnt,
|
||||
&dev_attr_lpfc_aer_support,
|
||||
&dev_attr_lpfc_aer_state_cleanup,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -4244,6 +4420,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
|
|||
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
|
||||
lpfc_enable_fip_init(phba, lpfc_enable_fip);
|
||||
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
|
||||
lpfc_aer_support_init(phba, lpfc_aer_support);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/aer.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
|
@ -7098,6 +7099,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
|
|||
/* Restore device state from PCI config space */
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
|
||||
if (pdev->is_busmaster)
|
||||
pci_set_master(pdev);
|
||||
|
||||
|
@ -7131,6 +7133,53 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpfc_sli_prep_dev_for_reset - Prepare SLI3 device for pci slot reset
|
||||
* @phba: pointer to lpfc hba data structure.
|
||||
*
|
||||
* This routine is called to prepare the SLI3 device for PCI slot reset. It
|
||||
* disables the device interrupt and pci device, and aborts the internal FCP
|
||||
* pending I/Os.
|
||||
**/
|
||||
static void
|
||||
lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
|
||||
{
|
||||
struct lpfc_sli *psli = &phba->sli;
|
||||
struct lpfc_sli_ring *pring;
|
||||
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"2710 PCI channel I/O frozen\n");
|
||||
/* Disable interrupt and pci device */
|
||||
lpfc_sli_disable_intr(phba);
|
||||
pci_disable_device(phba->pcidev);
|
||||
/*
|
||||
* There may be I/Os dropped by the firmware.
|
||||
* Error iocb (I/O) on txcmplq and let the SCSI layer
|
||||
* retry it after re-establishing link.
|
||||
*/
|
||||
pring = &psli->ring[psli->fcp_ring];
|
||||
lpfc_sli_abort_iocb_ring(phba, pring);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpfc_sli_prep_dev_for_perm_failure - Prepare SLI3 dev for pci slot disable
|
||||
* @phba: pointer to lpfc hba data structure.
|
||||
*
|
||||
* This routine is called to prepare the SLI3 device for PCI slot permanently
|
||||
* disabling. It blocks the SCSI transport layer traffic and flushes the FCP
|
||||
* pending I/Os.
|
||||
**/
|
||||
static void
|
||||
lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba)
|
||||
{
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"2711 PCI channel I/O permanent failure\n");
|
||||
/* Block all SCSI devices' I/Os on the host */
|
||||
lpfc_scsi_dev_block(phba);
|
||||
/* Clean up all driver's outstanding SCSI I/Os */
|
||||
lpfc_sli_flush_fcp_rings(phba);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error
|
||||
* @pdev: pointer to PCI device.
|
||||
|
@ -7145,6 +7194,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
|
|||
* as desired.
|
||||
*
|
||||
* Return codes
|
||||
* PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link
|
||||
* PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
|
||||
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
|
||||
**/
|
||||
|
@ -7153,33 +7203,26 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
|
|||
{
|
||||
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
||||
struct lpfc_sli *psli = &phba->sli;
|
||||
struct lpfc_sli_ring *pring;
|
||||
|
||||
if (state == pci_channel_io_perm_failure) {
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"0472 PCI channel I/O permanent failure\n");
|
||||
/* Block all SCSI devices' I/Os on the host */
|
||||
lpfc_scsi_dev_block(phba);
|
||||
/* Clean up all driver's outstanding SCSI I/Os */
|
||||
lpfc_sli_flush_fcp_rings(phba);
|
||||
switch (state) {
|
||||
case pci_channel_io_normal:
|
||||
/* Non-fatal error, do nothing */
|
||||
return PCI_ERS_RESULT_CAN_RECOVER;
|
||||
case pci_channel_io_frozen:
|
||||
/* Fatal error, prepare for slot reset */
|
||||
lpfc_sli_prep_dev_for_reset(phba);
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
case pci_channel_io_perm_failure:
|
||||
/* Permanent failure, prepare for device down */
|
||||
lpfc_prep_dev_for_perm_failure(phba);
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
default:
|
||||
/* Unknown state, prepare and request slot reset */
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"0472 Unknown PCI error state: x%x\n", state);
|
||||
lpfc_sli_prep_dev_for_reset(phba);
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
}
|
||||
|
||||
pci_disable_device(pdev);
|
||||
/*
|
||||
* There may be I/Os dropped by the firmware.
|
||||
* Error iocb (I/O) on txcmplq and let the SCSI layer
|
||||
* retry it after re-establishing link.
|
||||
*/
|
||||
pring = &psli->ring[psli->fcp_ring];
|
||||
lpfc_sli_abort_iocb_ring(phba, pring);
|
||||
|
||||
/* Disable interrupt */
|
||||
lpfc_sli_disable_intr(phba);
|
||||
|
||||
/* Request a slot reset. */
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7259,7 +7302,12 @@ lpfc_io_resume_s3(struct pci_dev *pdev)
|
|||
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||||
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
||||
|
||||
/* Bring the device online */
|
||||
lpfc_online(phba);
|
||||
|
||||
/* Clean up Advanced Error Reporting (AER) if needed */
|
||||
if (phba->hba_flag & HBA_AER_ENABLED)
|
||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_transport_fc.h>
|
||||
#include <scsi/fc/fc_fs.h>
|
||||
#include <linux/aer.h>
|
||||
|
||||
#include "lpfc_hw4.h"
|
||||
#include "lpfc_hw.h"
|
||||
|
@ -3551,9 +3552,13 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
|
|||
struct lpfc_sli *psli;
|
||||
volatile uint32_t word0;
|
||||
void __iomem *to_slim;
|
||||
uint32_t hba_aer_enabled;
|
||||
|
||||
spin_lock_irq(&phba->hbalock);
|
||||
|
||||
/* Take PCIe device Advanced Error Reporting (AER) state */
|
||||
hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
|
||||
|
||||
psli = &phba->sli;
|
||||
|
||||
/* Restart HBA */
|
||||
|
@ -3593,6 +3598,10 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
|
|||
/* Give the INITFF and Post time to settle. */
|
||||
mdelay(100);
|
||||
|
||||
/* Reset HBA AER if it was enabled, note hba_flag was reset above */
|
||||
if (hba_aer_enabled)
|
||||
pci_disable_pcie_error_reporting(phba->pcidev);
|
||||
|
||||
lpfc_hba_down_post(phba);
|
||||
|
||||
return 0;
|
||||
|
@ -4062,6 +4071,24 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
|
|||
if (rc)
|
||||
goto lpfc_sli_hba_setup_error;
|
||||
|
||||
/* Enable PCIe device Advanced Error Reporting (AER) if configured */
|
||||
if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
|
||||
rc = pci_enable_pcie_error_reporting(phba->pcidev);
|
||||
if (!rc) {
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
||||
"2709 This device supports "
|
||||
"Advanced Error Reporting (AER)\n");
|
||||
spin_lock_irq(&phba->hbalock);
|
||||
phba->hba_flag |= HBA_AER_ENABLED;
|
||||
spin_unlock_irq(&phba->hbalock);
|
||||
} else {
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
||||
"2708 This device does not support "
|
||||
"Advanced Error Reporting (AER)\n");
|
||||
phba->cfg_aer_support = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (phba->sli_rev == 3) {
|
||||
phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE;
|
||||
phba->iocb_rsp_size = SLI3_IOCB_RSP_SIZE;
|
||||
|
|
Reference in a new issue