patch-2.4.22 linux-2.4.22/drivers/scsi/3w-xxxx.c
Next file: linux-2.4.22/drivers/scsi/3w-xxxx.h
Previous file: linux-2.4.22/drivers/sbus/sbus.c
Back to the patch index
Back to the overall index
- Lines: 366
- Date:
2003-08-25 04:44:42.000000000 -0700
- Orig file:
linux-2.4.21/drivers/scsi/3w-xxxx.c
- Orig date:
2003-06-13 07:51:36.000000000 -0700
diff -urN linux-2.4.21/drivers/scsi/3w-xxxx.c linux-2.4.22/drivers/scsi/3w-xxxx.c
@@ -165,6 +165,14 @@
Add support for "twe" character device for ioctls.
Clean up request_id queueing code.
Fix tw_scsi_queue() spinlocks.
+ 1.02.00.033 - Fix tw_aen_complete() to not queue 'queue empty' AEN's.
+ Initialize queues correctly when loading with no valid units.
+ 1.02.00.034 - Fix tw_decode_bits() to handle multiple errors.
+ Add support for user configurable cmd_per_lun.
+ Add support for sht->select_queue_depths.
+ 1.02.00.035 - Improve tw_allocate_memory() memory allocation.
+ Fix tw_chrdev_ioctl() to sleep correctly.
+ 1.02.00.036 - Increase character ioctl timeout to 60 seconds.
*/
#include <linux/module.h>
@@ -232,7 +240,7 @@
};
/* Globals */
-char *tw_driver_version="1.02.00.032";
+char *tw_driver_version="1.02.00.036";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
static int twe_major = -1;
@@ -271,25 +279,24 @@
printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
}
}
- if (aen != 0x0)
+ if (aen != TW_AEN_QUEUE_EMPTY) {
tw_dev->aen_count++;
- /* Now queue the 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->aen_tail = tw_dev->aen_tail + 1;
- }
- if (tw_dev->aen_head == tw_dev->aen_tail) {
- if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
- tw_dev->aen_head = TW_Q_START;
+ /* Now queue the 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->aen_head = tw_dev->aen_head + 1;
+ tw_dev->aen_tail = tw_dev->aen_tail + 1;
+ }
+ if (tw_dev->aen_head == tw_dev->aen_tail) {
+ if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+ tw_dev->aen_head = TW_Q_START;
+ } else {
+ tw_dev->aen_head = tw_dev->aen_head + 1;
+ }
}
- }
- if (aen != TW_AEN_QUEUE_EMPTY) {
error = tw_aen_read_queue(tw_dev, request_id);
if (error) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
@@ -556,34 +563,33 @@
else
imax = TW_Q_LENGTH;
- for (i=0;i<imax;i++) {
- cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size, &dma_handle);
- if (cpu_addr == NULL) {
- printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n");
- return 1;
- }
+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*imax, &dma_handle);
+ if (cpu_addr == NULL) {
+ printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n");
+ return 1;
+ }
- if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
- printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
- pci_free_consistent(tw_dev->tw_pci_dev, size, cpu_addr, dma_handle);
- return 1;
- }
+ if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
+ printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
+ pci_free_consistent(tw_dev->tw_pci_dev, size*imax, cpu_addr, dma_handle);
+ return 1;
+ }
+
+ memset(cpu_addr, 0, size*imax);
+ for (i=0;i<imax;i++) {
switch(which) {
case 0:
- tw_dev->command_packet_virtual_address[i] = cpu_addr;
- tw_dev->command_packet_physical_address[i] = dma_handle;
- memset(tw_dev->command_packet_virtual_address[i], 0, size);
+ tw_dev->command_packet_physical_address[i] = dma_handle+(i*size);
+ tw_dev->command_packet_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
break;
case 1:
- tw_dev->alignment_virtual_address[i] = cpu_addr;
- tw_dev->alignment_physical_address[i] = dma_handle;
- memset(tw_dev->alignment_virtual_address[i], 0, size);
+ tw_dev->alignment_physical_address[i] = dma_handle+(i*size);
+ tw_dev->alignment_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
break;
case 2:
- tw_dev->bounce_buffer[i] = cpu_addr;
- tw_dev->bounce_buffer_phys[i] = dma_handle;
- memset(tw_dev->bounce_buffer[i], 0, size);
+ tw_dev->bounce_buffer_phys[i] = dma_handle+(i*size);
+ tw_dev->bounce_buffer[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
break;
default:
printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
@@ -631,11 +637,11 @@
int error, request_id;
dma_addr_t dma_handle;
unsigned short tw_aen_code;
- unsigned long before;
unsigned long flags;
unsigned int data_buffer_length = 0;
unsigned long data_buffer_length_adjusted = 0;
unsigned long *cpu_addr;
+ long timeout;
TW_New_Ioctl *tw_ioctl;
TW_Passthru *passthru;
TW_Device_Extension *tw_dev = tw_device_extension_list[MINOR(inode->i_rdev)];
@@ -735,30 +741,30 @@
tw_post_command_packet(tw_dev, request_id);
spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
/* Now wait for the command to complete */
- before = jiffies;
+ tw_wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
- while (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
- /* FIXME: we need to sleep here */
- udelay(10);
- if (time_after(jiffies, before + HZ *TW_IOCTL_CHRDEV_TIMEOUT)) {
- /* Now we need to reset the board */
+ /* Check if we timed out, got a signal, or didn't get
+ an interrupt */
+ if ((timeout <= 0) && (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE)) {
+ /* Now we need to reset the board */
+ if (timeout == -ERESTARTSYS) {
+ retval = timeout;
+ } else {
printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
- spin_lock_irqsave(&tw_dev->tw_lock, flags);
- tw_dev->state[request_id] = TW_S_COMPLETED;
- tw_state_request_finish(tw_dev, request_id);
- pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
- tw_dev->posted_request_count--;
- if (tw_reset_device_extension(tw_dev)) {
- printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
- }
- spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
- if (signal_pending(current))
- retval = -EINTR;
- else
- retval = -EIO;
- goto out2;
+ retval = -EIO;
+ }
+ spin_lock_irqsave(&tw_dev->tw_lock, flags);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ if (tw_reset_device_extension(tw_dev)) {
+ printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
}
+ spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ goto out2;
}
/* Now copy in the command packet response */
@@ -886,30 +892,33 @@
else
host[0] = '\0';
- switch (status_reg_value & TW_STATUS_UNEXPECTED_BITS) {
- case TW_STATUS_PCI_PARITY_ERROR:
+ if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
printk(KERN_WARNING "3w-xxxx:%s PCI Parity Error: clearing.\n", host);
outl(TW_CONTROL_CLEAR_PARITY_ERROR, tw_dev->registers.control_reg_addr);
- break;
- case TW_STATUS_MICROCONTROLLER_ERROR:
- if (tw_dev->reset_print == 0) {
- printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host);
- tw_dev->reset_print = 1;
- }
- return 1;
- case TW_STATUS_PCI_ABORT:
+ }
+
+ if (status_reg_value & TW_STATUS_PCI_ABORT) {
printk(KERN_WARNING "3w-xxxx:%s PCI Abort: clearing.\n", host);
outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr);
pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
- break;
- case TW_STATUS_QUEUE_ERROR:
+ }
+
+ if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
printk(KERN_WARNING "3w-xxxx:%s Controller Queue Error: clearing.\n", host);
outl(TW_CONTROL_CLEAR_QUEUE_ERROR, tw_dev->registers.control_reg_addr);
- break;
- case TW_STATUS_SBUF_WRITE_ERROR:
+ }
+
+ if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
printk(KERN_WARNING "3w-xxxx:%s SBUF Write Error: clearing.\n", host);
outl(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, tw_dev->registers.control_reg_addr);
- break;
+ }
+
+ if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
+ if (tw_dev->reset_print == 0) {
+ printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host);
+ tw_dev->reset_print = 1;
+ }
+ return 1;
}
return 0;
@@ -1143,19 +1152,24 @@
tw_dev->online = 1;
/* Calculate max cmds per lun, and setup queues */
- if (tw_dev->num_units > 0) {
- if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
- tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units;
- tw_dev->free_head = TW_Q_START;
- tw_dev->free_tail = TW_Q_START;
- tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1;
- } else {
- /* Use SHT cmd_per_lun here */
+ if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
+ tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units;
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1;
+ } else {
+ /* Check for user configured cmd_per_lun */
+#ifdef CONFIG_3W_XXXX_CMD_PER_LUN
+ tw_host->cmd_per_lun = CONFIG_3W_XXXX_CMD_PER_LUN;
+ if (tw_host->cmd_per_lun > TW_MAX_CMDS_PER_LUN)
tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN;
- tw_dev->free_head = TW_Q_START;
- tw_dev->free_tail = TW_Q_START;
- tw_dev->free_wrap = TW_Q_LENGTH - 1;
- }
+#else
+ /* Use SHT cmd_per_lun default */
+ tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN;
+#endif
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->free_wrap = TW_Q_LENGTH - 1;
}
/* Register the card with the kernel SCSI layer */
@@ -1184,6 +1198,8 @@
host->max_sectors = TW_MAX_SECTORS;
#endif
+ host->select_queue_depths = tw_select_queue_depths;
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
scsi_set_pci_device(host, tw_pci_dev);
#endif
@@ -1198,6 +1214,10 @@
if (host->hostdata) {
tw_dev2 = (TW_Device_Extension *)host->hostdata;
memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
+ /* Need to init the sem/wqueue after the copy */
+ init_MUTEX(&tw_dev2->ioctl_sem);
+ init_waitqueue_head(&tw_dev2->ioctl_wqueue);
+
tw_device_extension_list[tw_device_extension_count] = tw_dev2;
numcards++;
tw_device_extension_count = numcards;
@@ -1254,22 +1274,17 @@
/* This function will free up device extension resources */
void tw_free_device_extension(TW_Device_Extension *tw_dev)
{
- int i;
-
dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
+
/* Free command packet and generic buffer memory */
- for (i=0;i<TW_Q_LENGTH;i++) {
- if (tw_dev->command_packet_virtual_address[i])
- pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->command_packet_virtual_address[i], tw_dev->command_packet_physical_address[i]);
+ if (tw_dev->command_packet_physical_address[0])
+ pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command)*TW_Q_LENGTH, tw_dev->command_packet_virtual_address[0], tw_dev->command_packet_physical_address[0]);
- if (tw_dev->alignment_virtual_address[i])
- pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->alignment_virtual_address[i], tw_dev->alignment_physical_address[i]);
+ if (tw_dev->alignment_physical_address[0])
+ pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_Q_LENGTH, tw_dev->alignment_virtual_address[0], tw_dev->alignment_physical_address[0]);
- }
- for (i=0;i<TW_MAX_BOUNCEBUF;i++) {
- if (tw_dev->bounce_buffer[i])
- pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS, tw_dev->bounce_buffer[i], tw_dev->bounce_buffer_phys[i]);
- }
+ if (tw_dev->bounce_buffer[0])
+ pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS*TW_MAX_BOUNCEBUF, tw_dev->bounce_buffer[0], tw_dev->bounce_buffer_phys[0]);
} /* End tw_free_device_extension() */
/* Clean shutdown routine */
@@ -1379,8 +1394,6 @@
tw_dev->pending_tail = TW_Q_START;
spin_lock_init(&tw_dev->tw_lock);
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
- init_waitqueue_head(&tw_dev->ioctl_wqueue);
- init_MUTEX(&tw_dev->ioctl_sem);
return 0;
} /* End tw_initialize_device_extension() */
@@ -1720,6 +1733,7 @@
}
} else {
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
}
} else {
switch (tw_dev->srb[request_id]->cmnd[0]) {
@@ -3395,6 +3409,31 @@
return 0;
} /* End tw_scsiop_test_unit_ready_complete() */
+/* This function will select queue depths for a target */
+void tw_select_queue_depths(struct Scsi_Host *host, Scsi_Device *dev)
+{
+ Scsi_Device *ptr;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_select_queue_depths()\n");
+
+ for (ptr = dev; ptr != NULL; ptr = ptr->next) {
+ if (ptr->host == host) {
+ if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
+ ptr->queue_depth = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units;
+ } else {
+#ifdef CONFIG_3W_XXXX_CMD_PER_LUN
+ ptr->queue_depth = CONFIG_3W_XXXX_CMD_PER_LUN;
+ if (ptr->queue_depth > TW_MAX_CMDS_PER_LUN)
+ ptr->queue_depth = TW_MAX_CMDS_PER_LUN;
+#else
+ ptr->queue_depth = TW_MAX_CMDS_PER_LUN;
+#endif
+ }
+ }
+ }
+} /* End tw_select_queue_depths() */
+
/* Set a value in the features table */
int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
unsigned char *val)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)