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 | ||
287ac01a | 627 | static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, |
208d0961 | 628 | int clear, char *dbftag) |
1da177e4 | 629 | { |
287ac01a | 630 | struct zfcp_port *port; |
1da177e4 | 631 | |
ecf0c772 SS |
632 | read_lock(&adapter->port_list_lock); |
633 | list_for_each_entry(port, &adapter->port_list, list) | |
208d0961 | 634 | _zfcp_erp_port_reopen(port, clear, dbftag); |
ecf0c772 | 635 | read_unlock(&adapter->port_list_lock); |
1da177e4 LT |
636 | } |
637 | ||
b62a8d9b | 638 | static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, |
208d0961 | 639 | char *dbftag) |
1da177e4 | 640 | { |
b62a8d9b | 641 | struct scsi_device *sdev; |
1da177e4 | 642 | |
924dd584 MP |
643 | spin_lock(port->adapter->scsi_host->host_lock); |
644 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
b62a8d9b | 645 | if (sdev_to_zfcp(sdev)->port == port) |
208d0961 | 646 | _zfcp_erp_lun_reopen(sdev, clear, dbftag, 0); |
924dd584 | 647 | spin_unlock(port->adapter->scsi_host->host_lock); |
1da177e4 LT |
648 | } |
649 | ||
85600f7f | 650 | static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) |
1da177e4 | 651 | { |
df91eefd | 652 | switch (act->type) { |
287ac01a | 653 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
ea4a3a6a | 654 | _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1"); |
287ac01a | 655 | break; |
287ac01a | 656 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: |
ea4a3a6a | 657 | _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2"); |
287ac01a | 658 | break; |
287ac01a | 659 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
ea4a3a6a | 660 | _zfcp_erp_port_reopen(act->port, 0, "ersff_3"); |
287ac01a | 661 | break; |
b62a8d9b | 662 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
ea4a3a6a | 663 | _zfcp_erp_lun_reopen(act->sdev, 0, "ersff_4", 0); |
85600f7f CS |
664 | break; |
665 | } | |
666 | } | |
667 | ||
668 | static void zfcp_erp_strategy_followup_success(struct zfcp_erp_action *act) | |
669 | { | |
df91eefd | 670 | switch (act->type) { |
85600f7f | 671 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
ea4a3a6a | 672 | _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1"); |
85600f7f CS |
673 | break; |
674 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
ea4a3a6a | 675 | _zfcp_erp_port_reopen(act->port, 0, "ersfs_2"); |
85600f7f CS |
676 | break; |
677 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
ea4a3a6a | 678 | _zfcp_erp_lun_reopen_all(act->port, 0, "ersfs_3"); |
287ac01a | 679 | break; |
df91eefd SM |
680 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
681 | /* NOP */ | |
682 | break; | |
1da177e4 | 683 | } |
1da177e4 LT |
684 | } |
685 | ||
287ac01a | 686 | static void zfcp_erp_wakeup(struct zfcp_adapter *adapter) |
1da177e4 | 687 | { |
1da177e4 LT |
688 | unsigned long flags; |
689 | ||
ecf0c772 | 690 | read_lock_irqsave(&adapter->erp_lock, flags); |
287ac01a CS |
691 | if (list_empty(&adapter->erp_ready_head) && |
692 | list_empty(&adapter->erp_running_head)) { | |
805de8f4 | 693 | atomic_andnot(ZFCP_STATUS_ADAPTER_ERP_PENDING, |
287ac01a CS |
694 | &adapter->status); |
695 | wake_up(&adapter->erp_done_wqh); | |
1da177e4 | 696 | } |
ecf0c772 | 697 | read_unlock_irqrestore(&adapter->erp_lock, flags); |
287ac01a | 698 | } |
1da177e4 | 699 | |
287ac01a CS |
700 | static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter) |
701 | { | |
702 | struct zfcp_port *port; | |
703 | port = zfcp_port_enqueue(adapter, adapter->peer_wwpn, 0, | |
704 | adapter->peer_d_id); | |
705 | if (IS_ERR(port)) /* error or port already attached */ | |
706 | return; | |
ea4a3a6a | 707 | _zfcp_erp_port_reopen(port, 0, "ereptp1"); |
287ac01a | 708 | } |
1da177e4 | 709 | |
d5fcdced SM |
710 | static enum zfcp_erp_act_result zfcp_erp_adapter_strat_fsf_xconf( |
711 | struct zfcp_erp_action *erp_action) | |
287ac01a CS |
712 | { |
713 | int retries; | |
714 | int sleep = 1; | |
715 | struct zfcp_adapter *adapter = erp_action->adapter; | |
1da177e4 | 716 | |
805de8f4 | 717 | atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status); |
287ac01a CS |
718 | |
719 | for (retries = 7; retries; retries--) { | |
805de8f4 | 720 | atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, |
287ac01a CS |
721 | &adapter->status); |
722 | write_lock_irq(&adapter->erp_lock); | |
723 | zfcp_erp_action_to_running(erp_action); | |
724 | write_unlock_irq(&adapter->erp_lock); | |
725 | if (zfcp_fsf_exchange_config_data(erp_action)) { | |
805de8f4 | 726 | atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, |
287ac01a CS |
727 | &adapter->status); |
728 | return ZFCP_ERP_FAILED; | |
1da177e4 | 729 | } |
1da177e4 | 730 | |
347c6a96 CS |
731 | wait_event(adapter->erp_ready_wq, |
732 | !list_empty(&adapter->erp_ready_head)); | |
287ac01a CS |
733 | if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) |
734 | break; | |
1da177e4 | 735 | |
287ac01a CS |
736 | if (!(atomic_read(&adapter->status) & |
737 | ZFCP_STATUS_ADAPTER_HOST_CON_INIT)) | |
738 | break; | |
1da177e4 | 739 | |
287ac01a CS |
740 | ssleep(sleep); |
741 | sleep *= 2; | |
1da177e4 LT |
742 | } |
743 | ||
805de8f4 | 744 | atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, |
287ac01a | 745 | &adapter->status); |
1da177e4 | 746 | |
287ac01a CS |
747 | if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK)) |
748 | return ZFCP_ERP_FAILED; | |
41fa2ada | 749 | |
287ac01a CS |
750 | if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) |
751 | zfcp_erp_enqueue_ptp_port(adapter); | |
1da177e4 | 752 | |
287ac01a | 753 | return ZFCP_ERP_SUCCEEDED; |
1da177e4 LT |
754 | } |
755 | ||
d5fcdced SM |
756 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf_xport( |
757 | struct zfcp_erp_action *act) | |
1da177e4 | 758 | { |
287ac01a CS |
759 | int ret; |
760 | struct zfcp_adapter *adapter = act->adapter; | |
1da177e4 | 761 | |
287ac01a CS |
762 | write_lock_irq(&adapter->erp_lock); |
763 | zfcp_erp_action_to_running(act); | |
764 | write_unlock_irq(&adapter->erp_lock); | |
765 | ||
766 | ret = zfcp_fsf_exchange_port_data(act); | |
767 | if (ret == -EOPNOTSUPP) | |
768 | return ZFCP_ERP_SUCCEEDED; | |
769 | if (ret) | |
770 | return ZFCP_ERP_FAILED; | |
771 | ||
ae0904f6 | 772 | zfcp_dbf_rec_run("erasox1", act); |
347c6a96 CS |
773 | wait_event(adapter->erp_ready_wq, |
774 | !list_empty(&adapter->erp_ready_head)); | |
ae0904f6 | 775 | zfcp_dbf_rec_run("erasox2", act); |
287ac01a CS |
776 | if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) |
777 | return ZFCP_ERP_FAILED; | |
778 | ||
779 | return ZFCP_ERP_SUCCEEDED; | |
1da177e4 LT |
780 | } |
781 | ||
d5fcdced SM |
782 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf( |
783 | struct zfcp_erp_action *act) | |
1da177e4 | 784 | { |
287ac01a CS |
785 | if (zfcp_erp_adapter_strat_fsf_xconf(act) == ZFCP_ERP_FAILED) |
786 | return ZFCP_ERP_FAILED; | |
1da177e4 | 787 | |
287ac01a CS |
788 | if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED) |
789 | return ZFCP_ERP_FAILED; | |
1da177e4 | 790 | |
c7b279ae | 791 | if (mempool_resize(act->adapter->pool.sr_data, |
11d83360 | 792 | act->adapter->stat_read_buf_num)) |
8d88cf3f CS |
793 | return ZFCP_ERP_FAILED; |
794 | ||
795 | if (mempool_resize(act->adapter->pool.status_read_req, | |
11d83360 | 796 | act->adapter->stat_read_buf_num)) |
8d88cf3f CS |
797 | return ZFCP_ERP_FAILED; |
798 | ||
64deb6ef | 799 | atomic_set(&act->adapter->stat_miss, act->adapter->stat_read_buf_num); |
287ac01a CS |
800 | if (zfcp_status_read_refill(act->adapter)) |
801 | return ZFCP_ERP_FAILED; | |
1da177e4 | 802 | |
287ac01a CS |
803 | return ZFCP_ERP_SUCCEEDED; |
804 | } | |
1da177e4 | 805 | |
cf13c082 | 806 | static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act) |
287ac01a | 807 | { |
287ac01a | 808 | struct zfcp_adapter *adapter = act->adapter; |
1da177e4 | 809 | |
287ac01a | 810 | /* close queues to ensure that buffers are not accessed by adapter */ |
564e1c86 | 811 | zfcp_qdio_close(adapter->qdio); |
287ac01a CS |
812 | zfcp_fsf_req_dismiss_all(adapter); |
813 | adapter->fsf_req_seq_no = 0; | |
55c770fa | 814 | zfcp_fc_wka_ports_force_offline(adapter->gs); |
b62a8d9b | 815 | /* all ports and LUNs are closed */ |
edaed859 | 816 | zfcp_erp_clear_adapter_status(adapter, ZFCP_STATUS_COMMON_OPEN); |
cf13c082 | 817 | |
805de8f4 | 818 | atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK | |
cf13c082 | 819 | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); |
1da177e4 LT |
820 | } |
821 | ||
d5fcdced SM |
822 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open( |
823 | struct zfcp_erp_action *act) | |
1da177e4 | 824 | { |
cf13c082 | 825 | struct zfcp_adapter *adapter = act->adapter; |
1da177e4 | 826 | |
3d63d3b4 | 827 | if (zfcp_qdio_open(adapter->qdio)) { |
805de8f4 | 828 | atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK | |
cf13c082 SS |
829 | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, |
830 | &adapter->status); | |
831 | return ZFCP_ERP_FAILED; | |
832 | } | |
287ac01a | 833 | |
cf13c082 SS |
834 | if (zfcp_erp_adapter_strategy_open_fsf(act)) { |
835 | zfcp_erp_adapter_strategy_close(act); | |
836 | return ZFCP_ERP_FAILED; | |
837 | } | |
838 | ||
805de8f4 | 839 | atomic_or(ZFCP_STATUS_COMMON_OPEN, &adapter->status); |
cf13c082 SS |
840 | |
841 | return ZFCP_ERP_SUCCEEDED; | |
842 | } | |
287ac01a | 843 | |
d5fcdced SM |
844 | static enum zfcp_erp_act_result zfcp_erp_adapter_strategy( |
845 | struct zfcp_erp_action *act) | |
cf13c082 SS |
846 | { |
847 | struct zfcp_adapter *adapter = act->adapter; | |
848 | ||
849 | if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN) { | |
850 | zfcp_erp_adapter_strategy_close(act); | |
851 | if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY) | |
852 | return ZFCP_ERP_EXIT; | |
853 | } | |
854 | ||
855 | if (zfcp_erp_adapter_strategy_open(act)) { | |
287ac01a | 856 | ssleep(8); |
cf13c082 SS |
857 | return ZFCP_ERP_FAILED; |
858 | } | |
1da177e4 | 859 | |
cf13c082 | 860 | return ZFCP_ERP_SUCCEEDED; |
1da177e4 LT |
861 | } |
862 | ||
d5fcdced SM |
863 | static enum zfcp_erp_act_result zfcp_erp_port_forced_strategy_close( |
864 | struct zfcp_erp_action *act) | |
1da177e4 | 865 | { |
287ac01a CS |
866 | int retval; |
867 | ||
868 | retval = zfcp_fsf_close_physical_port(act); | |
869 | if (retval == -ENOMEM) | |
870 | return ZFCP_ERP_NOMEM; | |
871 | act->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING; | |
872 | if (retval) | |
873 | return ZFCP_ERP_FAILED; | |
874 | ||
875 | return ZFCP_ERP_CONTINUES; | |
1da177e4 LT |
876 | } |
877 | ||
d5fcdced SM |
878 | static enum zfcp_erp_act_result zfcp_erp_port_forced_strategy( |
879 | struct zfcp_erp_action *erp_action) | |
287ac01a CS |
880 | { |
881 | struct zfcp_port *port = erp_action->port; | |
882 | int status = atomic_read(&port->status); | |
883 | ||
884 | switch (erp_action->step) { | |
885 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
287ac01a CS |
886 | if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) && |
887 | (status & ZFCP_STATUS_COMMON_OPEN)) | |
888 | return zfcp_erp_port_forced_strategy_close(erp_action); | |
889 | else | |
890 | return ZFCP_ERP_FAILED; | |
891 | ||
892 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: | |
ddb3e0c1 | 893 | if (!(status & ZFCP_STATUS_PORT_PHYS_OPEN)) |
287ac01a | 894 | return ZFCP_ERP_SUCCEEDED; |
0023beec SM |
895 | break; |
896 | case ZFCP_ERP_STEP_PORT_CLOSING: | |
897 | case ZFCP_ERP_STEP_PORT_OPENING: | |
898 | case ZFCP_ERP_STEP_LUN_CLOSING: | |
899 | case ZFCP_ERP_STEP_LUN_OPENING: | |
900 | /* NOP */ | |
901 | break; | |
287ac01a CS |
902 | } |
903 | return ZFCP_ERP_FAILED; | |
1da177e4 LT |
904 | } |
905 | ||
d5fcdced SM |
906 | static enum zfcp_erp_act_result zfcp_erp_port_strategy_close( |
907 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 908 | { |
287ac01a | 909 | int retval; |
1da177e4 | 910 | |
287ac01a CS |
911 | retval = zfcp_fsf_close_port(erp_action); |
912 | if (retval == -ENOMEM) | |
913 | return ZFCP_ERP_NOMEM; | |
914 | erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING; | |
915 | if (retval) | |
916 | return ZFCP_ERP_FAILED; | |
917 | return ZFCP_ERP_CONTINUES; | |
1da177e4 LT |
918 | } |
919 | ||
d5fcdced SM |
920 | static enum zfcp_erp_act_result zfcp_erp_port_strategy_open_port( |
921 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 922 | { |
287ac01a | 923 | int retval; |
1da177e4 | 924 | |
287ac01a CS |
925 | retval = zfcp_fsf_open_port(erp_action); |
926 | if (retval == -ENOMEM) | |
927 | return ZFCP_ERP_NOMEM; | |
928 | erp_action->step = ZFCP_ERP_STEP_PORT_OPENING; | |
929 | if (retval) | |
930 | return ZFCP_ERP_FAILED; | |
931 | return ZFCP_ERP_CONTINUES; | |
932 | } | |
1da177e4 | 933 | |
287ac01a | 934 | static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act) |
1da177e4 | 935 | { |
287ac01a CS |
936 | struct zfcp_adapter *adapter = act->adapter; |
937 | struct zfcp_port *port = act->port; | |
1da177e4 | 938 | |
287ac01a | 939 | if (port->wwpn != adapter->peer_wwpn) { |
edaed859 | 940 | zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); |
287ac01a CS |
941 | return ZFCP_ERP_FAILED; |
942 | } | |
943 | port->d_id = adapter->peer_d_id; | |
287ac01a CS |
944 | return zfcp_erp_port_strategy_open_port(act); |
945 | } | |
946 | ||
d5fcdced SM |
947 | static enum zfcp_erp_act_result zfcp_erp_port_strategy_open_common( |
948 | struct zfcp_erp_action *act) | |
287ac01a CS |
949 | { |
950 | struct zfcp_adapter *adapter = act->adapter; | |
951 | struct zfcp_port *port = act->port; | |
287ac01a CS |
952 | int p_status = atomic_read(&port->status); |
953 | ||
954 | switch (act->step) { | |
955 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
956 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: | |
957 | case ZFCP_ERP_STEP_PORT_CLOSING: | |
958 | if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) | |
959 | return zfcp_erp_open_ptp_port(act); | |
b98478d7 | 960 | if (!port->d_id) { |
934aeb58 | 961 | zfcp_fc_trigger_did_lookup(port); |
799b76d0 | 962 | return ZFCP_ERP_EXIT; |
287ac01a | 963 | } |
287ac01a CS |
964 | return zfcp_erp_port_strategy_open_port(act); |
965 | ||
966 | case ZFCP_ERP_STEP_PORT_OPENING: | |
967 | /* D_ID might have changed during open */ | |
5ab944f9 | 968 | if (p_status & ZFCP_STATUS_COMMON_OPEN) { |
934aeb58 CS |
969 | if (!port->d_id) { |
970 | zfcp_fc_trigger_did_lookup(port); | |
971 | return ZFCP_ERP_EXIT; | |
5ab944f9 | 972 | } |
934aeb58 | 973 | return ZFCP_ERP_SUCCEEDED; |
5ab944f9 | 974 | } |
ea460a81 SS |
975 | if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) { |
976 | port->d_id = 0; | |
f7bd7c36 | 977 | return ZFCP_ERP_FAILED; |
ea460a81 | 978 | } |
0023beec SM |
979 | /* no early return otherwise, continue after switch case */ |
980 | break; | |
981 | case ZFCP_ERP_STEP_LUN_CLOSING: | |
982 | case ZFCP_ERP_STEP_LUN_OPENING: | |
983 | /* NOP */ | |
984 | break; | |
287ac01a CS |
985 | } |
986 | return ZFCP_ERP_FAILED; | |
987 | } | |
988 | ||
d5fcdced SM |
989 | static enum zfcp_erp_act_result zfcp_erp_port_strategy( |
990 | struct zfcp_erp_action *erp_action) | |
287ac01a CS |
991 | { |
992 | struct zfcp_port *port = erp_action->port; | |
934aeb58 | 993 | int p_status = atomic_read(&port->status); |
287ac01a | 994 | |
934aeb58 CS |
995 | if ((p_status & ZFCP_STATUS_COMMON_NOESC) && |
996 | !(p_status & ZFCP_STATUS_COMMON_OPEN)) | |
5ab944f9 SS |
997 | goto close_init_done; |
998 | ||
287ac01a CS |
999 | switch (erp_action->step) { |
1000 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
934aeb58 | 1001 | if (p_status & ZFCP_STATUS_COMMON_OPEN) |
287ac01a | 1002 | return zfcp_erp_port_strategy_close(erp_action); |
1da177e4 LT |
1003 | break; |
1004 | ||
287ac01a | 1005 | case ZFCP_ERP_STEP_PORT_CLOSING: |
934aeb58 | 1006 | if (p_status & ZFCP_STATUS_COMMON_OPEN) |
287ac01a | 1007 | return ZFCP_ERP_FAILED; |
1da177e4 | 1008 | break; |
0023beec SM |
1009 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: |
1010 | case ZFCP_ERP_STEP_PORT_OPENING: | |
1011 | case ZFCP_ERP_STEP_LUN_CLOSING: | |
1012 | case ZFCP_ERP_STEP_LUN_OPENING: | |
1013 | /* NOP */ | |
1014 | break; | |
1da177e4 | 1015 | } |
5ab944f9 SS |
1016 | |
1017 | close_init_done: | |
287ac01a CS |
1018 | if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY) |
1019 | return ZFCP_ERP_EXIT; | |
1da177e4 | 1020 | |
5ab944f9 | 1021 | return zfcp_erp_port_strategy_open_common(erp_action); |
287ac01a CS |
1022 | } |
1023 | ||
b62a8d9b | 1024 | static void zfcp_erp_lun_strategy_clearstati(struct scsi_device *sdev) |
287ac01a | 1025 | { |
b62a8d9b CS |
1026 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1027 | ||
805de8f4 | 1028 | atomic_andnot(ZFCP_STATUS_COMMON_ACCESS_DENIED, |
b62a8d9b | 1029 | &zfcp_sdev->status); |
287ac01a CS |
1030 | } |
1031 | ||
d5fcdced SM |
1032 | static enum zfcp_erp_act_result zfcp_erp_lun_strategy_close( |
1033 | struct zfcp_erp_action *erp_action) | |
287ac01a | 1034 | { |
b62a8d9b | 1035 | int retval = zfcp_fsf_close_lun(erp_action); |
287ac01a CS |
1036 | if (retval == -ENOMEM) |
1037 | return ZFCP_ERP_NOMEM; | |
b62a8d9b | 1038 | erp_action->step = ZFCP_ERP_STEP_LUN_CLOSING; |
287ac01a CS |
1039 | if (retval) |
1040 | return ZFCP_ERP_FAILED; | |
1041 | return ZFCP_ERP_CONTINUES; | |
1042 | } | |
1043 | ||
d5fcdced SM |
1044 | static enum zfcp_erp_act_result zfcp_erp_lun_strategy_open( |
1045 | struct zfcp_erp_action *erp_action) | |
287ac01a | 1046 | { |
b62a8d9b | 1047 | int retval = zfcp_fsf_open_lun(erp_action); |
287ac01a CS |
1048 | if (retval == -ENOMEM) |
1049 | return ZFCP_ERP_NOMEM; | |
b62a8d9b | 1050 | erp_action->step = ZFCP_ERP_STEP_LUN_OPENING; |
287ac01a CS |
1051 | if (retval) |
1052 | return ZFCP_ERP_FAILED; | |
1053 | return ZFCP_ERP_CONTINUES; | |
1da177e4 LT |
1054 | } |
1055 | ||
d5fcdced SM |
1056 | static enum zfcp_erp_act_result zfcp_erp_lun_strategy( |
1057 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 1058 | { |
b62a8d9b CS |
1059 | struct scsi_device *sdev = erp_action->sdev; |
1060 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); | |
287ac01a CS |
1061 | |
1062 | switch (erp_action->step) { | |
1063 | case ZFCP_ERP_STEP_UNINITIALIZED: | |
b62a8d9b CS |
1064 | zfcp_erp_lun_strategy_clearstati(sdev); |
1065 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) | |
1066 | return zfcp_erp_lun_strategy_close(erp_action); | |
3505144e SM |
1067 | /* already closed */ |
1068 | /* fall through */ | |
b62a8d9b CS |
1069 | case ZFCP_ERP_STEP_LUN_CLOSING: |
1070 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) | |
287ac01a CS |
1071 | return ZFCP_ERP_FAILED; |
1072 | if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY) | |
1073 | return ZFCP_ERP_EXIT; | |
b62a8d9b | 1074 | return zfcp_erp_lun_strategy_open(erp_action); |
287ac01a | 1075 | |
b62a8d9b CS |
1076 | case ZFCP_ERP_STEP_LUN_OPENING: |
1077 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) | |
287ac01a | 1078 | return ZFCP_ERP_SUCCEEDED; |
0023beec SM |
1079 | break; |
1080 | case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: | |
1081 | case ZFCP_ERP_STEP_PORT_CLOSING: | |
1082 | case ZFCP_ERP_STEP_PORT_OPENING: | |
1083 | /* NOP */ | |
1084 | break; | |
287ac01a CS |
1085 | } |
1086 | return ZFCP_ERP_FAILED; | |
1da177e4 LT |
1087 | } |
1088 | ||
d5fcdced SM |
1089 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_lun( |
1090 | struct scsi_device *sdev, enum zfcp_erp_act_result result) | |
1da177e4 | 1091 | { |
b62a8d9b CS |
1092 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1093 | ||
1da177e4 LT |
1094 | switch (result) { |
1095 | case ZFCP_ERP_SUCCEEDED : | |
b62a8d9b CS |
1096 | atomic_set(&zfcp_sdev->erp_counter, 0); |
1097 | zfcp_erp_lun_unblock(sdev); | |
1da177e4 LT |
1098 | break; |
1099 | case ZFCP_ERP_FAILED : | |
b62a8d9b CS |
1100 | atomic_inc(&zfcp_sdev->erp_counter); |
1101 | if (atomic_read(&zfcp_sdev->erp_counter) > ZFCP_MAX_ERPS) { | |
1102 | dev_err(&zfcp_sdev->port->adapter->ccw_device->dev, | |
1103 | "ERP failed for LUN 0x%016Lx on " | |
ff3b24fa | 1104 | "port 0x%016Lx\n", |
b62a8d9b CS |
1105 | (unsigned long long)zfcp_scsi_dev_lun(sdev), |
1106 | (unsigned long long)zfcp_sdev->port->wwpn); | |
edaed859 SS |
1107 | zfcp_erp_set_lun_status(sdev, |
1108 | ZFCP_STATUS_COMMON_ERP_FAILED); | |
ff3b24fa | 1109 | } |
1da177e4 | 1110 | break; |
d5fcdced SM |
1111 | case ZFCP_ERP_CONTINUES: |
1112 | case ZFCP_ERP_EXIT: | |
1113 | case ZFCP_ERP_DISMISSED: | |
1114 | case ZFCP_ERP_NOMEM: | |
1115 | /* NOP */ | |
1116 | break; | |
1da177e4 LT |
1117 | } |
1118 | ||
b62a8d9b CS |
1119 | if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { |
1120 | zfcp_erp_lun_block(sdev, 0); | |
1da177e4 LT |
1121 | result = ZFCP_ERP_EXIT; |
1122 | } | |
1da177e4 LT |
1123 | return result; |
1124 | } | |
1125 | ||
d5fcdced SM |
1126 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_port( |
1127 | struct zfcp_port *port, enum zfcp_erp_act_result result) | |
1da177e4 | 1128 | { |
1da177e4 LT |
1129 | switch (result) { |
1130 | case ZFCP_ERP_SUCCEEDED : | |
1131 | atomic_set(&port->erp_counter, 0); | |
1132 | zfcp_erp_port_unblock(port); | |
1133 | break; | |
287ac01a | 1134 | |
1da177e4 | 1135 | case ZFCP_ERP_FAILED : |
287ac01a | 1136 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) { |
cc8c2829 SS |
1137 | zfcp_erp_port_block(port, 0); |
1138 | result = ZFCP_ERP_EXIT; | |
1139 | } | |
1da177e4 | 1140 | atomic_inc(&port->erp_counter); |
ff3b24fa CS |
1141 | if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) { |
1142 | dev_err(&port->adapter->ccw_device->dev, | |
1143 | "ERP failed for remote port 0x%016Lx\n", | |
7ba58c9c | 1144 | (unsigned long long)port->wwpn); |
edaed859 SS |
1145 | zfcp_erp_set_port_status(port, |
1146 | ZFCP_STATUS_COMMON_ERP_FAILED); | |
ff3b24fa | 1147 | } |
1da177e4 | 1148 | break; |
d5fcdced SM |
1149 | case ZFCP_ERP_CONTINUES: |
1150 | case ZFCP_ERP_EXIT: | |
1151 | case ZFCP_ERP_DISMISSED: | |
1152 | case ZFCP_ERP_NOMEM: | |
1153 | /* NOP */ | |
1154 | break; | |
1da177e4 LT |
1155 | } |
1156 | ||
287ac01a CS |
1157 | if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { |
1158 | zfcp_erp_port_block(port, 0); | |
1da177e4 LT |
1159 | result = ZFCP_ERP_EXIT; |
1160 | } | |
1da177e4 LT |
1161 | return result; |
1162 | } | |
1163 | ||
d5fcdced SM |
1164 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_adapter( |
1165 | struct zfcp_adapter *adapter, enum zfcp_erp_act_result result) | |
1da177e4 | 1166 | { |
1da177e4 LT |
1167 | switch (result) { |
1168 | case ZFCP_ERP_SUCCEEDED : | |
1169 | atomic_set(&adapter->erp_counter, 0); | |
1170 | zfcp_erp_adapter_unblock(adapter); | |
1171 | break; | |
287ac01a | 1172 | |
1da177e4 LT |
1173 | case ZFCP_ERP_FAILED : |
1174 | atomic_inc(&adapter->erp_counter); | |
ff3b24fa CS |
1175 | if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) { |
1176 | dev_err(&adapter->ccw_device->dev, | |
1177 | "ERP cannot recover an error " | |
1178 | "on the FCP device\n"); | |
edaed859 SS |
1179 | zfcp_erp_set_adapter_status(adapter, |
1180 | ZFCP_STATUS_COMMON_ERP_FAILED); | |
ff3b24fa | 1181 | } |
1da177e4 | 1182 | break; |
d5fcdced SM |
1183 | case ZFCP_ERP_CONTINUES: |
1184 | case ZFCP_ERP_EXIT: | |
1185 | case ZFCP_ERP_DISMISSED: | |
1186 | case ZFCP_ERP_NOMEM: | |
1187 | /* NOP */ | |
1188 | break; | |
1da177e4 LT |
1189 | } |
1190 | ||
287ac01a CS |
1191 | if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { |
1192 | zfcp_erp_adapter_block(adapter, 0); | |
1da177e4 LT |
1193 | result = ZFCP_ERP_EXIT; |
1194 | } | |
1da177e4 LT |
1195 | return result; |
1196 | } | |
1197 | ||
d5fcdced SM |
1198 | static enum zfcp_erp_act_result zfcp_erp_strategy_check_target( |
1199 | struct zfcp_erp_action *erp_action, enum zfcp_erp_act_result result) | |
5f852be9 | 1200 | { |
287ac01a CS |
1201 | struct zfcp_adapter *adapter = erp_action->adapter; |
1202 | struct zfcp_port *port = erp_action->port; | |
b62a8d9b | 1203 | struct scsi_device *sdev = erp_action->sdev; |
287ac01a | 1204 | |
df91eefd | 1205 | switch (erp_action->type) { |
287ac01a | 1206 | |
b62a8d9b CS |
1207 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1208 | result = zfcp_erp_strategy_check_lun(sdev, result); | |
287ac01a CS |
1209 | break; |
1210 | ||
1211 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
1212 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
1213 | result = zfcp_erp_strategy_check_port(port, result); | |
1214 | break; | |
1215 | ||
1216 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
1217 | result = zfcp_erp_strategy_check_adapter(adapter, result); | |
1218 | break; | |
1219 | } | |
1220 | return result; | |
5f852be9 CS |
1221 | } |
1222 | ||
287ac01a | 1223 | static int zfcp_erp_strat_change_det(atomic_t *target_status, u32 erp_status) |
5f852be9 | 1224 | { |
287ac01a | 1225 | int status = atomic_read(target_status); |
5f852be9 | 1226 | |
287ac01a CS |
1227 | if ((status & ZFCP_STATUS_COMMON_RUNNING) && |
1228 | (erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY)) | |
1229 | return 1; /* take it online */ | |
5f852be9 | 1230 | |
287ac01a CS |
1231 | if (!(status & ZFCP_STATUS_COMMON_RUNNING) && |
1232 | !(erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY)) | |
1233 | return 1; /* take it offline */ | |
1234 | ||
1235 | return 0; | |
5f852be9 CS |
1236 | } |
1237 | ||
d5fcdced SM |
1238 | static enum zfcp_erp_act_result zfcp_erp_strategy_statechange( |
1239 | struct zfcp_erp_action *act, enum zfcp_erp_act_result result) | |
1da177e4 | 1240 | { |
df91eefd | 1241 | enum zfcp_erp_act_type type = act->type; |
287ac01a CS |
1242 | struct zfcp_adapter *adapter = act->adapter; |
1243 | struct zfcp_port *port = act->port; | |
b62a8d9b CS |
1244 | struct scsi_device *sdev = act->sdev; |
1245 | struct zfcp_scsi_dev *zfcp_sdev; | |
287ac01a | 1246 | u32 erp_status = act->status; |
1da177e4 | 1247 | |
df91eefd | 1248 | switch (type) { |
1da177e4 | 1249 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
287ac01a CS |
1250 | if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) { |
1251 | _zfcp_erp_adapter_reopen(adapter, | |
1252 | ZFCP_STATUS_COMMON_ERP_FAILED, | |
ea4a3a6a | 1253 | "ersscg1"); |
287ac01a CS |
1254 | return ZFCP_ERP_EXIT; |
1255 | } | |
1da177e4 LT |
1256 | break; |
1257 | ||
1258 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
1da177e4 | 1259 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
287ac01a CS |
1260 | if (zfcp_erp_strat_change_det(&port->status, erp_status)) { |
1261 | _zfcp_erp_port_reopen(port, | |
1262 | ZFCP_STATUS_COMMON_ERP_FAILED, | |
ea4a3a6a | 1263 | "ersscg2"); |
287ac01a CS |
1264 | return ZFCP_ERP_EXIT; |
1265 | } | |
1da177e4 LT |
1266 | break; |
1267 | ||
b62a8d9b CS |
1268 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1269 | zfcp_sdev = sdev_to_zfcp(sdev); | |
1270 | if (zfcp_erp_strat_change_det(&zfcp_sdev->status, erp_status)) { | |
1271 | _zfcp_erp_lun_reopen(sdev, | |
1272 | ZFCP_STATUS_COMMON_ERP_FAILED, | |
ea4a3a6a | 1273 | "ersscg3", 0); |
287ac01a CS |
1274 | return ZFCP_ERP_EXIT; |
1275 | } | |
1da177e4 LT |
1276 | break; |
1277 | } | |
d5fcdced | 1278 | return result; |
1da177e4 LT |
1279 | } |
1280 | ||
287ac01a | 1281 | static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) |
1da177e4 | 1282 | { |
287ac01a | 1283 | struct zfcp_adapter *adapter = erp_action->adapter; |
b62a8d9b | 1284 | struct zfcp_scsi_dev *zfcp_sdev; |
1da177e4 | 1285 | |
287ac01a CS |
1286 | adapter->erp_total_count--; |
1287 | if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { | |
1288 | adapter->erp_low_mem_count--; | |
1289 | erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; | |
507e4969 | 1290 | } |
1da177e4 | 1291 | |
287ac01a | 1292 | list_del(&erp_action->list); |
ae0904f6 | 1293 | zfcp_dbf_rec_run("eractd1", erp_action); |
1da177e4 | 1294 | |
df91eefd | 1295 | switch (erp_action->type) { |
b62a8d9b CS |
1296 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1297 | zfcp_sdev = sdev_to_zfcp(erp_action->sdev); | |
805de8f4 | 1298 | atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE, |
b62a8d9b | 1299 | &zfcp_sdev->status); |
287ac01a | 1300 | break; |
1da177e4 | 1301 | |
287ac01a CS |
1302 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: |
1303 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
805de8f4 | 1304 | atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE, |
287ac01a CS |
1305 | &erp_action->port->status); |
1306 | break; | |
1da177e4 | 1307 | |
287ac01a | 1308 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
805de8f4 | 1309 | atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE, |
287ac01a CS |
1310 | &erp_action->adapter->status); |
1311 | break; | |
1312 | } | |
1da177e4 LT |
1313 | } |
1314 | ||
6f2ce1c6 SM |
1315 | /** |
1316 | * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery | |
1317 | * @port: zfcp_port whose fc_rport we should try to unblock | |
1318 | */ | |
1319 | static void zfcp_erp_try_rport_unblock(struct zfcp_port *port) | |
1320 | { | |
1321 | unsigned long flags; | |
1322 | struct zfcp_adapter *adapter = port->adapter; | |
1323 | int port_status; | |
1324 | struct Scsi_Host *shost = adapter->scsi_host; | |
1325 | struct scsi_device *sdev; | |
1326 | ||
1327 | write_lock_irqsave(&adapter->erp_lock, flags); | |
1328 | port_status = atomic_read(&port->status); | |
1329 | if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || | |
1330 | (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE | | |
1331 | ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) { | |
1332 | /* new ERP of severity >= port triggered elsewhere meanwhile or | |
1333 | * local link down (adapter erp_failed but not clear unblock) | |
1334 | */ | |
1335 | zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action); | |
1336 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1337 | return; | |
1338 | } | |
1339 | spin_lock(shost->host_lock); | |
1340 | __shost_for_each_device(sdev, shost) { | |
1341 | struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev); | |
1342 | int lun_status; | |
1343 | ||
fe67888f SM |
1344 | if (sdev->sdev_state == SDEV_DEL || |
1345 | sdev->sdev_state == SDEV_CANCEL) | |
1346 | continue; | |
6f2ce1c6 SM |
1347 | if (zsdev->port != port) |
1348 | continue; | |
1349 | /* LUN under port of interest */ | |
1350 | lun_status = atomic_read(&zsdev->status); | |
1351 | if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0) | |
1352 | continue; /* unblock rport despite failed LUNs */ | |
1353 | /* LUN recovery not given up yet [maybe follow-up pending] */ | |
1354 | if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || | |
1355 | (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) { | |
1356 | /* LUN blocked: | |
1357 | * not yet unblocked [LUN recovery pending] | |
1358 | * or meanwhile blocked [new LUN recovery triggered] | |
1359 | */ | |
1360 | zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action); | |
1361 | spin_unlock(shost->host_lock); | |
1362 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1363 | return; | |
1364 | } | |
1365 | } | |
1366 | /* now port has no child or all children have completed recovery, | |
1367 | * and no ERP of severity >= port was meanwhile triggered elsewhere | |
1368 | */ | |
1369 | zfcp_scsi_schedule_rport_register(port); | |
1370 | spin_unlock(shost->host_lock); | |
1371 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1372 | } | |
1373 | ||
d5fcdced SM |
1374 | static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, |
1375 | enum zfcp_erp_act_result result) | |
1da177e4 | 1376 | { |
287ac01a CS |
1377 | struct zfcp_adapter *adapter = act->adapter; |
1378 | struct zfcp_port *port = act->port; | |
b62a8d9b | 1379 | struct scsi_device *sdev = act->sdev; |
1da177e4 | 1380 | |
df91eefd | 1381 | switch (act->type) { |
b62a8d9b | 1382 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
fdbd1c5e | 1383 | if (!(act->status & ZFCP_STATUS_ERP_NO_REF)) |
b62a8d9b | 1384 | scsi_device_put(sdev); |
6f2ce1c6 | 1385 | zfcp_erp_try_rport_unblock(port); |
287ac01a | 1386 | break; |
1da177e4 | 1387 | |
287ac01a | 1388 | case ZFCP_ERP_ACTION_REOPEN_PORT: |
4eeaa4f3 SM |
1389 | /* This switch case might also happen after a forced reopen |
1390 | * was successfully done and thus overwritten with a new | |
1391 | * non-forced reopen at `ersfs_2'. In this case, we must not | |
1392 | * do the clean-up of the non-forced version. | |
1393 | */ | |
1394 | if (act->step != ZFCP_ERP_STEP_UNINITIALIZED) | |
1395 | if (result == ZFCP_ERP_SUCCEEDED) | |
6f2ce1c6 | 1396 | zfcp_erp_try_rport_unblock(port); |
5767620c CS |
1397 | /* fall through */ |
1398 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
615f59e0 | 1399 | put_device(&port->dev); |
287ac01a CS |
1400 | break; |
1401 | ||
1402 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: | |
a2fa0aed | 1403 | if (result == ZFCP_ERP_SUCCEEDED) { |
bd43a42b | 1404 | register_service_level(&adapter->service_level); |
43f60cbd | 1405 | zfcp_fc_conditional_port_scan(adapter); |
038d9446 | 1406 | queue_work(adapter->work_queue, &adapter->ns_up_work); |
a2fa0aed CS |
1407 | } else |
1408 | unregister_service_level(&adapter->service_level); | |
038d9446 | 1409 | |
f3450c7b | 1410 | kref_put(&adapter->ref, zfcp_adapter_release); |
287ac01a CS |
1411 | break; |
1412 | } | |
1da177e4 LT |
1413 | } |
1414 | ||
d5fcdced SM |
1415 | static enum zfcp_erp_act_result zfcp_erp_strategy_do_action( |
1416 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 1417 | { |
df91eefd | 1418 | switch (erp_action->type) { |
287ac01a CS |
1419 | case ZFCP_ERP_ACTION_REOPEN_ADAPTER: |
1420 | return zfcp_erp_adapter_strategy(erp_action); | |
1421 | case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: | |
1422 | return zfcp_erp_port_forced_strategy(erp_action); | |
1423 | case ZFCP_ERP_ACTION_REOPEN_PORT: | |
1424 | return zfcp_erp_port_strategy(erp_action); | |
b62a8d9b CS |
1425 | case ZFCP_ERP_ACTION_REOPEN_LUN: |
1426 | return zfcp_erp_lun_strategy(erp_action); | |
287ac01a CS |
1427 | } |
1428 | return ZFCP_ERP_FAILED; | |
1da177e4 LT |
1429 | } |
1430 | ||
d5fcdced SM |
1431 | static enum zfcp_erp_act_result zfcp_erp_strategy( |
1432 | struct zfcp_erp_action *erp_action) | |
1da177e4 | 1433 | { |
d5fcdced | 1434 | enum zfcp_erp_act_result result; |
287ac01a | 1435 | unsigned long flags; |
ecf0c772 | 1436 | struct zfcp_adapter *adapter = erp_action->adapter; |
1da177e4 | 1437 | |
f3450c7b | 1438 | kref_get(&adapter->ref); |
1da177e4 | 1439 | |
f3450c7b | 1440 | write_lock_irqsave(&adapter->erp_lock, flags); |
287ac01a | 1441 | zfcp_erp_strategy_check_fsfreq(erp_action); |
1da177e4 | 1442 | |
287ac01a CS |
1443 | if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) { |
1444 | zfcp_erp_action_dequeue(erp_action); | |
d5fcdced | 1445 | result = ZFCP_ERP_DISMISSED; |
287ac01a | 1446 | goto unlock; |
22753fa5 | 1447 | } |
1da177e4 | 1448 | |
9c785d94 | 1449 | if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) { |
d5fcdced | 1450 | result = ZFCP_ERP_FAILED; |
9c785d94 CS |
1451 | goto check_target; |
1452 | } | |
1453 | ||
2f8f3ed5 | 1454 | zfcp_erp_action_to_running(erp_action); |
1da177e4 | 1455 | |
287ac01a | 1456 | /* no lock to allow for blocking operations */ |
ecf0c772 | 1457 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
d5fcdced | 1458 | result = zfcp_erp_strategy_do_action(erp_action); |
ecf0c772 | 1459 | write_lock_irqsave(&adapter->erp_lock, flags); |
1da177e4 | 1460 | |
287ac01a | 1461 | if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) |
d5fcdced | 1462 | result = ZFCP_ERP_CONTINUES; |
cc8c2829 | 1463 | |
d5fcdced | 1464 | switch (result) { |
287ac01a CS |
1465 | case ZFCP_ERP_NOMEM: |
1466 | if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) { | |
1467 | ++adapter->erp_low_mem_count; | |
1468 | erp_action->status |= ZFCP_STATUS_ERP_LOWMEM; | |
1da177e4 | 1469 | } |
287ac01a | 1470 | if (adapter->erp_total_count == adapter->erp_low_mem_count) |
ea4a3a6a | 1471 | _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1"); |
287ac01a CS |
1472 | else { |
1473 | zfcp_erp_strategy_memwait(erp_action); | |
d5fcdced | 1474 | result = ZFCP_ERP_CONTINUES; |
1da177e4 | 1475 | } |
287ac01a | 1476 | goto unlock; |
1da177e4 | 1477 | |
287ac01a CS |
1478 | case ZFCP_ERP_CONTINUES: |
1479 | if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { | |
1480 | --adapter->erp_low_mem_count; | |
1481 | erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; | |
1da177e4 | 1482 | } |
287ac01a | 1483 | goto unlock; |
d5fcdced SM |
1484 | case ZFCP_ERP_SUCCEEDED: |
1485 | case ZFCP_ERP_FAILED: | |
1486 | case ZFCP_ERP_EXIT: | |
1487 | case ZFCP_ERP_DISMISSED: | |
1488 | /* NOP */ | |
1489 | break; | |
1da177e4 LT |
1490 | } |
1491 | ||
9c785d94 | 1492 | check_target: |
d5fcdced | 1493 | result = zfcp_erp_strategy_check_target(erp_action, result); |
287ac01a | 1494 | zfcp_erp_action_dequeue(erp_action); |
d5fcdced SM |
1495 | result = zfcp_erp_strategy_statechange(erp_action, result); |
1496 | if (result == ZFCP_ERP_EXIT) | |
287ac01a | 1497 | goto unlock; |
d5fcdced | 1498 | if (result == ZFCP_ERP_SUCCEEDED) |
85600f7f | 1499 | zfcp_erp_strategy_followup_success(erp_action); |
d5fcdced | 1500 | if (result == ZFCP_ERP_FAILED) |
85600f7f | 1501 | zfcp_erp_strategy_followup_failed(erp_action); |
1da177e4 | 1502 | |
287ac01a | 1503 | unlock: |
ecf0c772 | 1504 | write_unlock_irqrestore(&adapter->erp_lock, flags); |
1da177e4 | 1505 | |
d5fcdced SM |
1506 | if (result != ZFCP_ERP_CONTINUES) |
1507 | zfcp_erp_action_cleanup(erp_action, result); | |
1da177e4 | 1508 | |
f3450c7b | 1509 | kref_put(&adapter->ref, zfcp_adapter_release); |
d5fcdced | 1510 | return result; |
1da177e4 LT |
1511 | } |
1512 | ||
287ac01a | 1513 | static int zfcp_erp_thread(void *data) |
1da177e4 | 1514 | { |
287ac01a CS |
1515 | struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; |
1516 | struct list_head *next; | |
1517 | struct zfcp_erp_action *act; | |
1518 | unsigned long flags; | |
94ab4b38 | 1519 | |
347c6a96 | 1520 | for (;;) { |
347c6a96 CS |
1521 | wait_event_interruptible(adapter->erp_ready_wq, |
1522 | !list_empty(&adapter->erp_ready_head) || | |
1523 | kthread_should_stop()); | |
94ab4b38 | 1524 | |
347c6a96 CS |
1525 | if (kthread_should_stop()) |
1526 | break; | |
1527 | ||
287ac01a CS |
1528 | write_lock_irqsave(&adapter->erp_lock, flags); |
1529 | next = adapter->erp_ready_head.next; | |
1530 | write_unlock_irqrestore(&adapter->erp_lock, flags); | |
1da177e4 | 1531 | |
287ac01a CS |
1532 | if (next != &adapter->erp_ready_head) { |
1533 | act = list_entry(next, struct zfcp_erp_action, list); | |
1da177e4 | 1534 | |
287ac01a CS |
1535 | /* there is more to come after dismission, no notify */ |
1536 | if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED) | |
1537 | zfcp_erp_wakeup(adapter); | |
1da177e4 | 1538 | } |
1da177e4 LT |
1539 | } |
1540 | ||
287ac01a CS |
1541 | return 0; |
1542 | } | |
1da177e4 | 1543 | |
287ac01a CS |
1544 | /** |
1545 | * zfcp_erp_thread_setup - Start ERP thread for adapter | |
1546 | * @adapter: Adapter to start the ERP thread for | |
1547 | * | |
64eba384 | 1548 | * Return: 0 on success, or error code from kthread_run(). |
287ac01a CS |
1549 | */ |
1550 | int zfcp_erp_thread_setup(struct zfcp_adapter *adapter) | |
1551 | { | |
347c6a96 | 1552 | struct task_struct *thread; |
1da177e4 | 1553 | |
347c6a96 CS |
1554 | thread = kthread_run(zfcp_erp_thread, adapter, "zfcperp%s", |
1555 | dev_name(&adapter->ccw_device->dev)); | |
1556 | if (IS_ERR(thread)) { | |
287ac01a | 1557 | dev_err(&adapter->ccw_device->dev, |
ff3b24fa | 1558 | "Creating an ERP thread for the FCP device failed.\n"); |
347c6a96 | 1559 | return PTR_ERR(thread); |
1da177e4 | 1560 | } |
347c6a96 CS |
1561 | |
1562 | adapter->erp_thread = thread; | |
287ac01a CS |
1563 | return 0; |
1564 | } | |
1da177e4 | 1565 | |
287ac01a CS |
1566 | /** |
1567 | * zfcp_erp_thread_kill - Stop ERP thread. | |
1568 | * @adapter: Adapter where the ERP thread should be stopped. | |
1569 | * | |
1570 | * The caller of this routine ensures that the specified adapter has | |
1571 | * been shut down and that this operation has been completed. Thus, | |
1572 | * there are no pending erp_actions which would need to be handled | |
1573 | * here. | |
1574 | */ | |
1575 | void zfcp_erp_thread_kill(struct zfcp_adapter *adapter) | |
1576 | { | |
347c6a96 CS |
1577 | kthread_stop(adapter->erp_thread); |
1578 | adapter->erp_thread = NULL; | |
143bb6bf CS |
1579 | WARN_ON(!list_empty(&adapter->erp_ready_head)); |
1580 | WARN_ON(!list_empty(&adapter->erp_running_head)); | |
1da177e4 LT |
1581 | } |
1582 | ||
287ac01a | 1583 | /** |
edaed859 SS |
1584 | * zfcp_erp_wait - wait for completion of error recovery on an adapter |
1585 | * @adapter: adapter for which to wait for completion of its error recovery | |
287ac01a | 1586 | */ |
edaed859 | 1587 | void zfcp_erp_wait(struct zfcp_adapter *adapter) |
1da177e4 | 1588 | { |
edaed859 SS |
1589 | wait_event(adapter->erp_done_wqh, |
1590 | !(atomic_read(&adapter->status) & | |
1591 | ZFCP_STATUS_ADAPTER_ERP_PENDING)); | |
287ac01a | 1592 | } |
1da177e4 | 1593 | |
287ac01a | 1594 | /** |
edaed859 SS |
1595 | * zfcp_erp_set_adapter_status - set adapter status bits |
1596 | * @adapter: adapter to change the status | |
1597 | * @mask: status bits to change | |
1598 | * | |
1599 | * Changes in common status bits are propagated to attached ports and LUNs. | |
287ac01a | 1600 | */ |
edaed859 | 1601 | void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) |
287ac01a | 1602 | { |
edaed859 SS |
1603 | struct zfcp_port *port; |
1604 | struct scsi_device *sdev; | |
1605 | unsigned long flags; | |
1606 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; | |
1da177e4 | 1607 | |
805de8f4 | 1608 | atomic_or(mask, &adapter->status); |
1da177e4 | 1609 | |
edaed859 SS |
1610 | if (!common_mask) |
1611 | return; | |
1612 | ||
1613 | read_lock_irqsave(&adapter->port_list_lock, flags); | |
1614 | list_for_each_entry(port, &adapter->port_list, list) | |
805de8f4 | 1615 | atomic_or(common_mask, &port->status); |
edaed859 SS |
1616 | read_unlock_irqrestore(&adapter->port_list_lock, flags); |
1617 | ||
924dd584 MP |
1618 | spin_lock_irqsave(adapter->scsi_host->host_lock, flags); |
1619 | __shost_for_each_device(sdev, adapter->scsi_host) | |
805de8f4 | 1620 | atomic_or(common_mask, &sdev_to_zfcp(sdev)->status); |
924dd584 | 1621 | spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); |
287ac01a | 1622 | } |
1da177e4 | 1623 | |
287ac01a | 1624 | /** |
edaed859 | 1625 | * zfcp_erp_clear_adapter_status - clear adapter status bits |
287ac01a | 1626 | * @adapter: adapter to change the status |
287ac01a | 1627 | * @mask: status bits to change |
287ac01a | 1628 | * |
b62a8d9b | 1629 | * Changes in common status bits are propagated to attached ports and LUNs. |
287ac01a | 1630 | */ |
edaed859 | 1631 | void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) |
1da177e4 | 1632 | { |
1da177e4 | 1633 | struct zfcp_port *port; |
edaed859 | 1634 | struct scsi_device *sdev; |
ecf0c772 | 1635 | unsigned long flags; |
287ac01a | 1636 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; |
edaed859 | 1637 | u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; |
1da177e4 | 1638 | |
805de8f4 | 1639 | atomic_andnot(mask, &adapter->status); |
edaed859 SS |
1640 | |
1641 | if (!common_mask) | |
1642 | return; | |
1643 | ||
1644 | if (clear_counter) | |
1645 | atomic_set(&adapter->erp_counter, 0); | |
1646 | ||
1647 | read_lock_irqsave(&adapter->port_list_lock, flags); | |
1648 | list_for_each_entry(port, &adapter->port_list, list) { | |
805de8f4 | 1649 | atomic_andnot(common_mask, &port->status); |
edaed859 SS |
1650 | if (clear_counter) |
1651 | atomic_set(&port->erp_counter, 0); | |
287ac01a | 1652 | } |
edaed859 | 1653 | read_unlock_irqrestore(&adapter->port_list_lock, flags); |
287ac01a | 1654 | |
924dd584 MP |
1655 | spin_lock_irqsave(adapter->scsi_host->host_lock, flags); |
1656 | __shost_for_each_device(sdev, adapter->scsi_host) { | |
805de8f4 | 1657 | atomic_andnot(common_mask, &sdev_to_zfcp(sdev)->status); |
edaed859 SS |
1658 | if (clear_counter) |
1659 | atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); | |
ecf0c772 | 1660 | } |
924dd584 | 1661 | spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); |
1da177e4 LT |
1662 | } |
1663 | ||
287ac01a | 1664 | /** |
edaed859 SS |
1665 | * zfcp_erp_set_port_status - set port status bits |
1666 | * @port: port to change the status | |
287ac01a | 1667 | * @mask: status bits to change |
287ac01a | 1668 | * |
b62a8d9b | 1669 | * Changes in common status bits are propagated to attached LUNs. |
287ac01a | 1670 | */ |
edaed859 | 1671 | void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask) |
1da177e4 | 1672 | { |
b62a8d9b | 1673 | struct scsi_device *sdev; |
287ac01a | 1674 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; |
924dd584 | 1675 | unsigned long flags; |
1da177e4 | 1676 | |
805de8f4 | 1677 | atomic_or(mask, &port->status); |
1da177e4 | 1678 | |
edaed859 SS |
1679 | if (!common_mask) |
1680 | return; | |
1681 | ||
924dd584 MP |
1682 | spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); |
1683 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
edaed859 | 1684 | if (sdev_to_zfcp(sdev)->port == port) |
805de8f4 | 1685 | atomic_or(common_mask, |
edaed859 | 1686 | &sdev_to_zfcp(sdev)->status); |
924dd584 | 1687 | spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); |
1da177e4 LT |
1688 | } |
1689 | ||
287ac01a | 1690 | /** |
edaed859 SS |
1691 | * zfcp_erp_clear_port_status - clear port status bits |
1692 | * @port: adapter to change the status | |
287ac01a | 1693 | * @mask: status bits to change |
edaed859 SS |
1694 | * |
1695 | * Changes in common status bits are propagated to attached LUNs. | |
287ac01a | 1696 | */ |
edaed859 | 1697 | void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) |
1da177e4 | 1698 | { |
edaed859 SS |
1699 | struct scsi_device *sdev; |
1700 | u32 common_mask = mask & ZFCP_COMMON_FLAGS; | |
1701 | u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; | |
924dd584 | 1702 | unsigned long flags; |
edaed859 | 1703 | |
805de8f4 | 1704 | atomic_andnot(mask, &port->status); |
edaed859 SS |
1705 | |
1706 | if (!common_mask) | |
1707 | return; | |
b62a8d9b | 1708 | |
edaed859 SS |
1709 | if (clear_counter) |
1710 | atomic_set(&port->erp_counter, 0); | |
1711 | ||
924dd584 MP |
1712 | spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); |
1713 | __shost_for_each_device(sdev, port->adapter->scsi_host) | |
edaed859 | 1714 | if (sdev_to_zfcp(sdev)->port == port) { |
805de8f4 | 1715 | atomic_andnot(common_mask, |
edaed859 SS |
1716 | &sdev_to_zfcp(sdev)->status); |
1717 | if (clear_counter) | |
1718 | atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); | |
287ac01a | 1719 | } |
924dd584 | 1720 | spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); |
1da177e4 LT |
1721 | } |
1722 | ||
287ac01a | 1723 | /** |
edaed859 SS |
1724 | * zfcp_erp_set_lun_status - set lun status bits |
1725 | * @sdev: SCSI device / lun to set the status bits | |
1726 | * @mask: status bits to change | |
287ac01a | 1727 | */ |
edaed859 | 1728 | void zfcp_erp_set_lun_status(struct scsi_device *sdev, u32 mask) |
d736a27b | 1729 | { |
edaed859 SS |
1730 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1731 | ||
805de8f4 | 1732 | atomic_or(mask, &zfcp_sdev->status); |
d736a27b AH |
1733 | } |
1734 | ||
287ac01a | 1735 | /** |
edaed859 SS |
1736 | * zfcp_erp_clear_lun_status - clear lun status bits |
1737 | * @sdev: SCSi device / lun to clear the status bits | |
1738 | * @mask: status bits to change | |
287ac01a | 1739 | */ |
edaed859 | 1740 | void zfcp_erp_clear_lun_status(struct scsi_device *sdev, u32 mask) |
d736a27b | 1741 | { |
edaed859 SS |
1742 | struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); |
1743 | ||
805de8f4 | 1744 | atomic_andnot(mask, &zfcp_sdev->status); |
edaed859 SS |
1745 | |
1746 | if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) | |
1747 | atomic_set(&zfcp_sdev->erp_counter, 0); | |
d736a27b | 1748 | } |
edaed859 | 1749 | |
35e9111a SM |
1750 | /** |
1751 | * zfcp_erp_adapter_reset_sync() - Really reopen adapter and wait. | |
1752 | * @adapter: Pointer to zfcp_adapter to reopen. | |
208d0961 | 1753 | * @dbftag: Trace tag string of length %ZFCP_DBF_TAG_LEN. |
35e9111a | 1754 | */ |
208d0961 | 1755 | void zfcp_erp_adapter_reset_sync(struct zfcp_adapter *adapter, char *dbftag) |
35e9111a SM |
1756 | { |
1757 | zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); | |
208d0961 | 1758 | zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, dbftag); |
35e9111a SM |
1759 | zfcp_erp_wait(adapter); |
1760 | } |