summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTakahiro Yasui <tyasui@redhat.com>2009-04-29 12:13:02 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-05-23 15:44:05 -0500
commit5c10e63c943b4c67561ddc6bf61e01d4141f881f (patch)
tree7b71f601bcf852159bae15a2cf6caa038d74e27d /drivers
parentb0d428adebe9f1232c72bf4c686a6f0eed047cc2 (diff)
[SCSI] limit state transitions in scsi_internal_device_unblock
scsi timeout on two or more devices may cause extremely long execution time for user applications because SDEV_OFFLINE state is changed to SDEV_RUNNING state during scsi error recovery procedures triggered by a bus reset or a host reset of scsi LLD, and scsi timeout can happens on the same devices many times. This happens because scsi_internal_device_unblock() changes device's state to SDEV_RUNNING even if a device in other states than SDEV_BLOCK, while the following two transitions are required in this function. SDEV_BLOCK -> SDEV_RUNNING SDEV_CREATED_BLOCK -> SDEV_CREATED Otherwise, it returns -EINVAL. Signed-off-by: Takahiro Yasui <tyasui@redhat.com> [matthew@wil.cx: supplied rewritten base for patch] Signed-off-by: Matthew Wilcox <matthew@wil.cx> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/scsi_lib.c14
1 files changed, 6 insertions, 8 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index bb218c8b6e98..27dbf2e8e34a 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2441,20 +2441,18 @@ int
scsi_internal_device_unblock(struct scsi_device *sdev)
{
struct request_queue *q = sdev->request_queue;
- int err;
unsigned long flags;
/*
* Try to transition the scsi device to SDEV_RUNNING
* and goose the device queue if successful.
*/
- err = scsi_device_set_state(sdev, SDEV_RUNNING);
- if (err) {
- err = scsi_device_set_state(sdev, SDEV_CREATED);
-
- if (err)
- return err;
- }
+ if (sdev->sdev_state == SDEV_BLOCK)
+ sdev->sdev_state = SDEV_RUNNING;
+ else if (sdev->sdev_state == SDEV_CREATED_BLOCK)
+ sdev->sdev_state = SDEV_CREATED;
+ else
+ return -EINVAL;
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q);