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