Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
41fa2ada | 2 | /* |
553448f6 | 3 | * zfcp device driver |
1da177e4 | 4 | * |
553448f6 | 5 | * Error Recovery Procedures (ERP). |
41fa2ada | 6 | * |
df91eefd | 7 | * Copyright IBM Corp. 2002, 2017 |
1da177e4 LT |
8 | */ |
9 | ||
ecf39d42 CS |
10 | #define KMSG_COMPONENT "zfcp" |
11 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
12 | ||
347c6a96 | 13 | #include <linux/kthread.h> |
1da177e4 | 14 | #include "zfcp_ext.h" |
b6bd2fb9 | 15 | #include "zfcp_reqlist.h" |
1da177e4 | 16 | |
287ac01a CS |
17 | #define ZFCP_MAX_ERPS 3 |
18 | ||
19 | enum zfcp_erp_act_flags { | |
20 | ZFCP_STATUS_ERP_TIMEDOUT = 0x10000000, | |
21 | ZFCP_STATUS_ERP_CLOSE_ONLY = 0x01000000, | |
287ac01a CS |
22 | ZFCP_STATUS_ERP_DISMISSED = 0x00200000, |
23 | ZFCP_STATUS_ERP_LOWMEM = 0x00400000, | |
fdbd1c5e | 24 | ZFCP_STATUS_ERP_NO_REF = 0x00800000, |
287ac01a | 25 | }; |
1da177e4 | 26 | |
df91eefd SM |
27 | /* |
28 | * Eyecatcher pseudo flag to bitwise or-combine with enum zfcp_erp_act_type. | |
29 | * Used to indicate that an ERP action could not be set up despite a detected | |
30 | * need for some recovery. | |
512857a7 | 31 | */ |
df91eefd SM |
32 | #define ZFCP_ERP_ACTION_NONE 0xc0 |
33 | /* | |
34 | * Eyecatcher pseudo flag to bitwise or-combine with enum zfcp_erp_act_type. | |
35 | * Used to indicate that ERP not needed because the object has | |
36 | * ZFCP_STATUS_COMMON_ERP_FAILED. | |
37 | */ | |
38 | #define ZFCP_ERP_ACTION_FAILED 0xe0 | |
287ac01a | 39 | |
287ac01a CS |
40 | enum zfcp_erp_act_result { |
41 | ZFCP_ERP_SUCCEEDED = 0, | |
42 | ZFCP_ERP_FAILED = 1, | |
43 | ZFCP_ERP_CONTINUES = 2, | |
44 | ZFCP_ERP_EXIT = 3, | |
45 | ZFCP_ERP_DISMISSED = 4, | |
46 | ZFCP_ERP_NOMEM = 5, | |
47 | }; | |
48 | ||
49 | static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask) | |
1da177e4 | 50 | { |
edaed859 SS |
51 | zfcp_erp_clear_adapter_status(adapter, |
52 | ZFCP_STATUS_COMMON_UNBLOCKED | mask); | |
2abbe866 | 53 | } |
1da177e4 | 54 | |
013af857 | 55 | static bool zfcp_erp_action_is_running(struct zfcp_erp_action *act) |
2abbe866 | 56 | { |
287ac01a CS |
57 | struct zfcp_erp_action *curr_act; |
58 | ||
59 | list_for_each_entry(curr_act, &act->adapter->erp_running_head, list) | |
60 | if (act == curr_act) | |
013af857 SM |
61 | return true; |
62 | return false; | |
1da177e4 LT |
63 | } |
64 | ||
287ac01a | 65 | static void zfcp_erp_action_ready(struct zfcp_erp_action *act) |
2abbe866 | 66 | { |
287ac01a CS |
67 | struct zfcp_adapter *adapter = act->adapter; |
68 | ||
69 | list_move(&act->list, &act->adapter->erp_ready_head); | |
ae0904f6 | 70 | zfcp_dbf_rec_run("erardy1", act); |
347c6a96 | 71 | wake_up(&adapter->erp_ready_wq); |
ae0904f6 | 72 | zfcp_dbf_rec_run("erardy2", act); |
2abbe866 AH |
73 | } |
74 | ||
287ac01a | 75 | static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act) |
1da177e4 | 76 | { |
287ac01a | 77 | act->status |= ZFCP_STATUS_ERP_DISMISSED; |
013af857 | 78 | if (zfcp_erp_action_is_running(act)) |
287ac01a CS |
79 | zfcp_erp_action_ready(act); |
80 | } | |
1da177e4 | 81 | |
b62a8d9b | 82 | static void zfcp_erp_action_dismiss_lun(struct scsi_device *sdev) |
287ac01a | 83 | { |
b62a8d9b CS |
84 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
85 | ||
86 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_INUSE) | |
87 | zfcp_erp_action_dismiss(&zfcp_sdev->erp_action); | |
287ac01a | 88 | } |
1da177e4 | 89 | |
287ac01a CS |
90 | static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) |
91 | { | |
b62a8d9b | 92 | struct scsi_device *sdev; |
1da177e4 | 93 | |
287ac01a CS |
94 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) |
95 | zfcp_erp_action_dismiss(&port->erp_action); | |
924dd584 MP |
96 | else { |
97 | spin_lock(port->adapter->scsi_host->host_lock); | |
98 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
b62a8d9b CS |
99 | if (sdev_to_zfcp(sdev)->port == port) |
100 | zfcp_erp_action_dismiss_lun(sdev); | |
924dd584 MP |
101 | spin_unlock(port->adapter->scsi_host->host_lock); |
102 | } | |
1da177e4 LT |
103 | } |
104 | ||
287ac01a | 105 | static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) |
1da177e4 | 106 | { |
287ac01a | 107 | struct zfcp_port *port; |
1da177e4 | 108 | |
287ac01a CS |
109 | if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE) |
110 | zfcp_erp_action_dismiss(&adapter->erp_action); | |
ecf0c772 SS |
111 | else { |
112 | read_lock(&adapter->port_list_lock); | |
113 | list_for_each_entry(port, &adapter->port_list, list) | |
287ac01a | 114 | zfcp_erp_action_dismiss_port(port); |
ecf0c772 SS |
115 | read_unlock(&adapter->port_list_lock); |
116 | } | |
1da177e4 LT |
117 | } |
118 | ||
df91eefd SM |
119 | static enum zfcp_erp_act_type zfcp_erp_handle_failed( |
120 | enum zfcp_erp_act_type want, struct zfcp_adapter *adapter, | |
121 | struct zfcp_port *port, struct scsi_device *sdev) | |
8c3d20aa | 122 | { |
df91eefd | 123 | enum zfcp_erp_act_type need = want; |
8c3d20aa SM |
124 | struct zfcp_scsi_dev *zsdev; |
125 | ||
126 | switch (want) { | |
127 | case ZFCP_ERP_ACTION_REOPEN_LUN: | |
128 | zsdev = sdev_to_zfcp(sdev); | |
129 | if (atomic_read(&zsdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) | |
130 | need = 0; | |
131 | break; | |
132 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
133 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) | |
134 | need = 0; | |
135 | break; | |
136 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
137 | if (atomic_read(&port->status) & | |
138 | ZFCP_STATUS_COMMON_ERP_FAILED) { | |
139 | need = 0; | |
140 | /* ensure propagation of failed status to new devices */ | |
141 | zfcp_erp_set_port_status( | |
142 | port, ZFCP_STATUS_COMMON_ERP_FAILED); | |
143 | } | |
144 | break; | |
145 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
146 | if (atomic_read(&adapter->status) & | |
147 | ZFCP_STATUS_COMMON_ERP_FAILED) { | |
148 | need = 0; | |
149 | /* ensure propagation of failed status to new devices */ | |
150 | zfcp_erp_set_adapter_status( | |
151 | adapter, ZFCP_STATUS_COMMON_ERP_FAILED); | |
152 | } | |
153 | break; | |
8c3d20aa SM |
154 | } |
155 | ||
156 | return need; | |
157 | } | |
158 | ||
df91eefd SM |
159 | static enum zfcp_erp_act_type zfcp_erp_required_act(enum zfcp_erp_act_type want, |
160 | struct zfcp_adapter *adapter, | |
287ac01a | 161 | struct zfcp_port *port, |
b62a8d9b | 162 | struct scsi_device *sdev) |
1da177e4 | 163 | { |
df91eefd | 164 | enum zfcp_erp_act_type need = want; |
b62a8d9b CS |
165 | int l_status, p_status, a_status; |
166 | struct zfcp_scsi_dev *zfcp_sdev; | |
1da177e4 | 167 | |
287ac01a | 168 | switch (want) { |
b62a8d9b CS |
169 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
170 | zfcp_sdev = sdev_to_zfcp(sdev); | |
171 | l_status = atomic_read(&zfcp_sdev->status); | |
172 | if (l_status & ZFCP_STATUS_COMMON_ERP_INUSE) | |
287ac01a CS |
173 | return 0; |
174 | p_status = atomic_read(&port->status); | |
175 | if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) || | |
176 | p_status & ZFCP_STATUS_COMMON_ERP_FAILED) | |
177 | return 0; | |
178 | if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED)) | |
179 | need = ZFCP_ERP_ACTION_REOPEN_PORT; | |
180 | /* fall through */ | |
287ac01a | 181 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: |
097ef3bd CS |
182 | p_status = atomic_read(&port->status); |
183 | if (!(p_status & ZFCP_STATUS_COMMON_OPEN)) | |
184 | need = ZFCP_ERP_ACTION_REOPEN_PORT; | |
185 | /* fall through */ | |
186 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
287ac01a CS |
187 | p_status = atomic_read(&port->status); |
188 | if (p_status & ZFCP_STATUS_COMMON_ERP_INUSE) | |
189 | return 0; | |
190 | a_status = atomic_read(&adapter->status); | |
191 | if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) || | |
192 | a_status & ZFCP_STATUS_COMMON_ERP_FAILED) | |
193 | return 0; | |
d3e1088d SS |
194 | if (p_status & ZFCP_STATUS_COMMON_NOESC) |
195 | return need; | |
287ac01a CS |
196 | if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED)) |
197 | need = ZFCP_ERP_ACTION_REOPEN_ADAPTER; | |
198 | /* fall through */ | |
199 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
200 | a_status = atomic_read(&adapter->status); | |
201 | if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE) | |
202 | return 0; | |
143bb6bf CS |
203 | if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) && |
204 | !(a_status & ZFCP_STATUS_COMMON_OPEN)) | |
205 | return 0; /* shutdown requested for closed adapter */ | |
287ac01a | 206 | } |
1da177e4 | 207 | |
287ac01a | 208 | return need; |
1da177e4 LT |
209 | } |
210 | ||
df91eefd SM |
211 | static struct zfcp_erp_action *zfcp_erp_setup_act(enum zfcp_erp_act_type need, |
212 | u32 act_status, | |
287ac01a CS |
213 | struct zfcp_adapter *adapter, |
214 | struct zfcp_port *port, | |
b62a8d9b | 215 | struct scsi_device *sdev) |
1da177e4 | 216 | { |
287ac01a | 217 | struct zfcp_erp_action *erp_action; |
b62a8d9b | 218 | struct zfcp_scsi_dev *zfcp_sdev; |
1da177e4 | 219 | |
287ac01a | 220 | switch (need) { |
b62a8d9b CS |
221 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
222 | zfcp_sdev = sdev_to_zfcp(sdev); | |
fdbd1c5e | 223 | if (!(act_status & ZFCP_STATUS_ERP_NO_REF)) |
b62a8d9b | 224 | if (scsi_device_get(sdev)) |
fdbd1c5e | 225 | return NULL; |
805de8f4 | 226 | atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, |
b62a8d9b CS |
227 | &zfcp_sdev->status); |
228 | erp_action = &zfcp_sdev->erp_action; | |
ab31fd0c SM |
229 | WARN_ON_ONCE(erp_action->port != port); |
230 | WARN_ON_ONCE(erp_action->sdev != sdev); | |
b62a8d9b CS |
231 | if (!(atomic_read(&zfcp_sdev->status) & |
232 | ZFCP_STATUS_COMMON_RUNNING)) | |
fdbd1c5e | 233 | act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; |
287ac01a | 234 | break; |
1da177e4 | 235 | |
287ac01a CS |
236 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
237 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
615f59e0 | 238 | if (!get_device(&port->dev)) |
6b183334 | 239 | return NULL; |
287ac01a | 240 | zfcp_erp_action_dismiss_port(port); |
805de8f4 | 241 | atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); |
287ac01a | 242 | erp_action = &port->erp_action; |
ab31fd0c SM |
243 | WARN_ON_ONCE(erp_action->port != port); |
244 | WARN_ON_ONCE(erp_action->sdev != NULL); | |
287ac01a | 245 | if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING)) |
fdbd1c5e | 246 | act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; |
287ac01a | 247 | break; |
1da177e4 | 248 | |
287ac01a | 249 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
f3450c7b | 250 | kref_get(&adapter->ref); |
287ac01a | 251 | zfcp_erp_action_dismiss_adapter(adapter); |
805de8f4 | 252 | atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); |
287ac01a | 253 | erp_action = &adapter->erp_action; |
ab31fd0c SM |
254 | WARN_ON_ONCE(erp_action->port != NULL); |
255 | WARN_ON_ONCE(erp_action->sdev != NULL); | |
287ac01a CS |
256 | if (!(atomic_read(&adapter->status) & |
257 | ZFCP_STATUS_COMMON_RUNNING)) | |
fdbd1c5e | 258 | act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; |
287ac01a | 259 | break; |
287ac01a | 260 | } |
1da177e4 | 261 | |
ab31fd0c SM |
262 | WARN_ON_ONCE(erp_action->adapter != adapter); |
263 | memset(&erp_action->list, 0, sizeof(erp_action->list)); | |
264 | memset(&erp_action->timer, 0, sizeof(erp_action->timer)); | |
265 | erp_action->step = ZFCP_ERP_STEP_UNINITIALIZED; | |
266 | erp_action->fsf_req_id = 0; | |
df91eefd | 267 | erp_action->type = need; |
fdbd1c5e | 268 | erp_action->status = act_status; |
1da177e4 | 269 | |
287ac01a | 270 | return erp_action; |
1da177e4 LT |
271 | } |
272 | ||
df91eefd SM |
273 | static void zfcp_erp_action_enqueue(enum zfcp_erp_act_type want, |
274 | struct zfcp_adapter *adapter, | |
2fdd45fd SM |
275 | struct zfcp_port *port, |
276 | struct scsi_device *sdev, | |
208d0961 | 277 | char *dbftag, u32 act_status) |
1da177e4 | 278 | { |
df91eefd | 279 | enum zfcp_erp_act_type need; |
ae0904f6 | 280 | struct zfcp_erp_action *act; |
1da177e4 | 281 | |
8c3d20aa SM |
282 | need = zfcp_erp_handle_failed(want, adapter, port, sdev); |
283 | if (!need) { | |
284 | need = ZFCP_ERP_ACTION_FAILED; /* marker for trace */ | |
285 | goto out; | |
286 | } | |
287 | ||
6a765508 SM |
288 | if (!adapter->erp_thread) { |
289 | need = ZFCP_ERP_ACTION_NONE; /* marker for trace */ | |
6a765508 SM |
290 | goto out; |
291 | } | |
1da177e4 | 292 | |
b62a8d9b | 293 | need = zfcp_erp_required_act(want, adapter, port, sdev); |
287ac01a | 294 | if (!need) |
1da177e4 | 295 | goto out; |
1da177e4 | 296 | |
b62a8d9b | 297 | act = zfcp_erp_setup_act(need, act_status, adapter, port, sdev); |
512857a7 SM |
298 | if (!act) { |
299 | need |= ZFCP_ERP_ACTION_NONE; /* marker for trace */ | |
287ac01a | 300 | goto out; |
512857a7 | 301 | } |
805de8f4 | 302 | atomic_or(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status); |
287ac01a CS |
303 | ++adapter->erp_total_count; |
304 | list_add_tail(&act->list, &adapter->erp_ready_head); | |
347c6a96 | 305 | wake_up(&adapter->erp_ready_wq); |
1da177e4 | 306 | out: |
208d0961 | 307 | zfcp_dbf_rec_trig(dbftag, adapter, port, sdev, want, need); |
1da177e4 LT |
308 | } |
309 | ||
208d0961 SM |
310 | void zfcp_erp_port_forced_no_port_dbf(char *dbftag, |
311 | struct zfcp_adapter *adapter, | |
96d92704 SM |
312 | u64 port_name, u32 port_id) |
313 | { | |
314 | unsigned long flags; | |
315 | static /* don't waste stack */ struct zfcp_port tmpport; | |
316 | ||
317 | write_lock_irqsave(&adapter->erp_lock, flags); | |
318 | /* Stand-in zfcp port with fields just good enough for | |
319 | * zfcp_dbf_rec_trig() and zfcp_dbf_set_common(). | |
320 | * Under lock because tmpport is static. | |
321 | */ | |
322 | atomic_set(&tmpport.status, -1); /* unknown */ | |
323 | tmpport.wwpn = port_name; | |
324 | tmpport.d_id = port_id; | |
208d0961 | 325 | zfcp_dbf_rec_trig(dbftag, adapter, &tmpport, NULL, |
96d92704 SM |
326 | ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, |
327 | ZFCP_ERP_ACTION_NONE); | |
328 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
329 | } | |
330 | ||
2fdd45fd | 331 | static void _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, |
208d0961 | 332 | int clear_mask, char *dbftag) |
287ac01a CS |
333 | { |
334 | zfcp_erp_adapter_block(adapter, clear_mask); | |
a2fa0aed | 335 | zfcp_scsi_schedule_rports_block(adapter); |
287ac01a | 336 | |
2fdd45fd | 337 | zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, |
208d0961 | 338 | adapter, NULL, NULL, dbftag, 0); |
287ac01a CS |
339 | } |
340 | ||
341 | /** | |
342 | * zfcp_erp_adapter_reopen - Reopen adapter. | |
343 | * @adapter: Adapter to reopen. | |
344 | * @clear: Status flags to clear. | |
208d0961 | 345 | * @dbftag: Tag for debug trace event. |
1da177e4 | 346 | */ |
208d0961 SM |
347 | void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, |
348 | char *dbftag) | |
1da177e4 | 349 | { |
1da177e4 | 350 | unsigned long flags; |
1da177e4 | 351 | |
ecf0c772 SS |
352 | zfcp_erp_adapter_block(adapter, clear); |
353 | zfcp_scsi_schedule_rports_block(adapter); | |
354 | ||
355 | write_lock_irqsave(&adapter->erp_lock, flags); | |
8c3d20aa | 356 | zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, |
208d0961 | 357 | NULL, NULL, dbftag, 0); |
ecf0c772 | 358 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
287ac01a | 359 | } |
1da177e4 | 360 | |
287ac01a CS |
361 | /** |
362 | * zfcp_erp_adapter_shutdown - Shutdown adapter. | |
363 | * @adapter: Adapter to shut down. | |
364 | * @clear: Status flags to clear. | |
208d0961 | 365 | * @dbftag: Tag for debug trace event. |
287ac01a CS |
366 | */ |
367 | void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear, | |
208d0961 | 368 | char *dbftag) |
287ac01a CS |
369 | { |
370 | int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; | |
208d0961 | 371 | zfcp_erp_adapter_reopen(adapter, clear | flags, dbftag); |
1da177e4 LT |
372 | } |
373 | ||
287ac01a CS |
374 | /** |
375 | * zfcp_erp_port_shutdown - Shutdown port | |
376 | * @port: Port to shut down. | |
377 | * @clear: Status flags to clear. | |
208d0961 | 378 | * @dbftag: Tag for debug trace event. |
1da177e4 | 379 | */ |
208d0961 | 380 | void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, char *dbftag) |
1da177e4 | 381 | { |
287ac01a | 382 | int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; |
208d0961 | 383 | zfcp_erp_port_reopen(port, clear | flags, dbftag); |
287ac01a CS |
384 | } |
385 | ||
287ac01a CS |
386 | static void zfcp_erp_port_block(struct zfcp_port *port, int clear) |
387 | { | |
edaed859 SS |
388 | zfcp_erp_clear_port_status(port, |
389 | ZFCP_STATUS_COMMON_UNBLOCKED | clear); | |
287ac01a CS |
390 | } |
391 | ||
ea4a3a6a | 392 | static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, |
208d0961 | 393 | char *dbftag) |
287ac01a CS |
394 | { |
395 | zfcp_erp_port_block(port, clear); | |
a2fa0aed | 396 | zfcp_scsi_schedule_rport_block(port); |
287ac01a | 397 | |
287ac01a | 398 | zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, |
208d0961 | 399 | port->adapter, port, NULL, dbftag, 0); |
287ac01a CS |
400 | } |
401 | ||
402 | /** | |
403 | * zfcp_erp_port_forced_reopen - Forced close of port and open again | |
404 | * @port: Port to force close and to reopen. | |
ea4a3a6a | 405 | * @clear: Status flags to clear. |
208d0961 | 406 | * @dbftag: Tag for debug trace event. |
287ac01a | 407 | */ |
208d0961 SM |
408 | void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, |
409 | char *dbftag) | |
287ac01a CS |
410 | { |
411 | unsigned long flags; | |
412 | struct zfcp_adapter *adapter = port->adapter; | |
413 | ||
ecf0c772 | 414 | write_lock_irqsave(&adapter->erp_lock, flags); |
208d0961 | 415 | _zfcp_erp_port_forced_reopen(port, clear, dbftag); |
ecf0c772 | 416 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
287ac01a CS |
417 | } |
418 | ||
208d0961 SM |
419 | static void _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, |
420 | char *dbftag) | |
287ac01a CS |
421 | { |
422 | zfcp_erp_port_block(port, clear); | |
a2fa0aed | 423 | zfcp_scsi_schedule_rport_block(port); |
1da177e4 | 424 | |
2fdd45fd | 425 | zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, |
208d0961 | 426 | port->adapter, port, NULL, dbftag, 0); |
1da177e4 LT |
427 | } |
428 | ||
429 | /** | |
287ac01a CS |
430 | * zfcp_erp_port_reopen - trigger remote port recovery |
431 | * @port: port to recover | |
8684d614 | 432 | * @clear: flags in port status to be cleared |
208d0961 | 433 | * @dbftag: Tag for debug trace event. |
1da177e4 | 434 | */ |
208d0961 | 435 | void zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *dbftag) |
1da177e4 | 436 | { |
ecf0c772 | 437 | unsigned long flags; |
1da177e4 LT |
438 | struct zfcp_adapter *adapter = port->adapter; |
439 | ||
ecf0c772 | 440 | write_lock_irqsave(&adapter->erp_lock, flags); |
208d0961 | 441 | _zfcp_erp_port_reopen(port, clear, dbftag); |
ecf0c772 | 442 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
1da177e4 LT |
443 | } |
444 | ||
b62a8d9b | 445 | static void zfcp_erp_lun_block(struct scsi_device *sdev, int clear_mask) |
287ac01a | 446 | { |
edaed859 SS |
447 | zfcp_erp_clear_lun_status(sdev, |
448 | ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask); | |
287ac01a CS |
449 | } |
450 | ||
208d0961 SM |
451 | static void _zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, |
452 | char *dbftag, u32 act_status) | |
1da177e4 | 453 | { |
b62a8d9b CS |
454 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
455 | struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; | |
1da177e4 | 456 | |
b62a8d9b | 457 | zfcp_erp_lun_block(sdev, clear); |
1da177e4 | 458 | |
b62a8d9b | 459 | zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_LUN, adapter, |
208d0961 | 460 | zfcp_sdev->port, sdev, dbftag, act_status); |
1da177e4 LT |
461 | } |
462 | ||
463 | /** | |
b62a8d9b CS |
464 | * zfcp_erp_lun_reopen - initiate reopen of a LUN |
465 | * @sdev: SCSI device / LUN to be reopened | |
8684d614 | 466 | * @clear: specifies flags in LUN status to be cleared |
208d0961 | 467 | * @dbftag: Tag for debug trace event. |
ea4a3a6a | 468 | * |
1da177e4 | 469 | * Return: 0 on success, < 0 on error |
1da177e4 | 470 | */ |
208d0961 | 471 | void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *dbftag) |
1da177e4 | 472 | { |
1da177e4 | 473 | unsigned long flags; |
b62a8d9b CS |
474 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
475 | struct zfcp_port *port = zfcp_sdev->port; | |
287ac01a | 476 | struct zfcp_adapter *adapter = port->adapter; |
1da177e4 | 477 | |
ecf0c772 | 478 | write_lock_irqsave(&adapter->erp_lock, flags); |
208d0961 | 479 | _zfcp_erp_lun_reopen(sdev, clear, dbftag, 0); |
fdbd1c5e CS |
480 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
481 | } | |
482 | ||
483 | /** | |
b62a8d9b CS |
484 | * zfcp_erp_lun_shutdown - Shutdown LUN |
485 | * @sdev: SCSI device / LUN to shut down. | |
fdbd1c5e | 486 | * @clear: Status flags to clear. |
208d0961 | 487 | * @dbftag: Tag for debug trace event. |
fdbd1c5e | 488 | */ |
208d0961 | 489 | void zfcp_erp_lun_shutdown(struct scsi_device *sdev, int clear, char *dbftag) |
fdbd1c5e CS |
490 | { |
491 | int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; | |
208d0961 | 492 | zfcp_erp_lun_reopen(sdev, clear | flags, dbftag); |
fdbd1c5e CS |
493 | } |
494 | ||
495 | /** | |
b62a8d9b CS |
496 | * zfcp_erp_lun_shutdown_wait - Shutdown LUN and wait for erp completion |
497 | * @sdev: SCSI device / LUN to shut down. | |
208d0961 | 498 | * @dbftag: Tag for debug trace event. |
fdbd1c5e | 499 | * |
b62a8d9b | 500 | * Do not acquire a reference for the LUN when creating the ERP |
fdbd1c5e | 501 | * action. It is safe, because this function waits for the ERP to |
b62a8d9b CS |
502 | * complete first. This allows to shutdown the LUN, even when the SCSI |
503 | * device is in the state SDEV_DEL when scsi_device_get will fail. | |
fdbd1c5e | 504 | */ |
208d0961 | 505 | void zfcp_erp_lun_shutdown_wait(struct scsi_device *sdev, char *dbftag) |
fdbd1c5e CS |
506 | { |
507 | unsigned long flags; | |
b62a8d9b CS |
508 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
509 | struct zfcp_port *port = zfcp_sdev->port; | |
fdbd1c5e CS |
510 | struct zfcp_adapter *adapter = port->adapter; |
511 | int clear = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; | |
512 | ||
513 | write_lock_irqsave(&adapter->erp_lock, flags); | |
208d0961 | 514 | _zfcp_erp_lun_reopen(sdev, clear, dbftag, ZFCP_STATUS_ERP_NO_REF); |
ecf0c772 | 515 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
fdbd1c5e CS |
516 | |
517 | zfcp_erp_wait(adapter); | |
1da177e4 LT |
518 | } |
519 | ||
d39eda54 | 520 | static int zfcp_erp_status_change_set(unsigned long mask, atomic_t *status) |
1da177e4 | 521 | { |
287ac01a | 522 | return (atomic_read(status) ^ mask) & mask; |
1da177e4 LT |
523 | } |
524 | ||
287ac01a | 525 | static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter) |
698ec016 | 526 | { |
d39eda54 SM |
527 | if (zfcp_erp_status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, |
528 | &adapter->status)) | |
ae0904f6 | 529 | zfcp_dbf_rec_run("eraubl1", &adapter->erp_action); |
805de8f4 | 530 | atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status); |
698ec016 MP |
531 | } |
532 | ||
287ac01a | 533 | static void zfcp_erp_port_unblock(struct zfcp_port *port) |
1da177e4 | 534 | { |
d39eda54 SM |
535 | if (zfcp_erp_status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, |
536 | &port->status)) | |
ae0904f6 | 537 | zfcp_dbf_rec_run("erpubl1", &port->erp_action); |
805de8f4 | 538 | atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status); |
1da177e4 LT |
539 | } |
540 | ||
b62a8d9b | 541 | static void zfcp_erp_lun_unblock(struct scsi_device *sdev) |
1da177e4 | 542 | { |
b62a8d9b CS |
543 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
544 | ||
d39eda54 SM |
545 | if (zfcp_erp_status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, |
546 | &zfcp_sdev->status)) | |
ae0904f6 | 547 | zfcp_dbf_rec_run("erlubl1", &sdev_to_zfcp(sdev)->erp_action); |
805de8f4 | 548 | atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status); |
1da177e4 LT |
549 | } |
550 | ||
287ac01a | 551 | static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) |
1da177e4 | 552 | { |
287ac01a | 553 | list_move(&erp_action->list, &erp_action->adapter->erp_running_head); |
ae0904f6 | 554 | zfcp_dbf_rec_run("erator1", erp_action); |
1da177e4 LT |
555 | } |
556 | ||
287ac01a | 557 | static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act) |
1da177e4 | 558 | { |
287ac01a | 559 | struct zfcp_adapter *adapter = act->adapter; |
e60a6d69 | 560 | struct zfcp_fsf_req *req; |
1da177e4 | 561 | |
e60a6d69 | 562 | if (!act->fsf_req_id) |
287ac01a | 563 | return; |
1da177e4 | 564 | |
b6bd2fb9 CS |
565 | spin_lock(&adapter->req_list->lock); |
566 | req = _zfcp_reqlist_find(adapter->req_list, act->fsf_req_id); | |
e60a6d69 | 567 | if (req && req->erp_action == act) { |
287ac01a CS |
568 | if (act->status & (ZFCP_STATUS_ERP_DISMISSED | |
569 | ZFCP_STATUS_ERP_TIMEDOUT)) { | |
e60a6d69 | 570 | req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; |
ae0904f6 | 571 | zfcp_dbf_rec_run("erscf_1", act); |
e60a6d69 | 572 | req->erp_action = NULL; |
1da177e4 | 573 | } |
287ac01a | 574 | if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) |
ae0904f6 | 575 | zfcp_dbf_rec_run("erscf_2", act); |
e60a6d69 CS |
576 | if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) |
577 | act->fsf_req_id = 0; | |
287ac01a | 578 | } else |
e60a6d69 | 579 | act->fsf_req_id = 0; |
b6bd2fb9 | 580 | spin_unlock(&adapter->req_list->lock); |
1da177e4 LT |
581 | } |
582 | ||
287ac01a CS |
583 | /** |
584 | * zfcp_erp_notify - Trigger ERP action. | |
585 | * @erp_action: ERP action to continue. | |
586 | * @set_mask: ERP action status flags to set. | |
1da177e4 | 587 | */ |
287ac01a | 588 | void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask) |
1da177e4 | 589 | { |
1da177e4 | 590 | struct zfcp_adapter *adapter = erp_action->adapter; |
287ac01a | 591 | unsigned long flags; |
1da177e4 | 592 | |
287ac01a | 593 | write_lock_irqsave(&adapter->erp_lock, flags); |
013af857 | 594 | if (zfcp_erp_action_is_running(erp_action)) { |
1da177e4 LT |
595 | erp_action->status |= set_mask; |
596 | zfcp_erp_action_ready(erp_action); | |
1da177e4 | 597 | } |
1da177e4 | 598 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
1da177e4 LT |
599 | } |
600 | ||
287ac01a CS |
601 | /** |
602 | * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request | |
8684d614 | 603 | * @t: timer list entry embedded in zfcp FSF request |
1da177e4 | 604 | */ |
75492a51 | 605 | void zfcp_erp_timeout_handler(struct timer_list *t) |
1da177e4 | 606 | { |
75492a51 KC |
607 | struct zfcp_fsf_req *fsf_req = from_timer(fsf_req, t, timer); |
608 | struct zfcp_erp_action *act = fsf_req->erp_action; | |
5c13db9b | 609 | |
287ac01a | 610 | zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT); |
1da177e4 LT |
611 | } |
612 | ||
75492a51 | 613 | static void zfcp_erp_memwait_handler(struct timer_list *t) |
1da177e4 | 614 | { |
75492a51 KC |
615 | struct zfcp_erp_action *act = from_timer(act, t, timer); |
616 | ||
617 | zfcp_erp_notify(act, 0); | |
1da177e4 LT |
618 | } |
619 | ||
287ac01a | 620 | static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action) |
1da177e4 | 621 | { |
5c13db9b | 622 | timer_setup(&erp_action->timer, zfcp_erp_memwait_handler, 0); |
287ac01a CS |
623 | erp_action->timer.expires = jiffies + HZ; |
624 | add_timer(&erp_action->timer); | |
1da177e4 LT |
625 | } |
626 | ||
242ec145 SM |
627 | void zfcp_erp_port_forced_reopen_all(struct zfcp_adapter *adapter, |
628 | int clear, char *dbftag) | |
629 | { | |
630 | unsigned long flags; | |
631 | struct zfcp_port *port; | |
632 | ||
633 | write_lock_irqsave(&adapter->erp_lock, flags); | |
634 | read_lock(&adapter->port_list_lock); | |
635 | list_for_each_entry(port, &adapter->port_list, list) | |
636 | _zfcp_erp_port_forced_reopen(port, clear, dbftag); | |
637 | read_unlock(&adapter->port_list_lock); | |
638 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
639 | } | |
640 | ||
287ac01a | 641 | static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, |
208d0961 | 642 | int clear, char *dbftag) |
1da177e4 | 643 | { |
287ac01a | 644 | struct zfcp_port *port; |
1da177e4 | 645 | |
ecf0c772 SS |
646 | read_lock(&adapter->port_list_lock); |
647 | list_for_each_entry(port, &adapter->port_list, list) | |
208d0961 | 648 | _zfcp_erp_port_reopen(port, clear, dbftag); |
ecf0c772 | 649 | read_unlock(&adapter->port_list_lock); |
1da177e4 LT |
650 | } |
651 | ||
b62a8d9b | 652 | static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, |
208d0961 | 653 | char *dbftag) |
1da177e4 | 654 | { |
b62a8d9b | 655 | struct scsi_device *sdev; |
1da177e4 | 656 | |
924dd584 MP |
657 | spin_lock(port->adapter->scsi_host->host_lock); |
658 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
b62a8d9b | 659 | if (sdev_to_zfcp(sdev)->port == port) |
208d0961 | 660 | _zfcp_erp_lun_reopen(sdev, clear, dbftag, 0); |
924dd584 | 661 | spin_unlock(port->adapter->scsi_host->host_lock); |
1da177e4 LT |
662 | } |
663 | ||
85600f7f | 664 | static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) |
1da177e4 | 665 | { |
df91eefd | 666 | switch (act->type) { |
287ac01a | 667 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
ea4a3a6a | 668 | _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1"); |
287ac01a | 669 | break; |
287ac01a | 670 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: |
ea4a3a6a | 671 | _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2"); |
287ac01a | 672 | break; |
287ac01a | 673 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
ea4a3a6a | 674 | _zfcp_erp_port_reopen(act->port, 0, "ersff_3"); |
287ac01a | 675 | break; |
b62a8d9b | 676 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
ea4a3a6a | 677 | _zfcp_erp_lun_reopen(act->sdev, 0, "ersff_4", 0); |
85600f7f CS |
678 | break; |
679 | } | |
680 | } | |
681 | ||
682 | static void zfcp_erp_strategy_followup_success(struct zfcp_erp_action *act) | |
683 | { | |
df91eefd | 684 | switch (act->type) { |
85600f7f | 685 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
ea4a3a6a | 686 | _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1"); |
85600f7f CS |
687 | break; |
688 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
ea4a3a6a | 689 | _zfcp_erp_port_reopen(act->port, 0, "ersfs_2"); |
85600f7f CS |
690 | break; |
691 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
ea4a3a6a | 692 | _zfcp_erp_lun_reopen_all(act->port, 0, "ersfs_3"); |
287ac01a | 693 | break; |
df91eefd SM |
694 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
695 | /* NOP */ | |
696 | break; | |
1da177e4 | 697 | } |
1da177e4 LT |
698 | } |
699 | ||
287ac01a | 700 | static void zfcp_erp_wakeup(struct zfcp_adapter *adapter) |
1da177e4 | 701 | { |
1da177e4 LT |
702 | unsigned long flags; |
703 | ||
ecf0c772 | 704 | read_lock_irqsave(&adapter->erp_lock, flags); |
287ac01a CS |
705 | if (list_empty(&adapter->erp_ready_head) && |
706 | list_empty(&adapter->erp_running_head)) { | |
805de8f4 | 707 | atomic_andnot(ZFCP_STATUS_ADAPTER_ERP_PENDING, |
287ac01a CS |
708 | &adapter->status); |
709 | wake_up(&adapter->erp_done_wqh); | |
1da177e4 | 710 | } |
ecf0c772 | 711 | read_unlock_irqrestore(&adapter->erp_lock, flags); |
287ac01a | 712 | } |
1da177e4 | 713 | |
287ac01a CS |
714 | static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter) |
715 | { | |
716 | struct zfcp_port *port; | |
717 | port = zfcp_port_enqueue(adapter, adapter->peer_wwpn, 0, | |
718 | adapter->peer_d_id); | |
719 | if (IS_ERR(port)) /* error or port already attached */ | |
720 | return; | |
ea4a3a6a | 721 | _zfcp_erp_port_reopen(port, 0, "ereptp1"); |
287ac01a | 722 | } |
1da177e4 | 723 | |
d5fcdced SM |
724 | static enum zfcp_erp_act_result zfcp_erp_adapter_strat_fsf_xconf( |
725 | struct zfcp_erp_action *erp_action) | |
287ac01a CS |
726 | { |
727 | int retries; | |
728 | int sleep = 1; | |
729 | struct zfcp_adapter *adapter = erp_action->adapter; | |
1da177e4 | 730 | |
805de8f4 | 731 | atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status); |
287ac01a CS |
732 | |
733 | for (retries = 7; retries; retries--) { | |
805de8f4 | 734 | atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, |
287ac01a CS |
735 | &adapter->status); |
736 | write_lock_irq(&adapter->erp_lock); | |
737 | zfcp_erp_action_to_running(erp_action); | |
738 | write_unlock_irq(&adapter->erp_lock); | |
739 | if (zfcp_fsf_exchange_config_data(erp_action)) { | |
805de8f4 | 740 | atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, |
287ac01a CS |
741 | &adapter->status); |
742 | return ZFCP_ERP_FAILED; | |
1da177e4 | 743 | } |
1da177e4 | 744 | |
347c6a96 CS |
745 | wait_event(adapter->erp_ready_wq, |
746 | !list_empty(&adapter->erp_ready_head)); | |
287ac01a CS |
747 | if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) |
748 | break; | |
1da177e4 | 749 | |
287ac01a CS |
750 | if (!(atomic_read(&adapter->status) & |
751 | ZFCP_STATUS_ADAPTER_HOST_CON_INIT)) | |
752 | break; | |
1da177e4 | 753 | |
287ac01a CS |
754 | ssleep(sleep); |
755 | sleep *= 2; | |
1da177e4 LT |
756 | } |
757 | ||
805de8f4 | 758 | atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, |
287ac01a | 759 | &adapter->status); |
1da177e4 | 760 | |
287ac01a CS |
761 | if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK)) |
762 | return ZFCP_ERP_FAILED; | |
41fa2ada | 763 | |
287ac01a CS |
764 | if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) |
765 | zfcp_erp_enqueue_ptp_port(adapter); | |
1da177e4 | 766 | |
287ac01a | 767 | return ZFCP_ERP_SUCCEEDED; |
1da177e4 LT |
768 | } |
769 | ||
d5fcdced SM |
770 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf_xport( |
771 | struct zfcp_erp_action *act) | |
1da177e4 | 772 | { |
287ac01a CS |
773 | int ret; |
774 | struct zfcp_adapter *adapter = act->adapter; | |
1da177e4 | 775 | |
287ac01a CS |
776 | write_lock_irq(&adapter->erp_lock); |
777 | zfcp_erp_action_to_running(act); | |
778 | write_unlock_irq(&adapter->erp_lock); | |
779 | ||
780 | ret = zfcp_fsf_exchange_port_data(act); | |
781 | if (ret == -EOPNOTSUPP) | |
782 | return ZFCP_ERP_SUCCEEDED; | |
783 | if (ret) | |
784 | return ZFCP_ERP_FAILED; | |
785 | ||
ae0904f6 | 786 | zfcp_dbf_rec_run("erasox1", act); |
347c6a96 CS |
787 | wait_event(adapter->erp_ready_wq, |
788 | !list_empty(&adapter->erp_ready_head)); | |
ae0904f6 | 789 | zfcp_dbf_rec_run("erasox2", act); |
287ac01a CS |
790 | if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) |
791 | return ZFCP_ERP_FAILED; | |
792 | ||
793 | return ZFCP_ERP_SUCCEEDED; | |
1da177e4 LT |
794 | } |
795 | ||
d5fcdced SM |
796 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf( |
797 | struct zfcp_erp_action *act) | |
1da177e4 | 798 | { |
287ac01a CS |
799 | if (zfcp_erp_adapter_strat_fsf_xconf(act) == ZFCP_ERP_FAILED) |
800 | return ZFCP_ERP_FAILED; | |
1da177e4 | 801 | |
287ac01a CS |
802 | if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED) |
803 | return ZFCP_ERP_FAILED; | |
1da177e4 | 804 | |
c7b279ae | 805 | if (mempool_resize(act->adapter->pool.sr_data, |
11d83360 | 806 | act->adapter->stat_read_buf_num)) |
8d88cf3f CS |
807 | return ZFCP_ERP_FAILED; |
808 | ||
809 | if (mempool_resize(act->adapter->pool.status_read_req, | |
11d83360 | 810 | act->adapter->stat_read_buf_num)) |
8d88cf3f CS |
811 | return ZFCP_ERP_FAILED; |
812 | ||
64deb6ef | 813 | atomic_set(&act->adapter->stat_miss, act->adapter->stat_read_buf_num); |
287ac01a CS |
814 | if (zfcp_status_read_refill(act->adapter)) |
815 | return ZFCP_ERP_FAILED; | |
1da177e4 | 816 | |
287ac01a CS |
817 | return ZFCP_ERP_SUCCEEDED; |
818 | } | |
1da177e4 | 819 | |
cf13c082 | 820 | static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act) |
287ac01a | 821 | { |
287ac01a | 822 | struct zfcp_adapter *adapter = act->adapter; |
1da177e4 | 823 | |
287ac01a | 824 | /* close queues to ensure that buffers are not accessed by adapter */ |
564e1c86 | 825 | zfcp_qdio_close(adapter->qdio); |
287ac01a CS |
826 | zfcp_fsf_req_dismiss_all(adapter); |
827 | adapter->fsf_req_seq_no = 0; | |
55c770fa | 828 | zfcp_fc_wka_ports_force_offline(adapter->gs); |
b62a8d9b | 829 | /* all ports and LUNs are closed */ |
edaed859 | 830 | zfcp_erp_clear_adapter_status(adapter, ZFCP_STATUS_COMMON_OPEN); |
cf13c082 | 831 | |
805de8f4 | 832 | atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK | |
cf13c082 | 833 | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); |
1da177e4 LT |
834 | } |
835 | ||
d5fcdced SM |
836 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open( |
837 | struct zfcp_erp_action *act) | |
1da177e4 | 838 | { |
cf13c082 | 839 | struct zfcp_adapter *adapter = act->adapter; |
1da177e4 | 840 | |
3d63d3b4 | 841 | if (zfcp_qdio_open(adapter->qdio)) { |
805de8f4 | 842 | atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK | |
cf13c082 SS |
843 | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, |
844 | &adapter->status); | |
845 | return ZFCP_ERP_FAILED; | |
846 | } | |
287ac01a | 847 | |
cf13c082 SS |
848 | if (zfcp_erp_adapter_strategy_open_fsf(act)) { |
849 | zfcp_erp_adapter_strategy_close(act); | |
850 | return ZFCP_ERP_FAILED; | |
851 | } | |
852 | ||
805de8f4 | 853 | atomic_or(ZFCP_STATUS_COMMON_OPEN, &adapter->status); |
cf13c082 SS |
854 | |
855 | return ZFCP_ERP_SUCCEEDED; | |
856 | } | |
287ac01a | 857 | |
d5fcdced SM |
858 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy( |
859 | struct zfcp_erp_action *act) | |
cf13c082 SS |
860 | { |
861 | struct zfcp_adapter *adapter = act->adapter; | |
862 | ||
863 | if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN) { | |
864 | zfcp_erp_adapter_strategy_close(act); | |
865 | if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY) | |
866 | return ZFCP_ERP_EXIT; | |
867 | } | |
868 | ||
869 | if (zfcp_erp_adapter_strategy_open(act)) { | |
287ac01a | 870 | ssleep(8); |
cf13c082 SS |
871 | return ZFCP_ERP_FAILED; |
872 | } | |
1da177e4 | 873 | |
cf13c082 | 874 | return ZFCP_ERP_SUCCEEDED; |
1da177e4 LT |
875 | } |
876 | ||
d5fcdced SM |
877 | static enum zfcp_erp_act_result zfcp_erp_port_forced_strategy_close( |
878 | struct zfcp_erp_action *act) | |
1da177e4 | 879 | { |
287ac01a CS |
880 | int retval; |
881 | ||
882 | retval = zfcp_fsf_close_physical_port(act); | |
883 | if (retval == -ENOMEM) | |
884 | return ZFCP_ERP_NOMEM; | |
885 | act->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING; | |
886 | if (retval) | |
887 | return ZFCP_ERP_FAILED; | |
888 | ||
889 | return ZFCP_ERP_CONTINUES; | |
1da177e4 LT |
890 | } |
891 | ||
d5fcdced SM |
892 | static enum zfcp_erp_act_result zfcp_erp_port_forced_strategy( |
893 | struct zfcp_erp_action *erp_action) | |
287ac01a CS |
894 | { |
895 | struct zfcp_port *port = erp_action->port; | |
896 | int status = atomic_read(&port->status); | |
897 | ||
898 | switch (erp_action->step) { | |
899 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
287ac01a CS |
900 | if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) && |
901 | (status & ZFCP_STATUS_COMMON_OPEN)) | |
902 | return zfcp_erp_port_forced_strategy_close(erp_action); | |
903 | else | |
904 | return ZFCP_ERP_FAILED; | |
905 | ||
906 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: | |
ddb3e0c1 | 907 | if (!(status & ZFCP_STATUS_PORT_PHYS_OPEN)) |
287ac01a | 908 | return ZFCP_ERP_SUCCEEDED; |
0023beec SM |
909 | break; |
910 | case ZFCP_ERP_STEP_PORT_CLOSING: | |
911 | case ZFCP_ERP_STEP_PORT_OPENING: | |
912 | case ZFCP_ERP_STEP_LUN_CLOSING: | |
913 | case ZFCP_ERP_STEP_LUN_OPENING: | |
914 | /* NOP */ | |
915 | break; | |
287ac01a CS |
916 | } |
917 | return ZFCP_ERP_FAILED; | |
1da177e4 LT |
918 | } |
919 | ||
d5fcdced SM |
920 | static enum zfcp_erp_act_result zfcp_erp_port_strategy_close( |
921 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 922 | { |
287ac01a | 923 | int retval; |
1da177e4 | 924 | |
287ac01a CS |
925 | retval = zfcp_fsf_close_port(erp_action); |
926 | if (retval == -ENOMEM) | |
927 | return ZFCP_ERP_NOMEM; | |
928 | erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING; | |
929 | if (retval) | |
930 | return ZFCP_ERP_FAILED; | |
931 | return ZFCP_ERP_CONTINUES; | |
1da177e4 LT |
932 | } |
933 | ||
d5fcdced SM |
934 | static enum zfcp_erp_act_result zfcp_erp_port_strategy_open_port( |
935 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 936 | { |
287ac01a | 937 | int retval; |
1da177e4 | 938 | |
287ac01a CS |
939 | retval = zfcp_fsf_open_port(erp_action); |
940 | if (retval == -ENOMEM) | |
941 | return ZFCP_ERP_NOMEM; | |
942 | erp_action->step = ZFCP_ERP_STEP_PORT_OPENING; | |
943 | if (retval) | |
944 | return ZFCP_ERP_FAILED; | |
945 | return ZFCP_ERP_CONTINUES; | |
946 | } | |
1da177e4 | 947 | |
287ac01a | 948 | static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act) |
1da177e4 | 949 | { |
287ac01a CS |
950 | struct zfcp_adapter *adapter = act->adapter; |
951 | struct zfcp_port *port = act->port; | |
1da177e4 | 952 | |
287ac01a | 953 | if (port->wwpn != adapter->peer_wwpn) { |
edaed859 | 954 | zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); |
287ac01a CS |
955 | return ZFCP_ERP_FAILED; |
956 | } | |
957 | port->d_id = adapter->peer_d_id; | |
287ac01a CS |
958 | return zfcp_erp_port_strategy_open_port(act); |
959 | } | |
960 | ||
d5fcdced SM |
961 | static enum zfcp_erp_act_result zfcp_erp_port_strategy_open_common( |
962 | struct zfcp_erp_action *act) | |
287ac01a CS |
963 | { |
964 | struct zfcp_adapter *adapter = act->adapter; | |
965 | struct zfcp_port *port = act->port; | |
287ac01a CS |
966 | int p_status = atomic_read(&port->status); |
967 | ||
968 | switch (act->step) { | |
969 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
970 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: | |
971 | case ZFCP_ERP_STEP_PORT_CLOSING: | |
972 | if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) | |
973 | return zfcp_erp_open_ptp_port(act); | |
b98478d7 | 974 | if (!port->d_id) { |
934aeb58 | 975 | zfcp_fc_trigger_did_lookup(port); |
799b76d0 | 976 | return ZFCP_ERP_EXIT; |
287ac01a | 977 | } |
287ac01a CS |
978 | return zfcp_erp_port_strategy_open_port(act); |
979 | ||
980 | case ZFCP_ERP_STEP_PORT_OPENING: | |
981 | /* D_ID might have changed during open */ | |
5ab944f9 | 982 | if (p_status & ZFCP_STATUS_COMMON_OPEN) { |
934aeb58 CS |
983 | if (!port->d_id) { |
984 | zfcp_fc_trigger_did_lookup(port); | |
985 | return ZFCP_ERP_EXIT; | |
5ab944f9 | 986 | } |
934aeb58 | 987 | return ZFCP_ERP_SUCCEEDED; |
5ab944f9 | 988 | } |
ea460a81 SS |
989 | if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) { |
990 | port->d_id = 0; | |
f7bd7c36 | 991 | return ZFCP_ERP_FAILED; |
ea460a81 | 992 | } |
0023beec SM |
993 | /* no early return otherwise, continue after switch case */ |
994 | break; | |
995 | case ZFCP_ERP_STEP_LUN_CLOSING: | |
996 | case ZFCP_ERP_STEP_LUN_OPENING: | |
997 | /* NOP */ | |
998 | break; | |
287ac01a CS |
999 | } |
1000 | return ZFCP_ERP_FAILED; | |
1001 | } | |
1002 | ||
d5fcdced SM |
1003 | static enum zfcp_erp_act_result zfcp_erp_port_strategy( |
1004 | struct zfcp_erp_action *erp_action) | |
287ac01a CS |
1005 | { |
1006 | struct zfcp_port *port = erp_action->port; | |
934aeb58 | 1007 | int p_status = atomic_read(&port->status); |
287ac01a | 1008 | |
934aeb58 CS |
1009 | if ((p_status & ZFCP_STATUS_COMMON_NOESC) && |
1010 | !(p_status & ZFCP_STATUS_COMMON_OPEN)) | |
5ab944f9 SS |
1011 | goto close_init_done; |
1012 | ||
287ac01a CS |
1013 | switch (erp_action->step) { |
1014 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
934aeb58 | 1015 | if (p_status & ZFCP_STATUS_COMMON_OPEN) |
287ac01a | 1016 | return zfcp_erp_port_strategy_close(erp_action); |
1da177e4 LT |
1017 | break; |
1018 | ||
287ac01a | 1019 | case ZFCP_ERP_STEP_PORT_CLOSING: |
934aeb58 | 1020 | if (p_status & ZFCP_STATUS_COMMON_OPEN) |
287ac01a | 1021 | return ZFCP_ERP_FAILED; |
1da177e4 | 1022 | break; |
0023beec SM |
1023 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: |
1024 | case ZFCP_ERP_STEP_PORT_OPENING: | |
1025 | case ZFCP_ERP_STEP_LUN_CLOSING: | |
1026 | case ZFCP_ERP_STEP_LUN_OPENING: | |
1027 | /* NOP */ | |
1028 | break; | |
1da177e4 | 1029 | } |
5ab944f9 SS |
1030 | |
1031 | close_init_done: | |
287ac01a CS |
1032 | if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY) |
1033 | return ZFCP_ERP_EXIT; | |
1da177e4 | 1034 | |
5ab944f9 | 1035 | return zfcp_erp_port_strategy_open_common(erp_action); |
287ac01a CS |
1036 | } |
1037 | ||
b62a8d9b | 1038 | static void zfcp_erp_lun_strategy_clearstati(struct scsi_device *sdev) |
287ac01a | 1039 | { |
b62a8d9b CS |
1040 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1041 | ||
805de8f4 | 1042 | atomic_andnot(ZFCP_STATUS_COMMON_ACCESS_DENIED, |
b62a8d9b | 1043 | &zfcp_sdev->status); |
287ac01a CS |
1044 | } |
1045 | ||
d5fcdced SM |
1046 | static enum zfcp_erp_act_result zfcp_erp_lun_strategy_close( |
1047 | struct zfcp_erp_action *erp_action) | |
287ac01a | 1048 | { |
b62a8d9b | 1049 | int retval = zfcp_fsf_close_lun(erp_action); |
287ac01a CS |
1050 | if (retval == -ENOMEM) |
1051 | return ZFCP_ERP_NOMEM; | |
b62a8d9b | 1052 | erp_action->step = ZFCP_ERP_STEP_LUN_CLOSING; |
287ac01a CS |
1053 | if (retval) |
1054 | return ZFCP_ERP_FAILED; | |
1055 | return ZFCP_ERP_CONTINUES; | |
1056 | } | |
1057 | ||
d5fcdced SM |
1058 | static enum zfcp_erp_act_result zfcp_erp_lun_strategy_open( |
1059 | struct zfcp_erp_action *erp_action) | |
287ac01a | 1060 | { |
b62a8d9b | 1061 | int retval = zfcp_fsf_open_lun(erp_action); |
287ac01a CS |
1062 | if (retval == -ENOMEM) |
1063 | return ZFCP_ERP_NOMEM; | |
b62a8d9b | 1064 | erp_action->step = ZFCP_ERP_STEP_LUN_OPENING; |
287ac01a CS |
1065 | if (retval) |
1066 | return ZFCP_ERP_FAILED; | |
1067 | return ZFCP_ERP_CONTINUES; | |
1da177e4 LT |
1068 | } |
1069 | ||
d5fcdced SM |
1070 | static enum zfcp_erp_act_result zfcp_erp_lun_strategy( |
1071 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 1072 | { |
b62a8d9b CS |
1073 | struct scsi_device *sdev = erp_action->sdev; |
1074 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); | |
287ac01a CS |
1075 | |
1076 | switch (erp_action->step) { | |
1077 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
b62a8d9b CS |
1078 | zfcp_erp_lun_strategy_clearstati(sdev); |
1079 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) | |
1080 | return zfcp_erp_lun_strategy_close(erp_action); | |
3505144e SM |
1081 | /* already closed */ |
1082 | /* fall through */ | |
b62a8d9b CS |
1083 | case ZFCP_ERP_STEP_LUN_CLOSING: |
1084 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) | |
287ac01a CS |
1085 | return ZFCP_ERP_FAILED; |
1086 | if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY) | |
1087 | return ZFCP_ERP_EXIT; | |
b62a8d9b | 1088 | return zfcp_erp_lun_strategy_open(erp_action); |
287ac01a | 1089 | |
b62a8d9b CS |
1090 | case ZFCP_ERP_STEP_LUN_OPENING: |
1091 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) | |
287ac01a | 1092 | return ZFCP_ERP_SUCCEEDED; |
0023beec SM |
1093 | break; |
1094 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: | |
1095 | case ZFCP_ERP_STEP_PORT_CLOSING: | |
1096 | case ZFCP_ERP_STEP_PORT_OPENING: | |
1097 | /* NOP */ | |
1098 | break; | |
287ac01a CS |
1099 | } |
1100 | return ZFCP_ERP_FAILED; | |
1da177e4 LT |
1101 | } |
1102 | ||
d5fcdced SM |
1103 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_lun( |
1104 | struct scsi_device *sdev, enum zfcp_erp_act_result result) | |
1da177e4 | 1105 | { |
b62a8d9b CS |
1106 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1107 | ||
1da177e4 LT |
1108 | switch (result) { |
1109 | case ZFCP_ERP_SUCCEEDED : | |
b62a8d9b CS |
1110 | atomic_set(&zfcp_sdev->erp_counter, 0); |
1111 | zfcp_erp_lun_unblock(sdev); | |
1da177e4 LT |
1112 | break; |
1113 | case ZFCP_ERP_FAILED : | |
b62a8d9b CS |
1114 | atomic_inc(&zfcp_sdev->erp_counter); |
1115 | if (atomic_read(&zfcp_sdev->erp_counter) > ZFCP_MAX_ERPS) { | |
1116 | dev_err(&zfcp_sdev->port->adapter->ccw_device->dev, | |
1117 | "ERP failed for LUN 0x%016Lx on " | |
ff3b24fa | 1118 | "port 0x%016Lx\n", |
b62a8d9b CS |
1119 | (unsigned long long)zfcp_scsi_dev_lun(sdev), |
1120 | (unsigned long long)zfcp_sdev->port->wwpn); | |
edaed859 SS |
1121 | zfcp_erp_set_lun_status(sdev, |
1122 | ZFCP_STATUS_COMMON_ERP_FAILED); | |
ff3b24fa | 1123 | } |
1da177e4 | 1124 | break; |
d5fcdced SM |
1125 | case ZFCP_ERP_CONTINUES: |
1126 | case ZFCP_ERP_EXIT: | |
1127 | case ZFCP_ERP_DISMISSED: | |
1128 | case ZFCP_ERP_NOMEM: | |
1129 | /* NOP */ | |
1130 | break; | |
1da177e4 LT |
1131 | } |
1132 | ||
b62a8d9b CS |
1133 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { |
1134 | zfcp_erp_lun_block(sdev, 0); | |
1da177e4 LT |
1135 | result = ZFCP_ERP_EXIT; |
1136 | } | |
1da177e4 LT |
1137 | return result; |
1138 | } | |
1139 | ||
d5fcdced SM |
1140 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_port( |
1141 | struct zfcp_port *port, enum zfcp_erp_act_result result) | |
1da177e4 | 1142 | { |
1da177e4 LT |
1143 | switch (result) { |
1144 | case ZFCP_ERP_SUCCEEDED : | |
1145 | atomic_set(&port->erp_counter, 0); | |
1146 | zfcp_erp_port_unblock(port); | |
1147 | break; | |
287ac01a | 1148 | |
1da177e4 | 1149 | case ZFCP_ERP_FAILED : |
287ac01a | 1150 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) { |
cc8c2829 SS |
1151 | zfcp_erp_port_block(port, 0); |
1152 | result = ZFCP_ERP_EXIT; | |
1153 | } | |
1da177e4 | 1154 | atomic_inc(&port->erp_counter); |
ff3b24fa CS |
1155 | if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) { |
1156 | dev_err(&port->adapter->ccw_device->dev, | |
1157 | "ERP failed for remote port 0x%016Lx\n", | |
7ba58c9c | 1158 | (unsigned long long)port->wwpn); |
edaed859 SS |
1159 | zfcp_erp_set_port_status(port, |
1160 | ZFCP_STATUS_COMMON_ERP_FAILED); | |
ff3b24fa | 1161 | } |
1da177e4 | 1162 | break; |
d5fcdced SM |
1163 | case ZFCP_ERP_CONTINUES: |
1164 | case ZFCP_ERP_EXIT: | |
1165 | case ZFCP_ERP_DISMISSED: | |
1166 | case ZFCP_ERP_NOMEM: | |
1167 | /* NOP */ | |
1168 | break; | |
1da177e4 LT |
1169 | } |
1170 | ||
287ac01a CS |
1171 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { |
1172 | zfcp_erp_port_block(port, 0); | |
1da177e4 LT |
1173 | result = ZFCP_ERP_EXIT; |
1174 | } | |
1da177e4 LT |
1175 | return result; |
1176 | } | |
1177 | ||
d5fcdced SM |
1178 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_adapter( |
1179 | struct zfcp_adapter *adapter, enum zfcp_erp_act_result result) | |
1da177e4 | 1180 | { |
1da177e4 LT |
1181 | switch (result) { |
1182 | case ZFCP_ERP_SUCCEEDED : | |
1183 | atomic_set(&adapter->erp_counter, 0); | |
1184 | zfcp_erp_adapter_unblock(adapter); | |
1185 | break; | |
287ac01a | 1186 | |
1da177e4 LT |
1187 | case ZFCP_ERP_FAILED : |
1188 | atomic_inc(&adapter->erp_counter); | |
ff3b24fa CS |
1189 | if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) { |
1190 | dev_err(&adapter->ccw_device->dev, | |
1191 | "ERP cannot recover an error " | |
1192 | "on the FCP device\n"); | |
edaed859 SS |
1193 | zfcp_erp_set_adapter_status(adapter, |
1194 | ZFCP_STATUS_COMMON_ERP_FAILED); | |
ff3b24fa | 1195 | } |
1da177e4 | 1196 | break; |
d5fcdced SM |
1197 | case ZFCP_ERP_CONTINUES: |
1198 | case ZFCP_ERP_EXIT: | |
1199 | case ZFCP_ERP_DISMISSED: | |
1200 | case ZFCP_ERP_NOMEM: | |
1201 | /* NOP */ | |
1202 | break; | |
1da177e4 LT |
1203 | } |
1204 | ||
287ac01a CS |
1205 | if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { |
1206 | zfcp_erp_adapter_block(adapter, 0); | |
1da177e4 LT |
1207 | result = ZFCP_ERP_EXIT; |
1208 | } | |
1da177e4 LT |
1209 | return result; |
1210 | } | |
1211 | ||
d5fcdced SM |
1212 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_target( |
1213 | struct zfcp_erp_action *erp_action, enum zfcp_erp_act_result result) | |
5f852be9 | 1214 | { |
287ac01a CS |
1215 | struct zfcp_adapter *adapter = erp_action->adapter; |
1216 | struct zfcp_port *port = erp_action->port; | |
b62a8d9b | 1217 | struct scsi_device *sdev = erp_action->sdev; |
287ac01a | 1218 | |
df91eefd | 1219 | switch (erp_action->type) { |
287ac01a | 1220 | |
b62a8d9b CS |
1221 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1222 | result = zfcp_erp_strategy_check_lun(sdev, result); | |
287ac01a CS |
1223 | break; |
1224 | ||
1225 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
1226 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
1227 | result = zfcp_erp_strategy_check_port(port, result); | |
1228 | break; | |
1229 | ||
1230 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
1231 | result = zfcp_erp_strategy_check_adapter(adapter, result); | |
1232 | break; | |
1233 | } | |
1234 | return result; | |
5f852be9 CS |
1235 | } |
1236 | ||
287ac01a | 1237 | static int zfcp_erp_strat_change_det(atomic_t *target_status, u32 erp_status) |
5f852be9 | 1238 | { |
287ac01a | 1239 | int status = atomic_read(target_status); |
5f852be9 | 1240 | |
287ac01a CS |
1241 | if ((status & ZFCP_STATUS_COMMON_RUNNING) && |
1242 | (erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY)) | |
1243 | return 1; /* take it online */ | |
5f852be9 | 1244 | |
287ac01a CS |
1245 | if (!(status & ZFCP_STATUS_COMMON_RUNNING) && |
1246 | !(erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY)) | |
1247 | return 1; /* take it offline */ | |
1248 | ||
1249 | return 0; | |
5f852be9 CS |
1250 | } |
1251 | ||
d5fcdced SM |
1252 | static enum zfcp_erp_act_result zfcp_erp_strategy_statechange( |
1253 | struct zfcp_erp_action *act, enum zfcp_erp_act_result result) | |
1da177e4 | 1254 | { |
df91eefd | 1255 | enum zfcp_erp_act_type type = act->type; |
287ac01a CS |
1256 | struct zfcp_adapter *adapter = act->adapter; |
1257 | struct zfcp_port *port = act->port; | |
b62a8d9b CS |
1258 | struct scsi_device *sdev = act->sdev; |
1259 | struct zfcp_scsi_dev *zfcp_sdev; | |
287ac01a | 1260 | u32 erp_status = act->status; |
1da177e4 | 1261 | |
df91eefd | 1262 | switch (type) { |
1da177e4 | 1263 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
287ac01a CS |
1264 | if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) { |
1265 | _zfcp_erp_adapter_reopen(adapter, | |
1266 | ZFCP_STATUS_COMMON_ERP_FAILED, | |
ea4a3a6a | 1267 | "ersscg1"); |
287ac01a CS |
1268 | return ZFCP_ERP_EXIT; |
1269 | } | |
1da177e4 LT |
1270 | break; |
1271 | ||
1272 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
1da177e4 | 1273 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
287ac01a CS |
1274 | if (zfcp_erp_strat_change_det(&port->status, erp_status)) { |
1275 | _zfcp_erp_port_reopen(port, | |
1276 | ZFCP_STATUS_COMMON_ERP_FAILED, | |
ea4a3a6a | 1277 | "ersscg2"); |
287ac01a CS |
1278 | return ZFCP_ERP_EXIT; |
1279 | } | |
1da177e4 LT |
1280 | break; |
1281 | ||
b62a8d9b CS |
1282 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1283 | zfcp_sdev = sdev_to_zfcp(sdev); | |
1284 | if (zfcp_erp_strat_change_det(&zfcp_sdev->status, erp_status)) { | |
1285 | _zfcp_erp_lun_reopen(sdev, | |
1286 | ZFCP_STATUS_COMMON_ERP_FAILED, | |
ea4a3a6a | 1287 | "ersscg3", 0); |
287ac01a CS |
1288 | return ZFCP_ERP_EXIT; |
1289 | } | |
1da177e4 LT |
1290 | break; |
1291 | } | |
d5fcdced | 1292 | return result; |
1da177e4 LT |
1293 | } |
1294 | ||
287ac01a | 1295 | static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) |
1da177e4 | 1296 | { |
287ac01a | 1297 | struct zfcp_adapter *adapter = erp_action->adapter; |
b62a8d9b | 1298 | struct zfcp_scsi_dev *zfcp_sdev; |
1da177e4 | 1299 | |
287ac01a CS |
1300 | adapter->erp_total_count--; |
1301 | if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { | |
1302 | adapter->erp_low_mem_count--; | |
1303 | erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; | |
507e4969 | 1304 | } |
1da177e4 | 1305 | |
287ac01a | 1306 | list_del(&erp_action->list); |
ae0904f6 | 1307 | zfcp_dbf_rec_run("eractd1", erp_action); |
1da177e4 | 1308 | |
df91eefd | 1309 | switch (erp_action->type) { |
b62a8d9b CS |
1310 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1311 | zfcp_sdev = sdev_to_zfcp(erp_action->sdev); | |
805de8f4 | 1312 | atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE, |
b62a8d9b | 1313 | &zfcp_sdev->status); |
287ac01a | 1314 | break; |
1da177e4 | 1315 | |
287ac01a CS |
1316 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: |
1317 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
805de8f4 | 1318 | atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE, |
287ac01a CS |
1319 | &erp_action->port->status); |
1320 | break; | |
1da177e4 | 1321 | |
287ac01a | 1322 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
805de8f4 | 1323 | atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE, |
287ac01a CS |
1324 | &erp_action->adapter->status); |
1325 | break; | |
1326 | } | |
1da177e4 LT |
1327 | } |
1328 | ||
6f2ce1c6 SM |
1329 | /** |
1330 | * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery | |
1331 | * @port: zfcp_port whose fc_rport we should try to unblock | |
1332 | */ | |
1333 | static void zfcp_erp_try_rport_unblock(struct zfcp_port *port) | |
1334 | { | |
1335 | unsigned long flags; | |
1336 | struct zfcp_adapter *adapter = port->adapter; | |
1337 | int port_status; | |
1338 | struct Scsi_Host *shost = adapter->scsi_host; | |
1339 | struct scsi_device *sdev; | |
1340 | ||
1341 | write_lock_irqsave(&adapter->erp_lock, flags); | |
1342 | port_status = atomic_read(&port->status); | |
1343 | if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || | |
1344 | (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE | | |
1345 | ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) { | |
1346 | /* new ERP of severity >= port triggered elsewhere meanwhile or | |
1347 | * local link down (adapter erp_failed but not clear unblock) | |
1348 | */ | |
1349 | zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action); | |
1350 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1351 | return; | |
1352 | } | |
1353 | spin_lock(shost->host_lock); | |
1354 | __shost_for_each_device(sdev, shost) { | |
1355 | struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev); | |
1356 | int lun_status; | |
1357 | ||
fe67888f SM |
1358 | if (sdev->sdev_state == SDEV_DEL || |
1359 | sdev->sdev_state == SDEV_CANCEL) | |
1360 | continue; | |
6f2ce1c6 SM |
1361 | if (zsdev->port != port) |
1362 | continue; | |
1363 | /* LUN under port of interest */ | |
1364 | lun_status = atomic_read(&zsdev->status); | |
1365 | if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0) | |
1366 | continue; /* unblock rport despite failed LUNs */ | |
1367 | /* LUN recovery not given up yet [maybe follow-up pending] */ | |
1368 | if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || | |
1369 | (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) { | |
1370 | /* LUN blocked: | |
1371 | * not yet unblocked [LUN recovery pending] | |
1372 | * or meanwhile blocked [new LUN recovery triggered] | |
1373 | */ | |
1374 | zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action); | |
1375 | spin_unlock(shost->host_lock); | |
1376 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1377 | return; | |
1378 | } | |
1379 | } | |
1380 | /* now port has no child or all children have completed recovery, | |
1381 | * and no ERP of severity >= port was meanwhile triggered elsewhere | |
1382 | */ | |
1383 | zfcp_scsi_schedule_rport_register(port); | |
1384 | spin_unlock(shost->host_lock); | |
1385 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1386 | } | |
1387 | ||
d5fcdced SM |
1388 | static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, |
1389 | enum zfcp_erp_act_result result) | |
1da177e4 | 1390 | { |
287ac01a CS |
1391 | struct zfcp_adapter *adapter = act->adapter; |
1392 | struct zfcp_port *port = act->port; | |
b62a8d9b | 1393 | struct scsi_device *sdev = act->sdev; |
1da177e4 | 1394 | |
df91eefd | 1395 | switch (act->type) { |
b62a8d9b | 1396 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
fdbd1c5e | 1397 | if (!(act->status & ZFCP_STATUS_ERP_NO_REF)) |
b62a8d9b | 1398 | scsi_device_put(sdev); |
6f2ce1c6 | 1399 | zfcp_erp_try_rport_unblock(port); |
287ac01a | 1400 | break; |
1da177e4 | 1401 | |
287ac01a | 1402 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
4eeaa4f3 SM |
1403 | /* This switch case might also happen after a forced reopen |
1404 | * was successfully done and thus overwritten with a new | |
1405 | * non-forced reopen at `ersfs_2'. In this case, we must not | |
1406 | * do the clean-up of the non-forced version. | |
1407 | */ | |
1408 | if (act->step != ZFCP_ERP_STEP_UNINITIALIZED) | |
1409 | if (result == ZFCP_ERP_SUCCEEDED) | |
6f2ce1c6 | 1410 | zfcp_erp_try_rport_unblock(port); |
5767620c CS |
1411 | /* fall through */ |
1412 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
615f59e0 | 1413 | put_device(&port->dev); |
287ac01a CS |
1414 | break; |
1415 | ||
1416 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
a2fa0aed | 1417 | if (result == ZFCP_ERP_SUCCEEDED) { |
bd43a42b | 1418 | register_service_level(&adapter->service_level); |
43f60cbd | 1419 | zfcp_fc_conditional_port_scan(adapter); |
038d9446 | 1420 | queue_work(adapter->work_queue, &adapter->ns_up_work); |
a2fa0aed CS |
1421 | } else |
1422 | unregister_service_level(&adapter->service_level); | |
038d9446 | 1423 | |
f3450c7b | 1424 | kref_put(&adapter->ref, zfcp_adapter_release); |
287ac01a CS |
1425 | break; |
1426 | } | |
1da177e4 LT |
1427 | } |
1428 | ||
d5fcdced SM |
1429 | static enum zfcp_erp_act_result zfcp_erp_strategy_do_action( |
1430 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 1431 | { |
df91eefd | 1432 | switch (erp_action->type) { |
287ac01a CS |
1433 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
1434 | return zfcp_erp_adapter_strategy(erp_action); | |
1435 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
1436 | return zfcp_erp_port_forced_strategy(erp_action); | |
1437 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
1438 | return zfcp_erp_port_strategy(erp_action); | |
b62a8d9b CS |
1439 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1440 | return zfcp_erp_lun_strategy(erp_action); | |
287ac01a CS |
1441 | } |
1442 | return ZFCP_ERP_FAILED; | |
1da177e4 LT |
1443 | } |
1444 | ||
d5fcdced SM |
1445 | static enum zfcp_erp_act_result zfcp_erp_strategy( |
1446 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 1447 | { |
d5fcdced | 1448 | enum zfcp_erp_act_result result; |
287ac01a | 1449 | unsigned long flags; |
ecf0c772 | 1450 | struct zfcp_adapter *adapter = erp_action->adapter; |
1da177e4 | 1451 | |
f3450c7b | 1452 | kref_get(&adapter->ref); |
1da177e4 | 1453 | |
f3450c7b | 1454 | write_lock_irqsave(&adapter->erp_lock, flags); |
287ac01a | 1455 | zfcp_erp_strategy_check_fsfreq(erp_action); |
1da177e4 | 1456 | |
287ac01a CS |
1457 | if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) { |
1458 | zfcp_erp_action_dequeue(erp_action); | |
d5fcdced | 1459 | result = ZFCP_ERP_DISMISSED; |
287ac01a | 1460 | goto unlock; |
22753fa5 | 1461 | } |
1da177e4 | 1462 | |
9c785d94 | 1463 | if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) { |
d5fcdced | 1464 | result = ZFCP_ERP_FAILED; |
9c785d94 CS |
1465 | goto check_target; |
1466 | } | |
1467 | ||
2f8f3ed5 | 1468 | zfcp_erp_action_to_running(erp_action); |
1da177e4 | 1469 | |
287ac01a | 1470 | /* no lock to allow for blocking operations */ |
ecf0c772 | 1471 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
d5fcdced | 1472 | result = zfcp_erp_strategy_do_action(erp_action); |
ecf0c772 | 1473 | write_lock_irqsave(&adapter->erp_lock, flags); |
1da177e4 | 1474 | |
287ac01a | 1475 | if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) |
d5fcdced | 1476 | result = ZFCP_ERP_CONTINUES; |
cc8c2829 | 1477 | |
d5fcdced | 1478 | switch (result) { |
287ac01a CS |
1479 | case ZFCP_ERP_NOMEM: |
1480 | if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) { | |
1481 | ++adapter->erp_low_mem_count; | |
1482 | erp_action->status |= ZFCP_STATUS_ERP_LOWMEM; | |
1da177e4 | 1483 | } |
287ac01a | 1484 | if (adapter->erp_total_count == adapter->erp_low_mem_count) |
ea4a3a6a | 1485 | _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1"); |
287ac01a CS |
1486 | else { |
1487 | zfcp_erp_strategy_memwait(erp_action); | |
d5fcdced | 1488 | result = ZFCP_ERP_CONTINUES; |
1da177e4 | 1489 | } |
287ac01a | 1490 | goto unlock; |
1da177e4 | 1491 | |
287ac01a CS |
1492 | case ZFCP_ERP_CONTINUES: |
1493 | if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { | |
1494 | --adapter->erp_low_mem_count; | |
1495 | erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; | |
1da177e4 | 1496 | } |
287ac01a | 1497 | goto unlock; |
d5fcdced SM |
1498 | case ZFCP_ERP_SUCCEEDED: |
1499 | case ZFCP_ERP_FAILED: | |
1500 | case ZFCP_ERP_EXIT: | |
1501 | case ZFCP_ERP_DISMISSED: | |
1502 | /* NOP */ | |
1503 | break; | |
1da177e4 LT |
1504 | } |
1505 | ||
9c785d94 | 1506 | check_target: |
d5fcdced | 1507 | result = zfcp_erp_strategy_check_target(erp_action, result); |
287ac01a | 1508 | zfcp_erp_action_dequeue(erp_action); |
d5fcdced SM |
1509 | result = zfcp_erp_strategy_statechange(erp_action, result); |
1510 | if (result == ZFCP_ERP_EXIT) | |
287ac01a | 1511 | goto unlock; |
d5fcdced | 1512 | if (result == ZFCP_ERP_SUCCEEDED) |
85600f7f | 1513 | zfcp_erp_strategy_followup_success(erp_action); |
d5fcdced | 1514 | if (result == ZFCP_ERP_FAILED) |
85600f7f | 1515 | zfcp_erp_strategy_followup_failed(erp_action); |
1da177e4 | 1516 | |
287ac01a | 1517 | unlock: |
ecf0c772 | 1518 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
1da177e4 | 1519 | |
d5fcdced SM |
1520 | if (result != ZFCP_ERP_CONTINUES) |
1521 | zfcp_erp_action_cleanup(erp_action, result); | |
1da177e4 | 1522 | |
f3450c7b | 1523 | kref_put(&adapter->ref, zfcp_adapter_release); |
d5fcdced | 1524 | return result; |
1da177e4 LT |
1525 | } |
1526 | ||
287ac01a | 1527 | static int zfcp_erp_thread(void *data) |
1da177e4 | 1528 | { |
287ac01a CS |
1529 | struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; |
1530 | struct list_head *next; | |
1531 | struct zfcp_erp_action *act; | |
1532 | unsigned long flags; | |
94ab4b38 | 1533 | |
347c6a96 | 1534 | for (;;) { |
347c6a96 CS |
1535 | wait_event_interruptible(adapter->erp_ready_wq, |
1536 | !list_empty(&adapter->erp_ready_head) || | |
1537 | kthread_should_stop()); | |
94ab4b38 | 1538 | |
347c6a96 CS |
1539 | if (kthread_should_stop()) |
1540 | break; | |
1541 | ||
287ac01a CS |
1542 | write_lock_irqsave(&adapter->erp_lock, flags); |
1543 | next = adapter->erp_ready_head.next; | |
1544 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1da177e4 | 1545 | |
287ac01a CS |
1546 | if (next != &adapter->erp_ready_head) { |
1547 | act = list_entry(next, struct zfcp_erp_action, list); | |
1da177e4 | 1548 | |
287ac01a CS |
1549 | /* there is more to come after dismission, no notify */ |
1550 | if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED) | |
1551 | zfcp_erp_wakeup(adapter); | |
1da177e4 | 1552 | } |
1da177e4 LT |
1553 | } |
1554 | ||
287ac01a CS |
1555 | return 0; |
1556 | } | |
1da177e4 | 1557 | |
287ac01a CS |
1558 | /** |
1559 | * zfcp_erp_thread_setup - Start ERP thread for adapter | |
1560 | * @adapter: Adapter to start the ERP thread for | |
1561 | * | |
64eba384 | 1562 | * Return: 0 on success, or error code from kthread_run(). |
287ac01a CS |
1563 | */ |
1564 | int zfcp_erp_thread_setup(struct zfcp_adapter *adapter) | |
1565 | { | |
347c6a96 | 1566 | struct task_struct *thread; |
1da177e4 | 1567 | |
347c6a96 CS |
1568 | thread = kthread_run(zfcp_erp_thread, adapter, "zfcperp%s", |
1569 | dev_name(&adapter->ccw_device->dev)); | |
1570 | if (IS_ERR(thread)) { | |
287ac01a | 1571 | dev_err(&adapter->ccw_device->dev, |
ff3b24fa | 1572 | "Creating an ERP thread for the FCP device failed.\n"); |
347c6a96 | 1573 | return PTR_ERR(thread); |
1da177e4 | 1574 | } |
347c6a96 CS |
1575 | |
1576 | adapter->erp_thread = thread; | |
287ac01a CS |
1577 | return 0; |
1578 | } | |
1da177e4 | 1579 | |
287ac01a CS |
1580 | /** |
1581 | * zfcp_erp_thread_kill - Stop ERP thread. | |
1582 | * @adapter: Adapter where the ERP thread should be stopped. | |
1583 | * | |
1584 | * The caller of this routine ensures that the specified adapter has | |
1585 | * been shut down and that this operation has been completed. Thus, | |
1586 | * there are no pending erp_actions which would need to be handled | |
1587 | * here. | |
1588 | */ | |
1589 | void zfcp_erp_thread_kill(struct zfcp_adapter *adapter) | |
1590 | { | |
347c6a96 CS |
1591 | kthread_stop(adapter->erp_thread); |
1592 | adapter->erp_thread = NULL; | |
143bb6bf CS |
1593 | WARN_ON(!list_empty(&adapter->erp_ready_head)); |
1594 | WARN_ON(!list_empty(&adapter->erp_running_head)); | |
1da177e4 LT |
1595 | } |
1596 | ||
287ac01a | 1597 | /** |
edaed859 SS |
1598 | * zfcp_erp_wait - wait for completion of error recovery on an adapter |
1599 | * @adapter: adapter for which to wait for completion of its error recovery | |
287ac01a | 1600 | */ |
edaed859 | 1601 | void zfcp_erp_wait(struct zfcp_adapter *adapter) |
1da177e4 | 1602 | { |
edaed859 SS |
1603 | wait_event(adapter->erp_done_wqh, |
1604 | !(atomic_read(&adapter->status) & | |
1605 | ZFCP_STATUS_ADAPTER_ERP_PENDING)); | |
287ac01a | 1606 | } |
1da177e4 | 1607 | |
287ac01a | 1608 | /** |
edaed859 SS |
1609 | * zfcp_erp_set_adapter_status - set adapter status bits |
1610 | * @adapter: adapter to change the status | |
1611 | * @mask: status bits to change | |
1612 | * | |
1613 | * Changes in common status bits are propagated to attached ports and LUNs. | |
287ac01a | 1614 | */ |
edaed859 | 1615 | void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) |
287ac01a | 1616 | { |
edaed859 SS |
1617 | struct zfcp_port *port; |
1618 | struct scsi_device *sdev; | |
1619 | unsigned long flags; | |
1620 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; | |
1da177e4 | 1621 | |
805de8f4 | 1622 | atomic_or(mask, &adapter->status); |
1da177e4 | 1623 | |
edaed859 SS |
1624 | if (!common_mask) |
1625 | return; | |
1626 | ||
1627 | read_lock_irqsave(&adapter->port_list_lock, flags); | |
1628 | list_for_each_entry(port, &adapter->port_list, list) | |
805de8f4 | 1629 | atomic_or(common_mask, &port->status); |
edaed859 SS |
1630 | read_unlock_irqrestore(&adapter->port_list_lock, flags); |
1631 | ||
924dd584 MP |
1632 | spin_lock_irqsave(adapter->scsi_host->host_lock, flags); |
1633 | __shost_for_each_device(sdev, adapter->scsi_host) | |
805de8f4 | 1634 | atomic_or(common_mask, &sdev_to_zfcp(sdev)->status); |
924dd584 | 1635 | spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); |
287ac01a | 1636 | } |
1da177e4 | 1637 | |
287ac01a | 1638 | /** |
edaed859 | 1639 | * zfcp_erp_clear_adapter_status - clear adapter status bits |
287ac01a | 1640 | * @adapter: adapter to change the status |
287ac01a | 1641 | * @mask: status bits to change |
287ac01a | 1642 | * |
b62a8d9b | 1643 | * Changes in common status bits are propagated to attached ports and LUNs. |
287ac01a | 1644 | */ |
edaed859 | 1645 | void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) |
1da177e4 | 1646 | { |
1da177e4 | 1647 | struct zfcp_port *port; |
edaed859 | 1648 | struct scsi_device *sdev; |
ecf0c772 | 1649 | unsigned long flags; |
287ac01a | 1650 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; |
edaed859 | 1651 | u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; |
1da177e4 | 1652 | |
805de8f4 | 1653 | atomic_andnot(mask, &adapter->status); |
edaed859 SS |
1654 | |
1655 | if (!common_mask) | |
1656 | return; | |
1657 | ||
1658 | if (clear_counter) | |
1659 | atomic_set(&adapter->erp_counter, 0); | |
1660 | ||
1661 | read_lock_irqsave(&adapter->port_list_lock, flags); | |
1662 | list_for_each_entry(port, &adapter->port_list, list) { | |
805de8f4 | 1663 | atomic_andnot(common_mask, &port->status); |
edaed859 SS |
1664 | if (clear_counter) |
1665 | atomic_set(&port->erp_counter, 0); | |
287ac01a | 1666 | } |
edaed859 | 1667 | read_unlock_irqrestore(&adapter->port_list_lock, flags); |
287ac01a | 1668 | |
924dd584 MP |
1669 | spin_lock_irqsave(adapter->scsi_host->host_lock, flags); |
1670 | __shost_for_each_device(sdev, adapter->scsi_host) { | |
805de8f4 | 1671 | atomic_andnot(common_mask, &sdev_to_zfcp(sdev)->status); |
edaed859 SS |
1672 | if (clear_counter) |
1673 | atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); | |
ecf0c772 | 1674 | } |
924dd584 | 1675 | spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); |
1da177e4 LT |
1676 | } |
1677 | ||
287ac01a | 1678 | /** |
edaed859 SS |
1679 | * zfcp_erp_set_port_status - set port status bits |
1680 | * @port: port to change the status | |
287ac01a | 1681 | * @mask: status bits to change |
287ac01a | 1682 | * |
b62a8d9b | 1683 | * Changes in common status bits are propagated to attached LUNs. |
287ac01a | 1684 | */ |
edaed859 | 1685 | void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask) |
1da177e4 | 1686 | { |
b62a8d9b | 1687 | struct scsi_device *sdev; |
287ac01a | 1688 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; |
924dd584 | 1689 | unsigned long flags; |
1da177e4 | 1690 | |
805de8f4 | 1691 | atomic_or(mask, &port->status); |
1da177e4 | 1692 | |
edaed859 SS |
1693 | if (!common_mask) |
1694 | return; | |
1695 | ||
924dd584 MP |
1696 | spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); |
1697 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
edaed859 | 1698 | if (sdev_to_zfcp(sdev)->port == port) |
805de8f4 | 1699 | atomic_or(common_mask, |
edaed859 | 1700 | &sdev_to_zfcp(sdev)->status); |
924dd584 | 1701 | spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); |
1da177e4 LT |
1702 | } |
1703 | ||
287ac01a | 1704 | /** |
edaed859 SS |
1705 | * zfcp_erp_clear_port_status - clear port status bits |
1706 | * @port: adapter to change the status | |
287ac01a | 1707 | * @mask: status bits to change |
edaed859 SS |
1708 | * |
1709 | * Changes in common status bits are propagated to attached LUNs. | |
287ac01a | 1710 | */ |
edaed859 | 1711 | void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) |
1da177e4 | 1712 | { |
edaed859 SS |
1713 | struct scsi_device *sdev; |
1714 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; | |
1715 | u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; | |
924dd584 | 1716 | unsigned long flags; |
edaed859 | 1717 | |
805de8f4 | 1718 | atomic_andnot(mask, &port->status); |
edaed859 SS |
1719 | |
1720 | if (!common_mask) | |
1721 | return; | |
b62a8d9b | 1722 | |
edaed859 SS |
1723 | if (clear_counter) |
1724 | atomic_set(&port->erp_counter, 0); | |
1725 | ||
924dd584 MP |
1726 | spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); |
1727 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
edaed859 | 1728 | if (sdev_to_zfcp(sdev)->port == port) { |
805de8f4 | 1729 | atomic_andnot(common_mask, |
edaed859 SS |
1730 | &sdev_to_zfcp(sdev)->status); |
1731 | if (clear_counter) | |
1732 | atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); | |
287ac01a | 1733 | } |
924dd584 | 1734 | spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); |
1da177e4 LT |
1735 | } |
1736 | ||
287ac01a | 1737 | /** |
edaed859 SS |
1738 | * zfcp_erp_set_lun_status - set lun status bits |
1739 | * @sdev: SCSI device / lun to set the status bits | |
1740 | * @mask: status bits to change | |
287ac01a | 1741 | */ |
edaed859 | 1742 | void zfcp_erp_set_lun_status(struct scsi_device *sdev, u32 mask) |
d736a27b | 1743 | { |
edaed859 SS |
1744 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1745 | ||
805de8f4 | 1746 | atomic_or(mask, &zfcp_sdev->status); |
d736a27b AH |
1747 | } |
1748 | ||
287ac01a | 1749 | /** |
edaed859 SS |
1750 | * zfcp_erp_clear_lun_status - clear lun status bits |
1751 | * @sdev: SCSi device / lun to clear the status bits | |
1752 | * @mask: status bits to change | |
287ac01a | 1753 | */ |
edaed859 | 1754 | void zfcp_erp_clear_lun_status(struct scsi_device *sdev, u32 mask) |
d736a27b | 1755 | { |
edaed859 SS |
1756 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1757 | ||
805de8f4 | 1758 | atomic_andnot(mask, &zfcp_sdev->status); |
edaed859 SS |
1759 | |
1760 | if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) | |
1761 | atomic_set(&zfcp_sdev->erp_counter, 0); | |
d736a27b | 1762 | } |
edaed859 | 1763 | |
35e9111a SM |
1764 | /** |
1765 | * zfcp_erp_adapter_reset_sync() - Really reopen adapter and wait. | |
1766 | * @adapter: Pointer to zfcp_adapter to reopen. | |
208d0961 | 1767 | * @dbftag: Trace tag string of length %ZFCP_DBF_TAG_LEN. |
35e9111a | 1768 | */ |
208d0961 | 1769 | void zfcp_erp_adapter_reset_sync(struct zfcp_adapter *adapter, char *dbftag) |
35e9111a SM |
1770 | { |
1771 | zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); | |
208d0961 | 1772 | zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, dbftag); |
35e9111a SM |
1773 | zfcp_erp_wait(adapter); |
1774 | } |