lkml.org 
[lkml]   [2002]   [Jul]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subjectusb storage cleanup
Attached is a cleanup for usb-storage.

Mainly it's an update to the synchonization: there were a few small
races left.
I've added a complete changelog into usb.h.

The patch is vs 2.5.24-dj2. I've tested it with my SaroTech device,
please test it with other usb storage devices.

--
Manfred

diff -u 2.5/drivers/usb/storage/freecom.c build-2.5/drivers/usb/storage/freecom.c
--- 2.5/drivers/usb/storage/freecom.c Tue Jun 4 20:39:19 2002
+++ build-2.5/drivers/usb/storage/freecom.c Wed Jul 3 20:31:14 2002
@@ -191,7 +191,7 @@
result, partial);

/* has the current command been aborted? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("freecom_readdata(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -232,7 +232,7 @@
result, partial);

/* has the current command been aborted? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("freecom_writedata(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -295,7 +295,7 @@
result, partial);

/* we canceled this transfer */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("freecom_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -310,7 +310,7 @@
US_DEBUGP("foo Status result %d %d\n", result, partial);

/* we canceled this transfer */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("freecom_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -348,7 +348,7 @@
result, partial);

/* we canceled this transfer */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("freecom_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -363,7 +363,7 @@
US_DEBUGP("bar Status result %d %d\n", result, partial);

/* we canceled this transfer */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("freecom_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -424,7 +424,7 @@
FCM_PACKET_LENGTH, &partial);
US_DEBUG(pdump ((void *) fst, partial));

- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP ("freecom_transport: transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -453,7 +453,7 @@
result = usb_stor_bulk_msg (us, fst, ipipe,
FCM_PACKET_LENGTH, &partial);

- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP ("freecom_transport: transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
diff -u 2.5/drivers/usb/storage/scsiglue.c build-2.5/drivers/usb/storage/scsiglue.c
--- 2.5/drivers/usb/storage/scsiglue.c Tue Jun 25 20:24:18 2002
+++ build-2.5/drivers/usb/storage/scsiglue.c Wed Jul 3 20:35:42 2002
@@ -104,6 +104,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)
{
@@ -117,7 +118,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));
@@ -150,7 +155,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;

@@ -174,12 +180,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));
+ usb_stor_abort_transport(us, 0);
scsi_lock(srb->host);
return SUCCESS;
}
@@ -198,16 +201,25 @@
US_DEBUGP("device_reset() called\n" );

/* if the device was removed, then we're already reset */
- if (!test_bit(DEV_ATTACHED, &us->bitflags))
- return SUCCESS;

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);
+ /* previous commands were stopped with command_abort(),
+ * and the scsi midlayer prevents new commands from comming
+ * in until device_reset returns.
+ */
+ BUG_ON(us->srb != NULL);
+ BUG_ON(us->sm_state != US_STATE_IDLE);
+ if (us->pusb_dev == NULL) {
+ result = SUCCESS;
+ } else {
+ us->srb = srb;
+ us->sm_state = US_STATE_RESETTING;
+ result = us->transport_reset(us);
+ us->srb = NULL;
+ us->sm_state = US_STATE_IDLE;
+ }

/* unlock the device pointers */
up(&(us->dev_semaphore));
@@ -215,68 +227,18 @@
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 )
{
- struct us_data *us = (struct us_data *)srb->host->hostdata[0];
- int i;
- int result;
- struct usb_device *pusb_dev_save = us->pusb_dev;
-
- /* we use the usb_reset_device() function to handle this for us */
- US_DEBUGP("bus_reset() called\n");
-
- /* if the device has been removed, this worked */
- if (!test_bit(DEV_ATTACHED, &us->bitflags)) {
- US_DEBUGP("-- device removed already\n");
- return SUCCESS;
- }
-
- /* 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) {
- scsi_lock(srb->host);
- return FAILED;
- }
-
- /* 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 */
- for (i = 0; i < pusb_dev_save->actconfig->bNumInterfaces; i++) {
- struct usb_interface *intf =
- &pusb_dev_save->actconfig->interface[i];
- const struct usb_device_id *id;
-
- /* if this is an unclaimed interface, skip it */
- if (!intf->driver) {
- continue;
- }
-
- US_DEBUGP("Examining driver %s...", intf->driver->name);
-
- /* simulate a disconnect and reconnect for all interfaces */
- 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");
- scsi_lock(srb->host);
- return SUCCESS;
+ printk(KERN_INFO "usb-storage: bus_reset() requested but not implemented\n" );
+ return device_reset(srb);
}

-/* 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);
}

/***********************************************************************
@@ -309,7 +271,12 @@
us = us->next;
}

- /* release our lock on the data structures */
+ /* Release our lock on the data structures early.
+ * 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 */
@@ -331,8 +298,7 @@

/* show the GUID of the device */
SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
- SPRINTF(" Attached: %s\n", (test_bit(DEV_ATTACHED, &us->bitflags)
- ? "Yes" : "No"));
+ SPRINTF(" Attached: %s\n", (us->pusb_dev != NULL) ? "Yes" : "No");

/*
* Calculate start of next buffer, and return value.
@@ -370,6 +336,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 Tue Jun 4 20:39:19 2002
+++ build-2.5/drivers/usb/storage/transport.c Wed Jul 3 20:36:17 2002
@@ -357,21 +357,21 @@
* We're very concered about races with a command abort. Hanging this code is
* a sure fire way to hang the kernel.
*
- * The abort function first sets the machine state, then tries to acquire the
+ * The abort function first sets abort_cmd, then tries to acquire the
* lock on the current_urb to abort it.
*
- * Once a function grabs the current_urb_sem, then it -MUST- test the state to
+ * Once a function grabs the 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.
*
- * The idea is that, once the current_urb_sem is held, the state can't really
+ * The idea is that, once the 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.
*
- * 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
+ * So, we've guaranteed that (after the abort_cmd test), if we do submit the
+ * URB it will get aborted when we release the 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.
+ * urb_sem, we can safely get out before the URB is submitted.
*/

/* This is the completion handler which will wake us up when an URB
@@ -385,7 +385,7 @@
}

/* 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.
*
* All URBs from the usb-storage driver _must_ pass through this function
* (or something like it) for the abort mechanisms to work properly.
@@ -395,6 +395,8 @@
struct completion urb_done;
int status;

+ BUG_NOT_DOWN(&us->dev_semaphore);
+
/* set up data structures for the wakeup system */
init_completion(&urb_done);

@@ -405,16 +407,19 @@
us->current_urb->transfer_flags = USB_ASYNC_UNLINK;

/* submit the URB */
- status = usb_submit_urb(us->current_urb, GFP_NOIO);
- if (status) {
- /* something went wrong */
- return status;
+ 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)
+ return status;

/* 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;
@@ -441,12 +446,11 @@
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);

/* has the current command been aborted? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
- up(&(us->current_urb_sem));
+ if (us->abort_cmd) {
return 0;
}

@@ -462,8 +466,6 @@
if (status >= 0)
status = us->current_urb->actual_length;

- /* release the lock and return status */
- up(&(us->current_urb_sem));
return status;
}

@@ -475,14 +477,12 @@
{
int status;

- /* lock the URB */
- down(&(us->current_urb_sem));
+ /* is the device access locked? */
+ BUG_NOT_DOWN(&us->dev_semaphore);

/* has the current command been aborted? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
- up(&(us->current_urb_sem));
+ if (us->abort_cmd)
return 0;
- }

/* fill the URB */
FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
@@ -494,8 +494,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;
}

@@ -571,7 +569,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;
}
@@ -618,6 +616,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)
@@ -849,44 +850,65 @@
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)
+void usb_stor_abort_transport(struct us_data *us, int call_scsidone)
{
- 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));
-
- /* set state to abort */
- 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 */
-
- /* 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.
+ /* 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().
+ *
+ * urb_sem also doubles as the memory barrier that
+ * makes the update to abort_cmd visible on all cpus in the
+ * system.
+ */
+ down(&us->urb_sem);
+ if (call_scsidone)
+ us->abort_cmd = ABORT_CALLSCSIDONE;
+ else
+ us->abort_cmd = ABORT_NOSCSIDONE;
+
+ /* Check if the worker thread is processing the
+ * command right now, abort it if yes.
*/
- down(&(us->current_urb_sem));
+
if (us->current_urb->status == -EINPROGRESS) {
US_DEBUGP("-- cancelling URB\n");
usb_unlink_urb(us->current_urb);
}
- up(&(us->current_urb_sem));

/* If we are waiting for an IRQ, simulate it */
- if (test_bit(IP_WANTED, &us->bitflags)) {
+ if (test_and_clear_bit(IP_WANTED, &us->bitflags)) {
+ struct us_data *us = (struct us_data *)us->irq_urb->context;
US_DEBUGP("-- simulating missing IRQ\n");
- usb_stor_CBI_irq(us->irq_urb);
+ /* wake up the command thread */
+ up(&us->ip_waitq);
}
+ up(&us->urb_sem);
+
+ spin_lock_irq(&us->queue_exclusion);
+ 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);
+ }
+ spin_unlock_irq(&us->queue_exclusion);
+ down(&us->urb_sem);
+ us->abort_cmd = 0;
+ up(&us->urb_sem);
}

/*
@@ -904,21 +926,6 @@
US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n",
us->irqbuf[0], us->irqbuf[1]);

- /* has the current command been aborted? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
-
- /* was this a wanted interrupt? */
- if (!test_and_clear_bit(IP_WANTED, &us->bitflags)) {
- US_DEBUGP("ERROR: Unwanted interrupt received!\n");
- return;
- }
- US_DEBUGP("-- command aborted\n");
-
- /* wake up the command thread */
- up(&us->ip_waitq);
- return;
- }
-
/* is the device removed? */
if (urb->status == -ENOENT) {
US_DEBUGP("-- device has been removed\n");
@@ -987,7 +994,7 @@
}

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_control_msg(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -999,7 +1006,7 @@
usb_sndctrlpipe(us->pusb_dev, 0));

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_control_msg(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1036,7 +1043,7 @@
down(&(us->ip_waitq));

/* has the current command been aborted? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("CBI interrupt aborted\n");
return USB_STOR_TRANSPORT_ABORTED;
}
@@ -1104,7 +1111,7 @@
US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
if (result < 0) {
/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_CB_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1116,7 +1123,7 @@
usb_sndctrlpipe(us->pusb_dev, 0));

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_CB_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1225,7 +1232,7 @@
US_DEBUGP("Bulk command transfer result=%d\n", result);

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_Bulk_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1236,7 +1243,7 @@
result = usb_stor_clear_halt(us, pipe);

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_Bulk_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1273,7 +1280,7 @@
&partial);

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_Bulk_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1284,7 +1291,7 @@
result = usb_stor_clear_halt(us, pipe);

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_Bulk_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1295,7 +1302,7 @@
US_BULK_CS_WRAP_LEN, &partial);

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_Bulk_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1306,7 +1313,7 @@
result = usb_stor_clear_halt(us, pipe);

/* did we abort this command? */
- if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+ if (us->abort_cmd) {
US_DEBUGP("usb_stor_Bulk_transport(): transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
@@ -1358,23 +1365,33 @@

struct us_timeout {
struct us_data *us;
- spinlock_t timer_lock;
+ struct completion done;
+ struct tq_struct event;
};

+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 event handler called\n");
+
+ /* abort the current request */
+ usb_stor_abort_transport(us, 1);
+
+ complete(&to->done);
+}
+
/* The timeout event handler
*/
static void usb_stor_timeout_handler(unsigned long to__)
{
struct us_timeout *to = (struct us_timeout *) to__;
- struct us_data *us = to->us;

US_DEBUGP("Timeout occurred\n");

- /* abort the current request */
- usb_stor_abort_transport(us);
-
- /* let the reset routine know we have finished */
- spin_unlock(&to->timer_lock);
+ INIT_TQUEUE(&to->event, usb_stor_event_handler, to);
+ schedule_task(&to->event);
}

/* This is the common part of the device reset code.
@@ -1389,11 +1406,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
@@ -1429,8 +1447,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/transport.h build-2.5/drivers/usb/storage/transport.h
--- 2.5/drivers/usb/storage/transport.h Sun May 26 11:05:08 2002
+++ build-2.5/drivers/usb/storage/transport.h Wed Jul 3 20:34:01 2002
@@ -149,7 +149,7 @@

extern unsigned int usb_stor_transfer_length(Scsi_Cmnd*);
extern void usb_stor_invoke_transport(Scsi_Cmnd*, struct us_data*);
-extern void usb_stor_abort_transport(struct us_data*);
+extern void usb_stor_abort_transport(struct us_data*, int call_scsidone);
extern int usb_stor_transfer_partial(struct us_data*, char*, int);

extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
diff -u 2.5/drivers/usb/storage/usb.c build-2.5/drivers/usb/storage/usb.c
--- 2.5/drivers/usb/storage/usb.c Tue Jun 25 20:28:28 2002
+++ build-2.5/drivers/usb/storage/usb.c Wed Jul 3 22:45:47 2002
@@ -101,7 +101,7 @@

/* 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);
@@ -326,6 +326,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));
@@ -333,12 +336,13 @@
for(;;) {
struct Scsi_Host *host;
US_DEBUGP("*** thread sleeping.\n");
- atomic_set(&us->sm_state, US_STATE_IDLE);
- 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");
- atomic_set(&us->sm_state, US_STATE_RUNNING);

/* lock access to the queue element */
spin_lock_irq(&us->queue_exclusion);
@@ -346,9 +350,6 @@
/* 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);

@@ -359,6 +360,8 @@
}

BUG_ON(action != US_ACT_COMMAND);
+ host = us->srb->host;
+

/* reject the command if the direction indicator
* is UNKNOWN
@@ -416,9 +419,10 @@

/* lock the device pointers */
down(&(us->dev_semaphore));
+ us->sm_state = US_STATE_RUNNING;

/* our device has gone - pretend not ready */
- if (!test_bit(DEV_ATTACHED, &us->bitflags)) {
+ if (us->pusb_dev == NULL) {
US_DEBUGP("Request is for removed device\n");
/* For REQUEST_SENSE, it's the data. But
* for anything else, it should look like
@@ -442,7 +446,7 @@
sizeof(usb_stor_sense_notready));
us->srb->result = CHECK_CONDITION << 1;
}
- } else { /* test_bit(DEV_ATTACHED, &us->bitflags) */
+ } else { /* us->pusb_dev != NULL */

/* Handle those devices which need us to fake
* their inquiry data */
@@ -461,23 +465,22 @@
us->proto_handler(us->srb, us);
}
}
+ us->sm_state = US_STATE_IDLE;

/* unlock the device pointers */
up(&(us->dev_semaphore));

/* 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);
+ scsi_lock(host);
+ US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result);
+ if (us->abort_cmd != ABORT_NOSCSIDONE) {
+ /* If we are in error recovery mode,
+ * no scsi_done callback needed.
+ */
us->srb->scsi_done(us->srb);
- us->srb = NULL;
- scsi_unlock(host);
- } else {
- US_DEBUGP("scsi command aborted\n");
- us->srb = NULL;
- complete(&(us->notify));
}
+ us->srb = NULL;
+ scsi_unlock(host);
} /* for (;;) */

/* notify the exit routine that we're actually exiting now */
@@ -500,13 +503,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;
}
@@ -527,12 +529,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;
}

@@ -569,6 +568,8 @@
intf[ifnum].altsetting + intf[ifnum].act_altsetting;
US_DEBUGP("act_altsetting 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));
@@ -686,10 +687,11 @@
* already on the system
*/
ss = us_list;
- while ((ss != NULL) &&
- (test_bit(DEV_ATTACHED, &ss->bitflags) ||
- !GUID_EQUAL(guid, ss->guid)))
+ while (ss != NULL) {
+ if (ss->pusb_dev == NULL && GUID_EQUAL(guid, ss->guid))
+ break;
ss = ss->next;
+ }

if (ss != NULL) {
/* Existing device -- re-connect */
@@ -701,8 +703,8 @@

/* establish the connection to the new device upon reconnect */
ss->ifnum = ifnum;
+ BUG_ON(ss->pusb_dev != NULL);
ss->pusb_dev = dev;
- set_bit(DEV_ATTACHED, &ss->bitflags);

/* copy over the endpoint data */
ss->ep_in = ep_in->bEndpointAddress &
@@ -749,9 +751,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;
@@ -969,9 +971,11 @@
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);
- set_bit(DEV_ATTACHED, &ss->bitflags);
+ ss->sm_state = US_STATE_BAD;
ss->pid = kernel_thread(usb_stor_control_thread, ss,
CLONE_VM);
if (ss->pid < 0) {
@@ -1019,20 +1023,17 @@
/* we come here if there are any problems */
BadDevice:
US_DEBUGP("storage_probe() failed\n");
- down(&ss->irq_urb_sem);
if (ss->irq_urb) {
usb_unlink_urb(ss->irq_urb);
usb_free_urb(ss->irq_urb);
ss->irq_urb = NULL;
}
- up(&ss->irq_urb_sem);
if (ss->current_urb) {
usb_unlink_urb(ss->current_urb);
usb_free_urb(ss->current_urb);
ss->current_urb = NULL;
}

- clear_bit(DEV_ATTACHED, &ss->bitflags);
ss->pusb_dev = NULL;
if (new_device)
kfree(ss);
@@ -1050,17 +1051,21 @@

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");
return;
}

+ /* kill a pending command */
+ usb_stor_abort_transport(ss, 1);
+
/* lock access to the device data structure */
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);
@@ -1068,7 +1073,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");
@@ -1080,7 +1084,6 @@
/* mark the device as gone */
usb_put_dev(ss->pusb_dev);
ss->pusb_dev = NULL;
- clear_bit(DEV_ATTACHED, &ss->bitflags);

/* unlock access to the device data structure */
up(&(ss->dev_semaphore));
@@ -1096,7 +1099,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 Tue Jun 25 20:28:28 2002
+++ build-2.5/drivers/usb/storage/usb.h Wed Jul 3 20:35:04 2002
@@ -39,6 +39,28 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Changelog:
+ * - remove the DEV_ATTACHED bitflag, it duplicates (->pusb_dev !=NULL)
+ * - make sm_state a normal variable, atomic_t do not provide synchronization
+ * for assignments
+ * - merge queue_srb and srb, that created a hole for crashes if a device_reset()
+ * happens too early. Add BUG_ON tests to catch errors.
+ * - device aborts happen asynchroneously, make abort_cmd a seperate variable
+ * - add BUG_ON tests to catch missing semaphore calls
+ * - bus_reset dropped for now, synchronization is too fragile, no real need for
+ * it
+ * - restrict commands to 128 sectors, large commands cause errors with my SaroTech
+ * USB enclosure
+ * - move urb_sem into usb_stor_msg_common(), asking the callers to lock & check for
+ * abort_cmd is too error prone.
+ * - add an explicit waitqueue for the device reset handler, reuse of the
+ * completion caused deadlocks in command_abort().
+ * - move the _timeout handler for usb_stor to process context.
+ * - fixoops during module unload.
+ * - abort a possibly pending command during storage_disconnect(), could cause
+ * excessive delays if the cable is pulled out while a command is pending.
+ * (C) Manfred Spraul <manfred@colorfullife.com> 2002
*/

#ifndef _USB_H_
@@ -107,13 +129,16 @@

/* kernel thread actions */
#define US_ACT_COMMAND 1
-#define US_ACT_EXIT 5
+#define US_ACT_EXIT 2

-/* processing state machine states */
+/* processing state machine states
+ * For debugging only, it can be wrong because queue_command()
+ * queues new requests asynchroneously.
+ * */
+#define US_STATE_BAD 0
#define US_STATE_IDLE 1
#define US_STATE_RUNNING 2
#define US_STATE_RESETTING 3
-#define US_STATE_ABORTING 4

#define USB_STOR_STRING_LEN 32

@@ -127,9 +152,12 @@
struct us_data *next; /* next device */

/* The device we're working with
- * It's important to note:
- * (o) you must hold dev_semaphore to change pusb_dev
- * (o) DEV_ATTACHED in bitflags should change whenever pusb_dev does
+ * It's important to note that you must hold dev_semaphore
+ * to change or use pusb_dev.
+ *
+ * Virtually the complete device access is serialized through
+ * dev_semaphore, the only notable exception is command abortion
+ * which is serialized by urb_sem.
*/
struct semaphore dev_semaphore; /* protect pusb_dev */
struct usb_device *pusb_dev; /* this usb_device */
@@ -166,25 +194,28 @@
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 long abort_cmd;
+/* 0 means do not abort */
+#define ABORT_CALLSCSIDONE 1
+#define ABORT_NOSCSIDONE 2

/* interrupt info for CBI devices -- only good if attached */
struct semaphore ip_waitq; /* for CBI interrupts */
- unsigned long bitflags; /* single-bit flags: */
+ unsigned long bitflags; /* atomic bitflags */
#define IP_WANTED 1 /* is an IRQ expected? */
-#define DEV_ATTACHED 2 /* is the dev. attached?*/

/* 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 */
@@ -221,4 +252,12 @@
#define sg_address(psg) ((psg)->address)
#endif

+#define BUG_NOT_DOWN(sem) \
+ do { \
+ if (!down_trylock(sem)) { \
+ up(sem); \
+ BUG(); \
+ } \
+ } while(0)
+
#endif
\
 
 \ /
  Last update: 2005-03-22 13:27    [W:0.144 / U:2.088 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site