1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2005-2014 Brocade Communications Systems, Inc. |
4 | * Copyright (c) 2014- QLogic Corporation. |
5 | * All rights reserved |
6 | * www.qlogic.com |
7 | * |
8 | * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter. |
9 | */ |
10 | |
11 | /* |
12 | * fcpim.c - FCP initiator mode i-t nexus state machine |
13 | */ |
14 | |
15 | #include "bfad_drv.h" |
16 | #include "bfa_fcs.h" |
17 | #include "bfa_fcbuild.h" |
18 | #include "bfad_im.h" |
19 | #include "bfa_fcpim.h" |
20 | |
21 | BFA_TRC_FILE(FCS, FCPIM); |
22 | |
23 | /* |
24 | * forward declarations |
25 | */ |
26 | static void bfa_fcs_itnim_timeout(void *arg); |
27 | static void bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim); |
28 | static void bfa_fcs_itnim_send_prli(void *itnim_cbarg, |
29 | struct bfa_fcxp_s *fcxp_alloced); |
30 | static void bfa_fcs_itnim_prli_response(void *fcsarg, |
31 | struct bfa_fcxp_s *fcxp, void *cbarg, |
32 | bfa_status_t req_status, u32 rsp_len, |
33 | u32 resid_len, struct fchs_s *rsp_fchs); |
34 | static void bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, |
35 | enum bfa_itnim_aen_event event); |
36 | |
37 | static void bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, |
38 | enum bfa_fcs_itnim_event event); |
39 | static void bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, |
40 | enum bfa_fcs_itnim_event event); |
41 | static void bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, |
42 | enum bfa_fcs_itnim_event event); |
43 | static void bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, |
44 | enum bfa_fcs_itnim_event event); |
45 | static void bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, |
46 | enum bfa_fcs_itnim_event event); |
47 | static void bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim, |
48 | enum bfa_fcs_itnim_event event); |
49 | static void bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, |
50 | enum bfa_fcs_itnim_event event); |
51 | static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, |
52 | enum bfa_fcs_itnim_event event); |
53 | static void bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, |
54 | enum bfa_fcs_itnim_event event); |
55 | |
56 | struct bfa_fcs_itnim_sm_table_s { |
57 | bfa_fcs_itnim_sm_t sm; /* state machine function */ |
58 | enum bfa_itnim_state state; /* state machine encoding */ |
59 | char *name; /* state name for display */ |
60 | }; |
61 | |
62 | static inline enum bfa_itnim_state |
63 | bfa_fcs_itnim_sm_to_state(struct bfa_fcs_itnim_sm_table_s *smt, bfa_fcs_itnim_sm_t sm) |
64 | { |
65 | int i = 0; |
66 | |
67 | while (smt[i].sm && smt[i].sm != sm) |
68 | i++; |
69 | return smt[i].state; |
70 | } |
71 | |
72 | static struct bfa_fcs_itnim_sm_table_s itnim_sm_table[] = { |
73 | {BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE}, |
74 | {BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND}, |
75 | {BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT}, |
76 | {BFA_SM(bfa_fcs_itnim_sm_prli_retry), BFA_ITNIM_PRLI_RETRY}, |
77 | {BFA_SM(bfa_fcs_itnim_sm_hcb_online), BFA_ITNIM_HCB_ONLINE}, |
78 | {BFA_SM(bfa_fcs_itnim_sm_online), BFA_ITNIM_ONLINE}, |
79 | {BFA_SM(bfa_fcs_itnim_sm_hcb_offline), BFA_ITNIM_HCB_OFFLINE}, |
80 | {BFA_SM(bfa_fcs_itnim_sm_initiator), BFA_ITNIM_INITIATIOR}, |
81 | }; |
82 | |
83 | /* |
84 | * fcs_itnim_sm FCS itnim state machine |
85 | */ |
86 | |
87 | static void |
88 | bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, |
89 | enum bfa_fcs_itnim_event event) |
90 | { |
91 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
92 | bfa_trc(itnim->fcs, event); |
93 | |
94 | switch (event) { |
95 | case BFA_FCS_ITNIM_SM_FCS_ONLINE: |
96 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); |
97 | itnim->prli_retries = 0; |
98 | bfa_fcs_itnim_send_prli(itnim_cbarg: itnim, NULL); |
99 | break; |
100 | |
101 | case BFA_FCS_ITNIM_SM_OFFLINE: |
102 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
103 | break; |
104 | |
105 | case BFA_FCS_ITNIM_SM_INITIATOR: |
106 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); |
107 | break; |
108 | |
109 | case BFA_FCS_ITNIM_SM_DELETE: |
110 | bfa_fcs_itnim_free(itnim); |
111 | break; |
112 | |
113 | default: |
114 | bfa_sm_fault(itnim->fcs, event); |
115 | } |
116 | |
117 | } |
118 | |
119 | static void |
120 | bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, |
121 | enum bfa_fcs_itnim_event event) |
122 | { |
123 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
124 | bfa_trc(itnim->fcs, event); |
125 | |
126 | switch (event) { |
127 | case BFA_FCS_ITNIM_SM_FRMSENT: |
128 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli); |
129 | break; |
130 | |
131 | case BFA_FCS_ITNIM_SM_INITIATOR: |
132 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); |
133 | bfa_fcxp_walloc_cancel(bfa: itnim->fcs->bfa, wqe: &itnim->fcxp_wqe); |
134 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); |
135 | break; |
136 | |
137 | case BFA_FCS_ITNIM_SM_OFFLINE: |
138 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
139 | bfa_fcxp_walloc_cancel(bfa: itnim->fcs->bfa, wqe: &itnim->fcxp_wqe); |
140 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
141 | break; |
142 | |
143 | case BFA_FCS_ITNIM_SM_DELETE: |
144 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
145 | bfa_fcxp_walloc_cancel(bfa: itnim->fcs->bfa, wqe: &itnim->fcxp_wqe); |
146 | bfa_fcs_itnim_free(itnim); |
147 | break; |
148 | |
149 | default: |
150 | bfa_sm_fault(itnim->fcs, event); |
151 | } |
152 | } |
153 | |
154 | static void |
155 | bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, |
156 | enum bfa_fcs_itnim_event event) |
157 | { |
158 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
159 | bfa_trc(itnim->fcs, event); |
160 | |
161 | switch (event) { |
162 | case BFA_FCS_ITNIM_SM_RSP_OK: |
163 | if (itnim->rport->scsi_function == BFA_RPORT_INITIATOR) |
164 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); |
165 | else |
166 | bfa_sm_set_state(itnim, |
167 | bfa_fcs_itnim_sm_hal_rport_online); |
168 | |
169 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); |
170 | break; |
171 | |
172 | case BFA_FCS_ITNIM_SM_RSP_ERROR: |
173 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_retry); |
174 | bfa_timer_start(itnim->fcs->bfa, &itnim->timer, |
175 | bfa_fcs_itnim_timeout, itnim, |
176 | BFA_FCS_RETRY_TIMEOUT); |
177 | break; |
178 | |
179 | case BFA_FCS_ITNIM_SM_RSP_NOT_SUPP: |
180 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
181 | break; |
182 | |
183 | case BFA_FCS_ITNIM_SM_OFFLINE: |
184 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
185 | bfa_fcxp_discard(fcxp: itnim->fcxp); |
186 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
187 | break; |
188 | |
189 | case BFA_FCS_ITNIM_SM_INITIATOR: |
190 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); |
191 | bfa_fcxp_discard(fcxp: itnim->fcxp); |
192 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); |
193 | break; |
194 | |
195 | case BFA_FCS_ITNIM_SM_DELETE: |
196 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
197 | bfa_fcxp_discard(fcxp: itnim->fcxp); |
198 | bfa_fcs_itnim_free(itnim); |
199 | break; |
200 | |
201 | default: |
202 | bfa_sm_fault(itnim->fcs, event); |
203 | } |
204 | } |
205 | |
206 | static void |
207 | bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim, |
208 | enum bfa_fcs_itnim_event event) |
209 | { |
210 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
211 | bfa_trc(itnim->fcs, event); |
212 | |
213 | switch (event) { |
214 | case BFA_FCS_ITNIM_SM_HAL_ONLINE: |
215 | if (!itnim->bfa_itnim) |
216 | itnim->bfa_itnim = bfa_itnim_create(bfa: itnim->fcs->bfa, |
217 | rport: itnim->rport->bfa_rport, itnim); |
218 | |
219 | if (itnim->bfa_itnim) { |
220 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online); |
221 | bfa_itnim_online(itnim: itnim->bfa_itnim, seq_rec: itnim->seq_rec); |
222 | } else { |
223 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
224 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_DELETE); |
225 | } |
226 | |
227 | break; |
228 | |
229 | case BFA_FCS_ITNIM_SM_OFFLINE: |
230 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
231 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
232 | break; |
233 | |
234 | case BFA_FCS_ITNIM_SM_DELETE: |
235 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
236 | bfa_fcs_itnim_free(itnim); |
237 | break; |
238 | |
239 | default: |
240 | bfa_sm_fault(itnim->fcs, event); |
241 | } |
242 | } |
243 | |
244 | static void |
245 | bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, |
246 | enum bfa_fcs_itnim_event event) |
247 | { |
248 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
249 | bfa_trc(itnim->fcs, event); |
250 | |
251 | switch (event) { |
252 | case BFA_FCS_ITNIM_SM_TIMEOUT: |
253 | if (itnim->prli_retries < BFA_FCS_RPORT_MAX_RETRIES) { |
254 | itnim->prli_retries++; |
255 | bfa_trc(itnim->fcs, itnim->prli_retries); |
256 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); |
257 | bfa_fcs_itnim_send_prli(itnim_cbarg: itnim, NULL); |
258 | } else { |
259 | /* invoke target offline */ |
260 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
261 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP); |
262 | } |
263 | break; |
264 | |
265 | |
266 | case BFA_FCS_ITNIM_SM_OFFLINE: |
267 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
268 | bfa_timer_stop(timer: &itnim->timer); |
269 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
270 | break; |
271 | |
272 | case BFA_FCS_ITNIM_SM_INITIATOR: |
273 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); |
274 | bfa_timer_stop(timer: &itnim->timer); |
275 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); |
276 | break; |
277 | |
278 | case BFA_FCS_ITNIM_SM_DELETE: |
279 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
280 | bfa_timer_stop(timer: &itnim->timer); |
281 | bfa_fcs_itnim_free(itnim); |
282 | break; |
283 | |
284 | default: |
285 | bfa_sm_fault(itnim->fcs, event); |
286 | } |
287 | } |
288 | |
289 | static void |
290 | bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, |
291 | enum bfa_fcs_itnim_event event) |
292 | { |
293 | struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad; |
294 | char lpwwn_buf[BFA_STRING_32]; |
295 | char rpwwn_buf[BFA_STRING_32]; |
296 | |
297 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
298 | bfa_trc(itnim->fcs, event); |
299 | |
300 | switch (event) { |
301 | case BFA_FCS_ITNIM_SM_HCB_ONLINE: |
302 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_online); |
303 | bfa_fcb_itnim_online(itnim_drv: itnim->itnim_drv); |
304 | wwn2str(wwn_str: lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port)); |
305 | wwn2str(wwn_str: rpwwn_buf, wwn: itnim->rport->pwwn); |
306 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, |
307 | "Target (WWN = %s) is online for initiator (WWN = %s)\n" , |
308 | rpwwn_buf, lpwwn_buf); |
309 | bfa_fcs_itnim_aen_post(itnim, event: BFA_ITNIM_AEN_ONLINE); |
310 | break; |
311 | |
312 | case BFA_FCS_ITNIM_SM_OFFLINE: |
313 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); |
314 | bfa_itnim_offline(itnim: itnim->bfa_itnim); |
315 | break; |
316 | |
317 | case BFA_FCS_ITNIM_SM_DELETE: |
318 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
319 | bfa_fcs_itnim_free(itnim); |
320 | break; |
321 | |
322 | default: |
323 | bfa_sm_fault(itnim->fcs, event); |
324 | } |
325 | } |
326 | |
327 | static void |
328 | bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, |
329 | enum bfa_fcs_itnim_event event) |
330 | { |
331 | struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad; |
332 | char lpwwn_buf[BFA_STRING_32]; |
333 | char rpwwn_buf[BFA_STRING_32]; |
334 | |
335 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
336 | bfa_trc(itnim->fcs, event); |
337 | |
338 | switch (event) { |
339 | case BFA_FCS_ITNIM_SM_OFFLINE: |
340 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); |
341 | bfa_fcb_itnim_offline(itnim_drv: itnim->itnim_drv); |
342 | bfa_itnim_offline(itnim: itnim->bfa_itnim); |
343 | wwn2str(wwn_str: lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port)); |
344 | wwn2str(wwn_str: rpwwn_buf, wwn: itnim->rport->pwwn); |
345 | if (bfa_fcs_lport_is_online(port: itnim->rport->port) == BFA_TRUE) { |
346 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
347 | "Target (WWN = %s) connectivity lost for " |
348 | "initiator (WWN = %s)\n" , rpwwn_buf, lpwwn_buf); |
349 | bfa_fcs_itnim_aen_post(itnim, event: BFA_ITNIM_AEN_DISCONNECT); |
350 | } else { |
351 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, |
352 | "Target (WWN = %s) offlined by initiator (WWN = %s)\n" , |
353 | rpwwn_buf, lpwwn_buf); |
354 | bfa_fcs_itnim_aen_post(itnim, event: BFA_ITNIM_AEN_OFFLINE); |
355 | } |
356 | break; |
357 | |
358 | case BFA_FCS_ITNIM_SM_DELETE: |
359 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
360 | bfa_fcs_itnim_free(itnim); |
361 | break; |
362 | |
363 | default: |
364 | bfa_sm_fault(itnim->fcs, event); |
365 | } |
366 | } |
367 | |
368 | static void |
369 | bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, |
370 | enum bfa_fcs_itnim_event event) |
371 | { |
372 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
373 | bfa_trc(itnim->fcs, event); |
374 | |
375 | switch (event) { |
376 | case BFA_FCS_ITNIM_SM_HCB_OFFLINE: |
377 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
378 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
379 | break; |
380 | |
381 | case BFA_FCS_ITNIM_SM_DELETE: |
382 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
383 | bfa_fcs_itnim_free(itnim); |
384 | break; |
385 | |
386 | default: |
387 | bfa_sm_fault(itnim->fcs, event); |
388 | } |
389 | } |
390 | |
391 | /* |
392 | * This state is set when a discovered rport is also in intiator mode. |
393 | * This ITN is marked as no_op and is not active and will not be truned into |
394 | * online state. |
395 | */ |
396 | static void |
397 | bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, |
398 | enum bfa_fcs_itnim_event event) |
399 | { |
400 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
401 | bfa_trc(itnim->fcs, event); |
402 | |
403 | switch (event) { |
404 | case BFA_FCS_ITNIM_SM_OFFLINE: |
405 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
406 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); |
407 | break; |
408 | |
409 | /* |
410 | * fcs_online is expected here for well known initiator ports |
411 | */ |
412 | case BFA_FCS_ITNIM_SM_FCS_ONLINE: |
413 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); |
414 | break; |
415 | |
416 | case BFA_FCS_ITNIM_SM_RSP_ERROR: |
417 | case BFA_FCS_ITNIM_SM_INITIATOR: |
418 | break; |
419 | |
420 | case BFA_FCS_ITNIM_SM_DELETE: |
421 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
422 | bfa_fcs_itnim_free(itnim); |
423 | break; |
424 | |
425 | default: |
426 | bfa_sm_fault(itnim->fcs, event); |
427 | } |
428 | } |
429 | |
430 | static void |
431 | bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, |
432 | enum bfa_itnim_aen_event event) |
433 | { |
434 | struct bfa_fcs_rport_s *rport = itnim->rport; |
435 | struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad; |
436 | struct bfa_aen_entry_s *aen_entry; |
437 | |
438 | /* Don't post events for well known addresses */ |
439 | if (BFA_FCS_PID_IS_WKA(rport->pid)) |
440 | return; |
441 | |
442 | bfad_get_aen_entry(bfad, aen_entry); |
443 | if (!aen_entry) |
444 | return; |
445 | |
446 | aen_entry->aen_data.itnim.vf_id = rport->port->fabric->vf_id; |
447 | aen_entry->aen_data.itnim.ppwwn = bfa_fcs_lport_get_pwwn( |
448 | bfa_fcs_get_base_port(itnim->fcs)); |
449 | aen_entry->aen_data.itnim.lpwwn = bfa_fcs_lport_get_pwwn(rport->port); |
450 | aen_entry->aen_data.itnim.rpwwn = rport->pwwn; |
451 | |
452 | /* Send the AEN notification */ |
453 | bfad_im_post_vendor_event(entry: aen_entry, drv: bfad, cnt: ++rport->fcs->fcs_aen_seq, |
454 | cat: BFA_AEN_CAT_ITNIM, evt: event); |
455 | } |
456 | |
457 | static void |
458 | bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced) |
459 | { |
460 | struct bfa_fcs_itnim_s *itnim = itnim_cbarg; |
461 | struct bfa_fcs_rport_s *rport = itnim->rport; |
462 | struct bfa_fcs_lport_s *port = rport->port; |
463 | struct fchs_s fchs; |
464 | struct bfa_fcxp_s *fcxp; |
465 | int len; |
466 | |
467 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
468 | |
469 | fcxp = fcxp_alloced ? fcxp_alloced : |
470 | bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE); |
471 | if (!fcxp) { |
472 | itnim->stats.fcxp_alloc_wait++; |
473 | bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe, |
474 | bfa_fcs_itnim_send_prli, itnim, BFA_TRUE); |
475 | return; |
476 | } |
477 | itnim->fcxp = fcxp; |
478 | |
479 | len = fc_prli_build(fchs: &fchs, pld: bfa_fcxp_get_reqbuf(fcxp), |
480 | d_id: itnim->rport->pid, bfa_fcs_lport_get_fcid(port), ox_id: 0); |
481 | |
482 | bfa_fcxp_send(fcxp, rport: rport->bfa_rport, vf_id: port->fabric->vf_id, lp_tag: port->lp_tag, |
483 | cts: BFA_FALSE, cos: FC_CLASS_3, reqlen: len, fchs: &fchs, |
484 | cbfn: bfa_fcs_itnim_prli_response, cbarg: (void *)itnim, |
485 | rsp_maxlen: FC_MAX_PDUSZ, FC_ELS_TOV); |
486 | |
487 | itnim->stats.prli_sent++; |
488 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_FRMSENT); |
489 | } |
490 | |
491 | static void |
492 | bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, |
493 | bfa_status_t req_status, u32 rsp_len, |
494 | u32 resid_len, struct fchs_s *rsp_fchs) |
495 | { |
496 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg; |
497 | struct fc_els_cmd_s *els_cmd; |
498 | struct fc_prli_s *prli_resp; |
499 | struct fc_ls_rjt_s *ls_rjt; |
500 | struct fc_prli_params_s *sparams; |
501 | |
502 | bfa_trc(itnim->fcs, req_status); |
503 | |
504 | /* |
505 | * Sanity Checks |
506 | */ |
507 | if (req_status != BFA_STATUS_OK) { |
508 | itnim->stats.prli_rsp_err++; |
509 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); |
510 | return; |
511 | } |
512 | |
513 | els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); |
514 | |
515 | if (els_cmd->els_code == FC_ELS_ACC) { |
516 | prli_resp = (struct fc_prli_s *) els_cmd; |
517 | |
518 | if (fc_prli_rsp_parse(prli: prli_resp, len: rsp_len) != FC_PARSE_OK) { |
519 | bfa_trc(itnim->fcs, rsp_len); |
520 | /* |
521 | * Check if this r-port is also in Initiator mode. |
522 | * If so, we need to set this ITN as a no-op. |
523 | */ |
524 | if (prli_resp->parampage.servparams.initiator) { |
525 | bfa_trc(itnim->fcs, prli_resp->parampage.type); |
526 | itnim->rport->scsi_function = |
527 | BFA_RPORT_INITIATOR; |
528 | itnim->stats.prli_rsp_acc++; |
529 | itnim->stats.initiator++; |
530 | bfa_sm_send_event(itnim, |
531 | BFA_FCS_ITNIM_SM_RSP_OK); |
532 | return; |
533 | } |
534 | |
535 | itnim->stats.prli_rsp_parse_err++; |
536 | return; |
537 | } |
538 | itnim->rport->scsi_function = BFA_RPORT_TARGET; |
539 | |
540 | sparams = &prli_resp->parampage.servparams; |
541 | itnim->seq_rec = sparams->retry; |
542 | itnim->rec_support = sparams->rec_support; |
543 | itnim->task_retry_id = sparams->task_retry_id; |
544 | itnim->conf_comp = sparams->confirm; |
545 | |
546 | itnim->stats.prli_rsp_acc++; |
547 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_OK); |
548 | } else { |
549 | ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); |
550 | |
551 | bfa_trc(itnim->fcs, ls_rjt->reason_code); |
552 | bfa_trc(itnim->fcs, ls_rjt->reason_code_expl); |
553 | |
554 | itnim->stats.prli_rsp_rjt++; |
555 | if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) { |
556 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_NOT_SUPP); |
557 | return; |
558 | } |
559 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); |
560 | } |
561 | } |
562 | |
563 | static void |
564 | bfa_fcs_itnim_timeout(void *arg) |
565 | { |
566 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) arg; |
567 | |
568 | itnim->stats.timeout++; |
569 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_TIMEOUT); |
570 | } |
571 | |
572 | static void |
573 | bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim) |
574 | { |
575 | if (itnim->bfa_itnim) { |
576 | bfa_itnim_delete(itnim: itnim->bfa_itnim); |
577 | itnim->bfa_itnim = NULL; |
578 | } |
579 | |
580 | bfa_fcb_itnim_free(bfad: itnim->fcs->bfad, itnim_drv: itnim->itnim_drv); |
581 | } |
582 | |
583 | |
584 | |
585 | /* |
586 | * itnim_public FCS ITNIM public interfaces |
587 | */ |
588 | |
589 | /* |
590 | * Called by rport when a new rport is created. |
591 | * |
592 | * @param[in] rport - remote port. |
593 | */ |
594 | struct bfa_fcs_itnim_s * |
595 | bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport) |
596 | { |
597 | struct bfa_fcs_lport_s *port = rport->port; |
598 | struct bfa_fcs_itnim_s *itnim; |
599 | struct bfad_itnim_s *itnim_drv; |
600 | int ret; |
601 | |
602 | /* |
603 | * call bfad to allocate the itnim |
604 | */ |
605 | ret = bfa_fcb_itnim_alloc(bfad: port->fcs->bfad, itnim: &itnim, itnim_drv: &itnim_drv); |
606 | if (ret) { |
607 | bfa_trc(port->fcs, rport->pwwn); |
608 | return NULL; |
609 | } |
610 | |
611 | /* |
612 | * Initialize itnim |
613 | */ |
614 | itnim->rport = rport; |
615 | itnim->fcs = rport->fcs; |
616 | itnim->itnim_drv = itnim_drv; |
617 | |
618 | itnim->bfa_itnim = NULL; |
619 | itnim->seq_rec = BFA_FALSE; |
620 | itnim->rec_support = BFA_FALSE; |
621 | itnim->conf_comp = BFA_FALSE; |
622 | itnim->task_retry_id = BFA_FALSE; |
623 | |
624 | /* |
625 | * Set State machine |
626 | */ |
627 | bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); |
628 | |
629 | return itnim; |
630 | } |
631 | |
632 | /* |
633 | * Called by rport to delete the instance of FCPIM. |
634 | * |
635 | * @param[in] rport - remote port. |
636 | */ |
637 | void |
638 | bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim) |
639 | { |
640 | bfa_trc(itnim->fcs, itnim->rport->pid); |
641 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_DELETE); |
642 | } |
643 | |
644 | /* |
645 | * Notification from rport that PLOGI is complete to initiate FC-4 session. |
646 | */ |
647 | void |
648 | bfa_fcs_itnim_brp_online(struct bfa_fcs_itnim_s *itnim) |
649 | { |
650 | itnim->stats.onlines++; |
651 | |
652 | if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid)) |
653 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HAL_ONLINE); |
654 | } |
655 | |
656 | /* |
657 | * Called by rport to handle a remote device offline. |
658 | */ |
659 | void |
660 | bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim) |
661 | { |
662 | itnim->stats.offlines++; |
663 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_OFFLINE); |
664 | } |
665 | |
666 | /* |
667 | * Called by rport when remote port is known to be an initiator from |
668 | * PRLI received. |
669 | */ |
670 | void |
671 | bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim) |
672 | { |
673 | bfa_trc(itnim->fcs, itnim->rport->pid); |
674 | itnim->stats.initiator++; |
675 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR); |
676 | } |
677 | |
678 | /* |
679 | * Called by rport to check if the itnim is online. |
680 | */ |
681 | bfa_status_t |
682 | bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim) |
683 | { |
684 | bfa_trc(itnim->fcs, itnim->rport->pid); |
685 | switch (bfa_fcs_itnim_sm_to_state(smt: itnim_sm_table, sm: itnim->sm)) { |
686 | case BFA_ITNIM_ONLINE: |
687 | case BFA_ITNIM_INITIATIOR: |
688 | return BFA_STATUS_OK; |
689 | |
690 | default: |
691 | return BFA_STATUS_NO_FCPIM_NEXUS; |
692 | } |
693 | } |
694 | |
695 | /* |
696 | * BFA completion callback for bfa_itnim_online(). |
697 | */ |
698 | void |
699 | bfa_cb_itnim_online(void *cbarg) |
700 | { |
701 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg; |
702 | |
703 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
704 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_ONLINE); |
705 | } |
706 | |
707 | /* |
708 | * BFA completion callback for bfa_itnim_offline(). |
709 | */ |
710 | void |
711 | bfa_cb_itnim_offline(void *cb_arg) |
712 | { |
713 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; |
714 | |
715 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
716 | bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_OFFLINE); |
717 | } |
718 | |
719 | /* |
720 | * Mark the beginning of PATH TOV handling. IO completion callbacks |
721 | * are still pending. |
722 | */ |
723 | void |
724 | bfa_cb_itnim_tov_begin(void *cb_arg) |
725 | { |
726 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; |
727 | |
728 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
729 | } |
730 | |
731 | /* |
732 | * Mark the end of PATH TOV handling. All pending IOs are already cleaned up. |
733 | */ |
734 | void |
735 | bfa_cb_itnim_tov(void *cb_arg) |
736 | { |
737 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; |
738 | struct bfad_itnim_s *itnim_drv = itnim->itnim_drv; |
739 | |
740 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
741 | itnim_drv->state = ITNIM_STATE_TIMEOUT; |
742 | } |
743 | |
744 | /* |
745 | * BFA notification to FCS/driver for second level error recovery. |
746 | * |
747 | * Atleast one I/O request has timedout and target is unresponsive to |
748 | * repeated abort requests. Second level error recovery should be initiated |
749 | * by starting implicit logout and recovery procedures. |
750 | */ |
751 | void |
752 | bfa_cb_itnim_sler(void *cb_arg) |
753 | { |
754 | struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; |
755 | |
756 | itnim->stats.sler++; |
757 | bfa_trc(itnim->fcs, itnim->rport->pwwn); |
758 | bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP); |
759 | } |
760 | |
761 | struct bfa_fcs_itnim_s * |
762 | bfa_fcs_itnim_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn) |
763 | { |
764 | struct bfa_fcs_rport_s *rport; |
765 | rport = bfa_fcs_rport_lookup(port, rpwwn); |
766 | |
767 | if (!rport) |
768 | return NULL; |
769 | |
770 | WARN_ON(rport->itnim == NULL); |
771 | return rport->itnim; |
772 | } |
773 | |
774 | bfa_status_t |
775 | bfa_fcs_itnim_attr_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn, |
776 | struct bfa_itnim_attr_s *attr) |
777 | { |
778 | struct bfa_fcs_itnim_s *itnim = NULL; |
779 | |
780 | itnim = bfa_fcs_itnim_lookup(port, rpwwn); |
781 | |
782 | if (itnim == NULL) |
783 | return BFA_STATUS_NO_FCPIM_NEXUS; |
784 | |
785 | attr->state = bfa_fcs_itnim_sm_to_state(smt: itnim_sm_table, sm: itnim->sm); |
786 | attr->retry = itnim->seq_rec; |
787 | attr->rec_support = itnim->rec_support; |
788 | attr->conf_comp = itnim->conf_comp; |
789 | attr->task_retry_id = itnim->task_retry_id; |
790 | return BFA_STATUS_OK; |
791 | } |
792 | |
793 | bfa_status_t |
794 | bfa_fcs_itnim_stats_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn, |
795 | struct bfa_itnim_stats_s *stats) |
796 | { |
797 | struct bfa_fcs_itnim_s *itnim = NULL; |
798 | |
799 | WARN_ON(port == NULL); |
800 | |
801 | itnim = bfa_fcs_itnim_lookup(port, rpwwn); |
802 | |
803 | if (itnim == NULL) |
804 | return BFA_STATUS_NO_FCPIM_NEXUS; |
805 | |
806 | memcpy(stats, &itnim->stats, sizeof(struct bfa_itnim_stats_s)); |
807 | |
808 | return BFA_STATUS_OK; |
809 | } |
810 | |
811 | bfa_status_t |
812 | bfa_fcs_itnim_stats_clear(struct bfa_fcs_lport_s *port, wwn_t rpwwn) |
813 | { |
814 | struct bfa_fcs_itnim_s *itnim = NULL; |
815 | |
816 | WARN_ON(port == NULL); |
817 | |
818 | itnim = bfa_fcs_itnim_lookup(port, rpwwn); |
819 | |
820 | if (itnim == NULL) |
821 | return BFA_STATUS_NO_FCPIM_NEXUS; |
822 | |
823 | memset(&itnim->stats, 0, sizeof(struct bfa_itnim_stats_s)); |
824 | return BFA_STATUS_OK; |
825 | } |
826 | |
827 | void |
828 | bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim, |
829 | struct fchs_s *fchs, u16 len) |
830 | { |
831 | struct fc_els_cmd_s *els_cmd; |
832 | |
833 | bfa_trc(itnim->fcs, fchs->type); |
834 | |
835 | if (fchs->type != FC_TYPE_ELS) |
836 | return; |
837 | |
838 | els_cmd = (struct fc_els_cmd_s *) (fchs + 1); |
839 | |
840 | bfa_trc(itnim->fcs, els_cmd->els_code); |
841 | |
842 | switch (els_cmd->els_code) { |
843 | case FC_ELS_PRLO: |
844 | bfa_fcs_rport_prlo(rport: itnim->rport, ox_id: fchs->ox_id); |
845 | break; |
846 | |
847 | default: |
848 | WARN_ON(1); |
849 | } |
850 | } |
851 | |