# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.543   -> 1.544  
#	drivers/usb/storage/jumpshot.c	1.10    -> 1.11   
#	drivers/usb/storage/usb.c	1.18    -> 1.19   
#	drivers/usb/storage/isd200.c	1.8     -> 1.9    
#	drivers/usb/storage/scsiglue.c	1.17    -> 1.18   
#	drivers/usb/storage/usb.h	1.7     -> 1.8    
#	drivers/usb/storage/datafab.c	1.9     -> 1.10   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/13	manfred@colorfullife.com	1.544
# [PATCH] usb-storage locking fixes
# 
# I found several SMP and UP locking errors in usb-storage, attached is a
# patch:
# 
# Changes:
# * srb->result is a bitfield, several << 1 were missing.
# * add scsi_lock calls around midlayer calls, release the lock before
#   calling usb functions that might sleep.
# * replace the queue semaphore with a queue spinlocks, queuecommand is
#   called from bh context.
# --------------------------------------------
#
diff -Nru a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
--- a/drivers/usb/storage/datafab.c	Mon May 13 15:53:53 2002
+++ b/drivers/usb/storage/datafab.c	Mon May 13 15:53:53 2002
@@ -822,7 +822,7 @@
 			srb->result = SUCCESS;
 		} else {
 			info->sense_key = UNIT_ATTENTION;
-			srb->result = CHECK_CONDITION;
+			srb->result = CHECK_CONDITION << 1;
 		}
 		return rc;
         }
diff -Nru a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
--- a/drivers/usb/storage/isd200.c	Mon May 13 15:53:53 2002
+++ b/drivers/usb/storage/isd200.c	Mon May 13 15:53:53 2002
@@ -864,7 +864,7 @@
 	 * condition, show that in the result code
 	 */
 	if (transferStatus == ISD200_TRANSPORT_FAILED)
-		srb->result = CHECK_CONDITION;
+		srb->result = CHECK_CONDITION << 1;
 }
 
 #ifdef CONFIG_USB_STORAGE_DEBUG
diff -Nru a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
--- a/drivers/usb/storage/jumpshot.c	Mon May 13 15:53:53 2002
+++ b/drivers/usb/storage/jumpshot.c	Mon May 13 15:53:53 2002
@@ -821,7 +821,7 @@
 			srb->result = SUCCESS;
 		} else {
 			info->sense_key = UNIT_ATTENTION;
-			srb->result = CHECK_CONDITION;
+			srb->result = CHECK_CONDITION << 1;
 		}
 		return rc;
         }
diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
--- a/drivers/usb/storage/scsiglue.c	Mon May 13 15:53:53 2002
+++ b/drivers/usb/storage/scsiglue.c	Mon May 13 15:53:53 2002
@@ -70,7 +70,11 @@
 	return "SCSI emulation for USB Mass Storage devices";
 }
 
-/* detect a virtual adapter (always works) */
+/* detect a virtual adapter (always works)
+ * Synchronization: 2.4: with the io_request_lock
+ * 			2.5: no locks.
+ * fortunately we don't care.
+ * */
 static int detect(struct SHT *sht)
 {
 	struct us_data *us;
@@ -82,7 +86,7 @@
 
 	/* set up the name of our subdirectory under /proc/scsi/ */
 	sprintf(local_name, "usb-storage-%d", us->host_number);
-	sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+	sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_ATOMIC);
 	if (!sht->proc_name)
 		return 0;
 	strcpy(sht->proc_name, local_name);
@@ -108,6 +112,7 @@
  *
  * NOTE: There is no contention here, because we're already deregistered
  * the driver and we're doing each virtual host in turn, not in parallel
+ * Synchronization: BLK, no spinlock.
  */
 static int release(struct Scsi_Host *psh)
 {
@@ -145,12 +150,13 @@
 static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
 {
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+	unsigned long flags;
 
 	US_DEBUGP("queuecommand() called\n");
 	srb->host_scribble = (unsigned char *)us;
 
 	/* get exclusive access to the structures we want */
-	down(&(us->queue_exclusion));
+	spin_lock_irqsave(&us->queue_exclusion, flags);
 
 	/* enqueue the command */
 	us->queue_srb = srb;
@@ -158,7 +164,7 @@
 	us->action = US_ACT_COMMAND;
 
 	/* release the lock on the structure */
-	up(&(us->queue_exclusion));
+	spin_unlock_irqrestore(&us->queue_exclusion, flags);
 
 	/* wake up the process task */
 	up(&(us->sema));
@@ -178,10 +184,12 @@
 	US_DEBUGP("command_abort() called\n");
 
 	if (atomic_read(&us->sm_state) == US_STATE_RUNNING) {
+ 		scsi_unlock(srb->host);
 		usb_stor_abort_transport(us);
 
 		/* wait for us to be done */
 		wait_for_completion(&(us->notify));
+ 		scsi_lock(srb->host);
 		return SUCCESS;
 	}
 
@@ -202,6 +210,7 @@
 	if (atomic_read(&us->sm_state) == US_STATE_DETACHED)
 		return SUCCESS;
 
+	scsi_unlock(srb->host);
 	/* lock the device pointers */
 	down(&(us->dev_semaphore));
 	us->srb = srb;
@@ -211,6 +220,7 @@
 
 	/* unlock the device pointers */
 	up(&(us->dev_semaphore));
+	scsi_lock(srb->host);
 	return result;
 }
 
@@ -234,10 +244,13 @@
 	}
 
 	/* attempt to reset the port */
+	scsi_unlock(srb->host);
 	result = usb_reset_device(pusb_dev_save);
 	US_DEBUGP("usb_reset_device returns %d\n", result);
-	if (result < 0)
+	if (result < 0) {
+		scsi_lock(srb->host);
 		return FAILED;
+	}
 
 	/* FIXME: This needs to lock out driver probing while it's working
 	 * or we can have race conditions */
@@ -262,8 +275,8 @@
 		intf->driver->probe(pusb_dev_save, i, id);
 		up(&intf->driver->serialize);
 	}
-
 	US_DEBUGP("bus_reset() complete\n");
+	scsi_lock(srb->host);
 	return SUCCESS;
 }
 
@@ -271,6 +284,7 @@
 static int host_reset( Scsi_Cmnd *srb )
 {
 	printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
+	bus_reset(srb);
 	return FAILED;
 }
 
diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
--- a/drivers/usb/storage/usb.c	Mon May 13 15:53:53 2002
+++ b/drivers/usb/storage/usb.c	Mon May 13 15:53:53 2002
@@ -337,9 +337,9 @@
 
 	/* signal that we've started the thread */
 	complete(&(us->notify));
-	set_current_state(TASK_INTERRUPTIBLE);
 
 	for(;;) {
+		struct Scsi_Host *host;
 		US_DEBUGP("*** thread sleeping.\n");
 		if(down_interruptible(&us->sema))
 			break;
@@ -347,15 +347,16 @@
 		US_DEBUGP("*** thread awakened.\n");
 
 		/* lock access to the queue element */
-		down(&(us->queue_exclusion));
+		spin_lock_irq(&us->queue_exclusion);
 
 		/* take the command off the queue */
 		action = us->action;
 		us->action = 0;
 		us->srb = us->queue_srb;
+		host = us->srb->host;
 
 		/* release the queue lock as fast as possible */
-		up(&(us->queue_exclusion));
+		spin_unlock_irq(&us->queue_exclusion);
 
 		switch (action) {
 		case US_ACT_COMMAND:
@@ -365,9 +366,10 @@
 			if (us->srb->sc_data_direction == SCSI_DATA_UNKNOWN) {
 				US_DEBUGP("UNKNOWN data direction\n");
 				us->srb->result = DID_ERROR << 16;
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -380,9 +382,10 @@
 					  us->srb->target, us->srb->lun);
 				us->srb->result = DID_BAD_TARGET << 16;
 
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -391,9 +394,10 @@
 					  us->srb->target, us->srb->lun);
 				us->srb->result = DID_BAD_TARGET << 16;
 
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -403,9 +407,10 @@
 				US_DEBUGP("Skipping START_STOP command\n");
 				us->srb->result = GOOD << 1;
 
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -466,14 +471,15 @@
 			if (us->srb->result != DID_ABORT << 16) {
 				US_DEBUGP("scsi cmd done, result=0x%x\n", 
 					   us->srb->result);
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
+				us->srb = NULL;
+				scsi_unlock(host);
 			} else {
 				US_DEBUGP("scsi command aborted\n");
-				set_current_state(TASK_INTERRUPTIBLE);
+				us->srb = NULL;
 				complete(&(us->notify));
 			}
-			us->srb = NULL;
 			break;
 
 		case US_ACT_DEVICE_RESET:
@@ -494,9 +500,6 @@
 		}
 	} /* for (;;) */
 
-	/* clean up after ourselves */
-	set_current_state(TASK_INTERRUPTIBLE);
-
 	/* notify the exit routine that we're actually exiting now */
 	complete(&(us->notify));
 
@@ -773,7 +776,7 @@
 		/* Initialize the mutexes only when the struct is new */
 		init_completion(&(ss->notify));
 		init_MUTEX_LOCKED(&(ss->ip_waitq));
-		init_MUTEX(&(ss->queue_exclusion));
+		spin_lock_init(&ss->queue_exclusion);
 		init_MUTEX(&(ss->irq_urb_sem));
 		init_MUTEX(&(ss->current_urb_sem));
 		init_MUTEX(&(ss->dev_semaphore));
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h	Mon May 13 15:53:53 2002
+++ b/drivers/usb/storage/usb.h	Mon May 13 15:53:53 2002
@@ -180,7 +180,7 @@
 
 	/* mutual exclusion structures */
 	struct completion	notify;		 /* thread begin/end	    */
-	struct semaphore	queue_exclusion; /* to protect data structs */
+	spinlock_t		queue_exclusion; /* to protect data structs */
 	struct us_unusual_dev   *unusual_dev;	 /* If unusual device       */
 	void			*extra;		 /* Any extra data          */
 	extra_data_destructor	extra_destructor;/* extra data destructor   */
@@ -196,5 +196,17 @@
 /* Function to fill an inquiry response. See usb.c for details */
 extern void fill_inquiry_response(struct us_data *us,
 	unsigned char *data, unsigned int data_len);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3)
+#define scsi_unlock(host)	spin_unlock_irq(host->host_lock)
+#define scsi_lock(host)		spin_lock_irq(host->host_lock)
+
+#define sg_address(psg)		(page_address((psg)->page) + (psg)->offset)
+#else
+#define scsi_unlock(host)	spin_unlock_irq(&io_request_lock)
+#define scsi_lock(host)		spin_lock_irq(&io_request_lock)
+
+#define sg_address(psg)		((psg)->address)
+#endif
 
 #endif
