Commit | Line | Data |
---|---|---|
7725ccfd JH |
1 | /* |
2 | * Copyright (c) 2005-2009 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 | * bfa_fcs_port.c BFA FCS port | |
20 | */ | |
21 | ||
22 | #include <fcs/bfa_fcs.h> | |
23 | #include <fcs/bfa_fcs_lport.h> | |
24 | #include <fcs/bfa_fcs_rport.h> | |
25 | #include <fcb/bfa_fcb_port.h> | |
26 | #include <bfa_svc.h> | |
27 | #include <log/bfa_log_fcs.h> | |
28 | #include "fcs.h" | |
29 | #include "fcs_lport.h" | |
30 | #include "fcs_vport.h" | |
31 | #include "fcs_rport.h" | |
32 | #include "fcs_fcxp.h" | |
33 | #include "fcs_trcmod.h" | |
34 | #include "lport_priv.h" | |
35 | #include <aen/bfa_aen_lport.h> | |
36 | ||
37 | BFA_TRC_FILE(FCS, PORT); | |
38 | ||
39 | /** | |
40 | * Forward declarations | |
41 | */ | |
42 | ||
43 | static void bfa_fcs_port_aen_post(struct bfa_fcs_port_s *port, | |
44 | enum bfa_lport_aen_event event); | |
45 | static void bfa_fcs_port_send_ls_rjt(struct bfa_fcs_port_s *port, | |
46 | struct fchs_s *rx_fchs, u8 reason_code, | |
47 | u8 reason_code_expl); | |
48 | static void bfa_fcs_port_plogi(struct bfa_fcs_port_s *port, | |
49 | struct fchs_s *rx_fchs, | |
50 | struct fc_logi_s *plogi); | |
51 | static void bfa_fcs_port_online_actions(struct bfa_fcs_port_s *port); | |
52 | static void bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port); | |
53 | static void bfa_fcs_port_unknown_init(struct bfa_fcs_port_s *port); | |
54 | static void bfa_fcs_port_unknown_online(struct bfa_fcs_port_s *port); | |
55 | static void bfa_fcs_port_unknown_offline(struct bfa_fcs_port_s *port); | |
56 | static void bfa_fcs_port_deleted(struct bfa_fcs_port_s *port); | |
57 | static void bfa_fcs_port_echo(struct bfa_fcs_port_s *port, | |
58 | struct fchs_s *rx_fchs, | |
59 | struct fc_echo_s *echo, u16 len); | |
60 | static void bfa_fcs_port_rnid(struct bfa_fcs_port_s *port, | |
61 | struct fchs_s *rx_fchs, | |
62 | struct fc_rnid_cmd_s *rnid, u16 len); | |
63 | static void bfa_fs_port_get_gen_topo_data(struct bfa_fcs_port_s *port, | |
64 | struct fc_rnid_general_topology_data_s *gen_topo_data); | |
65 | ||
66 | static struct { | |
67 | void (*init) (struct bfa_fcs_port_s *port); | |
68 | void (*online) (struct bfa_fcs_port_s *port); | |
69 | void (*offline) (struct bfa_fcs_port_s *port); | |
70 | } __port_action[] = { | |
71 | { | |
72 | bfa_fcs_port_unknown_init, bfa_fcs_port_unknown_online, | |
73 | bfa_fcs_port_unknown_offline}, { | |
74 | bfa_fcs_port_fab_init, bfa_fcs_port_fab_online, | |
75 | bfa_fcs_port_fab_offline}, { | |
76 | bfa_fcs_port_loop_init, bfa_fcs_port_loop_online, | |
77 | bfa_fcs_port_loop_offline}, { | |
78 | bfa_fcs_port_n2n_init, bfa_fcs_port_n2n_online, | |
79 | bfa_fcs_port_n2n_offline},}; | |
80 | ||
81 | /** | |
82 | * fcs_port_sm FCS logical port state machine | |
83 | */ | |
84 | ||
85 | enum bfa_fcs_port_event { | |
86 | BFA_FCS_PORT_SM_CREATE = 1, | |
87 | BFA_FCS_PORT_SM_ONLINE = 2, | |
88 | BFA_FCS_PORT_SM_OFFLINE = 3, | |
89 | BFA_FCS_PORT_SM_DELETE = 4, | |
90 | BFA_FCS_PORT_SM_DELRPORT = 5, | |
91 | }; | |
92 | ||
93 | static void bfa_fcs_port_sm_uninit(struct bfa_fcs_port_s *port, | |
94 | enum bfa_fcs_port_event event); | |
95 | static void bfa_fcs_port_sm_init(struct bfa_fcs_port_s *port, | |
96 | enum bfa_fcs_port_event event); | |
97 | static void bfa_fcs_port_sm_online(struct bfa_fcs_port_s *port, | |
98 | enum bfa_fcs_port_event event); | |
99 | static void bfa_fcs_port_sm_offline(struct bfa_fcs_port_s *port, | |
100 | enum bfa_fcs_port_event event); | |
101 | static void bfa_fcs_port_sm_deleting(struct bfa_fcs_port_s *port, | |
102 | enum bfa_fcs_port_event event); | |
103 | ||
104 | static void | |
105 | bfa_fcs_port_sm_uninit(struct bfa_fcs_port_s *port, | |
106 | enum bfa_fcs_port_event event) | |
107 | { | |
108 | bfa_trc(port->fcs, port->port_cfg.pwwn); | |
109 | bfa_trc(port->fcs, event); | |
110 | ||
111 | switch (event) { | |
112 | case BFA_FCS_PORT_SM_CREATE: | |
113 | bfa_sm_set_state(port, bfa_fcs_port_sm_init); | |
114 | break; | |
115 | ||
116 | default: | |
117 | bfa_assert(0); | |
118 | } | |
119 | } | |
120 | ||
121 | static void | |
122 | bfa_fcs_port_sm_init(struct bfa_fcs_port_s *port, enum bfa_fcs_port_event event) | |
123 | { | |
124 | bfa_trc(port->fcs, port->port_cfg.pwwn); | |
125 | bfa_trc(port->fcs, event); | |
126 | ||
127 | switch (event) { | |
128 | case BFA_FCS_PORT_SM_ONLINE: | |
129 | bfa_sm_set_state(port, bfa_fcs_port_sm_online); | |
130 | bfa_fcs_port_online_actions(port); | |
131 | break; | |
132 | ||
133 | case BFA_FCS_PORT_SM_DELETE: | |
134 | bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); | |
135 | bfa_fcs_port_deleted(port); | |
136 | break; | |
137 | ||
138 | default: | |
139 | bfa_assert(0); | |
140 | } | |
141 | } | |
142 | ||
143 | static void | |
144 | bfa_fcs_port_sm_online(struct bfa_fcs_port_s *port, | |
145 | enum bfa_fcs_port_event event) | |
146 | { | |
147 | struct bfa_fcs_rport_s *rport; | |
148 | struct list_head *qe, *qen; | |
149 | ||
150 | bfa_trc(port->fcs, port->port_cfg.pwwn); | |
151 | bfa_trc(port->fcs, event); | |
152 | ||
153 | switch (event) { | |
154 | case BFA_FCS_PORT_SM_OFFLINE: | |
155 | bfa_sm_set_state(port, bfa_fcs_port_sm_offline); | |
156 | bfa_fcs_port_offline_actions(port); | |
157 | break; | |
158 | ||
159 | case BFA_FCS_PORT_SM_DELETE: | |
160 | ||
161 | __port_action[port->fabric->fab_type].offline(port); | |
162 | ||
163 | if (port->num_rports == 0) { | |
164 | bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); | |
165 | bfa_fcs_port_deleted(port); | |
166 | } else { | |
167 | bfa_sm_set_state(port, bfa_fcs_port_sm_deleting); | |
168 | list_for_each_safe(qe, qen, &port->rport_q) { | |
169 | rport = (struct bfa_fcs_rport_s *)qe; | |
170 | bfa_fcs_rport_delete(rport); | |
171 | } | |
172 | } | |
173 | break; | |
174 | ||
175 | case BFA_FCS_PORT_SM_DELRPORT: | |
176 | break; | |
177 | ||
178 | default: | |
179 | bfa_assert(0); | |
180 | } | |
181 | } | |
182 | ||
183 | static void | |
184 | bfa_fcs_port_sm_offline(struct bfa_fcs_port_s *port, | |
185 | enum bfa_fcs_port_event event) | |
186 | { | |
187 | struct bfa_fcs_rport_s *rport; | |
188 | struct list_head *qe, *qen; | |
189 | ||
190 | bfa_trc(port->fcs, port->port_cfg.pwwn); | |
191 | bfa_trc(port->fcs, event); | |
192 | ||
193 | switch (event) { | |
194 | case BFA_FCS_PORT_SM_ONLINE: | |
195 | bfa_sm_set_state(port, bfa_fcs_port_sm_online); | |
196 | bfa_fcs_port_online_actions(port); | |
197 | break; | |
198 | ||
199 | case BFA_FCS_PORT_SM_DELETE: | |
200 | if (port->num_rports == 0) { | |
201 | bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); | |
202 | bfa_fcs_port_deleted(port); | |
203 | } else { | |
204 | bfa_sm_set_state(port, bfa_fcs_port_sm_deleting); | |
205 | list_for_each_safe(qe, qen, &port->rport_q) { | |
206 | rport = (struct bfa_fcs_rport_s *)qe; | |
207 | bfa_fcs_rport_delete(rport); | |
208 | } | |
209 | } | |
210 | break; | |
211 | ||
212 | case BFA_FCS_PORT_SM_DELRPORT: | |
213 | case BFA_FCS_PORT_SM_OFFLINE: | |
214 | break; | |
215 | ||
216 | default: | |
217 | bfa_assert(0); | |
218 | } | |
219 | } | |
220 | ||
221 | static void | |
222 | bfa_fcs_port_sm_deleting(struct bfa_fcs_port_s *port, | |
223 | enum bfa_fcs_port_event event) | |
224 | { | |
225 | bfa_trc(port->fcs, port->port_cfg.pwwn); | |
226 | bfa_trc(port->fcs, event); | |
227 | ||
228 | switch (event) { | |
229 | case BFA_FCS_PORT_SM_DELRPORT: | |
230 | if (port->num_rports == 0) { | |
231 | bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); | |
232 | bfa_fcs_port_deleted(port); | |
233 | } | |
234 | break; | |
235 | ||
236 | default: | |
237 | bfa_assert(0); | |
238 | } | |
239 | } | |
240 | ||
241 | ||
242 | ||
243 | /** | |
244 | * fcs_port_pvt | |
245 | */ | |
246 | ||
247 | /** | |
248 | * Send AEN notification | |
249 | */ | |
250 | static void | |
251 | bfa_fcs_port_aen_post(struct bfa_fcs_port_s *port, | |
252 | enum bfa_lport_aen_event event) | |
253 | { | |
254 | union bfa_aen_data_u aen_data; | |
255 | struct bfa_log_mod_s *logmod = port->fcs->logm; | |
256 | enum bfa_port_role role = port->port_cfg.roles; | |
257 | wwn_t lpwwn = bfa_fcs_port_get_pwwn(port); | |
258 | char lpwwn_ptr[BFA_STRING_32]; | |
259 | char *role_str[BFA_PORT_ROLE_FCP_MAX / 2 + 1] = | |
260 | { "Initiator", "Target", "IPFC" }; | |
261 | ||
262 | wwn2str(lpwwn_ptr, lpwwn); | |
263 | ||
264 | bfa_assert(role <= BFA_PORT_ROLE_FCP_MAX); | |
265 | ||
266 | switch (event) { | |
267 | case BFA_LPORT_AEN_ONLINE: | |
268 | bfa_log(logmod, BFA_AEN_LPORT_ONLINE, lpwwn_ptr, | |
269 | role_str[role / 2]); | |
270 | break; | |
271 | case BFA_LPORT_AEN_OFFLINE: | |
272 | bfa_log(logmod, BFA_AEN_LPORT_OFFLINE, lpwwn_ptr, | |
273 | role_str[role / 2]); | |
274 | break; | |
275 | case BFA_LPORT_AEN_NEW: | |
276 | bfa_log(logmod, BFA_AEN_LPORT_NEW, lpwwn_ptr, | |
277 | role_str[role / 2]); | |
278 | break; | |
279 | case BFA_LPORT_AEN_DELETE: | |
280 | bfa_log(logmod, BFA_AEN_LPORT_DELETE, lpwwn_ptr, | |
281 | role_str[role / 2]); | |
282 | break; | |
283 | case BFA_LPORT_AEN_DISCONNECT: | |
284 | bfa_log(logmod, BFA_AEN_LPORT_DISCONNECT, lpwwn_ptr, | |
285 | role_str[role / 2]); | |
286 | break; | |
287 | default: | |
288 | break; | |
289 | } | |
290 | ||
291 | aen_data.lport.vf_id = port->fabric->vf_id; | |
292 | aen_data.lport.roles = role; | |
293 | aen_data.lport.ppwwn = | |
294 | bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); | |
295 | aen_data.lport.lpwwn = lpwwn; | |
296 | } | |
297 | ||
298 | /* | |
299 | * Send a LS reject | |
300 | */ | |
301 | static void | |
302 | bfa_fcs_port_send_ls_rjt(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, | |
303 | u8 reason_code, u8 reason_code_expl) | |
304 | { | |
305 | struct fchs_s fchs; | |
306 | struct bfa_fcxp_s *fcxp; | |
307 | struct bfa_rport_s *bfa_rport = NULL; | |
308 | int len; | |
309 | ||
310 | bfa_trc(port->fcs, rx_fchs->s_id); | |
311 | ||
312 | fcxp = bfa_fcs_fcxp_alloc(port->fcs); | |
313 | if (!fcxp) | |
314 | return; | |
315 | ||
316 | len = fc_ls_rjt_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, | |
317 | bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, | |
318 | reason_code, reason_code_expl); | |
319 | ||
320 | bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, | |
321 | BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, | |
322 | FC_MAX_PDUSZ, 0); | |
323 | } | |
324 | ||
325 | /** | |
326 | * Process incoming plogi from a remote port. | |
327 | */ | |
328 | static void | |
329 | bfa_fcs_port_plogi(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, | |
330 | struct fc_logi_s *plogi) | |
331 | { | |
332 | struct bfa_fcs_rport_s *rport; | |
333 | ||
334 | bfa_trc(port->fcs, rx_fchs->d_id); | |
335 | bfa_trc(port->fcs, rx_fchs->s_id); | |
336 | ||
337 | /* | |
338 | * If min cfg mode is enabled, drop any incoming PLOGIs | |
339 | */ | |
340 | if (__fcs_min_cfg(port->fcs)) { | |
341 | bfa_trc(port->fcs, rx_fchs->s_id); | |
342 | return; | |
343 | } | |
344 | ||
345 | if (fc_plogi_parse(rx_fchs) != FC_PARSE_OK) { | |
346 | bfa_trc(port->fcs, rx_fchs->s_id); | |
347 | /* | |
348 | * send a LS reject | |
349 | */ | |
350 | bfa_fcs_port_send_ls_rjt(port, rx_fchs, | |
351 | FC_LS_RJT_RSN_PROTOCOL_ERROR, | |
352 | FC_LS_RJT_EXP_SPARMS_ERR_OPTIONS); | |
353 | return; | |
354 | } | |
355 | ||
356 | /** | |
357 | * Direct Attach P2P mode : verify address assigned by the r-port. | |
358 | */ | |
359 | if ((!bfa_fcs_fabric_is_switched(port->fabric)) | |
360 | && | |
361 | (memcmp | |
362 | ((void *)&bfa_fcs_port_get_pwwn(port), (void *)&plogi->port_name, | |
363 | sizeof(wwn_t)) < 0)) { | |
364 | if (BFA_FCS_PID_IS_WKA(rx_fchs->d_id)) { | |
365 | /* | |
366 | * Address assigned to us cannot be a WKA | |
367 | */ | |
368 | bfa_fcs_port_send_ls_rjt(port, rx_fchs, | |
369 | FC_LS_RJT_RSN_PROTOCOL_ERROR, | |
370 | FC_LS_RJT_EXP_INVALID_NPORT_ID); | |
371 | return; | |
372 | } | |
373 | port->pid = rx_fchs->d_id; | |
374 | } | |
375 | ||
376 | /** | |
377 | * First, check if we know the device by pwwn. | |
378 | */ | |
379 | rport = bfa_fcs_port_get_rport_by_pwwn(port, plogi->port_name); | |
380 | if (rport) { | |
381 | /** | |
382 | * Direct Attach P2P mode: handle address assigned by the rport. | |
383 | */ | |
384 | if ((!bfa_fcs_fabric_is_switched(port->fabric)) | |
385 | && | |
386 | (memcmp | |
387 | ((void *)&bfa_fcs_port_get_pwwn(port), | |
388 | (void *)&plogi->port_name, sizeof(wwn_t)) < 0)) { | |
389 | port->pid = rx_fchs->d_id; | |
390 | rport->pid = rx_fchs->s_id; | |
391 | } | |
392 | bfa_fcs_rport_plogi(rport, rx_fchs, plogi); | |
393 | return; | |
394 | } | |
395 | ||
396 | /** | |
397 | * Next, lookup rport by PID. | |
398 | */ | |
399 | rport = bfa_fcs_port_get_rport_by_pid(port, rx_fchs->s_id); | |
400 | if (!rport) { | |
401 | /** | |
402 | * Inbound PLOGI from a new device. | |
403 | */ | |
404 | bfa_fcs_rport_plogi_create(port, rx_fchs, plogi); | |
405 | return; | |
406 | } | |
407 | ||
408 | /** | |
409 | * Rport is known only by PID. | |
410 | */ | |
411 | if (rport->pwwn) { | |
412 | /** | |
413 | * This is a different device with the same pid. Old device | |
414 | * disappeared. Send implicit LOGO to old device. | |
415 | */ | |
416 | bfa_assert(rport->pwwn != plogi->port_name); | |
417 | bfa_fcs_rport_logo_imp(rport); | |
418 | ||
419 | /** | |
420 | * Inbound PLOGI from a new device (with old PID). | |
421 | */ | |
422 | bfa_fcs_rport_plogi_create(port, rx_fchs, plogi); | |
423 | return; | |
424 | } | |
425 | ||
426 | /** | |
427 | * PLOGI crossing each other. | |
428 | */ | |
429 | bfa_assert(rport->pwwn == WWN_NULL); | |
430 | bfa_fcs_rport_plogi(rport, rx_fchs, plogi); | |
431 | } | |
432 | ||
433 | /* | |
434 | * Process incoming ECHO. | |
435 | * Since it does not require a login, it is processed here. | |
436 | */ | |
437 | static void | |
438 | bfa_fcs_port_echo(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, | |
439 | struct fc_echo_s *echo, u16 rx_len) | |
440 | { | |
441 | struct fchs_s fchs; | |
442 | struct bfa_fcxp_s *fcxp; | |
443 | struct bfa_rport_s *bfa_rport = NULL; | |
444 | int len, pyld_len; | |
445 | ||
446 | bfa_trc(port->fcs, rx_fchs->s_id); | |
447 | bfa_trc(port->fcs, rx_fchs->d_id); | |
448 | bfa_trc(port->fcs, rx_len); | |
449 | ||
450 | fcxp = bfa_fcs_fcxp_alloc(port->fcs); | |
451 | if (!fcxp) | |
452 | return; | |
453 | ||
454 | len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, | |
455 | bfa_fcs_port_get_fcid(port), rx_fchs->ox_id); | |
456 | ||
457 | /* | |
458 | * Copy the payload (if any) from the echo frame | |
459 | */ | |
460 | pyld_len = rx_len - sizeof(struct fchs_s); | |
461 | bfa_trc(port->fcs, pyld_len); | |
462 | ||
463 | if (pyld_len > len) | |
464 | memcpy(((u8 *) bfa_fcxp_get_reqbuf(fcxp)) + | |
465 | sizeof(struct fc_echo_s), (echo + 1), | |
466 | (pyld_len - sizeof(struct fc_echo_s))); | |
467 | ||
468 | bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, | |
469 | BFA_FALSE, FC_CLASS_3, pyld_len, &fchs, NULL, NULL, | |
470 | FC_MAX_PDUSZ, 0); | |
471 | } | |
472 | ||
473 | /* | |
474 | * Process incoming RNID. | |
475 | * Since it does not require a login, it is processed here. | |
476 | */ | |
477 | static void | |
478 | bfa_fcs_port_rnid(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, | |
479 | struct fc_rnid_cmd_s *rnid, u16 rx_len) | |
480 | { | |
481 | struct fc_rnid_common_id_data_s common_id_data; | |
482 | struct fc_rnid_general_topology_data_s gen_topo_data; | |
483 | struct fchs_s fchs; | |
484 | struct bfa_fcxp_s *fcxp; | |
485 | struct bfa_rport_s *bfa_rport = NULL; | |
486 | u16 len; | |
487 | u32 data_format; | |
488 | ||
489 | bfa_trc(port->fcs, rx_fchs->s_id); | |
490 | bfa_trc(port->fcs, rx_fchs->d_id); | |
491 | bfa_trc(port->fcs, rx_len); | |
492 | ||
493 | fcxp = bfa_fcs_fcxp_alloc(port->fcs); | |
494 | if (!fcxp) | |
495 | return; | |
496 | ||
497 | /* | |
498 | * Check Node Indentification Data Format | |
499 | * We only support General Topology Discovery Format. | |
500 | * For any other requested Data Formats, we return Common Node Id Data | |
501 | * only, as per FC-LS. | |
502 | */ | |
503 | bfa_trc(port->fcs, rnid->node_id_data_format); | |
504 | if (rnid->node_id_data_format == RNID_NODEID_DATA_FORMAT_DISCOVERY) { | |
505 | data_format = RNID_NODEID_DATA_FORMAT_DISCOVERY; | |
506 | /* | |
507 | * Get General topology data for this port | |
508 | */ | |
509 | bfa_fs_port_get_gen_topo_data(port, &gen_topo_data); | |
510 | } else { | |
511 | data_format = RNID_NODEID_DATA_FORMAT_COMMON; | |
512 | } | |
513 | ||
514 | /* | |
515 | * Copy the Node Id Info | |
516 | */ | |
517 | common_id_data.port_name = bfa_fcs_port_get_pwwn(port); | |
518 | common_id_data.node_name = bfa_fcs_port_get_nwwn(port); | |
519 | ||
520 | len = fc_rnid_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, | |
521 | bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, | |
522 | data_format, &common_id_data, &gen_topo_data); | |
523 | ||
524 | bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, | |
525 | BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, | |
526 | FC_MAX_PDUSZ, 0); | |
527 | ||
528 | return; | |
529 | } | |
530 | ||
531 | /* | |
532 | * Fill out General Topolpgy Discovery Data for RNID ELS. | |
533 | */ | |
534 | static void | |
535 | bfa_fs_port_get_gen_topo_data(struct bfa_fcs_port_s *port, | |
536 | struct fc_rnid_general_topology_data_s *gen_topo_data) | |
537 | { | |
538 | ||
539 | bfa_os_memset(gen_topo_data, 0, | |
540 | sizeof(struct fc_rnid_general_topology_data_s)); | |
541 | ||
542 | gen_topo_data->asso_type = bfa_os_htonl(RNID_ASSOCIATED_TYPE_HOST); | |
543 | gen_topo_data->phy_port_num = 0; /* @todo */ | |
544 | gen_topo_data->num_attached_nodes = bfa_os_htonl(1); | |
545 | } | |
546 | ||
547 | static void | |
548 | bfa_fcs_port_online_actions(struct bfa_fcs_port_s *port) | |
549 | { | |
550 | bfa_trc(port->fcs, port->fabric->oper_type); | |
551 | ||
552 | __port_action[port->fabric->fab_type].init(port); | |
553 | __port_action[port->fabric->fab_type].online(port); | |
554 | ||
555 | bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_ONLINE); | |
556 | bfa_fcb_port_online(port->fcs->bfad, port->port_cfg.roles, | |
557 | port->fabric->vf_drv, (port->vport == NULL) ? | |
558 | NULL : port->vport->vport_drv); | |
559 | } | |
560 | ||
561 | static void | |
562 | bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port) | |
563 | { | |
564 | struct list_head *qe, *qen; | |
565 | struct bfa_fcs_rport_s *rport; | |
566 | ||
567 | bfa_trc(port->fcs, port->fabric->oper_type); | |
568 | ||
569 | __port_action[port->fabric->fab_type].offline(port); | |
570 | ||
571 | if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) { | |
572 | bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT); | |
573 | } else { | |
574 | bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE); | |
575 | } | |
576 | bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles, | |
577 | port->fabric->vf_drv, | |
578 | (port->vport == NULL) ? NULL : port->vport->vport_drv); | |
579 | ||
580 | list_for_each_safe(qe, qen, &port->rport_q) { | |
581 | rport = (struct bfa_fcs_rport_s *)qe; | |
582 | bfa_fcs_rport_offline(rport); | |
583 | } | |
584 | } | |
585 | ||
586 | static void | |
587 | bfa_fcs_port_unknown_init(struct bfa_fcs_port_s *port) | |
588 | { | |
589 | bfa_assert(0); | |
590 | } | |
591 | ||
592 | static void | |
593 | bfa_fcs_port_unknown_online(struct bfa_fcs_port_s *port) | |
594 | { | |
595 | bfa_assert(0); | |
596 | } | |
597 | ||
598 | static void | |
599 | bfa_fcs_port_unknown_offline(struct bfa_fcs_port_s *port) | |
600 | { | |
601 | bfa_assert(0); | |
602 | } | |
603 | ||
604 | static void | |
605 | bfa_fcs_port_deleted(struct bfa_fcs_port_s *port) | |
606 | { | |
607 | bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DELETE); | |
608 | ||
609 | /* | |
610 | * Base port will be deleted by the OS driver | |
611 | */ | |
612 | if (port->vport) { | |
613 | bfa_fcb_port_delete(port->fcs->bfad, port->port_cfg.roles, | |
614 | port->fabric->vf_drv, | |
615 | port->vport ? port->vport->vport_drv : NULL); | |
616 | bfa_fcs_vport_delete_comp(port->vport); | |
617 | } else { | |
618 | bfa_fcs_fabric_port_delete_comp(port->fabric); | |
619 | } | |
620 | } | |
621 | ||
622 | ||
623 | ||
624 | /** | |
625 | * fcs_lport_api BFA FCS port API | |
626 | */ | |
627 | /** | |
628 | * Module initialization | |
629 | */ | |
630 | void | |
631 | bfa_fcs_port_modinit(struct bfa_fcs_s *fcs) | |
632 | { | |
633 | ||
634 | } | |
635 | ||
636 | /** | |
637 | * Module cleanup | |
638 | */ | |
639 | void | |
640 | bfa_fcs_port_modexit(struct bfa_fcs_s *fcs) | |
641 | { | |
642 | bfa_fcs_modexit_comp(fcs); | |
643 | } | |
644 | ||
645 | /** | |
646 | * Unsolicited frame receive handling. | |
647 | */ | |
648 | void | |
649 | bfa_fcs_port_uf_recv(struct bfa_fcs_port_s *lport, struct fchs_s *fchs, | |
650 | u16 len) | |
651 | { | |
652 | u32 pid = fchs->s_id; | |
653 | struct bfa_fcs_rport_s *rport = NULL; | |
654 | struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); | |
655 | ||
656 | bfa_stats(lport, uf_recvs); | |
657 | ||
658 | if (!bfa_fcs_port_is_online(lport)) { | |
659 | bfa_stats(lport, uf_recv_drops); | |
660 | return; | |
661 | } | |
662 | ||
663 | /** | |
664 | * First, handle ELSs that donot require a login. | |
665 | */ | |
666 | /* | |
667 | * Handle PLOGI first | |
668 | */ | |
669 | if ((fchs->type == FC_TYPE_ELS) && | |
670 | (els_cmd->els_code == FC_ELS_PLOGI)) { | |
671 | bfa_fcs_port_plogi(lport, fchs, (struct fc_logi_s *) els_cmd); | |
672 | return; | |
673 | } | |
674 | ||
675 | /* | |
676 | * Handle ECHO separately. | |
677 | */ | |
678 | if ((fchs->type == FC_TYPE_ELS) && (els_cmd->els_code == FC_ELS_ECHO)) { | |
679 | bfa_fcs_port_echo(lport, fchs, | |
680 | (struct fc_echo_s *) els_cmd, len); | |
681 | return; | |
682 | } | |
683 | ||
684 | /* | |
685 | * Handle RNID separately. | |
686 | */ | |
687 | if ((fchs->type == FC_TYPE_ELS) && (els_cmd->els_code == FC_ELS_RNID)) { | |
688 | bfa_fcs_port_rnid(lport, fchs, | |
689 | (struct fc_rnid_cmd_s *) els_cmd, len); | |
690 | return; | |
691 | } | |
692 | ||
693 | /** | |
694 | * look for a matching remote port ID | |
695 | */ | |
696 | rport = bfa_fcs_port_get_rport_by_pid(lport, pid); | |
697 | if (rport) { | |
698 | bfa_trc(rport->fcs, fchs->s_id); | |
699 | bfa_trc(rport->fcs, fchs->d_id); | |
700 | bfa_trc(rport->fcs, fchs->type); | |
701 | ||
702 | bfa_fcs_rport_uf_recv(rport, fchs, len); | |
703 | return; | |
704 | } | |
705 | ||
706 | /** | |
707 | * Only handles ELS frames for now. | |
708 | */ | |
709 | if (fchs->type != FC_TYPE_ELS) { | |
710 | bfa_trc(lport->fcs, fchs->type); | |
711 | bfa_assert(0); | |
712 | return; | |
713 | } | |
714 | ||
715 | bfa_trc(lport->fcs, els_cmd->els_code); | |
716 | if (els_cmd->els_code == FC_ELS_RSCN) { | |
717 | bfa_fcs_port_scn_process_rscn(lport, fchs, len); | |
718 | return; | |
719 | } | |
720 | ||
721 | if (els_cmd->els_code == FC_ELS_LOGO) { | |
722 | /** | |
723 | * @todo Handle LOGO frames received. | |
724 | */ | |
725 | bfa_trc(lport->fcs, els_cmd->els_code); | |
726 | return; | |
727 | } | |
728 | ||
729 | if (els_cmd->els_code == FC_ELS_PRLI) { | |
730 | /** | |
731 | * @todo Handle PRLI frames received. | |
732 | */ | |
733 | bfa_trc(lport->fcs, els_cmd->els_code); | |
734 | return; | |
735 | } | |
736 | ||
737 | /** | |
738 | * Unhandled ELS frames. Send a LS_RJT. | |
739 | */ | |
740 | bfa_fcs_port_send_ls_rjt(lport, fchs, FC_LS_RJT_RSN_CMD_NOT_SUPP, | |
741 | FC_LS_RJT_EXP_NO_ADDL_INFO); | |
742 | ||
743 | } | |
744 | ||
745 | /** | |
746 | * PID based Lookup for a R-Port in the Port R-Port Queue | |
747 | */ | |
748 | struct bfa_fcs_rport_s * | |
749 | bfa_fcs_port_get_rport_by_pid(struct bfa_fcs_port_s *port, u32 pid) | |
750 | { | |
751 | struct bfa_fcs_rport_s *rport; | |
752 | struct list_head *qe; | |
753 | ||
754 | list_for_each(qe, &port->rport_q) { | |
755 | rport = (struct bfa_fcs_rport_s *)qe; | |
756 | if (rport->pid == pid) | |
757 | return rport; | |
758 | } | |
759 | ||
760 | bfa_trc(port->fcs, pid); | |
761 | return NULL; | |
762 | } | |
763 | ||
764 | /** | |
765 | * PWWN based Lookup for a R-Port in the Port R-Port Queue | |
766 | */ | |
767 | struct bfa_fcs_rport_s * | |
768 | bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn) | |
769 | { | |
770 | struct bfa_fcs_rport_s *rport; | |
771 | struct list_head *qe; | |
772 | ||
773 | list_for_each(qe, &port->rport_q) { | |
774 | rport = (struct bfa_fcs_rport_s *)qe; | |
775 | if (wwn_is_equal(rport->pwwn, pwwn)) | |
776 | return rport; | |
777 | } | |
778 | ||
779 | bfa_trc(port->fcs, pwwn); | |
780 | return (NULL); | |
781 | } | |
782 | ||
783 | /** | |
784 | * NWWN based Lookup for a R-Port in the Port R-Port Queue | |
785 | */ | |
786 | struct bfa_fcs_rport_s * | |
787 | bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn) | |
788 | { | |
789 | struct bfa_fcs_rport_s *rport; | |
790 | struct list_head *qe; | |
791 | ||
792 | list_for_each(qe, &port->rport_q) { | |
793 | rport = (struct bfa_fcs_rport_s *)qe; | |
794 | if (wwn_is_equal(rport->nwwn, nwwn)) | |
795 | return rport; | |
796 | } | |
797 | ||
798 | bfa_trc(port->fcs, nwwn); | |
799 | return (NULL); | |
800 | } | |
801 | ||
802 | /** | |
803 | * Called by rport module when new rports are discovered. | |
804 | */ | |
805 | void | |
806 | bfa_fcs_port_add_rport(struct bfa_fcs_port_s *port, | |
807 | struct bfa_fcs_rport_s *rport) | |
808 | { | |
809 | list_add_tail(&rport->qe, &port->rport_q); | |
810 | port->num_rports++; | |
811 | } | |
812 | ||
813 | /** | |
814 | * Called by rport module to when rports are deleted. | |
815 | */ | |
816 | void | |
817 | bfa_fcs_port_del_rport(struct bfa_fcs_port_s *port, | |
818 | struct bfa_fcs_rport_s *rport) | |
819 | { | |
820 | bfa_assert(bfa_q_is_on_q(&port->rport_q, rport)); | |
821 | list_del(&rport->qe); | |
822 | port->num_rports--; | |
823 | ||
824 | bfa_sm_send_event(port, BFA_FCS_PORT_SM_DELRPORT); | |
825 | } | |
826 | ||
827 | /** | |
828 | * Called by fabric for base port when fabric login is complete. | |
829 | * Called by vport for virtual ports when FDISC is complete. | |
830 | */ | |
831 | void | |
832 | bfa_fcs_port_online(struct bfa_fcs_port_s *port) | |
833 | { | |
834 | bfa_sm_send_event(port, BFA_FCS_PORT_SM_ONLINE); | |
835 | } | |
836 | ||
837 | /** | |
838 | * Called by fabric for base port when fabric goes offline. | |
839 | * Called by vport for virtual ports when virtual port becomes offline. | |
840 | */ | |
841 | void | |
842 | bfa_fcs_port_offline(struct bfa_fcs_port_s *port) | |
843 | { | |
844 | bfa_sm_send_event(port, BFA_FCS_PORT_SM_OFFLINE); | |
845 | } | |
846 | ||
847 | /** | |
848 | * Called by fabric to delete base lport and associated resources. | |
849 | * | |
850 | * Called by vport to delete lport and associated resources. Should call | |
851 | * bfa_fcs_vport_delete_comp() for vports on completion. | |
852 | */ | |
853 | void | |
854 | bfa_fcs_port_delete(struct bfa_fcs_port_s *port) | |
855 | { | |
856 | bfa_sm_send_event(port, BFA_FCS_PORT_SM_DELETE); | |
857 | } | |
858 | ||
859 | /** | |
860 | * Called by fabric in private loop topology to process LIP event. | |
861 | */ | |
862 | void | |
863 | bfa_fcs_port_lip(struct bfa_fcs_port_s *port) | |
864 | { | |
865 | } | |
866 | ||
867 | /** | |
868 | * Return TRUE if port is online, else return FALSE | |
869 | */ | |
870 | bfa_boolean_t | |
871 | bfa_fcs_port_is_online(struct bfa_fcs_port_s *port) | |
872 | { | |
873 | return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)); | |
874 | } | |
875 | ||
876 | /** | |
877 | * Logical port initialization of base or virtual port. | |
878 | * Called by fabric for base port or by vport for virtual ports. | |
879 | */ | |
880 | void | |
881 | bfa_fcs_lport_init(struct bfa_fcs_port_s *lport, struct bfa_fcs_s *fcs, | |
882 | u16 vf_id, struct bfa_port_cfg_s *port_cfg, | |
883 | struct bfa_fcs_vport_s *vport) | |
884 | { | |
885 | lport->fcs = fcs; | |
886 | lport->fabric = bfa_fcs_vf_lookup(fcs, vf_id); | |
887 | bfa_os_assign(lport->port_cfg, *port_cfg); | |
888 | lport->vport = vport; | |
889 | lport->lp_tag = (vport) ? bfa_lps_get_tag(vport->lps) : | |
890 | bfa_lps_get_tag(lport->fabric->lps); | |
891 | ||
892 | INIT_LIST_HEAD(&lport->rport_q); | |
893 | lport->num_rports = 0; | |
894 | ||
895 | lport->bfad_port = | |
896 | bfa_fcb_port_new(fcs->bfad, lport, lport->port_cfg.roles, | |
897 | lport->fabric->vf_drv, | |
898 | vport ? vport->vport_drv : NULL); | |
899 | bfa_fcs_port_aen_post(lport, BFA_LPORT_AEN_NEW); | |
900 | ||
901 | bfa_sm_set_state(lport, bfa_fcs_port_sm_uninit); | |
902 | bfa_sm_send_event(lport, BFA_FCS_PORT_SM_CREATE); | |
903 | } | |
904 | ||
905 | ||
906 | ||
907 | /** | |
908 | * fcs_lport_api | |
909 | */ | |
910 | ||
911 | void | |
912 | bfa_fcs_port_get_attr(struct bfa_fcs_port_s *port, | |
913 | struct bfa_port_attr_s *port_attr) | |
914 | { | |
915 | if (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)) | |
916 | port_attr->pid = port->pid; | |
917 | else | |
918 | port_attr->pid = 0; | |
919 | ||
920 | port_attr->port_cfg = port->port_cfg; | |
921 | ||
922 | if (port->fabric) { | |
923 | port_attr->port_type = bfa_fcs_fabric_port_type(port->fabric); | |
924 | port_attr->loopback = bfa_fcs_fabric_is_loopback(port->fabric); | |
925 | port_attr->fabric_name = bfa_fcs_port_get_fabric_name(port); | |
926 | memcpy(port_attr->fabric_ip_addr, | |
927 | bfa_fcs_port_get_fabric_ipaddr(port), | |
928 | BFA_FCS_FABRIC_IPADDR_SZ); | |
929 | ||
930 | if (port->vport != NULL) | |
931 | port_attr->port_type = BFA_PPORT_TYPE_VPORT; | |
932 | ||
933 | } else { | |
934 | port_attr->port_type = BFA_PPORT_TYPE_UNKNOWN; | |
935 | port_attr->state = BFA_PORT_UNINIT; | |
936 | } | |
937 | ||
938 | } | |
939 | ||
940 |