# 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.628   -> 1.629  
#       drivers/usb/storage/usb.c       1.28    -> 1.29   
#       drivers/usb/storage/transport.c 1.23    -> 1.24   
#       drivers/usb/storage/scsiglue.c  1.23    -> 1.24   
#       drivers/usb/storage/usb.h       1.13    -> 1.14   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/07/07      [EMAIL PROTECTED]   1.629
# [PATCH] PATCH: usb-storage: consolidate, cleanup, etc.
# 
# This patch changes how the exit code works to be cleaner, fixes the OOPS on
# rmmod, consolidates some anti-race code from several places to just one,
# and also eliminates some theoretical race conditions.
# --------------------------------------------
#
diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
--- a/drivers/usb/storage/scsiglue.c    Sun Jul  7 12:35:47 2002
+++ b/drivers/usb/storage/scsiglue.c    Sun Jul  7 12:35:47 2002
@@ -116,9 +116,8 @@
         * Enqueue the command, wake up the thread, and wait for 
         * notification that it's exited.
         */
-       US_DEBUGP("-- sending US_ACT_EXIT command to thread\n");
-       us->action = US_ACT_EXIT;
-       
+       US_DEBUGP("-- sending exit command to thread\n");
+       us->srb = NULL;
        up(&(us->sema));
        wait_for_completion(&(us->notify));
 
@@ -138,24 +137,17 @@
 }
 
 /* run command */
+/* This is always called with scsi_lock(srb->host) held */
 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 */
-       spin_lock_irqsave(&us->queue_exclusion, flags);
-
        /* enqueue the command */
-       us->queue_srb = srb;
        srb->scsi_done = done;
-       us->action = US_ACT_COMMAND;
-
-       /* release the lock on the structure */
-       spin_unlock_irqrestore(&us->queue_exclusion, flags);
+       us->srb = srb;
 
        /* wake up the process task */
        up(&(us->sema));
@@ -168,28 +160,26 @@
  ***********************************************************************/
 
 /* Command abort */
+/* This is always called with scsi_lock(srb->host) held */
 static int command_abort( Scsi_Cmnd *srb )
 {
        struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
        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;
+       /* Is this command still active? */
+       if (us->srb != srb) {
+               US_DEBUGP ("-- nothing to abort\n");
+               return FAILED;
        }
 
-       US_DEBUGP ("-- nothing to abort\n");
-       return FAILED;
+       usb_stor_abort_transport(us);
+       return SUCCESS;
 }
 
 /* This invokes the transport reset mechanism to reset the state of the
  * device */
+/* This is always called with scsi_lock(srb->host) held */
 static int device_reset( Scsi_Cmnd *srb )
 {
        struct us_data *us = (struct us_data *)srb->host->hostdata[0];
@@ -197,45 +187,54 @@
 
        US_DEBUGP("device_reset() called\n" );
 
-       /* if the device was removed, then we're already reset */
-       if (!(us->flags & US_FL_DEV_ATTACHED))
-               return SUCCESS;
-
+       /* set the state and release the lock */
+       atomic_set(&us->sm_state, US_STATE_RESETTING);
        scsi_unlock(srb->host);
+
        /* lock the device pointers */
        down(&(us->dev_semaphore));
-       us->srb = srb;
-       atomic_set(&us->sm_state, US_STATE_RESETTING);
-       result = us->transport_reset(us);
-       atomic_set(&us->sm_state, US_STATE_IDLE);
 
-       /* unlock the device pointers */
+       /* if the device was removed, then we're already reset */
+       if (!(us->flags & US_FL_DEV_ATTACHED))
+               result = SUCCESS;
+       else
+               result = us->transport_reset(us);
        up(&(us->dev_semaphore));
+
+       /* lock access to the state and clear it */
        scsi_lock(srb->host);
+       atomic_set(&us->sm_state, US_STATE_IDLE);
        return result;
 }
 
 /* This resets the device port, and simulates the device
  * disconnect/reconnect for all drivers which have claimed
  * interfaces, including ourself. */
+/* This is always called with scsi_lock(srb->host) held */
 static int bus_reset( Scsi_Cmnd *srb )
 {
        struct us_data *us = (struct us_data *)srb->host->hostdata[0];
        int i;
        int result;
-       struct usb_device *pusb_dev_save = us->pusb_dev;
+       struct usb_device *pusb_dev_save;
 
        /* we use the usb_reset_device() function to handle this for us */
        US_DEBUGP("bus_reset() called\n");
 
+       scsi_unlock(srb->host);
+
        /* if the device has been removed, this worked */
+       down(&us->dev_semaphore);
        if (!(us->flags & US_FL_DEV_ATTACHED)) {
                US_DEBUGP("-- device removed already\n");
+               up(&us->dev_semaphore);
+               scsi_lock(srb->host);
                return SUCCESS;
        }
+       pusb_dev_save = us->pusb_dev;
+       up(&us->dev_semaphore);
 
        /* 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) {
@@ -245,7 +244,7 @@
 
        /* FIXME: This needs to lock out driver probing while it's working
         * or we can have race conditions */
-       /* Is that still true?  I don't see how...  AS */
+       /* This functionality really should be provided by the khubd thread */
        for (i = 0; i < pusb_dev_save->actconfig->bNumInterfaces; i++) {
                struct usb_interface *intf =
                        &pusb_dev_save->actconfig->interface[i];
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c   Sun Jul  7 12:35:47 2002
+++ b/drivers/usb/storage/transport.c   Sun Jul  7 12:35:47 2002
@@ -354,24 +354,35 @@
 /*
  * This is subtle, so pay attention:
  * ---------------------------------
- * We're very concered about races with a command abort.  Hanging this code is
- * a sure fire way to hang the kernel.
+ * We're very concerned about races with a command abort.  Hanging this code
+ * is a sure fire way to hang the kernel.  (Note that this discussion applies
+ * only to transactions resulting from a scsi queued-command, since only
+ * these transactions are subject to a scsi abort.  Other transactions, such
+ * as those occurring during device-specific initialization, must be handled
+ * by a separate code path.)
  *
- * The abort function first sets the machine state, then tries to acquire the
- * lock on the current_urb to abort it.
+ * The abort function first sets the machine state, then acquires the lock
+ * on the current_urb before checking if it needs to be aborted.
  *
- * Once a function grabs the current_urb_sem, then it -MUST- test the state to
- * see if we just got aborted before the lock was grabbed.  If so, it's
- * essential to release the lock and return.
+ * When a function submits the current_urb, it must first grab the
+ * current_urb_sem to prevent the abort function from trying to cancel the
+ * URB while the submit call is underway.  After a function submits the
+ * current_urb, it -MUST- test the state to see if we got aborted just before
+ * the submission.  If so, it's essential to abort the URB if it's still in
+ * progress.  Either way, the function must then release the lock and wait
+ * for the URB to finish.
  *
- * The idea is that, once the current_urb_sem is held, the state can't really
- * change anymore without also engaging the usb_unlink_urb() call _after_ the
- * URB is actually submitted.
+ * (It's also permissible, but not necessary, to test the state -before-
+ * submitting the URB.  Doing so would prevent an unnecessary submission if
+ * the transaction had already been aborted, but this is very unlikely to
+ * happen, because the abort would have to have been requested during actual
+ * kernel processing rather than during an I/O delay.)
  *
- * So, we've guaranteed that (after the sm_state test), if we do submit the
- * URB it will get aborted when we release the current_urb_sem.  And we've
- * also guaranteed that if the abort code was called before we held the
- * current_urb_sem, we can safely get out before the URB is submitted.
+ * The idea is that (1) once the state is changed to ABORTING, either the
+ * aborting function or the submitting function is guaranteed to call
+ * usb_unlink_urb() for an active URB, and (2) current_urb_sem prevents
+ * usb_unlink_urb() from being called more than once or from being called
+ * during usb_submit_urb().
  */
 
 /* This is the completion handler which will wake us up when an URB
@@ -385,10 +396,10 @@
 }
 
 /* This is the common part of the URB message submission code
- * This function expects the current_urb_sem to be held upon entry.
  *
- * All URBs from the usb-storage driver _must_ pass through this function
- * (or something like it) for the abort mechanisms to work properly.
+ * All URBs from the usb-storage driver involved in handling a queued scsi
+ * command _must_ pass through this function (or something like it) for the
+ * abort mechanisms to work properly.
  */
 static int usb_stor_msg_common(struct us_data *us)
 {
@@ -404,17 +415,28 @@
        us->current_urb->error_count = 0;
        us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
 
-       /* submit the URB */
+       /* lock and submit the URB */
+       down(&(us->current_urb_sem));
        status = usb_submit_urb(us->current_urb, GFP_NOIO);
        if (status) {
                /* something went wrong */
+               up(&(us->current_urb_sem));
                return status;
        }
 
-       /* wait for the completion of the URB */
+       /* has the current command been aborted? */
+       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+
+               /* cancel the URB, if it hasn't been cancelled already */
+               if (us->current_urb->status == -EINPROGRESS) {
+                       US_DEBUGP("-- cancelling URB\n");
+                       usb_unlink_urb(us->current_urb);
+               }
+       }
        up(&(us->current_urb_sem));
+
+       /* wait for the completion of the URB */
        wait_for_completion(&urb_done);
-       down(&(us->current_urb_sem));
 
        /* return the URB status */
        return us->current_urb->status;
@@ -436,29 +458,15 @@
        us->dr->wIndex = cpu_to_le16(index);
        us->dr->wLength = cpu_to_le16(size);
 
-       /* lock the URB */
-       down(&(us->current_urb_sem));
-
-       /* has the current command been aborted? */
-       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
-               up(&(us->current_urb_sem));
-               return 0;
-       }
-
-       /* fill the URB */
+       /* fill and submit the URB */
        FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
                         (unsigned char*) us->dr, data, size, 
                         usb_stor_blocking_completion, NULL);
-
-       /* submit the URB */
        status = usb_stor_msg_common(us);
 
        /* return the actual length of the data transferred if no error*/
        if (status >= 0)
                status = us->current_urb->actual_length;
-
-       /* release the lock and return status */
-       up(&(us->current_urb_sem));
        return status;
 }
 
@@ -470,27 +478,13 @@
 {
        int status;
 
-       /* lock the URB */
-       down(&(us->current_urb_sem));
-
-       /* has the current command been aborted? */
-       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
-               up(&(us->current_urb_sem));
-               return 0;
-       }
-
-       /* fill the URB */
+       /* fill and submit the URB */
        FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
                      usb_stor_blocking_completion, NULL);
-
-       /* submit the URB */
        status = usb_stor_msg_common(us);
 
-       /* return the actual length of the data transferred */
+       /* store the actual length of the data transferred */
        *act_len = us->current_urb->actual_length;
-
-       /* release the lock and return status */
-       up(&(us->current_urb_sem));
        return status;
 }
 
@@ -845,31 +839,27 @@
 }
 
 /* Abort the currently running scsi command or device reset.
- */
+ * This must be called with scsi_lock(us->srb->host) held */
 void usb_stor_abort_transport(struct us_data *us)
 {
        int state = atomic_read(&us->sm_state);
 
        US_DEBUGP("usb_stor_abort_transport called\n");
 
-       /* If the current state is wrong or if there's
-        *  no srb, then there's nothing to do */
-       BUG_ON((state != US_STATE_RUNNING && state != US_STATE_RESETTING));
-       BUG_ON(!(us->srb));
+       /* Normally the current state is RUNNING.  If the control thread
+        * hasn't even started processing this command, the state will be
+        * IDLE.  Anything else is a bug. */
 
-       /* set state to abort */
+       /* set state to abort and release the lock */
        atomic_set(&us->sm_state, US_STATE_ABORTING);
+       scsi_unlock(us->srb->host);
 
        /* If the state machine is blocked waiting for an URB or an IRQ,
         * let's wake it up */
 
        /* If we have an URB pending, cancel it.  Note that we guarantee with
-        * the current_urb_sem that either (a) an URB has just been submitted,
-        * or (b) the URB will never get submitted.  But, because of the use
-        * of us->sm_state and current_urb_sem, we can't get an URB sumbitted
-        * _after_ we set the state to US_STATE_ABORTING and this section of
-        * code runs.  Thus we avoid deadlocks.
-        */
+        * the current_urb_sem that if a URB has just been submitted, it
+        * won't be cancelled more than once. */
        down(&(us->current_urb_sem));
        if (us->current_urb->status == -EINPROGRESS) {
                US_DEBUGP("-- cancelling URB\n");
@@ -882,6 +872,9 @@
                US_DEBUGP("-- simulating missing IRQ\n");
                usb_stor_CBI_irq(us->irq_urb);
        }
+
+       /* Reacquire the lock */
+       scsi_lock(us->srb->host);
 }
 
 /*
@@ -1397,11 +1390,9 @@
        /* return a result code based on the result of the control message */
        if (result < 0) {
                US_DEBUGP("Soft reset failed: %d\n", result);
-               us->srb->result = DID_ERROR << 16;
                result = FAILED;
        } else {
                US_DEBUGP("Soft reset done\n");
-               us->srb->result = GOOD << 1;
                result = SUCCESS;
        }
        return result;
diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
--- a/drivers/usb/storage/usb.c Sun Jul  7 12:35:47 2002
+++ b/drivers/usb/storage/usb.c Sun Jul  7 12:35:47 2002
@@ -301,7 +301,6 @@
 static int usb_stor_control_thread(void * __us)
 {
        struct us_data *us = (struct us_data *)__us;
-       int action;
 
        lock_kernel();
 
@@ -334,32 +333,30 @@
        for(;;) {
                struct Scsi_Host *host;
                US_DEBUGP("*** thread sleeping.\n");
-               atomic_set(&us->sm_state, US_STATE_IDLE);
                if(down_interruptible(&us->sema))
                        break;
                        
                US_DEBUGP("*** thread awakened.\n");
-               atomic_set(&us->sm_state, US_STATE_RUNNING);
-
-               /* lock access to the queue element */
-               spin_lock_irq(&us->queue_exclusion);
 
-               /* take the command off the queue */
-               action = us->action;
-               us->action = 0;
-               us->srb = us->queue_srb;
+               /* if us->srb is NULL, we are being asked to exit */
+               if (us->srb == NULL) {
+                       US_DEBUGP("-- exit command received\n");
+                       break;
+               }
                host = us->srb->host;
 
-               /* release the queue lock as fast as possible */
-               spin_unlock_irq(&us->queue_exclusion);
+               /* lock access to the state */
+               scsi_lock(host);
 
-               /* exit if we get a signal to exit */
-               if (action == US_ACT_EXIT) {
-                       US_DEBUGP("-- US_ACT_EXIT command received\n");
-                       break;
+               /* has the command been aborted *already* ? */
+               if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+                       us->srb->result = DID_ABORT << 16;
+                       goto SkipForAbort;
                }
 
-               BUG_ON(action != US_ACT_COMMAND);
+               /* set the state and release the lock */
+               atomic_set(&us->sm_state, US_STATE_RUNNING);
+               scsi_unlock(host);
 
                /* lock the device pointers */
                down(&(us->dev_semaphore));
@@ -444,19 +441,29 @@
                /* unlock the device pointers */
                up(&(us->dev_semaphore));
 
+               /* lock access to the state */
+               scsi_lock(host);
+
                /* indicate that the command is done */
                if (us->srb->result != DID_ABORT << 16) {
                        US_DEBUGP("scsi cmd done, result=0x%x\n", 
                                   us->srb->result);
-                       scsi_lock(host);
                        us->srb->scsi_done(us->srb);
-                       us->srb = NULL;
-                       scsi_unlock(host);
                } else {
+                       SkipForAbort:
                        US_DEBUGP("scsi command aborted\n");
-                       us->srb = NULL;
-                       complete(&(us->notify));
                }
+
+               /* in case an abort request was received after the command
+                * completed, we must use a separate test to see whether
+                * we need to signal that the abort has finished */
+               if (atomic_read(&us->sm_state) == US_STATE_ABORTING)
+                       complete(&(us->notify));
+
+               /* empty the queue, reset the state, and release the lock */
+               us->srb = NULL;
+               atomic_set(&us->sm_state, US_STATE_IDLE);
+               scsi_unlock(host);
        } /* for (;;) */
 
        /* notify the exit routine that we're actually exiting now */
@@ -478,19 +485,19 @@
        int maxp;
        int result;
 
-       /* allocate the URB we're going to use */
-       US_DEBUGP("Allocating URB\n");
-       ss->current_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!ss->current_urb) {
-               US_DEBUGP("allocation failed\n");
-               return 1;
-       }
-
        /* allocate the usb_ctrlrequest for control packets */
        US_DEBUGP("Allocating usb_ctrlrequest\n");
        ss->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
        if (!ss->dr) {
                US_DEBUGP("allocation failed\n");
+               return 1;
+       }
+
+       /* allocate the URB we're going to use */
+       US_DEBUGP("Allocating URB\n");
+       ss->current_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!ss->current_urb) {
+               US_DEBUGP("allocation failed\n");
                return 2;
        }
 
@@ -554,12 +561,6 @@
        }
        up(&(ss->irq_urb_sem));
 
-       /* free the usb_ctrlrequest buffer */
-       if (ss->dr) {
-               kfree(ss->dr);
-               ss->dr = NULL;
-       }
-
        /* free up the main URB for this device */
        if (ss->current_urb) {
                US_DEBUGP("-- releasing main URB\n");
@@ -569,6 +570,12 @@
                ss->current_urb = NULL;
        }
 
+       /* free the usb_ctrlrequest buffer */
+       if (ss->dr) {
+               kfree(ss->dr);
+               ss->dr = NULL;
+       }
+
        /* mark the device as gone */
        ss->flags &= ~ US_FL_DEV_ATTACHED;
        usb_put_dev(ss->pusb_dev);
@@ -777,10 +784,9 @@
                /* Initialize the mutexes only when the struct is new */
                init_completion(&(ss->notify));
                init_MUTEX_LOCKED(&(ss->ip_waitq));
-               spin_lock_init(&ss->queue_exclusion);
                init_MUTEX(&(ss->irq_urb_sem));
                init_MUTEX(&(ss->current_urb_sem));
-               init_MUTEX(&(ss->dev_semaphore));
+               init_MUTEX_LOCKED(&(ss->dev_semaphore));
 
                /* copy over the subclass and protocol data */
                ss->subclass = subclass;
@@ -1011,6 +1017,9 @@
                /* wait for the thread to start */
                wait_for_completion(&(ss->notify));
 
+               /* unlock the device pointers */
+               up(&(ss->dev_semaphore));
+
                /* now register  - our detect function will be called */
                ss->htmplt.module = THIS_MODULE;
                result = scsi_register_host(&(ss->htmplt));
@@ -1019,9 +1028,12 @@
                                "Unable to register the scsi host\n");
 
                        /* tell the control thread to exit */
-                       ss->action = US_ACT_EXIT;
+                       ss->srb = NULL;
                        up(&ss->sema);
                        wait_for_completion(&ss->notify);
+
+                       /* re-lock the device pointers */
+                       down(&ss->dev_semaphore);
                        goto BadDevice;
                }
 
@@ -1045,13 +1057,13 @@
        return ss;
 
        /* we come here if there are any problems */
+       /* ss->dev_semaphore must be locked */
        BadDevice:
        US_DEBUGP("storage_probe() failed\n");
        usb_stor_deallocate_urbs(ss);
+       up(&ss->dev_semaphore);
        if (new_device)
                kfree(ss);
-       else
-               up(&ss->dev_semaphore);
        return NULL;
 }
 
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h Sun Jul  7 12:35:47 2002
+++ b/drivers/usb/storage/usb.h Sun Jul  7 12:35:47 2002
@@ -107,10 +107,6 @@
 #define US_FLIDX_IP_WANTED   17  /* 0x00020000 is an IRQ expected?         */
 
 
-/* kernel thread actions */
-#define US_ACT_COMMAND         1
-#define US_ACT_EXIT            5
-
 /* processing state machine states */
 #define US_STATE_IDLE          1
 #define US_STATE_RUNNING       2
@@ -168,8 +164,6 @@
        Scsi_Cmnd               *srb;            /* current srb         */
 
        /* thread information */
-       Scsi_Cmnd               *queue_srb;      /* the single queue slot */
-       int                     action;          /* what to do            */
        int                     pid;             /* control thread        */
        atomic_t                sm_state;
 
@@ -192,7 +186,6 @@
 
        /* mutual exclusion structures */
        struct completion       notify;          /* thread begin/end        */
-       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   */
@@ -209,6 +202,8 @@
 extern void fill_inquiry_response(struct us_data *us,
        unsigned char *data, unsigned int data_len);
 
+/* The scsi_lock() and scsi_unlock() macros protect the sm_state and the
+ * single queue element srb for write access */
 #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)


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
We have stuff for geeks like you.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to