Commit bd7ae0db authored by Matthew Dharm's avatar Matthew Dharm Committed by Greg Kroah-Hartman

[PATCH] USB Storage: Fix race when removing the SCSI host

This patch fixes a race is disconnecting a usb-storage device that occurs
with the SCSI layer.  It's primarily reproducable via adding delays into
various disconnect and reset processing paths, but has also been
encountered in the field.

This patch started life as as281b, and was modified by me only to patch
properly against current kernels.

The main features of the patch are:

	Store the host pointer at the start of the control thread
	rather than trying to get it from srb->device; after the host
	is removed the SCSI device structure may no longer exist.

	Keep dev_semaphore locked during the entire time the control
	thread or reset handlers are using the us_data structure.

	Reorder the items in dissociate_dev() and release_resources()
	so that things are released in the opposite order from the way
	they were acquired originally.  Don't bother to increment and
	decrement the usb_device's reference count; it's unnecessary.

	In disconnect(), first set the DISCONNECTING flag so that no
	more I/O will take place and no more requests will be accepted.
	Next, cut short the current command and wait for it to finish.
	Then call scsi_remove_host().  The SCSI core guarantees that
	when scsi_remove_host() returns, the host will not be in error
	recovery and all outstanding commands will have been cancelled.

	Remove some old useless left-over code that was #if'ed out.

	Use a wait_queue for the 6-second delay during device resets
	so that we can be woken up in the middle if a disconnect occurs.

The key point here is that after scsi_remove_host(), everything is idle as
far as the SCSI midlayer is concerned.  But if there was a command in
progress at the time, the midlayer will abort it without telling us or
waiting for it to complete.  Hence we have to wait for the control thread
to be idle before we can try to kill it.  This should happen quickly,
since all I/O attempted by the thread will fail immediately.
Signed-off-by: default avatarMatthew Dharm <mdharm-usb@one-eyed-alien.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 47f1558e
......@@ -1113,10 +1113,11 @@ static int usb_stor_reset_common(struct us_data *us,
goto Done;
}
/* long wait for reset, so unlock to allow disconnects */
up(&us->dev_semaphore);
msleep(6000);
down(&us->dev_semaphore);
/* Give the device some time to recover from the reset,
* but don't delay disconnect processing. */
wait_event_interruptible_timeout(us->dev_reset_wait,
test_bit(US_FLIDX_DISCONNECTING, &us->flags),
HZ*6);
if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
US_DEBUGP("Reset interrupted by disconnect\n");
goto Done;
......
This diff is collapsed.
......@@ -158,9 +158,10 @@ struct us_data {
dma_addr_t cr_dma; /* buffer DMA addresses */
dma_addr_t iobuf_dma;
/* mutual exclusion structures */
/* mutual exclusion and synchronization structures */
struct semaphore sema; /* to sleep thread on */
struct completion notify; /* thread begin/end */
wait_queue_head_t dev_reset_wait; /* wait during reset */
/* subdriver information */
void *extra; /* Any extra data */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment