]>
Commit | Line | Data |
---|---|---|
4d1e5b62 AF |
1 | From: Chris Leech <christopher.leech@intel.com> |
2 | Subject: libfc: rport retry on LS_RJT from certain ELS | |
3 | Patch-mainline: 6147a1194ba86af4266f36c9522a7b0040af98fe | |
4 | References: bnc #468054 | |
5 | ||
6 | This allows any rport ELS to retry on LS_RJT. | |
7 | ||
8 | The rport error handling would only retry on resource allocation failures | |
9 | and exchange timeouts. I have a target that will occasionally reject PLOGI | |
10 | when we do a quick LOGO/PLOGI. When a critical ELS was rejected, libfc would | |
11 | fail silently leaving the rport in a dead state. | |
12 | ||
13 | The retry count and delay are managed by fc_rport_error_retry. If the retry | |
14 | count is exceeded fc_rport_error will be called. When retrying is not the | |
15 | correct course of action, fc_rport_error can be called directly. | |
16 | ||
17 | Signed-off-by: Chris Leech <christopher.leech@intel.com> | |
18 | Acked-by: Bernhard Walle <bwalle@suse.de> | |
19 | --- | |
20 | drivers/scsi/libfc/fc_exch.c | 2 | |
21 | drivers/scsi/libfc/fc_rport.c | 111 ++++++++++++++++++++++++------------------ | |
22 | include/scsi/fc/fc_fs.h | 5 + | |
23 | 3 files changed, 69 insertions(+), 49 deletions(-) | |
24 | ||
25 | --- a/drivers/scsi/libfc/fc_exch.c | |
26 | +++ b/drivers/scsi/libfc/fc_exch.c | |
27 | @@ -32,8 +32,6 @@ | |
28 | #include <scsi/libfc.h> | |
29 | #include <scsi/fc_encode.h> | |
30 | ||
31 | -#define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */ | |
32 | - | |
33 | /* | |
34 | * fc_exch_debug can be set in debugger or at compile time to get more logs. | |
35 | */ | |
36 | --- a/drivers/scsi/libfc/fc_rport.c | |
37 | +++ b/drivers/scsi/libfc/fc_rport.c | |
38 | @@ -81,6 +81,7 @@ static void fc_rport_recv_logo_req(struc | |
39 | struct fc_seq *, struct fc_frame *); | |
40 | static void fc_rport_timeout(struct work_struct *); | |
41 | static void fc_rport_error(struct fc_rport *, struct fc_frame *); | |
42 | +static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *); | |
43 | static void fc_rport_work(struct work_struct *); | |
44 | ||
45 | static const char *fc_rport_state_names[] = { | |
46 | @@ -405,55 +406,71 @@ static void fc_rport_timeout(struct work | |
47 | } | |
48 | ||
49 | /** | |
50 | - * fc_rport_error - Handler for any errors | |
51 | + * fc_rport_error - Error handler, called once retries have been exhausted | |
52 | * @rport: The fc_rport object | |
53 | * @fp: The frame pointer | |
54 | * | |
55 | - * If the error was caused by a resource allocation failure | |
56 | - * then wait for half a second and retry, otherwise retry | |
57 | - * immediately. | |
58 | - * | |
59 | * Locking Note: The rport lock is expected to be held before | |
60 | * calling this routine | |
61 | */ | |
62 | static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp) | |
63 | { | |
64 | struct fc_rport_libfc_priv *rdata = rport->dd_data; | |
65 | - unsigned long delay = 0; | |
66 | ||
67 | FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n", | |
68 | PTR_ERR(fp), fc_rport_state(rport), rdata->retries); | |
69 | ||
70 | - if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { | |
71 | - /* | |
72 | - * Memory allocation failure, or the exchange timed out. | |
73 | - * Retry after delay | |
74 | - */ | |
75 | - if (rdata->retries < rdata->local_port->max_retry_count) { | |
76 | - rdata->retries++; | |
77 | - if (!fp) | |
78 | - delay = msecs_to_jiffies(500); | |
79 | - get_device(&rport->dev); | |
80 | - schedule_delayed_work(&rdata->retry_work, delay); | |
81 | - } else { | |
82 | - switch (rdata->rp_state) { | |
83 | - case RPORT_ST_PLOGI: | |
84 | - case RPORT_ST_PRLI: | |
85 | - case RPORT_ST_LOGO: | |
86 | - rdata->event = RPORT_EV_FAILED; | |
87 | - queue_work(rport_event_queue, | |
88 | - &rdata->event_work); | |
89 | - break; | |
90 | - case RPORT_ST_RTV: | |
91 | - fc_rport_enter_ready(rport); | |
92 | - break; | |
93 | - case RPORT_ST_NONE: | |
94 | - case RPORT_ST_READY: | |
95 | - case RPORT_ST_INIT: | |
96 | - break; | |
97 | - } | |
98 | - } | |
99 | + switch (rdata->rp_state) { | |
100 | + case RPORT_ST_PLOGI: | |
101 | + case RPORT_ST_PRLI: | |
102 | + case RPORT_ST_LOGO: | |
103 | + rdata->event = RPORT_EV_FAILED; | |
104 | + queue_work(rport_event_queue, | |
105 | + &rdata->event_work); | |
106 | + break; | |
107 | + case RPORT_ST_RTV: | |
108 | + fc_rport_enter_ready(rport); | |
109 | + break; | |
110 | + case RPORT_ST_NONE: | |
111 | + case RPORT_ST_READY: | |
112 | + case RPORT_ST_INIT: | |
113 | + break; | |
114 | + } | |
115 | +} | |
116 | + | |
117 | +/** | |
118 | + * fc_rport_error_retry - Error handler when retries are desired | |
119 | + * @rport: The fc_rport object | |
120 | + * @fp: The frame pointer | |
121 | + * | |
122 | + * If the error was an exchange timeout retry immediately, | |
123 | + * otherwise wait for E_D_TOV. | |
124 | + * | |
125 | + * Locking Note: The rport lock is expected to be held before | |
126 | + * calling this routine | |
127 | + */ | |
128 | +static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp) | |
129 | +{ | |
130 | + struct fc_rport_libfc_priv *rdata = rport->dd_data; | |
131 | + unsigned long delay = FC_DEF_E_D_TOV; | |
132 | + | |
133 | + /* make sure this isn't an FC_EX_CLOSED error, never retry those */ | |
134 | + if (PTR_ERR(fp) == -FC_EX_CLOSED) | |
135 | + return fc_rport_error(rport, fp); | |
136 | + | |
137 | + if (rdata->retries < rdata->local_port->max_retry_count) { | |
138 | + FC_DEBUG_RPORT("Error %ld in state %s, retrying\n", | |
139 | + PTR_ERR(fp), fc_rport_state(rport)); | |
140 | + rdata->retries++; | |
141 | + /* no additional delay on exchange timeouts */ | |
142 | + if (PTR_ERR(fp) == -FC_EX_TIMEOUT) | |
143 | + delay = 0; | |
144 | + get_device(&rport->dev); | |
145 | + schedule_delayed_work(&rdata->retry_work, delay); | |
146 | + return; | |
147 | } | |
148 | + | |
149 | + return fc_rport_error(rport, fp); | |
150 | } | |
151 | ||
152 | /** | |
153 | @@ -490,7 +507,7 @@ static void fc_rport_plogi_resp(struct f | |
154 | } | |
155 | ||
156 | if (IS_ERR(fp)) { | |
157 | - fc_rport_error(rport, fp); | |
158 | + fc_rport_error_retry(rport, fp); | |
159 | goto err; | |
160 | } | |
161 | ||
162 | @@ -522,7 +539,7 @@ static void fc_rport_plogi_resp(struct f | |
163 | else | |
164 | fc_rport_enter_prli(rport); | |
165 | } else | |
166 | - fc_rport_error(rport, fp); | |
167 | + fc_rport_error_retry(rport, fp); | |
168 | ||
169 | out: | |
170 | fc_frame_free(fp); | |
171 | @@ -552,14 +569,14 @@ static void fc_rport_enter_plogi(struct | |
172 | rport->maxframe_size = FC_MIN_MAX_PAYLOAD; | |
173 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); | |
174 | if (!fp) { | |
175 | - fc_rport_error(rport, fp); | |
176 | + fc_rport_error_retry(rport, fp); | |
177 | return; | |
178 | } | |
179 | rdata->e_d_tov = lport->e_d_tov; | |
180 | ||
181 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI, | |
182 | fc_rport_plogi_resp, rport, lport->e_d_tov)) | |
183 | - fc_rport_error(rport, fp); | |
184 | + fc_rport_error_retry(rport, fp); | |
185 | else | |
186 | get_device(&rport->dev); | |
187 | } | |
188 | @@ -599,7 +616,7 @@ static void fc_rport_prli_resp(struct fc | |
189 | } | |
190 | ||
191 | if (IS_ERR(fp)) { | |
192 | - fc_rport_error(rport, fp); | |
193 | + fc_rport_error_retry(rport, fp); | |
194 | goto err; | |
195 | } | |
196 | ||
197 | @@ -657,7 +674,7 @@ static void fc_rport_logo_resp(struct fc | |
198 | rport->port_id); | |
199 | ||
200 | if (IS_ERR(fp)) { | |
201 | - fc_rport_error(rport, fp); | |
202 | + fc_rport_error_retry(rport, fp); | |
203 | goto err; | |
204 | } | |
205 | ||
206 | @@ -707,13 +724,13 @@ static void fc_rport_enter_prli(struct f | |
207 | ||
208 | fp = fc_frame_alloc(lport, sizeof(*pp)); | |
209 | if (!fp) { | |
210 | - fc_rport_error(rport, fp); | |
211 | + fc_rport_error_retry(rport, fp); | |
212 | return; | |
213 | } | |
214 | ||
215 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI, | |
216 | fc_rport_prli_resp, rport, lport->e_d_tov)) | |
217 | - fc_rport_error(rport, fp); | |
218 | + fc_rport_error_retry(rport, fp); | |
219 | else | |
220 | get_device(&rport->dev); | |
221 | } | |
222 | @@ -804,13 +821,13 @@ static void fc_rport_enter_rtv(struct fc | |
223 | ||
224 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv)); | |
225 | if (!fp) { | |
226 | - fc_rport_error(rport, fp); | |
227 | + fc_rport_error_retry(rport, fp); | |
228 | return; | |
229 | } | |
230 | ||
231 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV, | |
232 | fc_rport_rtv_resp, rport, lport->e_d_tov)) | |
233 | - fc_rport_error(rport, fp); | |
234 | + fc_rport_error_retry(rport, fp); | |
235 | else | |
236 | get_device(&rport->dev); | |
237 | } | |
238 | @@ -835,13 +852,13 @@ static void fc_rport_enter_logo(struct f | |
239 | ||
240 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo)); | |
241 | if (!fp) { | |
242 | - fc_rport_error(rport, fp); | |
243 | + fc_rport_error_retry(rport, fp); | |
244 | return; | |
245 | } | |
246 | ||
247 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO, | |
248 | fc_rport_logo_resp, rport, lport->e_d_tov)) | |
249 | - fc_rport_error(rport, fp); | |
250 | + fc_rport_error_retry(rport, fp); | |
251 | else | |
252 | get_device(&rport->dev); | |
253 | } | |
254 | --- a/include/scsi/fc/fc_fs.h | |
255 | +++ b/include/scsi/fc/fc_fs.h | |
256 | @@ -337,4 +337,9 @@ enum fc_pf_rjt_reason { | |
257 | FC_RJT_VENDOR = 0xff, /* vendor specific reject */ | |
258 | }; | |
259 | ||
260 | +/* default timeout values */ | |
261 | + | |
262 | +#define FC_DEF_E_D_TOV 2000UL | |
263 | +#define FC_DEF_R_A_TOV 10000UL | |
264 | + | |
265 | #endif /* _FC_FS_H_ */ |