Skip to content

Commit

Permalink
[SCSI] limit state transitions in scsi_internal_device_unblock
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takahiro Yasui authored and James Bottomley committed May 23, 2009
1 parent b0d428a commit 5c10e63
Showing 1 changed file with 6 additions and 8 deletions.
14 changes: 6 additions & 8 deletions drivers/scsi/scsi_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 5c10e63

Please sign in to comment.