Commit 580ada3c authored by Stephen M. Cameron's avatar Stephen M. Cameron Committed by James Bottomley

[SCSI] hpsa: do a better job of detecting controller reset failure

Detect failure of controller reset by noticing if the 32 bytes of
"driver version" we store on the hardware in the config table
fail to get zeroed out.  Previously we noticed if the controller
did not transition to "simple mode", but this did not detect reset
failure if the controller was already in simple mode prior to
the reset attempt (e.g. due to module parameter hpsa_simple_mode=1).
Signed-off-by: default avatarStephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: default avatarJames Bottomley <jbottomley@parallels.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent a2a431a4
...@@ -3184,6 +3184,59 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev, ...@@ -3184,6 +3184,59 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
return 0; return 0;
} }
static __devinit void init_driver_version(char *driver_version, int len)
{
memset(driver_version, 0, len);
strncpy(driver_version, "hpsa " HPSA_DRIVER_VERSION, len - 1);
}
static __devinit int write_driver_ver_to_cfgtable(
struct CfgTable __iomem *cfgtable)
{
char *driver_version;
int i, size = sizeof(cfgtable->driver_version);
driver_version = kmalloc(size, GFP_KERNEL);
if (!driver_version)
return -ENOMEM;
init_driver_version(driver_version, size);
for (i = 0; i < size; i++)
writeb(driver_version[i], &cfgtable->driver_version[i]);
kfree(driver_version);
return 0;
}
static __devinit void read_driver_ver_from_cfgtable(
struct CfgTable __iomem *cfgtable, unsigned char *driver_ver)
{
int i;
for (i = 0; i < sizeof(cfgtable->driver_version); i++)
driver_ver[i] = readb(&cfgtable->driver_version[i]);
}
static __devinit int controller_reset_failed(
struct CfgTable __iomem *cfgtable)
{
char *driver_ver, *old_driver_ver;
int rc, size = sizeof(cfgtable->driver_version);
old_driver_ver = kmalloc(2 * size, GFP_KERNEL);
if (!old_driver_ver)
return -ENOMEM;
driver_ver = old_driver_ver + size;
/* After a reset, the 32 bytes of "driver version" in the cfgtable
* should have been changed, otherwise we know the reset failed.
*/
init_driver_version(old_driver_ver, size);
read_driver_ver_from_cfgtable(cfgtable, driver_ver);
rc = !memcmp(driver_ver, old_driver_ver, size);
kfree(old_driver_ver);
return rc;
}
/* This does a hard reset of the controller using PCI power management /* This does a hard reset of the controller using PCI power management
* states or the using the doorbell register. * states or the using the doorbell register.
*/ */
...@@ -3194,7 +3247,7 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) ...@@ -3194,7 +3247,7 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
u64 cfg_base_addr_index; u64 cfg_base_addr_index;
void __iomem *vaddr; void __iomem *vaddr;
unsigned long paddr; unsigned long paddr;
u32 misc_fw_support, active_transport; u32 misc_fw_support;
int rc; int rc;
struct CfgTable __iomem *cfgtable; struct CfgTable __iomem *cfgtable;
bool use_doorbell; bool use_doorbell;
...@@ -3256,6 +3309,9 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) ...@@ -3256,6 +3309,9 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
rc = -ENOMEM; rc = -ENOMEM;
goto unmap_vaddr; goto unmap_vaddr;
} }
rc = write_driver_ver_to_cfgtable(cfgtable);
if (rc)
goto unmap_vaddr;
/* If reset via doorbell register is supported, use that. */ /* If reset via doorbell register is supported, use that. */
misc_fw_support = readl(&cfgtable->misc_fw_support); misc_fw_support = readl(&cfgtable->misc_fw_support);
...@@ -3289,19 +3345,16 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) ...@@ -3289,19 +3345,16 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
"failed waiting for board to become ready\n"); "failed waiting for board to become ready\n");
goto unmap_cfgtable; goto unmap_cfgtable;
} }
dev_info(&pdev->dev, "board ready.\n");
/* Controller should be in simple mode at this point. If it's not, rc = controller_reset_failed(vaddr);
* It means we're on one of those controllers which doesn't support if (rc < 0)
* the doorbell reset method and on which the PCI power management reset goto unmap_cfgtable;
* method doesn't work (P800, for example.) if (rc) {
* In those cases, don't try to proceed, as it generally doesn't work.
*/
active_transport = readl(&cfgtable->TransportActive);
if (active_transport & PERFORMANT_MODE) {
dev_warn(&pdev->dev, "Unable to successfully reset controller," dev_warn(&pdev->dev, "Unable to successfully reset controller,"
" Ignoring controller.\n"); " Ignoring controller.\n");
rc = -ENODEV; rc = -ENODEV;
} else {
dev_info(&pdev->dev, "board ready.\n");
} }
unmap_cfgtable: unmap_cfgtable:
...@@ -3542,6 +3595,9 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h) ...@@ -3542,6 +3595,9 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
cfg_base_addr_index) + cfg_offset, sizeof(*h->cfgtable)); cfg_base_addr_index) + cfg_offset, sizeof(*h->cfgtable));
if (!h->cfgtable) if (!h->cfgtable)
return -ENOMEM; return -ENOMEM;
rc = write_driver_ver_to_cfgtable(h->cfgtable);
if (rc)
return rc;
/* Find performant mode table. */ /* Find performant mode table. */
trans_offset = readl(&h->cfgtable->TransMethodOffset); trans_offset = readl(&h->cfgtable->TransMethodOffset);
h->transtable = remap_pci_mem(pci_resource_start(h->pdev, h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
......
...@@ -337,6 +337,7 @@ struct CfgTable { ...@@ -337,6 +337,7 @@ struct CfgTable {
u8 reserved[0x78 - 0x58]; u8 reserved[0x78 - 0x58];
u32 misc_fw_support; /* offset 0x78 */ u32 misc_fw_support; /* offset 0x78 */
#define MISC_FW_DOORBELL_RESET (0x02) #define MISC_FW_DOORBELL_RESET (0x02)
u8 driver_version[32];
}; };
#define NUM_BLOCKFETCH_ENTRIES 8 #define NUM_BLOCKFETCH_ENTRIES 8
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment