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 | |
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>
-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 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 14 |
4 files changed, 54 insertions, 16 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) |
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index cc46652e4658..b49e725be039 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
@@ -42,9 +42,11 @@ enum scsi_device_state { | |||
42 | * originate in the mid-layer) */ | 42 | * originate in the mid-layer) */ |
43 | SDEV_OFFLINE, /* Device offlined (by error handling or | 43 | SDEV_OFFLINE, /* Device offlined (by error handling or |
44 | * user request */ | 44 | * user request */ |
45 | SDEV_BLOCK, /* Device blocked by scsi lld. No scsi | 45 | SDEV_BLOCK, /* Device blocked by scsi lld. No |
46 | * commands from user or midlayer should be issued | 46 | * scsi commands from user or midlayer |
47 | * to the scsi lld. */ | 47 | * should be issued to the scsi |
48 | * lld. */ | ||
49 | SDEV_CREATED_BLOCK, /* same as above but for created devices */ | ||
48 | }; | 50 | }; |
49 | 51 | ||
50 | enum scsi_device_event { | 52 | enum scsi_device_event { |
@@ -393,11 +395,13 @@ static inline int scsi_device_online(struct scsi_device *sdev) | |||
393 | } | 395 | } |
394 | static inline int scsi_device_blocked(struct scsi_device *sdev) | 396 | static inline int scsi_device_blocked(struct scsi_device *sdev) |
395 | { | 397 | { |
396 | return sdev->sdev_state == SDEV_BLOCK; | 398 | return sdev->sdev_state == SDEV_BLOCK || |
399 | sdev->sdev_state == SDEV_CREATED_BLOCK; | ||
397 | } | 400 | } |
398 | static inline int scsi_device_created(struct scsi_device *sdev) | 401 | static inline int scsi_device_created(struct scsi_device *sdev) |
399 | { | 402 | { |
400 | return sdev->sdev_state == SDEV_CREATED; | 403 | return sdev->sdev_state == SDEV_CREATED || |
404 | sdev->sdev_state == SDEV_CREATED_BLOCK; | ||
401 | } | 405 | } |
402 | 406 | ||
403 | /* accessor functions for the SCSI parameters */ | 407 | /* accessor functions for the SCSI parameters */ |