]>
Commit | Line | Data |
---|---|---|
1 | From: Gerald Schaefer <geraldsc@de.ibm.com> | |
2 | Subject: zfcp: Block remote ports (rports) early | |
3 | References: bnc#482818,LTC#51961 | |
4 | ||
5 | Symptom: On path failover, the SCSI error recovery can set | |
6 | SCSI devices offline. | |
7 | Problem: For path failover to work correctly, the FC rports | |
8 | in the Linux kernel have to be in the state BLOCKED. | |
9 | This prevents SCSI timeouts from triggering the SCSI | |
10 | error recovery. | |
11 | Solution: On any problem (local link, remote port or any type | |
12 | of error recovery) set the FC rports to BLOCKED early. | |
13 | ||
14 | Acked-by: John Jolly <jjolly@suse.de> | |
15 | --- | |
16 | drivers/s390/scsi/zfcp_aux.c | 2 | |
17 | drivers/s390/scsi/zfcp_dbf.c | 1 | |
18 | drivers/s390/scsi/zfcp_def.h | 2 | |
19 | drivers/s390/scsi/zfcp_erp.c | 48 ++--------------- | |
20 | drivers/s390/scsi/zfcp_ext.h | 4 + | |
21 | drivers/s390/scsi/zfcp_fc.c | 20 ++++++- | |
22 | drivers/s390/scsi/zfcp_fsf.c | 1 | |
23 | drivers/s390/scsi/zfcp_scsi.c | 117 +++++++++++++++++++++++++++++++++++++++++- | |
24 | 8 files changed, 149 insertions(+), 46 deletions(-) | |
25 | ||
26 | --- a/drivers/s390/scsi/zfcp_aux.c | |
27 | +++ b/drivers/s390/scsi/zfcp_aux.c | |
28 | @@ -612,10 +612,12 @@ struct zfcp_port *zfcp_port_enqueue(stru | |
29 | INIT_LIST_HEAD(&port->unit_list_head); | |
30 | INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup); | |
31 | INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); | |
32 | + INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); | |
33 | ||
34 | port->adapter = adapter; | |
35 | port->d_id = d_id; | |
36 | port->wwpn = wwpn; | |
37 | + port->rport_task = RPORT_NONE; | |
38 | ||
39 | /* mark port unusable as long as sysfs registration is not complete */ | |
40 | atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); | |
41 | --- a/drivers/s390/scsi/zfcp_dbf.c | |
42 | +++ b/drivers/s390/scsi/zfcp_dbf.c | |
43 | @@ -644,6 +644,7 @@ static const char *zfcp_rec_dbf_ids[] = | |
44 | [149] = "port scan", | |
45 | [150] = "ptp attach", | |
46 | [151] = "port validation failed", | |
47 | + [152] = "term rport io", | |
48 | }; | |
49 | ||
50 | static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view, | |
51 | --- a/drivers/s390/scsi/zfcp_def.h | |
52 | +++ b/drivers/s390/scsi/zfcp_def.h | |
53 | @@ -540,6 +540,8 @@ struct zfcp_port { | |
54 | u32 supported_classes; | |
55 | struct work_struct gid_pn_work; | |
56 | struct work_struct test_link_work; | |
57 | + struct work_struct rport_work; | |
58 | + enum { RPORT_NONE, RPORT_ADD, RPORT_DEL } rport_task; | |
59 | }; | |
60 | ||
61 | struct zfcp_unit { | |
62 | --- a/drivers/s390/scsi/zfcp_erp.c | |
63 | +++ b/drivers/s390/scsi/zfcp_erp.c | |
64 | @@ -239,6 +239,7 @@ static int _zfcp_erp_adapter_reopen(stru | |
65 | int clear_mask, u8 id, void *ref) | |
66 | { | |
67 | zfcp_erp_adapter_block(adapter, clear_mask); | |
68 | + zfcp_scsi_schedule_rports_block(adapter); | |
69 | ||
70 | /* ensure propagation of failed status to new devices */ | |
71 | if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { | |
72 | @@ -319,6 +320,7 @@ static void _zfcp_erp_port_forced_reopen | |
73 | int clear, u8 id, void *ref) | |
74 | { | |
75 | zfcp_erp_port_block(port, clear); | |
76 | + zfcp_scsi_schedule_rport_block(port); | |
77 | ||
78 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) | |
79 | return; | |
80 | @@ -350,6 +352,7 @@ static int _zfcp_erp_port_reopen(struct | |
81 | void *ref) | |
82 | { | |
83 | zfcp_erp_port_block(port, clear); | |
84 | + zfcp_scsi_schedule_rport_block(port); | |
85 | ||
86 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { | |
87 | /* ensure propagation of failed status to new devices */ | |
88 | @@ -1218,37 +1221,6 @@ static void zfcp_erp_schedule_work(struc | |
89 | queue_work(zfcp_data.work_queue, &p->work); | |
90 | } | |
91 | ||
92 | -static void zfcp_erp_rport_register(struct zfcp_port *port) | |
93 | -{ | |
94 | - struct fc_rport_identifiers ids; | |
95 | - ids.node_name = port->wwnn; | |
96 | - ids.port_name = port->wwpn; | |
97 | - ids.port_id = port->d_id; | |
98 | - ids.roles = FC_RPORT_ROLE_FCP_TARGET; | |
99 | - port->rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids); | |
100 | - if (!port->rport) { | |
101 | - dev_err(&port->adapter->ccw_device->dev, | |
102 | - "Registering port 0x%016Lx failed\n", | |
103 | - (unsigned long long)port->wwpn); | |
104 | - return; | |
105 | - } | |
106 | - | |
107 | - scsi_target_unblock(&port->rport->dev); | |
108 | - port->rport->maxframe_size = port->maxframe_size; | |
109 | - port->rport->supported_classes = port->supported_classes; | |
110 | -} | |
111 | - | |
112 | -static void zfcp_erp_rports_del(struct zfcp_adapter *adapter) | |
113 | -{ | |
114 | - struct zfcp_port *port; | |
115 | - list_for_each_entry(port, &adapter->port_list_head, list) { | |
116 | - if (!port->rport) | |
117 | - continue; | |
118 | - fc_remote_port_delete(port->rport); | |
119 | - port->rport = NULL; | |
120 | - } | |
121 | -} | |
122 | - | |
123 | static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) | |
124 | { | |
125 | struct zfcp_adapter *adapter = act->adapter; | |
126 | @@ -1257,8 +1229,8 @@ static void zfcp_erp_action_cleanup(stru | |
127 | ||
128 | switch (act->action) { | |
129 | case ZFCP_ERP_ACTION_REOPEN_UNIT: | |
130 | - if ((result == ZFCP_ERP_SUCCEEDED) && | |
131 | - !unit->device && port->rport) { | |
132 | + flush_work(&port->rport_work); | |
133 | + if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) { | |
134 | atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED, | |
135 | &unit->status); | |
136 | if (!(atomic_read(&unit->status) & | |
137 | @@ -1274,18 +1246,12 @@ static void zfcp_erp_action_cleanup(stru | |
138 | zfcp_port_put(port); | |
139 | return; | |
140 | } | |
141 | - if ((result == ZFCP_ERP_SUCCEEDED) && !port->rport) | |
142 | - zfcp_erp_rport_register(port); | |
143 | - if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) { | |
144 | - fc_remote_port_delete(port->rport); | |
145 | - port->rport = NULL; | |
146 | - } | |
147 | + if (result == ZFCP_ERP_SUCCEEDED) | |
148 | + zfcp_scsi_schedule_rport_register(port); | |
149 | zfcp_port_put(port); | |
150 | break; | |
151 | ||
152 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
153 | - if (result != ZFCP_ERP_SUCCEEDED) | |
154 | - zfcp_erp_rports_del(adapter); | |
155 | zfcp_adapter_put(adapter); | |
156 | break; | |
157 | } | |
158 | --- a/drivers/s390/scsi/zfcp_ext.h | |
159 | +++ b/drivers/s390/scsi/zfcp_ext.h | |
160 | @@ -154,6 +154,10 @@ extern int zfcp_adapter_scsi_register(st | |
161 | extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *); | |
162 | extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *); | |
163 | extern struct fc_function_template zfcp_transport_functions; | |
164 | +extern void zfcp_scsi_rport_work(struct work_struct *); | |
165 | +extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *); | |
166 | +extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *); | |
167 | +extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *); | |
168 | ||
169 | /* zfcp_sysfs.c */ | |
170 | extern struct attribute_group zfcp_sysfs_unit_attrs; | |
171 | --- a/drivers/s390/scsi/zfcp_fc.c | |
172 | +++ b/drivers/s390/scsi/zfcp_fc.c | |
173 | @@ -370,9 +370,14 @@ static void zfcp_fc_adisc_handler(unsign | |
174 | port->wwnn = ls_adisc->wwnn; | |
175 | ||
176 | if ((port->wwpn != ls_adisc->wwpn) || | |
177 | - !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) | |
178 | + !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) { | |
179 | zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, 64, | |
180 | NULL); | |
181 | + goto out; | |
182 | + } | |
183 | + | |
184 | + /* port is good, unblock rport without going through erp */ | |
185 | + zfcp_scsi_schedule_rport_register(port); | |
186 | ||
187 | out: | |
188 | zfcp_port_put(port); | |
189 | @@ -417,14 +422,23 @@ void zfcp_fc_link_test_work(struct work_ | |
190 | container_of(work, struct zfcp_port, test_link_work); | |
191 | int retval; | |
192 | ||
193 | + if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_UNBLOCKED)) { | |
194 | + zfcp_port_put(port); | |
195 | + return; /* port erp is running and will update rport status */ | |
196 | + } | |
197 | + | |
198 | + zfcp_port_get(port); | |
199 | + port->rport_task = RPORT_DEL; | |
200 | + zfcp_scsi_rport_work(&port->rport_work); | |
201 | + | |
202 | retval = zfcp_fc_adisc(port); | |
203 | if (retval == 0) | |
204 | return; | |
205 | ||
206 | /* send of ADISC was not possible */ | |
207 | + zfcp_erp_port_forced_reopen(port, 0, 65, NULL); | |
208 | + | |
209 | zfcp_port_put(port); | |
210 | - if (retval != -EBUSY) | |
211 | - zfcp_erp_port_forced_reopen(port, 0, 65, NULL); | |
212 | } | |
213 | ||
214 | /** | |
215 | --- a/drivers/s390/scsi/zfcp_fsf.c | |
216 | +++ b/drivers/s390/scsi/zfcp_fsf.c | |
217 | @@ -180,6 +180,7 @@ static void zfcp_fsf_link_down_info_eval | |
218 | return; | |
219 | ||
220 | atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); | |
221 | + zfcp_scsi_schedule_rports_block(adapter); | |
222 | ||
223 | if (!link_down) | |
224 | goto out; | |
225 | --- a/drivers/s390/scsi/zfcp_scsi.c | |
226 | +++ b/drivers/s390/scsi/zfcp_scsi.c | |
227 | @@ -57,8 +57,8 @@ static int zfcp_scsi_queuecommand(struct | |
228 | { | |
229 | struct zfcp_unit *unit; | |
230 | struct zfcp_adapter *adapter; | |
231 | - int status; | |
232 | - int ret; | |
233 | + int status, scsi_result, ret; | |
234 | + struct fc_rport *rport = starget_to_rport(scsi_target(scpnt->device)); | |
235 | ||
236 | /* reset the status for this request */ | |
237 | scpnt->result = 0; | |
238 | @@ -80,6 +80,14 @@ static int zfcp_scsi_queuecommand(struct | |
239 | return 0; | |
240 | } | |
241 | ||
242 | + scsi_result = fc_remote_port_chkready(rport); | |
243 | + if (unlikely(scsi_result)) { | |
244 | + scpnt->result = scsi_result; | |
245 | + zfcp_scsi_dbf_event_result("fail", 4, adapter, scpnt, NULL); | |
246 | + scpnt->scsi_done(scpnt); | |
247 | + return 0; | |
248 | + } | |
249 | + | |
250 | status = atomic_read(&unit->status); | |
251 | if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) || | |
252 | !(status & ZFCP_STATUS_COMMON_RUNNING))) { | |
253 | @@ -478,6 +486,109 @@ static void zfcp_set_rport_dev_loss_tmo( | |
254 | rport->dev_loss_tmo = timeout; | |
255 | } | |
256 | ||
257 | +/** | |
258 | + * zfcp_scsi_dev_loss_tmo_callbk - Free any reference to rport | |
259 | + * @rport: The rport that is about to be deleted. | |
260 | + */ | |
261 | +static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport) | |
262 | +{ | |
263 | + struct zfcp_port *port = rport->dd_data; | |
264 | + | |
265 | + write_lock_irq(&zfcp_data.config_lock); | |
266 | + port->rport = NULL; | |
267 | + write_unlock_irq(&zfcp_data.config_lock); | |
268 | +} | |
269 | + | |
270 | +/** | |
271 | + * zfcp_scsi_terminate_rport_io - Terminate all I/O on a rport | |
272 | + * @rport: The FC rport where to teminate I/O | |
273 | + * | |
274 | + * Abort all pending SCSI commands for a port by closing the | |
275 | + * port. Using a reopen for avoids a conflict with a shutdown | |
276 | + * overwriting a reopen. | |
277 | + */ | |
278 | +static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) | |
279 | +{ | |
280 | + struct zfcp_port *port = rport->dd_data; | |
281 | + | |
282 | + zfcp_erp_port_reopen(port, 0, 152, NULL); | |
283 | +} | |
284 | + | |
285 | +static void zfcp_scsi_rport_register(struct zfcp_port *port) | |
286 | +{ | |
287 | + struct fc_rport_identifiers ids; | |
288 | + struct fc_rport *rport; | |
289 | + | |
290 | + ids.node_name = port->wwnn; | |
291 | + ids.port_name = port->wwpn; | |
292 | + ids.port_id = port->d_id; | |
293 | + ids.roles = FC_RPORT_ROLE_FCP_TARGET; | |
294 | + | |
295 | + rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids); | |
296 | + if (!rport) { | |
297 | + dev_err(&port->adapter->ccw_device->dev, | |
298 | + "Registering port 0x%016Lx failed\n", | |
299 | + (unsigned long long)port->wwpn); | |
300 | + return; | |
301 | + } | |
302 | + | |
303 | + rport->dd_data = port; | |
304 | + rport->maxframe_size = port->maxframe_size; | |
305 | + rport->supported_classes = port->supported_classes; | |
306 | + port->rport = rport; | |
307 | +} | |
308 | + | |
309 | +static void zfcp_scsi_rport_block(struct zfcp_port *port) | |
310 | +{ | |
311 | + if (port->rport) | |
312 | + fc_remote_port_delete(port->rport); | |
313 | +} | |
314 | + | |
315 | +void zfcp_scsi_schedule_rport_register(struct zfcp_port *port) | |
316 | +{ | |
317 | + zfcp_port_get(port); | |
318 | + port->rport_task = RPORT_ADD; | |
319 | + | |
320 | + if (!queue_work(zfcp_data.work_queue, &port->rport_work)) | |
321 | + zfcp_port_put(port); | |
322 | +} | |
323 | + | |
324 | +void zfcp_scsi_schedule_rport_block(struct zfcp_port *port) | |
325 | +{ | |
326 | + zfcp_port_get(port); | |
327 | + port->rport_task = RPORT_DEL; | |
328 | + | |
329 | + if (!queue_work(zfcp_data.work_queue, &port->rport_work)) | |
330 | + zfcp_port_put(port); | |
331 | +} | |
332 | + | |
333 | +void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter) | |
334 | +{ | |
335 | + struct zfcp_port *port; | |
336 | + | |
337 | + list_for_each_entry(port, &adapter->port_list_head, list) | |
338 | + zfcp_scsi_schedule_rport_block(port); | |
339 | +} | |
340 | + | |
341 | +void zfcp_scsi_rport_work(struct work_struct *work) | |
342 | +{ | |
343 | + struct zfcp_port *port = container_of(work, struct zfcp_port, | |
344 | + rport_work); | |
345 | + | |
346 | + while (port->rport_task) { | |
347 | + if (port->rport_task == RPORT_ADD) { | |
348 | + port->rport_task = RPORT_NONE; | |
349 | + zfcp_scsi_rport_register(port); | |
350 | + } else { | |
351 | + port->rport_task = RPORT_NONE; | |
352 | + zfcp_scsi_rport_block(port); | |
353 | + } | |
354 | + } | |
355 | + | |
356 | + zfcp_port_put(port); | |
357 | +} | |
358 | + | |
359 | + | |
360 | struct fc_function_template zfcp_transport_functions = { | |
361 | .show_starget_port_id = 1, | |
362 | .show_starget_port_name = 1, | |
363 | @@ -496,6 +607,8 @@ struct fc_function_template zfcp_transpo | |
364 | .reset_fc_host_stats = zfcp_reset_fc_host_stats, | |
365 | .set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo, | |
366 | .get_host_port_state = zfcp_get_host_port_state, | |
367 | + .dev_loss_tmo_callbk = zfcp_scsi_dev_loss_tmo_callbk, | |
368 | + .terminate_rport_io = zfcp_scsi_terminate_rport_io, | |
369 | .show_host_port_state = 1, | |
370 | /* no functions registered for following dynamic attributes but | |
371 | directly set by LLDD */ |