Empty stale responses before draining aen queue.
Fix tw_scsi_eh_abort() to not reset on every io abort.
Set can_queue in SHT to 255 to prevent hang from AEN.
+ 1.02.00.022 - Fix possible null pointer dereference in tw_scsi_release().
+ 1.02.00.023 - Fix bug in tw_aen_drain_queue() where unit # was always zero.
+ 1.02.00.024 - Add severity levels to AEN strings.
+ 1.02.00.025 - Fix command interrupt spurious error messages.
+ Fix bug in raw command post with data ioctl method.
+ Fix bug where rollcall sometimes failed with cable errors.
+ Print unit # on all command timeouts.
*/
#include <linux/module.h>
MODULE_AUTHOR ("3ware Inc.");
+#ifdef __SMP__
+MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver (SMP)");
+#else
MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
+#endif
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
};
/* Globals */
-char *tw_driver_version="1.02.00.021";
+char *tw_driver_version="1.02.00.025";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
/* Print some useful info when certain aen codes come out */
if (aen == 0x0ff) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: AEN queue overflow.\n", tw_dev->host->host_no);
+ printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: INFO: AEN queue overflow.\n", tw_dev->host->host_no);
} else {
if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
status_reg_addr = tw_dev->registers.status_reg_addr;
response_que_addr = tw_dev->registers.response_que_addr;
- if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 30)) {
+ if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT | TW_STATUS_MICROCONTROLLER_READY, 30)) {
dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
return 1;
}
break;
default:
if (aen == 0x0ff) {
- printk(KERN_WARNING "3w-xxxx: AEN: AEN queue overflow.\n");
+ printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n");
} else {
if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
/* Now put the aen on the aen_queue */
if (queue == 1) {
- tw_dev->aen_queue[tw_dev->aen_tail] = aen_code;
+ tw_dev->aen_queue[tw_dev->aen_tail] = aen;
if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
tw_dev->aen_tail = TW_Q_START;
} else {
}
tw_dev->pending_request_count--;
} else {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Error posting pending commands.\n", tw_dev->host->host_no);
+ /* If we get here, we will continue re-posting on the next command interrupt */
break;
}
}
/* Check for correct state */
if (tw_dev->state[request_id] != TW_S_POSTED) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", tw_dev->host->host_no, request_id, command_packet->byte0.opcode);
- error = 1;
+ /* Handle timed out ioctl's */
+ if (tw_dev->srb[request_id] != 0) {
+ if (tw_dev->srb[request_id]->cmnd[0] != TW_IOCTL) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", tw_dev->host->host_no, request_id, command_packet->byte0.opcode);
+ error = 1;
+ }
+ }
}
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
TW_Passthru *passthru = NULL;
int tw_aen_code, i, use_sg;
char *data_ptr;
- int total_bytes = 0;
+ int total_bytes = 0, posted = 0;
dma_addr_t dma_handle;
+ struct timeval before, timeout;
ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
if (ioctl == NULL) {
if ((u32 *)command_packet->byte8.param.sgl[i].address != NULL) {
error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length);
if (error) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no);
+ dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no);
goto tw_ioctl_bail;
}
} else {
if ((u32 *)command_packet->byte8.io.sgl[i].address != NULL) {
error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length);
if (error) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no);
+ dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no);
goto tw_ioctl_bail;
}
} else {
command_packet->byte8.io.sgl[0].length = total_bytes;
}
+ spin_unlock(&tw_dev->tw_lock);
spin_unlock_irq(tw_dev->host->host_lock);
- spin_unlock_irq(&tw_dev->tw_lock);
+
+ set_bit(TW_IN_IOCTL, &tw_dev->flags);
/* Finally post the command packet */
tw_post_command_packet(tw_dev, request_id);
+ posted = 1;
+ do_gettimeofday(&before);
+ tw_ioctl_retry:
mdelay(TW_IOCTL_WAIT_TIME);
- spin_lock_irq(&tw_dev->tw_lock);
+ if (test_bit(TW_IN_IOCTL, &tw_dev->flags)) {
+ do_gettimeofday(&timeout);
+ if (before.tv_sec + TW_IOCTL_TIMEOUT < timeout.tv_sec) {
+ spin_lock_irq(tw_dev->host->host_lock);
+ spin_lock(&tw_dev->tw_lock);
+ goto tw_ioctl_bail;
+ } else {
+ goto tw_ioctl_retry;
+ }
+ }
+
spin_lock_irq(tw_dev->host->host_lock);
+ spin_lock(&tw_dev->tw_lock);
if (signal_pending(current)) {
dprintk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Signal pending, aborting ioctl().\n", tw_dev->host->host_no);
if ((u32 *)command_save->byte8.param.sgl[i].address != NULL) {
error = copy_to_user((u32 *)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length);
if (error) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no);
+ dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no);
goto tw_ioctl_bail;
}
dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.param.sgl[i].length, current->pid);
if ((u32 *)command_save->byte8.io.sgl[i].address != NULL) {
error = copy_to_user((u32 *)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length);
if (error) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no);
+ dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no);
goto tw_ioctl_bail;
}
dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.io.sgl[i].length, current->pid);
/* Now complete the io */
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
- tw_dev->posted_request_count--;
+ if (posted)
+ tw_dev->posted_request_count--;
tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
return 0;
} else {
break;
case TW_CMD_PACKET_WITH_DATA:
dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n");
+ clear_bit(TW_IN_IOCTL, &tw_dev->flags);
return TW_ISR_DONT_COMPLETE; /* Special case for isr to not complete io */
default:
memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
for (i=0;i<TW_Q_LENGTH;i++) {
if (tw_dev->srb[i] == SCpnt) {
if (tw_dev->state[i] == TW_S_STARTED) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Command (0x%x) timed out.\n", tw_dev->host->host_no, (u32)SCpnt);
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (0x%x) timed out.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, (u32)SCpnt);
tw_dev->state[i] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, i);
spin_unlock(&tw_dev->tw_lock);
return (SUCCESS);
}
if (tw_dev->state[i] == TW_S_PENDING) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Command (0x%x) timed out.\n", tw_dev->host->host_no, (u32)SCpnt);
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (0x%x) timed out.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, (u32)SCpnt);
if (tw_dev->pending_head == TW_Q_LENGTH-1) {
tw_dev->pending_head = TW_Q_START;
} else {
}
if (tw_dev->state[i] == TW_S_POSTED) {
/* If the command has already been posted, we have to reset the card */
- printk(KERN_WARNING "3w-xxxx: scsi%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, (u32)SCpnt);
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, (u32)SCpnt);
/* We have to let AEN requests through before the reset */
spin_unlock(&tw_dev->tw_lock);
spin_unlock_irq(tw_dev->host->host_lock);
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_release()\n");
+ /* Fake like we just shut down, so notify the card that
+ * we "shut down cleanly".
+ */
+ tw_halt(0, 0, 0); // parameters aren't actually used
+
/* Free up the IO region */
release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
/* Tell kernel scsi-layer we are gone */
scsi_unregister(tw_host);
-
- /* Fake like we just shut down, so notify the card that
- * we "shut down cleanly".
- */
- tw_halt(0, 0, 0); // parameters aren't actually used
return 0;
} /* End tw_scsi_release() */
/* AEN strings */
static char *tw_aen_string[] = {
- "AEN queue empty", // 0x000
- "Soft reset occurred", // 0x001
- "Unit degraded: Unit #", // 0x002
- "Controller error", // 0x003
- "Rebuild failed: Unit #", // 0x004
- "Rebuild complete: Unit #", // 0x005
- "Incomplete unit detected: Unit #", // 0x006
- "Initialization complete: Unit #", // 0x007
- "Unclean shutdown detected: Unit #", // 0x008
- "ATA port timeout: Port #", // 0x009
- "Drive error: Port #", // 0x00A
- "Rebuild started: Unit #", // 0x00B
- "Initialization started: Unit #", // 0x00C
- "Logical unit deleted: Unit #", // 0x00D
- NULL, // 0x00E unused
- "SMART threshold exceeded: Port #", // 0x00F
+ "INFO: AEN queue empty", // 0x000
+ "INFO: Soft reset occurred", // 0x001
+ "ERROR: Unit degraded: Unit #", // 0x002
+ "ERROR: Controller error", // 0x003
+ "ERROR: Rebuild failed: Unit #", // 0x004
+ "INFO: Rebuild complete: Unit #", // 0x005
+ "ERROR: Incomplete unit detected: Unit #", // 0x006
+ "INFO: Initialization complete: Unit #", // 0x007
+ "WARNING: Unclean shutdown detected: Unit #", // 0x008
+ "WARNING: ATA port timeout: Port #", // 0x009
+ "ERROR: Drive error: Port #", // 0x00A
+ "INFO: Rebuild started: Unit #", // 0x00B
+ "INFO: Initialization started: Unit #", // 0x00C
+ "ERROR: Logical unit deleted: Unit #", // 0x00D
+ NULL, // 0x00E unused
+ "WARNING: SMART threshold exceeded: Port #", // 0x00F
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, // 0x010-0x020 unused
- "ATA UDMA downgrade: Port #", // 0x021
- "ATA UDMA upgrade: Port #", // 0x022
- "Sector repair occurred: Port #", // 0x023
- "SBUF integrity check failure", // 0x024
- "Lost cached write: Port #", // 0x025
- "Drive ECC error detected: Port #", // 0x026
- "DCB checksum error: Port #", // 0x027
- "DCB unsupported version: Port #", // 0x028
- "Verify started: Unit #", // 0x029
- "Verify failed: Port #", // 0x02A
- "Verify complete: Unit #", // 0x02B
- "Overwrote bad sector during rebuild: Port #", //0x2C
- "Encountered bad sector during rebuild: Port #", //0x2D
- "Replacement drive is too small: Port #" //0x2E
+ NULL, NULL, // 0x010-0x020 unused
+ "WARNING: ATA UDMA downgrade: Port #", // 0x021
+ "WARNING: ATA UDMA upgrade: Port #", // 0x022
+ "WARNING: Sector repair occurred: Port #", // 0x023
+ "ERROR: SBUF integrity check failure", // 0x024
+ "ERROR: Lost cached write: Port #", // 0x025
+ "ERROR: Drive ECC error detected: Port #", // 0x026
+ "ERROR: DCB checksum error: Port #", // 0x027
+ "ERROR: DCB unsupported version: Port #", // 0x028
+ "INFO: Verify started: Unit #", // 0x029
+ "ERROR: Verify failed: Port #", // 0x02A
+ "INFO: Verify complete: Unit #", // 0x02B
+ "ERROR: Overwrote bad sector during rebuild: Port #", //0x02C
+ "ERROR: Encountered bad sector during rebuild: Port #", //0x02D
+ "INFO: Replacement drive is too small: Port #", //0x02E
+ "WARNING: Verify error: Unit not previously initialized: Unit #" //0x02F
};
-#define TW_AEN_STRING_MAX 0x02F
+#define TW_AEN_STRING_MAX 0x030
/*
Sense key lookup table
#define TW_MAX_AEN_TRIES 100
#define TW_UNIT_ONLINE 1
#define TW_IN_INTR 1
+#define TW_IN_IOCTL 2
#define TW_MAX_SECTORS 256
#define TW_AEN_WAIT_TIME 1000
#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
#define TW_ISR_DONT_COMPLETE 2
#define TW_ISR_DONT_RESULT 3
+#define TW_IOCTL_TIMEOUT 25 /* 25 seconds */
/* Macros */
#define TW_STATUS_ERRORS(x) \