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

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)