diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-08-22 17:53:31 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-10-03 12:46:13 -0400 |
commit | 6f4267e3bd1211b3d09130e626b0b3d885077610 (patch) | |
tree | e9350f919238866c3bcd9c340ffea639a0c5de1d /drivers/scsi | |
parent | 0f1d87a2acb8fd1f2ef8af109a785123ddc1a6cb (diff) |
[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>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/scsi_lib.c | 39 | ||||
-rw-r--r-- | drivers/scsi/scsi_scan.c | 16 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 1 |
3 files changed, 45 insertions, 11 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 62307bd794a9..d2884bffa1b9 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -1251,6 +1251,7 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) | |||
1251 | break; | 1251 | break; |
1252 | case SDEV_QUIESCE: | 1252 | case SDEV_QUIESCE: |
1253 | case SDEV_BLOCK: | 1253 | case SDEV_BLOCK: |
1254 | case SDEV_CREATED_BLOCK: | ||
1254 | /* | 1255 | /* |
1255 | * If the devices is blocked we defer normal commands. | 1256 | * If the devices is blocked we defer normal commands. |
1256 | */ | 1257 | */ |
@@ -2064,10 +2065,13 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) | |||
2064 | 2065 | ||
2065 | switch (state) { | 2066 | switch (state) { |
2066 | case SDEV_CREATED: | 2067 | case SDEV_CREATED: |
2067 | /* There are no legal states that come back to | 2068 | switch (oldstate) { |
2068 | * created. This is the manually initialised start | 2069 | case SDEV_CREATED_BLOCK: |
2069 | * state */ | 2070 | break; |
2070 | goto illegal; | 2071 | default: |
2072 | goto illegal; | ||
2073 | } | ||
2074 | break; | ||
2071 | 2075 | ||
2072 | case SDEV_RUNNING: | 2076 | case SDEV_RUNNING: |
2073 | switch (oldstate) { | 2077 | switch (oldstate) { |
@@ -2105,8 +2109,17 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) | |||
2105 | 2109 | ||
2106 | case SDEV_BLOCK: | 2110 | case SDEV_BLOCK: |
2107 | switch (oldstate) { | 2111 | switch (oldstate) { |
2108 | case SDEV_CREATED: | ||
2109 | case SDEV_RUNNING: | 2112 | case SDEV_RUNNING: |
2113 | case SDEV_CREATED_BLOCK: | ||
2114 | break; | ||
2115 | default: | ||
2116 | goto illegal; | ||
2117 | } | ||
2118 | break; | ||
2119 | |||
2120 | case SDEV_CREATED_BLOCK: | ||
2121 | switch (oldstate) { | ||
2122 | case SDEV_CREATED: | ||
2110 | break; | 2123 | break; |
2111 | default: | 2124 | default: |
2112 | goto illegal; | 2125 | goto illegal; |
@@ -2394,8 +2407,12 @@ scsi_internal_device_block(struct scsi_device *sdev) | |||
2394 | int err = 0; | 2407 | int err = 0; |
2395 | 2408 | ||
2396 | err = scsi_device_set_state(sdev, SDEV_BLOCK); | 2409 | err = scsi_device_set_state(sdev, SDEV_BLOCK); |
2397 | if (err) | 2410 | if (err) { |
2398 | return err; | 2411 | err = scsi_device_set_state(sdev, SDEV_CREATED_BLOCK); |
2412 | |||
2413 | if (err) | ||
2414 | return err; | ||
2415 | } | ||
2399 | 2416 | ||
2400 | /* | 2417 | /* |
2401 | * The device has transitioned to SDEV_BLOCK. Stop the | 2418 | * The device has transitioned to SDEV_BLOCK. Stop the |
@@ -2438,8 +2455,12 @@ scsi_internal_device_unblock(struct scsi_device *sdev) | |||
2438 | * and goose the device queue if successful. | 2455 | * and goose the device queue if successful. |
2439 | */ | 2456 | */ |
2440 | err = scsi_device_set_state(sdev, SDEV_RUNNING); | 2457 | err = scsi_device_set_state(sdev, SDEV_RUNNING); |
2441 | if (err) | 2458 | if (err) { |
2442 | return err; | 2459 | err = scsi_device_set_state(sdev, SDEV_CREATED); |
2460 | |||
2461 | if (err) | ||
2462 | return err; | ||
2463 | } | ||
2443 | 2464 | ||
2444 | spin_lock_irqsave(q->queue_lock, flags); | 2465 | spin_lock_irqsave(q->queue_lock, flags); |
2445 | blk_start_queue(q); | 2466 | blk_start_queue(q); |
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 2926baaac31e..334862e26a1b 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c | |||
@@ -730,6 +730,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, | |||
730 | static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, | 730 | static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, |
731 | int *bflags, int async) | 731 | int *bflags, int async) |
732 | { | 732 | { |
733 | int ret; | ||
734 | |||
733 | /* | 735 | /* |
734 | * XXX do not save the inquiry, since it can change underneath us, | 736 | * XXX do not save the inquiry, since it can change underneath us, |
735 | * save just vendor/model/rev. | 737 | * save just vendor/model/rev. |
@@ -885,7 +887,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, | |||
885 | 887 | ||
886 | /* set the device running here so that slave configure | 888 | /* set the device running here so that slave configure |
887 | * may do I/O */ | 889 | * may do I/O */ |
888 | scsi_device_set_state(sdev, SDEV_RUNNING); | 890 | ret = scsi_device_set_state(sdev, SDEV_RUNNING); |
891 | if (ret) { | ||
892 | ret = scsi_device_set_state(sdev, SDEV_BLOCK); | ||
893 | |||
894 | if (ret) { | ||
895 | sdev_printk(KERN_ERR, sdev, | ||
896 | "in wrong state %s to complete scan\n", | ||
897 | scsi_device_state_name(sdev->sdev_state)); | ||
898 | return SCSI_SCAN_NO_RESPONSE; | ||
899 | } | ||
900 | } | ||
889 | 901 | ||
890 | if (*bflags & BLIST_MS_192_BYTES_FOR_3F) | 902 | if (*bflags & BLIST_MS_192_BYTES_FOR_3F) |
891 | sdev->use_192_bytes_for_3f = 1; | 903 | 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 | transport_configure_device(&sdev->sdev_gendev); | 911 | transport_configure_device(&sdev->sdev_gendev); |
900 | 912 | ||
901 | if (sdev->host->hostt->slave_configure) { | 913 | if (sdev->host->hostt->slave_configure) { |
902 | int ret = sdev->host->hostt->slave_configure(sdev); | 914 | ret = sdev->host->hostt->slave_configure(sdev); |
903 | if (ret) { | 915 | if (ret) { |
904 | /* | 916 | /* |
905 | * if LLDD reports slave not present, don't clutter | 917 | * if LLDD reports slave not present, don't clutter |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ab3c71869be5..09d311d559d1 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -34,6 +34,7 @@ static const struct { | |||
34 | { SDEV_QUIESCE, "quiesce" }, | 34 | { SDEV_QUIESCE, "quiesce" }, |
35 | { SDEV_OFFLINE, "offline" }, | 35 | { SDEV_OFFLINE, "offline" }, |
36 | { SDEV_BLOCK, "blocked" }, | 36 | { SDEV_BLOCK, "blocked" }, |
37 | { SDEV_CREATED_BLOCK, "created-blocked" }, | ||
37 | }; | 38 | }; |
38 | 39 | ||
39 | const char *scsi_device_state_name(enum scsi_device_state state) | 40 | const char *scsi_device_state_name(enum scsi_device_state state) |