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