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 */ |
