diff options
| author | James Smart <James.Smart@Emulex.Com> | 2006-03-13 08:28:57 -0500 |
|---|---|---|
| committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-03-13 09:58:58 -0500 |
| commit | c829c394165f981d49f05a9be228404d7a9398d4 (patch) | |
| tree | 649ca326052a4a35d31f150f274347ca9d38c678 | |
| parent | ce313db240862d809c736c5b1dfc759817fc7ca9 (diff) | |
[SCSI] FC transport : Avoid device offline cases by stalling aborts until device unblocked
This moves the eh_timed_out functionality from the scsi_host_template
to the transport_template. Given that this is now a transport function,
the EH_RESET_TIMER case no longer caps the timer reschedulings. The
transport guarantees that this is not an infinite condition.
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
| -rw-r--r-- | drivers/scsi/scsi_error.c | 9 | ||||
| -rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 37 | ||||
| -rw-r--r-- | include/scsi/scsi_host.h | 14 | ||||
| -rw-r--r-- | include/scsi/scsi_transport.h | 11 |
4 files changed, 51 insertions, 20 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 5cc97b721661..9cf020622134 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include <scsi/scsi_dbg.h> | 29 | #include <scsi/scsi_dbg.h> |
| 30 | #include <scsi/scsi_device.h> | 30 | #include <scsi/scsi_device.h> |
| 31 | #include <scsi/scsi_eh.h> | 31 | #include <scsi/scsi_eh.h> |
| 32 | #include <scsi/scsi_transport.h> | ||
| 32 | #include <scsi/scsi_host.h> | 33 | #include <scsi/scsi_host.h> |
| 33 | #include <scsi/scsi_ioctl.h> | 34 | #include <scsi/scsi_ioctl.h> |
| 34 | #include <scsi/scsi_request.h> | 35 | #include <scsi/scsi_request.h> |
| @@ -163,16 +164,12 @@ void scsi_times_out(struct scsi_cmnd *scmd) | |||
| 163 | { | 164 | { |
| 164 | scsi_log_completion(scmd, TIMEOUT_ERROR); | 165 | scsi_log_completion(scmd, TIMEOUT_ERROR); |
| 165 | 166 | ||
| 166 | if (scmd->device->host->hostt->eh_timed_out) | 167 | if (scmd->device->host->transportt->eh_timed_out) |
| 167 | switch (scmd->device->host->hostt->eh_timed_out(scmd)) { | 168 | switch (scmd->device->host->transportt->eh_timed_out(scmd)) { |
| 168 | case EH_HANDLED: | 169 | case EH_HANDLED: |
| 169 | __scsi_done(scmd); | 170 | __scsi_done(scmd); |
| 170 | return; | 171 | return; |
| 171 | case EH_RESET_TIMER: | 172 | case EH_RESET_TIMER: |
| 172 | /* This allows a single retry even of a command | ||
| 173 | * with allowed == 0 */ | ||
| 174 | if (scmd->retries++ > scmd->allowed) | ||
| 175 | break; | ||
| 176 | scsi_add_timer(scmd, scmd->timeout_per_command, | 173 | scsi_add_timer(scmd, scmd->timeout_per_command, |
| 177 | scsi_times_out); | 174 | scsi_times_out); |
| 178 | return; | 175 | return; |
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 56012b2694d2..3c3baa9f5f28 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <scsi/scsi_host.h> | 31 | #include <scsi/scsi_host.h> |
| 32 | #include <scsi/scsi_transport.h> | 32 | #include <scsi/scsi_transport.h> |
| 33 | #include <scsi/scsi_transport_fc.h> | 33 | #include <scsi/scsi_transport_fc.h> |
| 34 | #include <scsi/scsi_cmnd.h> | ||
| 34 | #include "scsi_priv.h" | 35 | #include "scsi_priv.h" |
| 35 | 36 | ||
| 36 | /* | 37 | /* |
| @@ -1090,6 +1091,40 @@ static int fc_rport_match(struct attribute_container *cont, | |||
| 1090 | } | 1091 | } |
| 1091 | 1092 | ||
| 1092 | 1093 | ||
| 1094 | /** | ||
| 1095 | * fc_timed_out - FC Transport I/O timeout intercept handler | ||
| 1096 | * | ||
| 1097 | * @scmd: The SCSI command which timed out | ||
| 1098 | * | ||
| 1099 | * This routine protects against error handlers getting invoked while a | ||
| 1100 | * rport is in a blocked state, typically due to a temporarily loss of | ||
| 1101 | * connectivity. If the error handlers are allowed to proceed, requests | ||
| 1102 | * to abort i/o, reset the target, etc will likely fail as there is no way | ||
| 1103 | * to communicate with the device to perform the requested function. These | ||
| 1104 | * failures may result in the midlayer taking the device offline, requiring | ||
| 1105 | * manual intervention to restore operation. | ||
| 1106 | * | ||
| 1107 | * This routine, called whenever an i/o times out, validates the state of | ||
| 1108 | * the underlying rport. If the rport is blocked, it returns | ||
| 1109 | * EH_RESET_TIMER, which will continue to reschedule the timeout. | ||
| 1110 | * Eventually, either the device will return, or devloss_tmo will fire, | ||
| 1111 | * and when the timeout then fires, it will be handled normally. | ||
| 1112 | * If the rport is not blocked, normal error handling continues. | ||
| 1113 | * | ||
| 1114 | * Notes: | ||
| 1115 | * This routine assumes no locks are held on entry. | ||
| 1116 | **/ | ||
| 1117 | static enum scsi_eh_timer_return | ||
| 1118 | fc_timed_out(struct scsi_cmnd *scmd) | ||
| 1119 | { | ||
| 1120 | struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device)); | ||
| 1121 | |||
| 1122 | if (rport->port_state == FC_PORTSTATE_BLOCKED) | ||
| 1123 | return EH_RESET_TIMER; | ||
| 1124 | |||
| 1125 | return EH_NOT_HANDLED; | ||
| 1126 | } | ||
| 1127 | |||
| 1093 | /* | 1128 | /* |
| 1094 | * Must be called with shost->host_lock held | 1129 | * Must be called with shost->host_lock held |
| 1095 | */ | 1130 | */ |
| @@ -1146,6 +1181,8 @@ fc_attach_transport(struct fc_function_template *ft) | |||
| 1146 | /* Transport uses the shost workq for scsi scanning */ | 1181 | /* Transport uses the shost workq for scsi scanning */ |
| 1147 | i->t.create_work_queue = 1; | 1182 | i->t.create_work_queue = 1; |
| 1148 | 1183 | ||
| 1184 | i->t.eh_timed_out = fc_timed_out; | ||
| 1185 | |||
| 1149 | i->t.user_scan = fc_user_scan; | 1186 | i->t.user_scan = fc_user_scan; |
| 1150 | 1187 | ||
| 1151 | /* | 1188 | /* |
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 827992949c4b..a6cf3e535c0b 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h | |||
| @@ -147,20 +147,6 @@ struct scsi_host_template { | |||
| 147 | int (* eh_host_reset_handler)(struct scsi_cmnd *); | 147 | int (* eh_host_reset_handler)(struct scsi_cmnd *); |
| 148 | 148 | ||
| 149 | /* | 149 | /* |
| 150 | * This is an optional routine to notify the host that the scsi | ||
| 151 | * timer just fired. The returns tell the timer routine what to | ||
| 152 | * do about this: | ||
| 153 | * | ||
| 154 | * EH_HANDLED: I fixed the error, please complete the command | ||
| 155 | * EH_RESET_TIMER: I need more time, reset the timer and | ||
| 156 | * begin counting again | ||
| 157 | * EH_NOT_HANDLED Begin normal error recovery | ||
| 158 | * | ||
| 159 | * Status: OPTIONAL | ||
| 160 | */ | ||
| 161 | enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); | ||
| 162 | |||
| 163 | /* | ||
| 164 | * Before the mid layer attempts to scan for a new device where none | 150 | * Before the mid layer attempts to scan for a new device where none |
| 165 | * currently exists, it will call this entry in your driver. Should | 151 | * currently exists, it will call this entry in your driver. Should |
| 166 | * your driver need to allocate any structs or perform any other init | 152 | * your driver need to allocate any structs or perform any other init |
diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index e7b1054adf86..b3657f111937 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h | |||
| @@ -48,6 +48,17 @@ struct scsi_transport_template { | |||
| 48 | * True if the transport wants to use a host-based work-queue | 48 | * True if the transport wants to use a host-based work-queue |
| 49 | */ | 49 | */ |
| 50 | unsigned int create_work_queue : 1; | 50 | unsigned int create_work_queue : 1; |
| 51 | |||
| 52 | /* | ||
| 53 | * This is an optional routine that allows the transport to become | ||
| 54 | * involved when a scsi io timer fires. The return value tells the | ||
| 55 | * timer routine how to finish the io timeout handling: | ||
| 56 | * EH_HANDLED: I fixed the error, please complete the command | ||
| 57 | * EH_RESET_TIMER: I need more time, reset the timer and | ||
| 58 | * begin counting again | ||
| 59 | * EH_NOT_HANDLED Begin normal error recovery | ||
| 60 | */ | ||
| 61 | enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); | ||
| 51 | }; | 62 | }; |
| 52 | 63 | ||
| 53 | #define transport_class_to_shost(tc) \ | 64 | #define transport_class_to_shost(tc) \ |
