aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorStephen M. Cameron <scameron@beardog.cce.hp.com>2011-05-03 15:53:00 -0400
committerJens Axboe <jaxboe@fusionio.com>2011-05-06 10:23:46 -0400
commit62710ae1ceb839de1eebb5b4492ec8a7fbcf8d02 (patch)
treee11fe26e7d281618ba47a95a91ea26ecb7643973 /drivers
parent9bd3c20487b7c13d397dc11dd51e30256bf4c9b3 (diff)
cciss: 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: Stephen M. Cameron <scameron@beardog.cce.hp.com> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/cciss.c82
-rw-r--r--drivers/block/cciss_cmd.h1
2 files changed, 73 insertions, 10 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 9bf13988f1a2..ed6aa83d86dd 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -194,6 +194,8 @@ static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev,
194static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev, 194static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev,
195 unsigned long *memory_bar); 195 unsigned long *memory_bar);
196static inline u32 cciss_tag_discard_error_bits(ctlr_info_t *h, u32 tag); 196static inline u32 cciss_tag_discard_error_bits(ctlr_info_t *h, u32 tag);
197static __devinit int write_driver_ver_to_cfgtable(
198 CfgTable_struct __iomem *cfgtable);
197 199
198/* performant mode helper functions */ 200/* performant mode helper functions */
199static void calc_bucket_map(int *bucket, int num_buckets, int nsgs, 201static void calc_bucket_map(int *bucket, int num_buckets, int nsgs,
@@ -4078,6 +4080,9 @@ static int __devinit cciss_find_cfgtables(ctlr_info_t *h)
4078 cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable)); 4080 cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable));
4079 if (!h->cfgtable) 4081 if (!h->cfgtable)
4080 return -ENOMEM; 4082 return -ENOMEM;
4083 rc = write_driver_ver_to_cfgtable(h->cfgtable);
4084 if (rc)
4085 return rc;
4081 /* Find performant mode table. */ 4086 /* Find performant mode table. */
4082 trans_offset = readl(&h->cfgtable->TransMethodOffset); 4087 trans_offset = readl(&h->cfgtable->TransMethodOffset);
4083 h->transtable = remap_pci_mem(pci_resource_start(h->pdev, 4088 h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
@@ -4428,6 +4433,60 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev,
4428 return 0; 4433 return 0;
4429} 4434}
4430 4435
4436static __devinit void init_driver_version(char *driver_version, int len)
4437{
4438 memset(driver_version, 0, len);
4439 strncpy(driver_version, "cciss " DRIVER_NAME, len - 1);
4440}
4441
4442static __devinit int write_driver_ver_to_cfgtable(
4443 CfgTable_struct __iomem *cfgtable)
4444{
4445 char *driver_version;
4446 int i, size = sizeof(cfgtable->driver_version);
4447
4448 driver_version = kmalloc(size, GFP_KERNEL);
4449 if (!driver_version)
4450 return -ENOMEM;
4451
4452 init_driver_version(driver_version, size);
4453 for (i = 0; i < size; i++)
4454 writeb(driver_version[i], &cfgtable->driver_version[i]);
4455 kfree(driver_version);
4456 return 0;
4457}
4458
4459static __devinit void read_driver_ver_from_cfgtable(
4460 CfgTable_struct __iomem *cfgtable, unsigned char *driver_ver)
4461{
4462 int i;
4463
4464 for (i = 0; i < sizeof(cfgtable->driver_version); i++)
4465 driver_ver[i] = readb(&cfgtable->driver_version[i]);
4466}
4467
4468static __devinit int controller_reset_failed(
4469 CfgTable_struct __iomem *cfgtable)
4470{
4471
4472 char *driver_ver, *old_driver_ver;
4473 int rc, size = sizeof(cfgtable->driver_version);
4474
4475 old_driver_ver = kmalloc(2 * size, GFP_KERNEL);
4476 if (!old_driver_ver)
4477 return -ENOMEM;
4478 driver_ver = old_driver_ver + size;
4479
4480 /* After a reset, the 32 bytes of "driver version" in the cfgtable
4481 * should have been changed, otherwise we know the reset failed.
4482 */
4483 init_driver_version(old_driver_ver, size);
4484 read_driver_ver_from_cfgtable(cfgtable, driver_ver);
4485 rc = !memcmp(driver_ver, old_driver_ver, size);
4486 kfree(old_driver_ver);
4487 return rc;
4488}
4489
4431/* This does a hard reset of the controller using PCI power management 4490/* This does a hard reset of the controller using PCI power management
4432 * states or using the doorbell register. */ 4491 * states or using the doorbell register. */
4433static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) 4492static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
@@ -4437,7 +4496,7 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
4437 u64 cfg_base_addr_index; 4496 u64 cfg_base_addr_index;
4438 void __iomem *vaddr; 4497 void __iomem *vaddr;
4439 unsigned long paddr; 4498 unsigned long paddr;
4440 u32 misc_fw_support, active_transport; 4499 u32 misc_fw_support;
4441 int rc; 4500 int rc;
4442 CfgTable_struct __iomem *cfgtable; 4501 CfgTable_struct __iomem *cfgtable;
4443 bool use_doorbell; 4502 bool use_doorbell;
@@ -4497,6 +4556,9 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
4497 rc = -ENOMEM; 4556 rc = -ENOMEM;
4498 goto unmap_vaddr; 4557 goto unmap_vaddr;
4499 } 4558 }
4559 rc = write_driver_ver_to_cfgtable(cfgtable);
4560 if (rc)
4561 goto unmap_vaddr;
4500 4562
4501 /* If reset via doorbell register is supported, use that. */ 4563 /* If reset via doorbell register is supported, use that. */
4502 misc_fw_support = readl(&cfgtable->misc_fw_support); 4564 misc_fw_support = readl(&cfgtable->misc_fw_support);
@@ -4535,21 +4597,21 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
4535 "failed waiting for board to become ready\n"); 4597 "failed waiting for board to become ready\n");
4536 goto unmap_cfgtable; 4598 goto unmap_cfgtable;
4537 } 4599 }
4538 dev_info(&pdev->dev, "board ready.\n");
4539 4600
4540 /* Controller should be in simple mode at this point. If it's not, 4601 rc = controller_reset_failed(vaddr);
4541 * It means we're on one of those controllers which doesn't support 4602 if (rc < 0)
4542 * the doorbell reset method and on which the PCI power management reset 4603 goto unmap_cfgtable;
4543 * method doesn't work (P800, for example.) 4604 if (rc) {
4544 * In those cases, don't try to proceed, as it generally doesn't work.
4545 */
4546 active_transport = readl(&cfgtable->TransportActive);
4547 if (active_transport & PERFORMANT_MODE) {
4548 dev_warn(&pdev->dev, "Unable to successfully reset controller," 4605 dev_warn(&pdev->dev, "Unable to successfully reset controller,"
4549 " Ignoring controller.\n"); 4606 " Ignoring controller.\n");
4550 rc = -ENODEV; 4607 rc = -ENODEV;
4608 goto unmap_cfgtable;
4609 } else {
4610 dev_info(&pdev->dev, "board ready.\n");
4551 } 4611 }
4552 4612
4613 dev_info(&pdev->dev, "board ready.\n");
4614
4553unmap_cfgtable: 4615unmap_cfgtable:
4554 iounmap(cfgtable); 4616 iounmap(cfgtable);
4555 4617
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index cd441bef031f..a2e68d21efe3 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -235,6 +235,7 @@ typedef struct _CfgTable_struct {
235 u8 reserved[0x78 - 0x58]; 235 u8 reserved[0x78 - 0x58];
236 u32 misc_fw_support; /* offset 0x78 */ 236 u32 misc_fw_support; /* offset 0x78 */
237#define MISC_FW_DOORBELL_RESET (0x02) 237#define MISC_FW_DOORBELL_RESET (0x02)
238 u8 driver_version[32];
238} CfgTable_struct; 239} CfgTable_struct;
239 240
240struct TransTable_struct { 241struct TransTable_struct {