[SCSI] Update the SCSI state model to allow blocking in the created state

Brian King <brking@linux.vnet.ibm.com> reported that fibre channel
devices can oops during scanning if their ports block (because the
device goes from CREATED -> BLOCK -> RUNNING rather than CREATED ->
BLOCK -> CREATED).

Fix this by adding a new state: CREATED_BLOCK which can only transition
back to CREATED and disallow the CREATED -> BLOCK transition.  Now both
the created and blocked states that the mid-layer recognises can include
CREATED_BLOCK.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
James Bottomley
2008-08-22 16:53:31 -05:00
parent 0f1d87a2ac
commit 6f4267e3bd
4 changed files with 54 additions and 16 deletions

View File

@@ -1251,6 +1251,7 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req)
break;
case SDEV_QUIESCE:
case SDEV_BLOCK:
case SDEV_CREATED_BLOCK:
/*
* If the devices is blocked we defer normal commands.
*/
@@ -2064,10 +2065,13 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
switch (state) {
case SDEV_CREATED:
/* There are no legal states that come back to
* created. This is the manually initialised start
* state */
goto illegal;
switch (oldstate) {
case SDEV_CREATED_BLOCK:
break;
default:
goto illegal;
}
break;
case SDEV_RUNNING:
switch (oldstate) {
@@ -2105,8 +2109,17 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
case SDEV_BLOCK:
switch (oldstate) {
case SDEV_CREATED:
case SDEV_RUNNING:
case SDEV_CREATED_BLOCK:
break;
default:
goto illegal;
}
break;
case SDEV_CREATED_BLOCK:
switch (oldstate) {
case SDEV_CREATED:
break;
default:
goto illegal;
@@ -2394,8 +2407,12 @@ scsi_internal_device_block(struct scsi_device *sdev)
int err = 0;
err = scsi_device_set_state(sdev, SDEV_BLOCK);
if (err)
return err;
if (err) {
err = scsi_device_set_state(sdev, SDEV_CREATED_BLOCK);
if (err)
return err;
}
/*
* The device has transitioned to SDEV_BLOCK. Stop the
@@ -2438,8 +2455,12 @@ scsi_internal_device_unblock(struct scsi_device *sdev)
* and goose the device queue if successful.
*/
err = scsi_device_set_state(sdev, SDEV_RUNNING);
if (err)
return err;
if (err) {
err = scsi_device_set_state(sdev, SDEV_CREATED);
if (err)
return err;
}
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q);