aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJack Hammer <jack_hammer@adaptec.com>2005-08-29 10:44:34 -0400
committerJames Bottomley <jejb@mulgrave.(none)>2005-10-31 21:17:16 -0500
commitee807c2d43b54183c16580857837dae8ccb2ed22 (patch)
tree8e99189a4dcb3dd76339051f5fa240ad9819e4c5 /drivers/scsi
parenta3632fa3ecefe50d88fc70af90610f79b99e0715 (diff)
[SCSI] ips: Fix initialization bug with kdump
If I/O is active on the adapter, and an unexpected interrupt is pending during initialization, the driver blows it's brains out. Since the driver didn't initiate the I/O, the data in it's internal tables will contain NULL pointers. When this condition is detected, a "flush cache and reset" is performed. The flush cache allows any pending "lazy writes" that the adapter is processing to complete ( a "must have" for a RAID adapter ) and the reset puts the adapter back into a known, good state. Signed-off-by: Jack Hammer <jack_hammer@adaptec.com> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/ips.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 0a252e7aca6e..749c95bb7df7 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -355,6 +355,9 @@ static int ips_init_phase2(int index);
355static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr); 355static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr);
356static int ips_register_scsi(int index); 356static int ips_register_scsi(int index);
357 357
358static int ips_poll_for_flush_complete(ips_ha_t * ha);
359static void ips_flush_and_reset(ips_ha_t *ha);
360
358/* 361/*
359 * global variables 362 * global variables
360 */ 363 */
@@ -4830,6 +4833,9 @@ ips_isinit_morpheus(ips_ha_t * ha)
4830 uint32_t bits; 4833 uint32_t bits;
4831 4834
4832 METHOD_TRACE("ips_is_init_morpheus", 1); 4835 METHOD_TRACE("ips_is_init_morpheus", 1);
4836
4837 if (ips_isintr_morpheus(ha))
4838 ips_flush_and_reset(ha);
4833 4839
4834 post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); 4840 post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
4835 bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR); 4841 bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
@@ -4844,6 +4850,93 @@ ips_isinit_morpheus(ips_ha_t * ha)
4844 4850
4845/****************************************************************************/ 4851/****************************************************************************/
4846/* */ 4852/* */
4853/* Routine Name: ips_flush_and_reset */
4854/* */
4855/* Routine Description: */
4856/* */
4857/* Perform cleanup ( FLUSH and RESET ) when the adapter is in an unknown */
4858/* state ( was trying to INIT and an interrupt was already pending ) ... */
4859/* */
4860/****************************************************************************/
4861static void
4862ips_flush_and_reset(ips_ha_t *ha)
4863{
4864 ips_scb_t *scb;
4865 int ret;
4866 int time;
4867 int done;
4868 dma_addr_t command_dma;
4869
4870 /* Create a usuable SCB */
4871 scb = pci_alloc_consistent(ha->pcidev, sizeof(ips_scb_t), &command_dma);
4872 if (scb) {
4873 memset(scb, 0, sizeof(ips_scb_t));
4874 ips_init_scb(ha, scb);
4875 scb->scb_busaddr = command_dma;
4876
4877 scb->timeout = ips_cmd_timeout;
4878 scb->cdb[0] = IPS_CMD_FLUSH;
4879
4880 scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
4881 scb->cmd.flush_cache.command_id = IPS_MAX_CMDS; /* Use an ID that would otherwise not exist */
4882 scb->cmd.flush_cache.state = IPS_NORM_STATE;
4883 scb->cmd.flush_cache.reserved = 0;
4884 scb->cmd.flush_cache.reserved2 = 0;
4885 scb->cmd.flush_cache.reserved3 = 0;
4886 scb->cmd.flush_cache.reserved4 = 0;
4887
4888 ret = ips_send_cmd(ha, scb); /* Send the Flush Command */
4889
4890 if (ret == IPS_SUCCESS) {
4891 time = 60 * IPS_ONE_SEC; /* Max Wait time is 60 seconds */
4892 done = 0;
4893
4894 while ((time > 0) && (!done)) {
4895 done = ips_poll_for_flush_complete(ha);
4896 /* This may look evil, but it's only done during extremely rare start-up conditions ! */
4897 udelay(1000);
4898 time--;
4899 }
4900 }
4901 }
4902
4903 /* Now RESET and INIT the adapter */
4904 (*ha->func.reset) (ha);
4905
4906 pci_free_consistent(ha->pcidev, sizeof(ips_scb_t), scb, command_dma);
4907 return;
4908}
4909
4910/****************************************************************************/
4911/* */
4912/* Routine Name: ips_poll_for_flush_complete */
4913/* */
4914/* Routine Description: */
4915/* */
4916/* Poll for the Flush Command issued by ips_flush_and_reset() to complete */
4917/* All other responses are just taken off the queue and ignored */
4918/* */
4919/****************************************************************************/
4920static int
4921ips_poll_for_flush_complete(ips_ha_t * ha)
4922{
4923 IPS_STATUS cstatus;
4924
4925 while (TRUE) {
4926 cstatus.value = (*ha->func.statupd) (ha);
4927
4928 if (cstatus.value == 0xffffffff) /* If No Interrupt to process */
4929 break;
4930
4931 /* Success is when we see the Flush Command ID */
4932 if (cstatus.fields.command_id == IPS_MAX_CMDS )
4933 return 1;
4934 }
4935
4936 return 0;
4937
4938/****************************************************************************/
4939/* */
4847/* Routine Name: ips_enable_int_copperhead */ 4940/* Routine Name: ips_enable_int_copperhead */
4848/* */ 4941/* */
4849/* Routine Description: */ 4942/* Routine Description: */