Commit 6f4267e3 authored by James Bottomley's avatar James Bottomley

[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: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 0f1d87a2
...@@ -1251,6 +1251,7 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) ...@@ -1251,6 +1251,7 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req)
break; break;
case SDEV_QUIESCE: case SDEV_QUIESCE:
case SDEV_BLOCK: case SDEV_BLOCK:
case SDEV_CREATED_BLOCK:
/* /*
* If the devices is blocked we defer normal commands. * 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) ...@@ -2064,10 +2065,13 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
switch (state) { switch (state) {
case SDEV_CREATED: case SDEV_CREATED:
/* There are no legal states that come back to switch (oldstate) {
* created. This is the manually initialised start case SDEV_CREATED_BLOCK:
* state */ break;
goto illegal; default:
goto illegal;
}
break;
case SDEV_RUNNING: case SDEV_RUNNING:
switch (oldstate) { switch (oldstate) {
...@@ -2105,8 +2109,17 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) ...@@ -2105,8 +2109,17 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
case SDEV_BLOCK: case SDEV_BLOCK:
switch (oldstate) { switch (oldstate) {
case SDEV_CREATED:
case SDEV_RUNNING: case SDEV_RUNNING:
case SDEV_CREATED_BLOCK:
break;
default:
goto illegal;
}
break;
case SDEV_CREATED_BLOCK:
switch (oldstate) {
case SDEV_CREATED:
break; break;
default: default:
goto illegal; goto illegal;
...@@ -2394,8 +2407,12 @@ scsi_internal_device_block(struct scsi_device *sdev) ...@@ -2394,8 +2407,12 @@ scsi_internal_device_block(struct scsi_device *sdev)
int err = 0; int err = 0;
err = scsi_device_set_state(sdev, SDEV_BLOCK); err = scsi_device_set_state(sdev, SDEV_BLOCK);
if (err) if (err) {
return err; err = scsi_device_set_state(sdev, SDEV_CREATED_BLOCK);
if (err)
return err;
}
/* /*
* The device has transitioned to SDEV_BLOCK. Stop the * The device has transitioned to SDEV_BLOCK. Stop the
...@@ -2438,8 +2455,12 @@ scsi_internal_device_unblock(struct scsi_device *sdev) ...@@ -2438,8 +2455,12 @@ scsi_internal_device_unblock(struct scsi_device *sdev)
* and goose the device queue if successful. * and goose the device queue if successful.
*/ */
err = scsi_device_set_state(sdev, SDEV_RUNNING); err = scsi_device_set_state(sdev, SDEV_RUNNING);
if (err) if (err) {
return err; err = scsi_device_set_state(sdev, SDEV_CREATED);
if (err)
return err;
}
spin_lock_irqsave(q->queue_lock, flags); spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q); blk_start_queue(q);
......
...@@ -730,6 +730,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, ...@@ -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, static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
int *bflags, int async) int *bflags, int async)
{ {
int ret;
/* /*
* XXX do not save the inquiry, since it can change underneath us, * XXX do not save the inquiry, since it can change underneath us,
* save just vendor/model/rev. * save just vendor/model/rev.
...@@ -885,7 +887,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, ...@@ -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 /* set the device running here so that slave configure
* may do I/O */ * 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) if (*bflags & BLIST_MS_192_BYTES_FOR_3F)
sdev->use_192_bytes_for_3f = 1; sdev->use_192_bytes_for_3f = 1;
...@@ -899,7 +911,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, ...@@ -899,7 +911,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
transport_configure_device(&sdev->sdev_gendev); transport_configure_device(&sdev->sdev_gendev);
if (sdev->host->hostt->slave_configure) { if (sdev->host->hostt->slave_configure) {
int ret = sdev->host->hostt->slave_configure(sdev); ret = sdev->host->hostt->slave_configure(sdev);
if (ret) { if (ret) {
/* /*
* if LLDD reports slave not present, don't clutter * if LLDD reports slave not present, don't clutter
......
...@@ -34,6 +34,7 @@ static const struct { ...@@ -34,6 +34,7 @@ static const struct {
{ SDEV_QUIESCE, "quiesce" }, { SDEV_QUIESCE, "quiesce" },
{ SDEV_OFFLINE, "offline" }, { SDEV_OFFLINE, "offline" },
{ SDEV_BLOCK, "blocked" }, { SDEV_BLOCK, "blocked" },
{ SDEV_CREATED_BLOCK, "created-blocked" },
}; };
const char *scsi_device_state_name(enum scsi_device_state state) const char *scsi_device_state_name(enum scsi_device_state state)
......
...@@ -42,9 +42,11 @@ enum scsi_device_state { ...@@ -42,9 +42,11 @@ enum scsi_device_state {
* originate in the mid-layer) */ * originate in the mid-layer) */
SDEV_OFFLINE, /* Device offlined (by error handling or SDEV_OFFLINE, /* Device offlined (by error handling or
* user request */ * user request */
SDEV_BLOCK, /* Device blocked by scsi lld. No scsi SDEV_BLOCK, /* Device blocked by scsi lld. No
* commands from user or midlayer should be issued * scsi commands from user or midlayer
* to the scsi lld. */ * should be issued to the scsi
* lld. */
SDEV_CREATED_BLOCK, /* same as above but for created devices */
}; };
enum scsi_device_event { enum scsi_device_event {
...@@ -393,11 +395,13 @@ static inline int scsi_device_online(struct scsi_device *sdev) ...@@ -393,11 +395,13 @@ static inline int scsi_device_online(struct scsi_device *sdev)
} }
static inline int scsi_device_blocked(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) 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 */ /* accessor functions for the SCSI parameters */
......
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