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