Commit 518a6e87 authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[PATCH] udpated 3ware driver from vendor.

parent 6eb57548
......@@ -141,12 +141,23 @@
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>
......@@ -190,7 +201,7 @@ static struct notifier_block tw_notifier = {
};
/* 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;
......@@ -214,7 +225,7 @@ int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
/* 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]) == '#') {
......@@ -286,7 +297,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
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;
}
......@@ -396,7 +407,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
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]) == '#') {
......@@ -413,7 +424,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
/* 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 {
......@@ -1348,7 +1359,7 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
}
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;
}
}
......@@ -1379,9 +1390,14 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
/* Check for correct state */
if (tw_dev->state[request_id] != TW_S_POSTED) {
/* 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);
......@@ -1472,8 +1488,9 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int 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) {
......@@ -1625,7 +1642,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
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 {
......@@ -1656,7 +1673,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
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 {
......@@ -1672,15 +1689,31 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
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);
......@@ -1697,7 +1730,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
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);
......@@ -1717,7 +1750,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
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);
......@@ -1742,6 +1775,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
/* Now complete the io */
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
if (posted)
tw_dev->posted_request_count--;
tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
return 0;
......@@ -1818,6 +1852,7 @@ int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id)
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);
......@@ -2121,14 +2156,14 @@ int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt)
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 {
......@@ -2142,7 +2177,7 @@ int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt)
}
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);
......@@ -2360,6 +2395,11 @@ int tw_scsi_release(struct Scsi_Host *tw_host)
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);
......@@ -2372,11 +2412,6 @@ int tw_scsi_release(struct Scsi_Host *tw_host)
/* 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() */
......
......@@ -60,43 +60,44 @@
/* 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
"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
"SMART threshold exceeded: Port #", // 0x00F
"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
"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
......@@ -225,11 +226,13 @@ static unsigned char tw_sense_table[][4] =
#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) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment