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