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