diff options
Diffstat (limited to 'drivers/scsi/bfa/bfa_fcs_lport.c')
-rw-r--r-- | drivers/scsi/bfa/bfa_fcs_lport.c | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c new file mode 100644 index 000000000000..8975ed041dc0 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_lport.c | |||
@@ -0,0 +1,940 @@ | |||
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 | |||