diff options
author | Jack Hammer <jack_hammer@adaptec.com> | 2005-08-29 10:44:34 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2005-10-31 21:17:16 -0500 |
commit | ee807c2d43b54183c16580857837dae8ccb2ed22 (patch) | |
tree | 8e99189a4dcb3dd76339051f5fa240ad9819e4c5 /drivers/scsi/ips.c | |
parent | a3632fa3ecefe50d88fc70af90610f79b99e0715 (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/ips.c')
-rw-r--r-- | drivers/scsi/ips.c | 93 |
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); | |||
355 | static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr); | 355 | static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr); |
356 | static int ips_register_scsi(int index); | 356 | static int ips_register_scsi(int index); |
357 | 357 | ||
358 | static int ips_poll_for_flush_complete(ips_ha_t * ha); | ||
359 | static 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 | /****************************************************************************/ | ||
4861 | static void | ||
4862 | ips_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 | /****************************************************************************/ | ||
4920 | static int | ||
4921 | ips_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: */ |