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 | * rport_ftrs.c Remote port features (RPF) implementation. | |
20 | */ | |
21 | ||
22 | #include <bfa.h> | |
23 | #include <bfa_svc.h> | |
24 | #include "fcbuild.h" | |
25 | #include "fcs_rport.h" | |
26 | #include "fcs_lport.h" | |
27 | #include "fcs_trcmod.h" | |
28 | #include "fcs_fcxp.h" | |
29 | #include "fcs.h" | |
30 | ||
31 | BFA_TRC_FILE(FCS, RPORT_FTRS); | |
32 | ||
33 | #define BFA_FCS_RPF_RETRIES (3) | |
34 | #define BFA_FCS_RPF_RETRY_TIMEOUT (1000) /* 1 sec (In millisecs) */ | |
35 | ||
36 | static void bfa_fcs_rpf_send_rpsc2(void *rport_cbarg, | |
37 | struct bfa_fcxp_s *fcxp_alloced); | |
38 | static void bfa_fcs_rpf_rpsc2_response(void *fcsarg, | |
39 | struct bfa_fcxp_s *fcxp, void *cbarg, | |
40 | bfa_status_t req_status, u32 rsp_len, | |
41 | u32 resid_len, | |
42 | struct fchs_s *rsp_fchs); | |
43 | static void bfa_fcs_rpf_timeout(void *arg); | |
44 | ||
45 | /** | |
46 | * fcs_rport_ftrs_sm FCS rport state machine events | |
47 | */ | |
48 | ||
49 | enum rpf_event { | |
50 | RPFSM_EVENT_RPORT_OFFLINE = 1, /* Rport offline */ | |
51 | RPFSM_EVENT_RPORT_ONLINE = 2, /* Rport online */ | |
52 | RPFSM_EVENT_FCXP_SENT = 3, /* Frame from has been sent */ | |
53 | RPFSM_EVENT_TIMEOUT = 4, /* Rport SM timeout event */ | |
54 | RPFSM_EVENT_RPSC_COMP = 5, | |
55 | RPFSM_EVENT_RPSC_FAIL = 6, | |
56 | RPFSM_EVENT_RPSC_ERROR = 7, | |
57 | }; | |
58 | ||
59 | static void bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, | |
60 | enum rpf_event event); | |
61 | static void bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, | |
62 | enum rpf_event event); | |
63 | static void bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, | |
64 | enum rpf_event event); | |
65 | static void bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, | |
66 | enum rpf_event event); | |
67 | static void bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, | |
68 | enum rpf_event event); | |
69 | static void bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, | |
70 | enum rpf_event event); | |
71 | ||
72 | static void | |
73 | bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) | |
74 | { | |
75 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
4f1806bc | 76 | struct bfa_fcs_fabric_s *fabric = &rport->fcs->fabric; |
7725ccfd JH |
77 | |
78 | bfa_trc(rport->fcs, rport->pwwn); | |
79 | bfa_trc(rport->fcs, rport->pid); | |
80 | bfa_trc(rport->fcs, event); | |
81 | ||
82 | switch (event) { | |
f8ceafde | 83 | case RPFSM_EVENT_RPORT_ONLINE: |
4f1806bc JH |
84 | /* Send RPSC2 to a Brocade fabric only. */ |
85 | if ((!BFA_FCS_PID_IS_WKA(rport->pid)) && | |
86 | ((bfa_lps_is_brcd_fabric(rport->port->fabric->lps)) || | |
87 | (bfa_fcs_fabric_get_switch_oui(fabric) == | |
88 | BFA_FCS_BRCD_SWITCH_OUI))) { | |
7725ccfd JH |
89 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); |
90 | rpf->rpsc_retries = 0; | |
91 | bfa_fcs_rpf_send_rpsc2(rpf, NULL); | |
4f1806bc JH |
92 | } |
93 | break; | |
7725ccfd | 94 | |
f8ceafde | 95 | case RPFSM_EVENT_RPORT_OFFLINE: |
7725ccfd JH |
96 | break; |
97 | ||
98 | default: | |
e641de37 | 99 | bfa_sm_fault(rport->fcs, event); |
7725ccfd JH |
100 | } |
101 | } | |
102 | ||
103 | static void | |
104 | bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) | |
105 | { | |
106 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
107 | ||
108 | bfa_trc(rport->fcs, event); | |
109 | ||
110 | switch (event) { | |
111 | case RPFSM_EVENT_FCXP_SENT: | |
112 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc); | |
113 | break; | |
114 | ||
f8ceafde | 115 | case RPFSM_EVENT_RPORT_OFFLINE: |
7725ccfd JH |
116 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); |
117 | bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe); | |
118 | rpf->rpsc_retries = 0; | |
119 | break; | |
120 | ||
121 | default: | |
e641de37 | 122 | bfa_sm_fault(rport->fcs, event); |
7725ccfd JH |
123 | } |
124 | } | |
125 | ||
126 | static void | |
127 | bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) | |
128 | { | |
129 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
130 | ||
131 | bfa_trc(rport->fcs, rport->pid); | |
132 | bfa_trc(rport->fcs, event); | |
133 | ||
134 | switch (event) { | |
135 | case RPFSM_EVENT_RPSC_COMP: | |
136 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); | |
137 | /* Update speed info in f/w via BFA */ | |
f8ceafde | 138 | if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) |
7725ccfd | 139 | bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed); |
f8ceafde | 140 | else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) |
7725ccfd | 141 | bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed); |
7725ccfd JH |
142 | break; |
143 | ||
144 | case RPFSM_EVENT_RPSC_FAIL: | |
145 | /* RPSC not supported by rport */ | |
146 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); | |
147 | break; | |
148 | ||
149 | case RPFSM_EVENT_RPSC_ERROR: | |
150 | /* need to retry...delayed a bit. */ | |
151 | if (rpf->rpsc_retries++ < BFA_FCS_RPF_RETRIES) { | |
152 | bfa_timer_start(rport->fcs->bfa, &rpf->timer, | |
153 | bfa_fcs_rpf_timeout, rpf, | |
154 | BFA_FCS_RPF_RETRY_TIMEOUT); | |
155 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_retry); | |
156 | } else { | |
157 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); | |
158 | } | |
159 | break; | |
160 | ||
f8ceafde | 161 | case RPFSM_EVENT_RPORT_OFFLINE: |
7725ccfd JH |
162 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); |
163 | bfa_fcxp_discard(rpf->fcxp); | |
164 | rpf->rpsc_retries = 0; | |
165 | break; | |
166 | ||
167 | default: | |
e641de37 | 168 | bfa_sm_fault(rport->fcs, event); |
7725ccfd JH |
169 | } |
170 | } | |
171 | ||
172 | static void | |
173 | bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) | |
174 | { | |
175 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
176 | ||
177 | bfa_trc(rport->fcs, rport->pid); | |
178 | bfa_trc(rport->fcs, event); | |
179 | ||
180 | switch (event) { | |
f8ceafde | 181 | case RPFSM_EVENT_TIMEOUT: |
7725ccfd JH |
182 | /* re-send the RPSC */ |
183 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); | |
184 | bfa_fcs_rpf_send_rpsc2(rpf, NULL); | |
185 | break; | |
186 | ||
f8ceafde | 187 | case RPFSM_EVENT_RPORT_OFFLINE: |
7725ccfd JH |
188 | bfa_timer_stop(&rpf->timer); |
189 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); | |
190 | rpf->rpsc_retries = 0; | |
191 | break; | |
192 | ||
193 | default: | |
e641de37 | 194 | bfa_sm_fault(rport->fcs, event); |
7725ccfd JH |
195 | } |
196 | } | |
197 | ||
198 | static void | |
199 | bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) | |
200 | { | |
201 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
202 | ||
203 | bfa_trc(rport->fcs, rport->pwwn); | |
204 | bfa_trc(rport->fcs, rport->pid); | |
205 | bfa_trc(rport->fcs, event); | |
206 | ||
207 | switch (event) { | |
f8ceafde | 208 | case RPFSM_EVENT_RPORT_OFFLINE: |
7725ccfd JH |
209 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); |
210 | rpf->rpsc_retries = 0; | |
211 | break; | |
212 | ||
213 | default: | |
e641de37 | 214 | bfa_sm_fault(rport->fcs, event); |
7725ccfd JH |
215 | } |
216 | } | |
217 | ||
218 | static void | |
219 | bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) | |
220 | { | |
221 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
222 | ||
223 | bfa_trc(rport->fcs, rport->pwwn); | |
224 | bfa_trc(rport->fcs, rport->pid); | |
225 | bfa_trc(rport->fcs, event); | |
226 | ||
227 | switch (event) { | |
f8ceafde | 228 | case RPFSM_EVENT_RPORT_ONLINE: |
7725ccfd JH |
229 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); |
230 | bfa_fcs_rpf_send_rpsc2(rpf, NULL); | |
231 | break; | |
232 | ||
f8ceafde | 233 | case RPFSM_EVENT_RPORT_OFFLINE: |
7725ccfd JH |
234 | break; |
235 | ||
236 | default: | |
e641de37 | 237 | bfa_sm_fault(rport->fcs, event); |
7725ccfd JH |
238 | } |
239 | } | |
240 | /** | |
241 | * Called when Rport is created. | |
242 | */ | |
243 | void bfa_fcs_rpf_init(struct bfa_fcs_rport_s *rport) | |
244 | { | |
245 | struct bfa_fcs_rpf_s *rpf = &rport->rpf; | |
246 | ||
247 | bfa_trc(rport->fcs, rport->pid); | |
248 | rpf->rport = rport; | |
249 | ||
250 | bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_uninit); | |
251 | } | |
252 | ||
253 | /** | |
254 | * Called when Rport becomes online | |
255 | */ | |
256 | void bfa_fcs_rpf_rport_online(struct bfa_fcs_rport_s *rport) | |
257 | { | |
258 | bfa_trc(rport->fcs, rport->pid); | |
259 | ||
260 | if (__fcs_min_cfg(rport->port->fcs)) | |
261 | return; | |
262 | ||
263 | if (bfa_fcs_fabric_is_switched(rport->port->fabric)) | |
264 | bfa_sm_send_event(&rport->rpf, RPFSM_EVENT_RPORT_ONLINE); | |
265 | } | |
266 | ||
267 | /** | |
268 | * Called when Rport becomes offline | |
269 | */ | |
270 | void bfa_fcs_rpf_rport_offline(struct bfa_fcs_rport_s *rport) | |
271 | { | |
272 | bfa_trc(rport->fcs, rport->pid); | |
273 | ||
274 | if (__fcs_min_cfg(rport->port->fcs)) | |
275 | return; | |
276 | ||
c5073417 | 277 | rport->rpf.rpsc_speed = 0; |
7725ccfd JH |
278 | bfa_sm_send_event(&rport->rpf, RPFSM_EVENT_RPORT_OFFLINE); |
279 | } | |
280 | ||
281 | static void | |
282 | bfa_fcs_rpf_timeout(void *arg) | |
283 | { | |
284 | struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *) arg; | |
285 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
286 | ||
287 | bfa_trc(rport->fcs, rport->pid); | |
288 | bfa_sm_send_event(rpf, RPFSM_EVENT_TIMEOUT); | |
289 | } | |
290 | ||
291 | static void | |
292 | bfa_fcs_rpf_send_rpsc2(void *rpf_cbarg, struct bfa_fcxp_s *fcxp_alloced) | |
293 | { | |
294 | struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *)rpf_cbarg; | |
295 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
296 | struct bfa_fcs_port_s *port = rport->port; | |
297 | struct fchs_s fchs; | |
298 | int len; | |
299 | struct bfa_fcxp_s *fcxp; | |
300 | ||
301 | bfa_trc(rport->fcs, rport->pwwn); | |
302 | ||
303 | fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); | |
304 | if (!fcxp) { | |
305 | bfa_fcxp_alloc_wait(port->fcs->bfa, &rpf->fcxp_wqe, | |
306 | bfa_fcs_rpf_send_rpsc2, rpf); | |
307 | return; | |
308 | } | |
309 | rpf->fcxp = fcxp; | |
310 | ||
311 | len = fc_rpsc2_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, | |
312 | bfa_fcs_port_get_fcid(port), &rport->pid, 1); | |
313 | ||
314 | bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, | |
315 | FC_CLASS_3, len, &fchs, bfa_fcs_rpf_rpsc2_response, | |
4f1806bc | 316 | rpf, FC_MAX_PDUSZ, FC_ELS_TOV); |
7725ccfd JH |
317 | rport->stats.rpsc_sent++; |
318 | bfa_sm_send_event(rpf, RPFSM_EVENT_FCXP_SENT); | |
319 | ||
320 | } | |
321 | ||
322 | static void | |
323 | bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, | |
324 | bfa_status_t req_status, u32 rsp_len, | |
325 | u32 resid_len, struct fchs_s *rsp_fchs) | |
326 | { | |
327 | struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *) cbarg; | |
328 | struct bfa_fcs_rport_s *rport = rpf->rport; | |
329 | struct fc_ls_rjt_s *ls_rjt; | |
330 | struct fc_rpsc2_acc_s *rpsc2_acc; | |
331 | u16 num_ents; | |
332 | ||
333 | bfa_trc(rport->fcs, req_status); | |
334 | ||
335 | if (req_status != BFA_STATUS_OK) { | |
336 | bfa_trc(rport->fcs, req_status); | |
337 | if (req_status == BFA_STATUS_ETIMER) | |
338 | rport->stats.rpsc_failed++; | |
339 | bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); | |
340 | return; | |
341 | } | |
342 | ||
343 | rpsc2_acc = (struct fc_rpsc2_acc_s *) BFA_FCXP_RSP_PLD(fcxp); | |
344 | if (rpsc2_acc->els_cmd == FC_ELS_ACC) { | |
345 | rport->stats.rpsc_accs++; | |
346 | num_ents = bfa_os_ntohs(rpsc2_acc->num_pids); | |
347 | bfa_trc(rport->fcs, num_ents); | |
348 | if (num_ents > 0) { | |
349 | bfa_assert(rpsc2_acc->port_info[0].pid != rport->pid); | |
350 | bfa_trc(rport->fcs, | |
351 | bfa_os_ntohs(rpsc2_acc->port_info[0].pid)); | |
352 | bfa_trc(rport->fcs, | |
353 | bfa_os_ntohs(rpsc2_acc->port_info[0].speed)); | |
354 | bfa_trc(rport->fcs, | |
355 | bfa_os_ntohs(rpsc2_acc->port_info[0].index)); | |
356 | bfa_trc(rport->fcs, | |
357 | rpsc2_acc->port_info[0].type); | |
358 | ||
359 | if (rpsc2_acc->port_info[0].speed == 0) { | |
360 | bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); | |
361 | return; | |
362 | } | |
363 | ||
364 | rpf->rpsc_speed = fc_rpsc_operspeed_to_bfa_speed( | |
365 | bfa_os_ntohs(rpsc2_acc->port_info[0].speed)); | |
366 | ||
367 | bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_COMP); | |
368 | } | |
369 | } else { | |
370 | ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); | |
371 | bfa_trc(rport->fcs, ls_rjt->reason_code); | |
372 | bfa_trc(rport->fcs, ls_rjt->reason_code_expl); | |
373 | rport->stats.rpsc_rejects++; | |
f8ceafde | 374 | if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) |
7725ccfd | 375 | bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL); |
f8ceafde | 376 | else |
7725ccfd | 377 | bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); |
7725ccfd JH |
378 | } |
379 | } |