patch-2.4.6 linux/drivers/scsi/3w-xxxx.c

Next file: linux/drivers/scsi/3w-xxxx.h
Previous file: linux/drivers/sbus/char/zs.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/drivers/scsi/3w-xxxx.c linux/drivers/scsi/3w-xxxx.c
@@ -76,6 +76,20 @@
                  Make tw_setfeature() call with interrupts disabled.
                  Register interrupt handler before enabling interrupts.
                  Clear attention interrupt before draining aen queue.
+   1.02.00.005 - Allocate bounce buffers and custom queue depth for raid5 for
+                 6000 and 5000 series controllers.
+                 Reduce polling mdelays causing problems on some systems.
+                 Fix use_sg = 1 calculation bug.
+                 Check for scsi_register returning NULL.
+                 Add aen count to /proc/scsi/3w-xxxx.
+                 Remove aen code unit masking in tw_aen_complete().
+   1.02.00.006 - Remove unit from printk in tw_scsi_eh_abort(), causing
+                 possible oops.
+                 Fix possible null pointer dereference in tw_scsi_queue()
+                 if done function pointer was invalid.
+   1.02.00.007 - Fix possible null pointer dereferences in tw_ioctl().
+                 Remove check for invalid done function pointer from
+                 tw_scsi_queue().
 */
 
 #include <linux/module.h>
@@ -121,7 +135,7 @@
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.004";
+char *tw_driver_version="1.02.00.007";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 
@@ -131,7 +145,7 @@
 int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id) 
 {
 	TW_Param *param;
-	unsigned short aen, aen_code;
+	unsigned short aen;
 
 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
 		printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n");
@@ -139,10 +153,9 @@
 	}
 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
 	aen = *(unsigned short *)(param->data);
-	aen_code = (aen & 0x0ff);
-	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen_code);
+	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen);
 	/* Now queue the code */
-	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 {
@@ -241,7 +254,7 @@
     
 		/* Now poll for completion */
 		for (i=0;i<imax;i++) {
-			mdelay(10);
+			mdelay(5);
 			status_reg_value = inl(status_reg_addr);
 			if (tw_check_bits(status_reg_value)) {
 				printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected bits.\n");
@@ -437,13 +450,21 @@
 		return 1;
 	}
 
-	if (which == 0) {
+	switch(which) {
+	case 0:
 		tw_dev->command_packet_virtual_address[request_id] = virt_addr;
-		tw_dev->command_packet_physical_address[request_id] = 
-		virt_to_bus(virt_addr);
-	} else {
+		tw_dev->command_packet_physical_address[request_id] = virt_to_bus(virt_addr);
+		break;
+	case 1:
 		tw_dev->alignment_virtual_address[request_id] = virt_addr;
 		tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr);
+		break;
+	case 2:
+		tw_dev->bounce_buffer[request_id] = virt_addr;
+		break;
+	default:
+		printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
+		return 1;
 	}
 	return 0;
 } /* End tw_allocate_memory() */
@@ -709,8 +730,8 @@
 
 		/* Register the card with the kernel SCSI layer */
 			host = scsi_register(tw_host, sizeof(TW_Device_Extension));
-			if( host == NULL)
-			{
+			if (host == NULL) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards-1);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
@@ -788,6 +809,9 @@
 
 		if (tw_dev->alignment_virtual_address[i])
 			kfree(tw_dev->alignment_virtual_address[i]);
+
+		if (tw_dev->bounce_buffer[i])
+			kfree(tw_dev->bounce_buffer[i]);
 	}
 } /* End tw_free_device_extension() */
 
@@ -853,7 +877,7 @@
 	/* Poll for completion */
 	imax = TW_POLL_MAX_RETRIES;
 	for (i=0;i<imax;i++) {
-		mdelay(10);
+		mdelay(5);
 		status_reg_value = inl(status_reg_addr);
 		if (tw_check_bits(status_reg_value)) {
 			printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected bits.\n");
@@ -909,8 +933,10 @@
 		tw_dev->aen_queue[i] = 0;
 	}
 
-	for (i=0;i<TW_MAX_UNITS;i++)
+	for (i=0;i<TW_MAX_UNITS;i++) {
 		tw_dev->is_unit_present[i] = 0;
+		tw_dev->is_raid_five[i] = 0;
+	}
 
 	tw_dev->num_units = 0;
 	tw_dev->num_aborts = 0;
@@ -928,6 +954,8 @@
 	tw_dev->aen_tail = 0;
 	tw_dev->sector_count = 0;
 	tw_dev->max_sector_count = 0;
+	tw_dev->aen_count = 0;
+	tw_dev->num_raid_five = 0;
 	spin_lock_init(&tw_dev->tw_lock);
 	tw_dev->flags = 0;
 	return 0;
@@ -940,13 +968,14 @@
 	unsigned char request_id = 0;
 	TW_Command *command_packet;
 	TW_Param *param;
-	int i, imax, num_units = 0;
+	int i, j, imax, num_units = 0, num_raid_five = 0;
 	u32 status_reg_addr, status_reg_value;
 	u32 command_que_addr, command_que_value;
 	u32 response_que_addr;
 	TW_Response_Queue response_queue;
 	u32 param_value;
 	unsigned char *is_unit_present;
+	unsigned char *raid_level;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units()\n");
 
@@ -1001,7 +1030,7 @@
 	/* Poll for completion */
 	imax = TW_POLL_MAX_RETRIES;
 	for(i=0; i<imax; i++) {
-		mdelay(10);
+		mdelay(5);
 		status_reg_value = inl(status_reg_addr);
 		if (tw_check_bits(status_reg_value)) {
 			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
@@ -1052,6 +1081,110 @@
 		printk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): No units found.\n");
 		return 1;
 	}
+
+	/* Find raid 5 arrays */
+	for (j=0;j<TW_MAX_UNITS;j++) {
+		if (tw_dev->is_unit_present[j] == 0)
+			continue;
+		command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+		if (command_packet == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet virtual address.\n");
+			return 1;
+		}
+		memset(command_packet, 0, sizeof(TW_Sector));
+		command_packet->byte0.opcode      = TW_OP_GET_PARAM;
+		command_packet->byte0.sgl_offset  = 2;
+		command_packet->size              = 4;
+		command_packet->request_id        = request_id;
+		command_packet->byte3.unit        = 0;
+		command_packet->byte3.host_id     = 0;
+		command_packet->status            = 0;
+		command_packet->flags             = 0;
+		command_packet->byte6.block_count = 1;
+
+		/* Now setup the param */
+		if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment virtual address.\n");
+			return 1;
+		}
+		param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+		memset(param, 0, sizeof(TW_Sector));
+		param->table_id = 0x300+j; /* unit summary table */
+		param->parameter_id = 0x6; /* unit descriptor */
+		param->parameter_size_bytes = 0xc;
+		param_value = tw_dev->alignment_physical_address[request_id];
+		if (param_value == 0) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment physical address.\n");
+			return 1;
+		}
+
+		command_packet->byte8.param.sgl[0].address = param_value;
+		command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+		/* Post the command packet to the board */
+		command_que_value = tw_dev->command_packet_physical_address[request_id];
+		if (command_que_value == 0) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet physical address.\n");
+			return 1;
+		}
+		outl(command_que_value, command_que_addr);
+
+		/* Poll for completion */
+		imax = TW_POLL_MAX_RETRIES;
+		for(i=0; i<imax; i++) {
+			mdelay(5);
+			status_reg_value = inl(status_reg_addr);
+			if (tw_check_bits(status_reg_value)) {
+				printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
+				return 1;
+			}
+			if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+				response_queue.value = inl(response_que_addr);
+				request_id = (unsigned char)response_queue.u.response_id;
+				if (request_id != 0) {
+					/* unexpected request id */
+					printk(KERN_WARNING "3w-xxxx: tw_initia
+lize_units(): Unexpected request id.\n");
+					return 1;
+				}
+				if (command_packet->status != 0) {
+					/* bad response */
+					printk(KERN_WARNING "3w-xxxx: tw_initia
+lize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
+					return 1;
+				}
+				found = 1;
+				break;
+			}
+		}
+		if (found == 0) {
+			/* response never received */
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): No
+ response.\n");
+			return 1;
+		}
+
+		param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+		raid_level = (unsigned char *)&(param->data[1]);
+		if (*raid_level == 5) {
+			dprintk(KERN_WARNING "3w-xxxx: Found unit %d to be a raid5 unit.\n", j);
+			tw_dev->is_raid_five[j] = 1;
+			num_raid_five++;
+		}
+	}
+	tw_dev->num_raid_five = num_raid_five;
+
+	/* Now allocate raid5 bounce buffers */
+	if ((num_raid_five != 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
+		for (i=0;i<TW_Q_LENGTH;i++) {
+			tw_allocate_memory(tw_dev, i, sizeof(TW_Sector)*256, 2);
+			if (tw_dev->bounce_buffer[i] == NULL) {
+				printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bounce buffer allocation failed.\n");
+				return 1;
+			}
+			memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*256);
+		}
+	}
   
 	return 0;
 } /* End tw_initialize_units() */
@@ -1288,12 +1421,17 @@
 		case TW_OP_SET_PARAM:
 			dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_SET_PARAM: table_id = %d, parameter_id = %d, parameter_size_bytes = %d.\n",
 			ioctl->table_id, ioctl->parameter_id, ioctl->parameter_size_bytes);
-			command_packet->byte0.opcode = TW_OP_SET_PARAM;
-			param->table_id = ioctl->table_id;
-			param->parameter_id = ioctl->parameter_id;
-			param->parameter_size_bytes = ioctl->parameter_size_bytes;
-			memcpy(param->data, ioctl->data, ioctl->parameter_size_bytes);
-			break;
+			if (ioctl->data != NULL) {
+				command_packet->byte0.opcode = TW_OP_SET_PARAM;
+				param->table_id = ioctl->table_id;
+				param->parameter_id = ioctl->parameter_id;
+				param->parameter_size_bytes = ioctl->parameter_size_bytes;
+				memcpy(param->data, ioctl->data, ioctl->parameter_size_bytes);
+				break;
+			} else {
+				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
+				return 1;
+			}
 		case TW_OP_AEN_LISTEN:
 			dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_AEN_LISTEN.\n");
 			if (tw_dev->aen_head == tw_dev->aen_tail) {
@@ -1318,11 +1456,15 @@
 			tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
 			return 0;
 	        case TW_CMD_PACKET:
-		  memcpy(command_packet, ioctl->data, sizeof(TW_Command));
-		  command_packet->request_id = request_id;
-		  tw_post_command_packet(tw_dev, request_id);
-
-		  return 0;
+			if (ioctl->data != NULL) {
+				memcpy(command_packet, ioctl->data, sizeof(TW_Command));
+				command_packet->request_id = request_id;
+				tw_post_command_packet(tw_dev, request_id);
+				return 0;
+			} else {
+				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
+				return 1;
+			}
 		default:
 			printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
 			tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1602,10 +1744,8 @@
 		return 0;
 	}
 
-	spin_unlock_irq(&io_request_lock);
 	ret = tw_findcards(tw_host);
-	spin_lock_irq(&io_request_lock);
-	
+
 	return ret;
 } /* End tw_scsi_detect() */
 
@@ -1763,6 +1903,7 @@
 		tw_copy_info(&info, "Max sector count:              %3d\n", tw_dev->max_sector_count);
 		tw_copy_info(&info, "Resets:                        %3d\n", tw_dev->num_resets);
 		tw_copy_info(&info, "Aborts:                        %3d\n", tw_dev->num_aborts);
+		tw_copy_info(&info, "AEN's:                         %3d\n", tw_dev->aen_count);
 	}
 	if (info.position > info.offset) {
 		return (info.position - info.offset);
@@ -1780,6 +1921,13 @@
 	int flags = 0;
 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata;
 
+	if (tw_dev == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid device extension.\n");
+		SCpnt->result = (DID_ERROR << 16);
+		done(SCpnt);
+		return 0;
+	}
+
 	spin_lock_irqsave(&tw_dev->tw_lock, flags);
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n");
 
@@ -1790,20 +1938,6 @@
 		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
 		return 0;
 	}
-	if (done == NULL) {
-		printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid done function.\n");
-		SCpnt->result = (DID_ERROR << 16);
-		done(SCpnt);
-		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
-		return 0;
-	}
-	if (tw_dev == NULL) {
-		printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid device extension.\n");
-		SCpnt->result = (DID_ERROR << 16);
-		done(SCpnt);
-		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
-		return 0;
-	}
 	
 	/* Save done function into Scsi_Cmnd struct */
 	SCpnt->scsi_done = done;
@@ -2104,7 +2238,7 @@
 	TW_Command *command_packet;
 	u32 command_que_addr, command_que_value = 0;
 	u32 lba = 0x0, num_sectors = 0x0;
-	int i;
+	int i, count = 0;
 	Scsi_Cmnd *srb;
 	struct scatterlist *sglist;
 
@@ -2161,23 +2295,45 @@
 	command_packet->byte8.io.lba = lba;
 	command_packet->byte6.block_count = num_sectors;
 
-	/* Do this if there are no sg list entries */
-	if (tw_dev->srb[request_id]->use_sg == 0) {    
-		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
-		command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer);
-		command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
-	}
-
-	/* Do this if we have multiple sg list entries */
-	if (tw_dev->srb[request_id]->use_sg > 0) {
-		for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
-			command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address);
-			command_packet->byte8.io.sgl[i].length = sglist[i].length;
-			command_packet->size+=2;
+	if ((tw_dev->is_raid_five[tw_dev->srb[request_id]->target] == 0) || (srb->cmnd[0] == READ_6) || (srb->cmnd[0] == READ_10) || (tw_dev->tw_pci_dev->device == TW_DEVICE_ID2)) {
+		/* Do this if there are no sg list entries */
+		if (tw_dev->srb[request_id]->use_sg == 0) {    
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
+			command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer);
+			command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+		}
+
+		/* Do this if we have multiple sg list entries */
+		if (tw_dev->srb[request_id]->use_sg > 0) {
+			for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
+				command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address);
+				command_packet->byte8.io.sgl[i].length = sglist[i].length;
+				command_packet->size+=2;
+			}
+			if (tw_dev->srb[request_id]->use_sg >= 1)
+				command_packet->size-=2;
 		}
-		if (tw_dev->srb[request_id]->use_sg > 1)
-			command_packet->size-=2;
-	}
+	} else {
+                /* Do this if there are no sg list entries for raid 5 */
+                if (tw_dev->srb[request_id]->use_sg == 0) {
+			dprintk(KERN_WARNING "doing raid 5 write use_sg = 0, bounce_buffer[%d] = 0x%p\n", request_id, tw_dev->bounce_buffer[request_id]);
+			memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
+			command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
+			command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+                }
+
+                /* Do this if we have multiple sg list entries for raid 5 */
+                if (tw_dev->srb[request_id]->use_sg > 0) {
+                        dprintk(KERN_WARNING "doing raid 5 write use_sg = %d, sglist[0].length = %d\n", tw_dev->srb[request_id]->use_sg, sglist[0].length);
+                        for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
+                                memcpy((char *)(tw_dev->bounce_buffer[request_id])+count, sglist[i].address, sglist[i].length);
+				count+=sglist[i].length;
+                        }
+                        command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
+                        command_packet->byte8.io.sgl[0].length = count;
+                        command_packet->size = 5; /* single sgl */
+                }
+        }
 
 	/* Update SG statistics */
 	tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
@@ -2287,7 +2443,7 @@
 	/* Poll for completion */
 	imax = TW_POLL_MAX_RETRIES;
 	for (i=0;i<imax;i++) {
-		mdelay(10);
+		mdelay(5);
 		status_reg_value = inl(status_reg_addr);
 		if (tw_check_bits(status_reg_value)) {
 			printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected bits.\n");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)