Hi Matthew,

I ran into a hang during stress testing of usb-storage from 2.5.18,
and in the following review I found a few bugs. You've fixed some
of them in 2.5.19, here are the remaining points from my TODO list:

- First, please fix the bug I've introduced into
        usb_stor_control_thread():
  The assignment
        host = us->srb->host;
  happens too early. us->srb is NULL for US_ACT_EXIT actions.
  The result is an oops during module unload. That assignment
  should only happen for US_ACT_COMMAND actions.
- the definition of US_ACT_{COMMAND,EXIT} is duplicated in
        scsiglue.c and usb.c. IMHO it belongs into a header file.
- merge us->queue_srb and us->srb. If scsi_driver.command_abort is
        called before the usb worker thread has a chance to move the
        command from queue_srb to srb, then
        usb_stor_abort_transport() will BUG_ON(!(us->srb));
- sm_state access is not locked correctly:
  * check the definitions of atomic_read() and atomic_set(): they are
    just assignments. I'd replace them with a normal integer
    assignment, using atomic_{set,read} obfuscates the code.
  * usb_stor_abort_transport() does
        atomic_set(&us->sm_state, US_STATE_ABORTING);
    and usb_stor_control_thread() does
        atomic_set(us->sm_state, US_STATE_RUNNING);
    The assignments can overwrite each other.
 I've moved the abort state into a seperate variable, and I perform
 all changes to sm_state under the dev_semaphore spinlock.
- usb_stor_abort_transport can sleep, and is called by the
        usb_stor_timeout_handler. The timeout handler runs at bottom
        half context --> panic.
  The timeout handler must schedule an event, and the event handler
  then calls usb_stor_timeout_handler().
- the timer_lock is broken, a completion is needed instead of a
        spinlock.
- AFAICS you haven't fixed the error I found during stress testing:
        What if command_abort() is called, but the command finishes
        with DID_ERROR?
        Then complete(&(us->notify)) is never called, and the error
        handler hangs.
  I've added a wait queue, and use us->notify only for thread
  creation and destruction.
- If the device initialization fails, then urb structures are
        freed with kfree instead of usb_free_urb()

And one unreleated issue: the driver ate my harddisk, I must figure
out how to low-level format it. dd if=/dev/zero of=/dev/sda fails :-(
It seems that the ScanLogic firmware doesn't handle large write
requests, it fails & destroys the disk with requests > 128 kB.
Is that yet another odd bug of the ScanLogic firmware? Could you try
it with other devices?
I've worked around the issue by adding "max_sectors=128" in the scsi
host template. max_sectors=256 should work, too, but I don't want to
figure out the exact limit until I've found a way to low-level format.

As a reference, I've attached my patch against 2.5.18. It's only
partially tested, and I don't like the locking/memory barrier around
the simulated interrupt in usb_stor_abort_transport().

--
        Manfred

<<<<<<<<<<<
diff -u 2.5/drivers/usb/storage/scsiglue.c build-2.5/drivers/usb/storage/scsiglue.c
--- 2.5/drivers/usb/storage/scsiglue.c  Sun May 26 11:05:08 2002
+++ build-2.5/drivers/usb/storage/scsiglue.c    Sat Jun  1 02:11:21 2002
@@ -51,16 +51,6 @@
 
 #include <linux/slab.h>
 
-/*
- * kernel thread actions
- */
-
-#define US_ACT_COMMAND         1
-#define US_ACT_DEVICE_RESET    2
-#define US_ACT_BUS_RESET       3
-#define US_ACT_HOST_RESET      4
-#define US_ACT_EXIT            5
-
 /***********************************************************************
  * Host functions 
  ***********************************************************************/
@@ -113,6 +103,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.
+ * The function sleeps, no need for _irqsave.
  */
 static int release(struct Scsi_Host *psh)
 {
@@ -126,7 +117,11 @@
         * notification that it's exited.
         */
        US_DEBUGP("-- sending US_ACT_EXIT command to thread\n");
+
+       spin_lock_irq(&us->queue_exclusion);
        us->action = US_ACT_EXIT;
+       BUG_ON(us->srb != NULL);
+       spin_unlock_irq(&us->queue_exclusion);
        
        up(&(us->sema));
        wait_for_completion(&(us->notify));
@@ -159,7 +154,8 @@
        spin_lock_irqsave(&us->queue_exclusion, flags);
 
        /* enqueue the command */
-       us->queue_srb = srb;
+       BUG_ON(us->srb != NULL);
+       us->srb = srb;
        srb->scsi_done = done;
        us->action = US_ACT_COMMAND;
 
@@ -183,12 +179,9 @@
 
        US_DEBUGP("command_abort() called\n");
 
-       if (atomic_read(&us->sm_state) == US_STATE_RUNNING) {
+       if (us->srb) {
                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;
        }
@@ -205,46 +198,75 @@
        int result;
 
        US_DEBUGP("device_reset() called\n" );
-
-       /* if the device was removed, then we're already reset */
-       if (atomic_read(&us->sm_state) == US_STATE_DETACHED)
-               return SUCCESS;
-
        scsi_unlock(srb->host);
+
        /* lock the device pointers */
-       down(&(us->dev_semaphore));
+       down(&us->dev_semaphore);
+
+       /* if the device was removed, then we're already reset */
+       if (us->sm_state == US_STATE_DETACHED) {
+               US_DEBUGP("-- device removed already\n");
+               result = SUCCESS;
+               goto out;
+       }
+       /* scsi_lock not needed, midlayer guarantees that no
+        * new commands will be forwarded until reset returns.
+        */
+       BUG_ON(us->srb != NULL);
        us->srb = srb;
-       atomic_set(&us->sm_state, US_STATE_RESETTING);
+
+       us->sm_state = US_STATE_RESETTING;
        result = us->transport_reset(us);
-       atomic_set(&us->sm_state, US_STATE_IDLE);
+       us->sm_state = US_STATE_IDLE;
+       US_DEBUGP("-- device reset done\n");
 
+       BUG_ON(us->srb != srb);
+       us->srb = NULL;
+       wake_up(&us->wq_cmd);
+
+out:
        /* unlock the device pointers */
        up(&(us->dev_semaphore));
        scsi_lock(srb->host);
        return result;
 }
 
-/* This resets the device port, and simulates the device
- * disconnect/reconnect for all drivers which have claimed
- * interfaces, including ourself. */
+/* FIXME: is that the best we can do? */
 static int bus_reset( Scsi_Cmnd *srb )
 {
+       printk(KERN_INFO "usb-storage: bus_reset() requested but not implemented\n" );
+       return device_reset(srb);
+#if 0
+/* This resets the device port, and simulates the device
+ * disconnect/reconnect for all drivers which have claimed
+ * interfaces, including ourself.
+ * Right now broken due to deadlocks:
+ * - we must own dev_semaphore, otherwise the pusb_dev can disappear
+ * - we must own driver->serialize to avoid concurrent probes/disconnects
+ * - the locking order is first driver->serialize, then us->semaphore.
+ * - we can't honor the locking order, since we don't know the driver object
+ *   pointer(s) upfront.
+ * */
        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);
 
+       down(&usb_storage_driver.serialize);
+       down(&us->dev_semaphore);
        /* if the device has been removed, this worked */
-       if (atomic_read(&us->sm_state) == US_STATE_DETACHED) {
+       if (us->sm_state == US_STATE_DETACHED) {
                US_DEBUGP("-- device removed already\n");
-               return SUCCESS;
+               result = SUCCESS;
+               goto out;
        }
 
        /* attempt to reset the port */
-       scsi_unlock(srb->host);
+       pusb_dev_save = us->pusb_dev;
        result = usb_reset_device(pusb_dev_save);
        US_DEBUGP("usb_reset_device returns %d\n", result);
        if (result < 0) {
@@ -268,24 +290,26 @@
                US_DEBUGP("Examining driver %s...", intf->driver->name);
 
                /* simulate a disconnect and reconnect for all interfaces */
+               up(&intf->driver->serialize);
+               BUG(&inf->driver != usb_storage_driver);
                US_DEBUGPX("simulating disconnect/reconnect.\n");
-               down(&intf->driver->serialize);
                intf->driver->disconnect(pusb_dev_save, intf->private_data);
                id = usb_match_id(pusb_dev_save, intf, intf->driver->id_table);
                intf->driver->probe(pusb_dev_save, i, id);
-               up(&intf->driver->serialize);
        }
        US_DEBUGP("bus_reset() complete\n");
+out:
+       up(&us->dev_semaphore);
        scsi_lock(srb->host);
        return SUCCESS;
+#endif
 }
 
-/* FIXME: This doesn't do anything right now */
+/* FIXME: is that the best we can do? */
 static int host_reset( Scsi_Cmnd *srb )
 {
-       printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
-       bus_reset(srb);
-       return FAILED;
+       printk(KERN_INFO "usb-storage: host_reset() requested but not implemented\n" );
+       return device_reset(srb);
 }
 
 /***********************************************************************
@@ -318,7 +342,12 @@
                us = us->next;
        }
 
-       /* release our lock on the data structures */
+       /* release our lock on the data structures
+        * The only function that frees us structures is
+        * usb_stor_exit(), and that happens after
+        * scsi_unregister_host, i.e. never while the
+        * /proc interface is in use.
+        */
        up(&us_list_semaphore);
 
        /* if we couldn't find it, we return an error */
@@ -340,8 +369,8 @@
 
        /* show the GUID of the device */
        SPRINTF("         GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
-       SPRINTF("     Attached: %s\n", (atomic_read(&us->sm_state) ==
-                       US_STATE_DETACHED) ? "Yes" : "No");
+       SPRINTF("     Attached: %s\n", (us->sm_state != US_STATE_DETACHED) ? "Yes" : 
+"No");
+
 
        /*
         * Calculate start of next buffer, and return value.
@@ -379,6 +408,7 @@
        this_id:                -1,
 
        sg_tablesize:           SG_ALL,
+       max_sectors:    128,
        cmd_per_lun:            1,
        present:                0,
        unchecked_isa_dma:      FALSE,
diff -u 2.5/drivers/usb/storage/transport.c build-2.5/drivers/usb/storage/transport.c
--- 2.5/drivers/usb/storage/transport.c Sun May 26 11:04:45 2002
+++ build-2.5/drivers/usb/storage/transport.c   Sat Jun  1 02:32:17 2002
@@ -362,13 +362,15 @@
 }
 
 /* This is the common part of the URB message submission code
- * This function expects the current_urb_sem to be held upon entry.
+ * This function expects the dev_semaphore to be held upon entry.
  */
 static int usb_stor_msg_common(struct us_data *us)
 {
        struct completion urb_done;
        int status;
 
+       BUG_NOT_DOWN(&us->dev_semaphore);
+       
        /* set up data structures for the wakeup system */
        init_completion(&urb_done);
 
@@ -379,26 +381,20 @@
        us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
 
        /* submit the URB */
-       status = usb_submit_urb(us->current_urb, GFP_NOIO);
+       down(&us->urb_sem);
+       if (us->abort_cmd) {
+               /* we are in abort mode, do not submit the new urb */
+               status = -ENOENT;
+       } else {
+               status = usb_submit_urb(us->current_urb, GFP_NOIO);
+       }
+       up(&us->urb_sem);
        if (status) {
                /* something went wrong */
                return status;
        }
 
-       /* has the current command been aborted? */
-       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
-
-               /* avoid a race with usb_stor_abort_transport():
-                *  if the abort took place before we submitted
-                *  the URB, we must cancel it ourselves */
-               if (us->current_urb->status == -EINPROGRESS)
-                       usb_unlink_urb(us->current_urb);
-               }
-
-       /* wait for the completion of the URB */
-       up(&(us->current_urb_sem));
        wait_for_completion(&urb_done);
-       down(&(us->current_urb_sem));
 
        /* return the URB status */
        return us->current_urb->status;
@@ -425,8 +421,8 @@
        dr->wIndex = cpu_to_le16(index);
        dr->wLength = cpu_to_le16(size);
 
-       /* lock the URB */
-       down(&(us->current_urb_sem));
+       /* is the device access locked? */
+       BUG_NOT_DOWN(&us->dev_semaphore);
 
        /* fill the URB */
        FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
@@ -440,8 +436,6 @@
        if (status >= 0)
                status = us->current_urb->actual_length;
 
-       /* release the lock and return status */
-       up(&(us->current_urb_sem));
        return status;
 }
 
@@ -453,8 +447,8 @@
 {
        int status;
 
-       /* lock the URB */
-       down(&(us->current_urb_sem));
+       /* is the device access locked? */
+       BUG_NOT_DOWN(&us->dev_semaphore);
 
        /* fill the URB */
        FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
@@ -466,8 +460,6 @@
        /* return 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;
 }
 
@@ -535,7 +527,7 @@
        }
 
        /* did we abort this command? */
-       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+       if (us->abort_cmd) {
                US_DEBUGP("usb_stor_transfer_partial(): transfer aborted\n");
                return US_BULK_TRANSFER_ABORTED;
        }
@@ -582,6 +574,9 @@
        /* calculate how much we want to transfer */
        transfer_amount = usb_stor_transfer_length(srb);
 
+       US_DEBUGP("usb_stor_transfer(): request for %d/%d bytes\n",
+                       transfer_amount, srb->request_bufflen);
+
        /* was someone foolish enough to request more data than available
         * buffer space? */
        if (transfer_amount > srb->request_bufflen)
@@ -813,37 +808,58 @@
                srb->sense_buffer[0] = 0x0;
 }
 
-/* Abort the currently running scsi command or device reset.
+/*
+ * Abort the currently running scsi command or device reset.
+ * The function waits until the currently executing command
+ * has finished.
  */
 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 */
-       if ( !(state == US_STATE_RUNNING || state == US_STATE_RESETTING)
-                   || !us->srb) {
-               US_DEBUGP("-- invalid current state\n");
-               return;
-       }
-       atomic_set(&us->sm_state, US_STATE_ABORTING);
-
-       /* If the state machine is blocked waiting for an URB or an IRQ,
-        *  let's wake it up */
+       /* urb_sem is required to synchronize usb_unlink_urb()
+        * and submit_urb():
+        * The caller must guarantee that the urb that is 
+        * passed to unlink_urb was submitted with submit_urb().
+        * Without the lock, we could call unlink_urb in the middle
+        * of submit_urb().
+        */
+       down(&us->urb_sem);
+       spin_lock_irq(&us->queue_exclusion);
+       us->abort_cmd = 1;
+       mb();
+       if(us->sm_state == US_STATE_RUNNING || us->sm_state == US_STATE_RESETTING) {
+               /* the worker thread is processing the
+                * command right now, abort it.
+                */
+               spin_unlock_irq(&us->queue_exclusion);
 
-       /* if we have an URB pending, cancel it */
-       if (us->current_urb->status == -EINPROGRESS) {
-               US_DEBUGP("-- cancelling URB\n");
-               usb_unlink_urb(us->current_urb);
-       }
+               /* if we have an URB pending, cancel it */
+               if (us->current_urb->status == -EINPROGRESS) {
+                       US_DEBUGP("-- cancelling URB\n");
+                       usb_unlink_urb(us->current_urb);
+               }
 
-       /* if we are waiting for an IRQ, simulate it */
-       else if (test_bit(IP_WANTED, &us->bitflags)) {
-               US_DEBUGP("-- simulating missing IRQ\n");
-               usb_stor_CBI_irq(us->irq_urb);
+               /* if we are waiting for an IRQ, simulate it */
+               if (test_bit(IP_WANTED, &us->bitflags)) {
+                       US_DEBUGP("-- simulating missing IRQ\n");
+                       usb_stor_CBI_irq(us->irq_urb);
+               }
+               spin_lock_irq(&us->queue_exclusion);
        }
+       up(&us->urb_sem);
+       while (us->srb) {
+               DECLARE_WAITQUEUE(wait, current);
+               current->state = TASK_UNINTERRUPTIBLE;
+               add_wait_queue(&us->wq_cmd, &wait);
+               spin_unlock_irq(&us->queue_exclusion);
+               schedule();
+               spin_lock_irq(&us->queue_exclusion);
+               remove_wait_queue(&us->wq_cmd, &wait);
+       }
+       us->abort_cmd = 0;
+       mb();
+       spin_unlock_irq(&us->queue_exclusion);
 }
 
 /*
@@ -860,9 +876,9 @@
        US_DEBUGP("-- IRQ state is %d\n", urb->status);
        US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n",
                        us->irqbuf[0], us->irqbuf[1]);
-
+       mb();
        /* has the current command been aborted? */
-       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+       if (us->abort_cmd) {
 
                /* was this a wanted interrupt? */
                if (!test_and_clear_bit(IP_WANTED, &us->bitflags)) {
@@ -988,7 +1004,8 @@
        down(&(us->ip_waitq));
 
        /* has the current command been aborted? */
-       if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+       mb();
+       if (us->abort_cmd) {
                US_DEBUGP("CBI interrupt aborted\n");
                return USB_STOR_TRANSPORT_ABORTED;
        }
@@ -1294,23 +1311,33 @@
 
 struct us_timeout {
        struct us_data *us;
-       spinlock_t timer_lock;
+       struct completion done;
+       struct tq_struct event;
 };
 
-/* The timeout event handler
- */
-static void usb_stor_timeout_handler(unsigned long to__)
+static void usb_stor_event_handler(void* to__)
 {
        struct us_timeout *to = (struct us_timeout *) to__;
        struct us_data *us = to->us;
 
-       US_DEBUGP("Timeout occurred\n");
+       US_DEBUGP("timeout event handler called\n");
 
        /* abort the current request */
        usb_stor_abort_transport(us);
 
-       /* let the reset routine know we have finished */
-       spin_unlock(&to->timer_lock);
+       complete(&to->done);
+}
+
+/* The timeout event handler
+ */
+static void usb_stor_timeout_handler(unsigned long to__)
+{
+       struct us_timeout *to = (struct us_timeout *) to__;
+
+       US_DEBUGP("Timeout occurred\n");
+
+       INIT_TQUEUE(&to->event, usb_stor_event_handler, to);
+       schedule_task(&to->event);
 }
 
 /* This is the common part of the device reset code.
@@ -1325,11 +1352,12 @@
                u16 value, u16 index, void *data, u16 size)
 {
        int result;
-       struct us_timeout timeout_data = {us, SPIN_LOCK_UNLOCKED};
+       struct us_timeout timeout_data;
        struct timer_list timeout_list;
 
        /* prepare the timeout handler */
-       spin_lock(&timeout_data.timer_lock);
+       timeout_data.us = us;
+       init_completion(&timeout_data.done);
        init_timer(&timeout_list);
 
        /* A 20-second timeout may seem rather long, but a LaCie
@@ -1365,8 +1393,7 @@
 
        /* prevent the timer from coming back to haunt us */
        if (!del_timer(&timeout_list)) {
-               /* the handler has already started; wait for it to finish */
-               spin_lock(&timeout_data.timer_lock);
+               wait_for_completion(&timeout_data.done);
                /* change the abort into a timeout */
                if (result == -ENOENT)
                        result = -ETIMEDOUT;
diff -u 2.5/drivers/usb/storage/usb.c build-2.5/drivers/usb/storage/usb.c
--- 2.5/drivers/usb/storage/usb.c       Fri May 31 22:08:29 2002
+++ build-2.5/drivers/usb/storage/usb.c Sat Jun  1 00:36:54 2002
@@ -99,19 +99,9 @@
 
 static int my_host_number;
 
-/*
- * kernel thread actions
- */
-
-#define US_ACT_COMMAND         1
-#define US_ACT_DEVICE_RESET    2
-#define US_ACT_BUS_RESET       3
-#define US_ACT_HOST_RESET      4
-#define US_ACT_EXIT            5
-
 /* The list of structures and the protective lock for them */
 struct us_data *us_list;
-struct semaphore us_list_semaphore;
+DECLARE_MUTEX(us_list_semaphore);
 
 static void * storage_probe(struct usb_device *dev, unsigned int ifnum,
                            const struct usb_device_id *id);
@@ -337,6 +327,9 @@
 
        /* set up for wakeups by new commands */
        init_MUTEX_LOCKED(&us->sema);
+       down(&us->dev_semaphore);
+       us->sm_state = US_STATE_IDLE;
+       up(&us->dev_semaphore);
 
        /* signal that we've started the thread */
        complete(&(us->notify));
@@ -344,8 +337,11 @@
        for(;;) {
                struct Scsi_Host *host;
                US_DEBUGP("*** thread sleeping.\n");
-               if(down_interruptible(&us->sema))
+               wake_up(&us->wq_cmd);
+               if(down_interruptible(&us->sema)) {
+                       printk(KERN_ERR "Received unexpected signal in 
+usb_stor_control_thread().\n");
                        break;
+               }
                        
                US_DEBUGP("*** thread awakened.\n");
 
@@ -355,17 +351,17 @@
                /* 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 */
                spin_unlock_irq(&us->queue_exclusion);
+               US_DEBUGP("Got command %d.\n", action);
 
                switch (action) {
                case US_ACT_COMMAND:
                        /* reject the command if the direction indicator 
                         * is UNKNOWN
                         */
+                       host = us->srb->host;
                        if (us->srb->sc_data_direction == SCSI_DATA_UNKNOWN) {
                                US_DEBUGP("UNKNOWN data direction\n");
                                us->srb->result = DID_ERROR << 16;
@@ -421,7 +417,7 @@
                        down(&(us->dev_semaphore));
 
                        /* our device has gone - pretend not ready */
-                       if (atomic_read(&us->sm_state) == US_STATE_DETACHED) {
+                       if (us->sm_state == US_STATE_DETACHED) {
                                US_DEBUGP("Request is for removed device\n");
                                /* For REQUEST_SENSE, it's the data.  But
                                 * for anything else, it should look like
@@ -445,27 +441,29 @@
                                               sizeof(usb_stor_sense_notready));
                                        us->srb->result = CHECK_CONDITION << 1;
                                }
-                       } else { /* atomic_read(&us->sm_state) == STATE_DETACHED */
-
+                       } else if ((us->srb->cmnd[0] == INQUIRY) &&
+                                   (us->flags & US_FL_FIX_INQUIRY)) {
                                /* Handle those devices which need us to fake 
                                 * their inquiry data */
-                               if ((us->srb->cmnd[0] == INQUIRY) &&
-                                   (us->flags & US_FL_FIX_INQUIRY)) {
-                                       unsigned char data_ptr[36] = {
-                                           0x00, 0x80, 0x02, 0x02,
-                                           0x1F, 0x00, 0x00, 0x00};
+                               unsigned char data_ptr[36] = {
+                                   0x00, 0x80, 0x02, 0x02,
+                                   0x1F, 0x00, 0x00, 0x00};
 
-                                       US_DEBUGP("Faking INQUIRY command\n");
-                                       fill_inquiry_response(us, data_ptr, 36);
-                                       us->srb->result = GOOD << 1;
+                               US_DEBUGP("Faking INQUIRY command\n");
+                               fill_inquiry_response(us, data_ptr, 36);
+                               us->srb->result = GOOD << 1;
+                       } else {
+                               /* we've got a command, let's do it! */
+                               US_DEBUG(usb_stor_show_command(us->srb));
+                               us->sm_state = US_STATE_RUNNING;
+                               mb();
+                               if (us->abort_cmd) {
+                                       us->srb->result = DID_ABORT << 16;
                                } else {
-                                       /* we've got a command, let's do it! */
-                                       US_DEBUG(usb_stor_show_command(us->srb));
-                                       atomic_set(&us->sm_state, US_STATE_RUNNING);
                                        us->proto_handler(us->srb, us);
-                                       atomic_set(&us->sm_state, US_STATE_IDLE);
                                }
-                       }
+                               us->sm_state = US_STATE_IDLE;
+                       } 
 
                        /* unlock the device pointers */
                        up(&(us->dev_semaphore));
@@ -480,28 +478,18 @@
                                scsi_unlock(host);
                        } else {
                                US_DEBUGP("scsi command aborted\n");
+                               scsi_lock(host);
                                us->srb = NULL;
-                               complete(&(us->notify));
+                               scsi_unlock(host);
                        }
                        break;
-
-               case US_ACT_DEVICE_RESET:
-                       break;
-
-               case US_ACT_BUS_RESET:
-                       break;
-
-               case US_ACT_HOST_RESET:
-                       break;
-
-               } /* end switch on action */
-
-               /* exit if we get a signal to exit */
-               if (action == US_ACT_EXIT) {
+               case US_ACT_EXIT:
+                       /* exit if we get a signal to exit */
                        US_DEBUGP("-- US_ACT_EXIT command received\n");
-                       break;
-               }
+                       goto out;
+               } /* end switch on action */
        } /* for (;;) */
+out:
 
        /* notify the exit routine that we're actually exiting now */
        complete(&(us->notify));
@@ -523,13 +511,12 @@
 
        US_DEBUGP("Allocating IRQ for CBI transport\n");
 
-       /* lock access to the data structure */
-       down(&(ss->irq_urb_sem));
+       /* check that the device is locked properly */
+       BUG_NOT_DOWN(&ss->dev_semaphore);
 
        /* allocate the URB */
        ss->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!ss->irq_urb) {
-               up(&(ss->irq_urb_sem));
                US_DEBUGP("couldn't allocate interrupt URB");
                return 1;
        }
@@ -550,12 +537,9 @@
        US_DEBUGP("usb_submit_urb() returns %d\n", result);
        if (result) {
                usb_free_urb(ss->irq_urb);
-               up(&(ss->irq_urb_sem));
                return 2;
        }
 
-       /* unlock the data structure and return success */
-       up(&(ss->irq_urb_sem));
        return 0;
 }
 
@@ -593,6 +577,8 @@
                intf[ifnum].altsetting + intf[ifnum].act_altsetting;
        US_DEBUGP("act_altsettting is %d\n", intf[ifnum].act_altsetting);
 
+       BUG_NOT_DOWN(&usb_storage_driver.serialize);
+
        /* clear the temporary strings */
        memset(mf, 0, sizeof(mf));
        memset(prod, 0, sizeof(prod));
@@ -725,7 +711,6 @@
                /* establish the connection to the new device upon reconnect */
                ss->ifnum = ifnum;
                ss->pusb_dev = dev;
-               atomic_set(&ss->sm_state, US_STATE_IDLE);
 
                /* copy over the endpoint data */
                if (ep_in)
@@ -752,6 +737,8 @@
                 /* Re-Initialize the device if it needs it */
                if (unusual_dev && unusual_dev->initFunction)
                        (unusual_dev->initFunction)(ss);
+               BUG_ON(ss->sm_state != US_STATE_DETACHED);
+               ss->sm_state = US_STATE_IDLE;
 
                /* unlock the device pointers */
                up(&(ss->dev_semaphore));
@@ -780,9 +767,9 @@
                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(&ss->urb_sem);
+               init_MUTEX_LOCKED(&(ss->dev_semaphore));
+               init_waitqueue_head(&ss->wq_cmd);
 
                /* copy over the subclass and protocol data */
                ss->subclass = subclass;
@@ -920,7 +907,7 @@
 
                default:
                        ss->transport_name = "Unknown";
-                       kfree(ss->current_urb);
+                       usb_free_urb(ss->current_urb);
                        kfree(ss);
                        usb_put_dev(dev);
                        return NULL;
@@ -975,7 +962,7 @@
 
                default:
                        ss->protocol_name = "Unknown";
-                       kfree(ss->current_urb);
+                       usb_free_urb(ss->current_urb);
                        kfree(ss);
                        usb_put_dev(dev);
                        return NULL;
@@ -985,7 +972,7 @@
 
                /* allocate an IRQ callback if one is needed */
                if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) {
-                       kfree(ss->current_urb);
+                       usb_free_urb(ss->current_urb);
                        kfree(ss);
                        usb_put_dev(dev);
                        return NULL;
@@ -1014,14 +1001,16 @@
                if (unusual_dev && unusual_dev->initFunction)
                        unusual_dev->initFunction(ss);
 
+               /* Now we are ready */
+               up(&ss->dev_semaphore);
+
                /* start up our control thread */
-               atomic_set(&ss->sm_state, US_STATE_IDLE);
                ss->pid = kernel_thread(usb_stor_control_thread, ss,
                                        CLONE_VM);
                if (ss->pid < 0) {
                        printk(KERN_WARNING USB_STORAGE 
                               "Unable to start control thread\n");
-                       kfree(ss->current_urb);
+                       usb_free_urb(ss->current_urb);
                        kfree(ss);
                        usb_put_dev(dev);
                        return NULL;
@@ -1062,6 +1051,8 @@
 
        US_DEBUGP("storage_disconnect() called\n");
 
+       BUG_NOT_DOWN(&usb_storage_driver.serialize);
+
        /* this is the odd case -- we disconnected but weren't using it */
        if (!ss) {
                US_DEBUGP("-- device was not in use\n");
@@ -1072,7 +1063,6 @@
        down(&(ss->dev_semaphore));
 
        /* release the IRQ, if we have one */
-       down(&(ss->irq_urb_sem));
        if (ss->irq_urb) {
                US_DEBUGP("-- releasing irq URB\n");
                result = usb_unlink_urb(ss->irq_urb);
@@ -1080,7 +1070,6 @@
                usb_free_urb(ss->irq_urb);
                ss->irq_urb = NULL;
        }
-       up(&(ss->irq_urb_sem));
 
        /* free up the main URB for this device */
        US_DEBUGP("-- releasing main URB\n");
@@ -1092,7 +1081,7 @@
        /* mark the device as gone */
        usb_put_dev(ss->pusb_dev);
        ss->pusb_dev = NULL;
-       atomic_set(&ss->sm_state, US_STATE_DETACHED);
+       ss->sm_state = US_STATE_DETACHED;
 
        /* unlock access to the device data structure */
        up(&(ss->dev_semaphore));
@@ -1108,7 +1097,6 @@
 
        /* initialize internal global data elements */
        us_list = NULL;
-       init_MUTEX(&us_list_semaphore);
        my_host_number = 0;
 
        /* register the driver, return -1 if error */
diff -u 2.5/drivers/usb/storage/usb.h build-2.5/drivers/usb/storage/usb.h
--- 2.5/drivers/usb/storage/usb.h       Sun May 26 11:05:08 2002
+++ build-2.5/drivers/usb/storage/usb.h Sat Jun  1 00:36:54 2002
@@ -103,11 +103,18 @@
 #define US_FL_SCM_MULT_TARG   0x00000020 /* supports multiple targets */
 #define US_FL_FIX_INQUIRY     0x00000040 /* INQUIRY response needs fixing */
 
-#define US_STATE_DETACHED      1       /* State machine states */
+#define US_STATE_BAD           0       /* State machine states */
+#define US_STATE_DETACHED      1
 #define US_STATE_IDLE          2
 #define US_STATE_RUNNING       3
 #define US_STATE_RESETTING     4
-#define US_STATE_ABORTING      5
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND         1
+#define US_ACT_EXIT            2
 
 #define USB_STOR_STRING_LEN 32
 
@@ -156,10 +163,13 @@
        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;
+       unsigned long           sm_state;
+       wait_queue_head_t       wq_cmd;          /* command completion queue */
+       struct semaphore        urb_sem;         /* synchronize {unlink,submit}_urb */
+
+       unsigned int            abort_cmd;
 
        /* interrupt info for CBI devices -- only good if attached */
        struct semaphore        ip_waitq;        /* for CBI interrupts   */
@@ -167,13 +177,11 @@
 #define IP_WANTED      1                        /* is an IRQ expected?  */
 
        /* interrupt communications data */
-       struct semaphore        irq_urb_sem;     /* to protect irq_urb   */
        struct urb              *irq_urb;        /* for USB int requests */
        unsigned char           irqbuf[2];       /* buffer for USB IRQ   */
        unsigned char           irqdata[2];      /* data from USB IRQ    */
 
        /* control and bulk communications data */
-       struct semaphore        current_urb_sem; /* to protect irq_urb   */
        struct urb              *current_urb;    /* non-int USB requests */
 
        /* the semaphore for sleeping the control thread */
@@ -210,4 +218,12 @@
 #define sg_address(psg)                ((psg)->address)
 #endif
 
+#define BUG_NOT_DOWN(sem) \
+       do { \
+               if (!down_trylock(sem)) { \
+                       up(sem); \
+                       BUG(); \
+               } \
+       } while(0)
+
 #endif
<<<<<<<<<<<


_______________________________________________________________

Don't miss the 2002 Sprint PCS Application Developer's Conference
August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm

_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to