[SCSI] ips: Fix initialization bug with kdump
authorJack Hammer <jack_hammer@adaptec.com>
Mon, 29 Aug 2005 14:44:34 +0000 (10:44 -0400)
committerJames Bottomley <jejb@mulgrave.(none)>
Tue, 1 Nov 2005 02:17:16 +0000 (18:17 -0800)
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>
drivers/scsi/ips.c

index 0a252e7aca6e4b568b3b51cac1af0af5316c9bc7..749c95bb7df7c0ea34d720584d8feef4b98b6cce 100644 (file)
@@ -355,6 +355,9 @@ static int ips_init_phase2(int index);
 static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr);
 static int ips_register_scsi(int index);
 
+static int  ips_poll_for_flush_complete(ips_ha_t * ha);
+static void ips_flush_and_reset(ips_ha_t *ha);
+
 /*
  * global variables
  */
@@ -4830,6 +4833,9 @@ ips_isinit_morpheus(ips_ha_t * ha)
        uint32_t bits;
 
        METHOD_TRACE("ips_is_init_morpheus", 1);
+   
+       if (ips_isintr_morpheus(ha)) 
+           ips_flush_and_reset(ha);
 
        post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
        bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
@@ -4842,6 +4848,93 @@ ips_isinit_morpheus(ips_ha_t * ha)
                return (1);
 }
 
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_flush_and_reset                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Perform cleanup ( FLUSH and RESET ) when the adapter is in an unknown  */
+/*   state ( was trying to INIT and an interrupt was already pending ) ...  */
+/*                                                                          */
+/****************************************************************************/
+static void 
+ips_flush_and_reset(ips_ha_t *ha)
+{
+       ips_scb_t *scb;
+       int  ret;
+       int  time;
+       int  done;
+       dma_addr_t command_dma;
+
+       /* Create a usuable SCB */
+       scb = pci_alloc_consistent(ha->pcidev, sizeof(ips_scb_t), &command_dma);
+       if (scb) {
+           memset(scb, 0, sizeof(ips_scb_t));
+           ips_init_scb(ha, scb);
+           scb->scb_busaddr = command_dma;
+
+           scb->timeout = ips_cmd_timeout;
+           scb->cdb[0] = IPS_CMD_FLUSH;
+
+           scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+           scb->cmd.flush_cache.command_id = IPS_MAX_CMDS;   /* Use an ID that would otherwise not exist */
+           scb->cmd.flush_cache.state = IPS_NORM_STATE;
+           scb->cmd.flush_cache.reserved = 0;
+           scb->cmd.flush_cache.reserved2 = 0;
+           scb->cmd.flush_cache.reserved3 = 0;
+           scb->cmd.flush_cache.reserved4 = 0;
+
+           ret = ips_send_cmd(ha, scb);                      /* Send the Flush Command */
+
+           if (ret == IPS_SUCCESS) {
+               time = 60 * IPS_ONE_SEC;                      /* Max Wait time is 60 seconds */
+               done = 0;
+                   
+               while ((time > 0) && (!done)) {
+                  done = ips_poll_for_flush_complete(ha);         
+                  /* This may look evil, but it's only done during extremely rare start-up conditions ! */
+                  udelay(1000);
+                  time--;
+               }
+        }
+       }
+
+       /* Now RESET and INIT the adapter */
+       (*ha->func.reset) (ha);
+
+       pci_free_consistent(ha->pcidev, sizeof(ips_scb_t), scb, command_dma);
+       return;
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_poll_for_flush_complete                                */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Poll for the Flush Command issued by ips_flush_and_reset() to complete */
+/*   All other responses are just taken off the queue and ignored           */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_poll_for_flush_complete(ips_ha_t * ha)
+{
+       IPS_STATUS cstatus;
+    
+       while (TRUE) {
+           cstatus.value = (*ha->func.statupd) (ha);
+
+           if (cstatus.value == 0xffffffff)      /* If No Interrupt to process */
+                       break;
+            
+           /* Success is when we see the Flush Command ID */
+           if (cstatus.fields.command_id == IPS_MAX_CMDS ) 
+               return 1;
+        }      
+
+       return 0;
+
 /****************************************************************************/
 /*                                                                          */
 /* Routine Name: ips_enable_int_copperhead                                  */