[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:
parent
0f1d87a2ac
commit
6f4267e3bd
4 changed files with 54 additions and 16 deletions
|
@ -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);
|
||||
|
|
|
@ -730,6 +730,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
|||
static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
||||
int *bflags, int async)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX do not save the inquiry, since it can change underneath us,
|
||||
* save just vendor/model/rev.
|
||||
|
@ -885,7 +887,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
|||
|
||||
/* set the device running here so that slave configure
|
||||
* may do I/O */
|
||||
scsi_device_set_state(sdev, SDEV_RUNNING);
|
||||
ret = scsi_device_set_state(sdev, SDEV_RUNNING);
|
||||
if (ret) {
|
||||
ret = scsi_device_set_state(sdev, SDEV_BLOCK);
|
||||
|
||||
if (ret) {
|
||||
sdev_printk(KERN_ERR, sdev,
|
||||
"in wrong state %s to complete scan\n",
|
||||
scsi_device_state_name(sdev->sdev_state));
|
||||
return SCSI_SCAN_NO_RESPONSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (*bflags & BLIST_MS_192_BYTES_FOR_3F)
|
||||
sdev->use_192_bytes_for_3f = 1;
|
||||
|
@ -899,7 +911,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
|||
transport_configure_device(&sdev->sdev_gendev);
|
||||
|
||||
if (sdev->host->hostt->slave_configure) {
|
||||
int ret = sdev->host->hostt->slave_configure(sdev);
|
||||
ret = sdev->host->hostt->slave_configure(sdev);
|
||||
if (ret) {
|
||||
/*
|
||||
* if LLDD reports slave not present, don't clutter
|
||||
|
|
|
@ -34,6 +34,7 @@ static const struct {
|
|||
{ SDEV_QUIESCE, "quiesce" },
|
||||
{ SDEV_OFFLINE, "offline" },
|
||||
{ SDEV_BLOCK, "blocked" },
|
||||
{ SDEV_CREATED_BLOCK, "created-blocked" },
|
||||
};
|
||||
|
||||
const char *scsi_device_state_name(enum scsi_device_state state)
|
||||
|
|
|
@ -42,9 +42,11 @@ enum scsi_device_state {
|
|||
* originate in the mid-layer) */
|
||||
SDEV_OFFLINE, /* Device offlined (by error handling or
|
||||
* user request */
|
||||
SDEV_BLOCK, /* Device blocked by scsi lld. No scsi
|
||||
* commands from user or midlayer should be issued
|
||||
* to the scsi lld. */
|
||||
SDEV_BLOCK, /* Device blocked by scsi lld. No
|
||||
* scsi commands from user or midlayer
|
||||
* should be issued to the scsi
|
||||
* lld. */
|
||||
SDEV_CREATED_BLOCK, /* same as above but for created devices */
|
||||
};
|
||||
|
||||
enum scsi_device_event {
|
||||
|
@ -393,11 +395,13 @@ static inline int scsi_device_online(struct scsi_device *sdev)
|
|||
}
|
||||
static inline int scsi_device_blocked(struct scsi_device *sdev)
|
||||
{
|
||||
return sdev->sdev_state == SDEV_BLOCK;
|
||||
return sdev->sdev_state == SDEV_BLOCK ||
|
||||
sdev->sdev_state == SDEV_CREATED_BLOCK;
|
||||
}
|
||||
static inline int scsi_device_created(struct scsi_device *sdev)
|
||||
{
|
||||
return sdev->sdev_state == SDEV_CREATED;
|
||||
return sdev->sdev_state == SDEV_CREATED ||
|
||||
sdev->sdev_state == SDEV_CREATED_BLOCK;
|
||||
}
|
||||
|
||||
/* accessor functions for the SCSI parameters */
|
||||
|
|
Loading…
Reference in a new issue