diff options
author | Jing Huang <huangj@brocade.com> | 2009-09-23 20:46:15 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-10-02 10:47:40 -0400 |
commit | 7725ccfda59715ecf8f99e3b520a0b84cc2ea79e (patch) | |
tree | df76910891c6b92bf23c06c84955bf600c9d7573 /drivers/scsi/bfa/scn.c | |
parent | 5415907af1f5ef80c95147bacbd321b0d4236dd5 (diff) |
[SCSI] bfa: Brocade BFA FC SCSI driver
Add new driver for Brocade Hardware
Signed-off-by: Jing Huang <huangj@brocade.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/bfa/scn.c')
-rw-r--r-- | drivers/scsi/bfa/scn.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/drivers/scsi/bfa/scn.c b/drivers/scsi/bfa/scn.c new file mode 100644 index 000000000000..bd4771ff62c8 --- /dev/null +++ b/drivers/scsi/bfa/scn.c | |||
@@ -0,0 +1,482 @@ | |||
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 | #include <bfa.h> | ||
19 | #include <bfa_svc.h> | ||
20 | #include "fcs_lport.h" | ||
21 | #include "fcs_rport.h" | ||
22 | #include "fcs_ms.h" | ||
23 | #include "fcs_trcmod.h" | ||
24 | #include "fcs_fcxp.h" | ||
25 | #include "fcs.h" | ||
26 | #include "lport_priv.h" | ||
27 | |||
28 | BFA_TRC_FILE(FCS, SCN); | ||
29 | |||
30 | #define FC_QOS_RSCN_EVENT 0x0c | ||
31 | #define FC_FABRIC_NAME_RSCN_EVENT 0x0d | ||
32 | |||
33 | /* | ||
34 | * forward declarations | ||
35 | */ | ||
36 | static void bfa_fcs_port_scn_send_scr(void *scn_cbarg, | ||
37 | struct bfa_fcxp_s *fcxp_alloced); | ||
38 | static void bfa_fcs_port_scn_scr_response(void *fcsarg, | ||
39 | struct bfa_fcxp_s *fcxp, | ||
40 | void *cbarg, | ||
41 | bfa_status_t req_status, | ||
42 | u32 rsp_len, | ||
43 | u32 resid_len, | ||
44 | struct fchs_s *rsp_fchs); | ||
45 | static void bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, | ||
46 | struct fchs_s *rx_fchs); | ||
47 | static void bfa_fcs_port_scn_timeout(void *arg); | ||
48 | |||
49 | /** | ||
50 | * fcs_scm_sm FCS SCN state machine | ||
51 | */ | ||
52 | |||
53 | /** | ||
54 | * VPort SCN State Machine events | ||
55 | */ | ||
56 | enum port_scn_event { | ||
57 | SCNSM_EVENT_PORT_ONLINE = 1, | ||
58 | SCNSM_EVENT_PORT_OFFLINE = 2, | ||
59 | SCNSM_EVENT_RSP_OK = 3, | ||
60 | SCNSM_EVENT_RSP_ERROR = 4, | ||
61 | SCNSM_EVENT_TIMEOUT = 5, | ||
62 | SCNSM_EVENT_SCR_SENT = 6, | ||
63 | }; | ||
64 | |||
65 | static void bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, | ||
66 | enum port_scn_event event); | ||
67 | static void bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, | ||
68 | enum port_scn_event event); | ||
69 | static void bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, | ||
70 | enum port_scn_event event); | ||
71 | static void bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, | ||
72 | enum port_scn_event event); | ||
73 | static void bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn, | ||
74 | enum port_scn_event event); | ||
75 | |||
76 | /** | ||
77 | * Starting state - awaiting link up. | ||
78 | */ | ||
79 | static void | ||
80 | bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, | ||
81 | enum port_scn_event event) | ||
82 | { | ||
83 | switch (event) { | ||
84 | case SCNSM_EVENT_PORT_ONLINE: | ||
85 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr); | ||
86 | bfa_fcs_port_scn_send_scr(scn, NULL); | ||
87 | break; | ||
88 | |||
89 | case SCNSM_EVENT_PORT_OFFLINE: | ||
90 | break; | ||
91 | |||
92 | default: | ||
93 | bfa_assert(0); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | static void | ||
98 | bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, | ||
99 | enum port_scn_event event) | ||
100 | { | ||
101 | switch (event) { | ||
102 | case SCNSM_EVENT_SCR_SENT: | ||
103 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr); | ||
104 | break; | ||
105 | |||
106 | case SCNSM_EVENT_PORT_OFFLINE: | ||
107 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); | ||
108 | bfa_fcxp_walloc_cancel(scn->port->fcs->bfa, &scn->fcxp_wqe); | ||
109 | break; | ||
110 | |||
111 | default: | ||
112 | bfa_assert(0); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static void | ||
117 | bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, | ||
118 | enum port_scn_event event) | ||
119 | { | ||
120 | struct bfa_fcs_port_s *port = scn->port; | ||
121 | |||
122 | switch (event) { | ||
123 | case SCNSM_EVENT_RSP_OK: | ||
124 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_online); | ||
125 | break; | ||
126 | |||
127 | case SCNSM_EVENT_RSP_ERROR: | ||
128 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr_retry); | ||
129 | bfa_timer_start(port->fcs->bfa, &scn->timer, | ||
130 | bfa_fcs_port_scn_timeout, scn, | ||
131 | BFA_FCS_RETRY_TIMEOUT); | ||
132 | break; | ||
133 | |||
134 | case SCNSM_EVENT_PORT_OFFLINE: | ||
135 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); | ||
136 | bfa_fcxp_discard(scn->fcxp); | ||
137 | break; | ||
138 | |||
139 | default: | ||
140 | bfa_assert(0); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | static void | ||
145 | bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, | ||
146 | enum port_scn_event event) | ||
147 | { | ||
148 | switch (event) { | ||
149 | case SCNSM_EVENT_TIMEOUT: | ||
150 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr); | ||
151 | bfa_fcs_port_scn_send_scr(scn, NULL); | ||
152 | break; | ||
153 | |||
154 | case SCNSM_EVENT_PORT_OFFLINE: | ||
155 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); | ||
156 | bfa_timer_stop(&scn->timer); | ||
157 | break; | ||
158 | |||
159 | default: | ||
160 | bfa_assert(0); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | static void | ||
165 | bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn, | ||
166 | enum port_scn_event event) | ||
167 | { | ||
168 | switch (event) { | ||
169 | case SCNSM_EVENT_PORT_OFFLINE: | ||
170 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); | ||
171 | break; | ||
172 | |||
173 | default: | ||
174 | bfa_assert(0); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | |||
179 | |||
180 | /** | ||
181 | * fcs_scn_private FCS SCN private functions | ||
182 | */ | ||
183 | |||
184 | /** | ||
185 | * This routine will be called to send a SCR command. | ||
186 | */ | ||
187 | static void | ||
188 | bfa_fcs_port_scn_send_scr(void *scn_cbarg, struct bfa_fcxp_s *fcxp_alloced) | ||
189 | { | ||
190 | struct bfa_fcs_port_scn_s *scn = scn_cbarg; | ||
191 | struct bfa_fcs_port_s *port = scn->port; | ||
192 | struct fchs_s fchs; | ||
193 | int len; | ||
194 | struct bfa_fcxp_s *fcxp; | ||
195 | |||
196 | bfa_trc(port->fcs, port->pid); | ||
197 | bfa_trc(port->fcs, port->port_cfg.pwwn); | ||
198 | |||
199 | fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); | ||
200 | if (!fcxp) { | ||
201 | bfa_fcxp_alloc_wait(port->fcs->bfa, &scn->fcxp_wqe, | ||
202 | bfa_fcs_port_scn_send_scr, scn); | ||
203 | return; | ||
204 | } | ||
205 | scn->fcxp = fcxp; | ||
206 | |||
207 | /* | ||
208 | * Handle VU registrations for Base port only | ||
209 | */ | ||
210 | if ((!port->vport) && bfa_ioc_get_fcmode(&port->fcs->bfa->ioc)) { | ||
211 | len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), | ||
212 | bfa_lps_is_brcd_fabric(port->fabric->lps), | ||
213 | port->pid, 0); | ||
214 | } else { | ||
215 | len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), BFA_FALSE, | ||
216 | port->pid, 0); | ||
217 | } | ||
218 | |||
219 | bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, | ||
220 | FC_CLASS_3, len, &fchs, bfa_fcs_port_scn_scr_response, | ||
221 | (void *)scn, FC_MAX_PDUSZ, FC_RA_TOV); | ||
222 | |||
223 | bfa_sm_send_event(scn, SCNSM_EVENT_SCR_SENT); | ||
224 | } | ||
225 | |||
226 | static void | ||
227 | bfa_fcs_port_scn_scr_response(void *fcsarg, struct bfa_fcxp_s *fcxp, | ||
228 | void *cbarg, bfa_status_t req_status, | ||
229 | u32 rsp_len, u32 resid_len, | ||
230 | struct fchs_s *rsp_fchs) | ||
231 | { | ||
232 | struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)cbarg; | ||
233 | struct bfa_fcs_port_s *port = scn->port; | ||
234 | struct fc_els_cmd_s *els_cmd; | ||
235 | struct fc_ls_rjt_s *ls_rjt; | ||
236 | |||
237 | bfa_trc(port->fcs, port->port_cfg.pwwn); | ||
238 | |||
239 | /* | ||
240 | * Sanity Checks | ||
241 | */ | ||
242 | if (req_status != BFA_STATUS_OK) { | ||
243 | bfa_trc(port->fcs, req_status); | ||
244 | bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); | ||
249 | |||
250 | switch (els_cmd->els_code) { | ||
251 | |||
252 | case FC_ELS_ACC: | ||
253 | bfa_sm_send_event(scn, SCNSM_EVENT_RSP_OK); | ||
254 | break; | ||
255 | |||
256 | case FC_ELS_LS_RJT: | ||
257 | |||
258 | ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); | ||
259 | |||
260 | bfa_trc(port->fcs, ls_rjt->reason_code); | ||
261 | bfa_trc(port->fcs, ls_rjt->reason_code_expl); | ||
262 | |||
263 | bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); | ||
264 | break; | ||
265 | |||
266 | default: | ||
267 | bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Send a LS Accept | ||
273 | */ | ||
274 | static void | ||
275 | bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, | ||
276 | struct fchs_s *rx_fchs) | ||
277 | { | ||
278 | struct fchs_s fchs; | ||
279 | struct bfa_fcxp_s *fcxp; | ||
280 | struct bfa_rport_s *bfa_rport = NULL; | ||
281 | int len; | ||
282 | |||
283 | bfa_trc(port->fcs, rx_fchs->s_id); | ||
284 | |||
285 | fcxp = bfa_fcs_fcxp_alloc(port->fcs); | ||
286 | if (!fcxp) | ||
287 | return; | ||
288 | |||
289 | len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, | ||
290 | bfa_fcs_port_get_fcid(port), rx_fchs->ox_id); | ||
291 | |||
292 | bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, | ||
293 | BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, | ||
294 | FC_MAX_PDUSZ, 0); | ||
295 | } | ||
296 | |||
297 | /** | ||
298 | * This routine will be called by bfa_timer on timer timeouts. | ||
299 | * | ||
300 | * param[in] vport - pointer to bfa_fcs_port_t. | ||
301 | * param[out] vport_status - pointer to return vport status in | ||
302 | * | ||
303 | * return | ||
304 | * void | ||
305 | * | ||
306 | * Special Considerations: | ||
307 | * | ||
308 | * note | ||
309 | */ | ||
310 | static void | ||
311 | bfa_fcs_port_scn_timeout(void *arg) | ||
312 | { | ||
313 | struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)arg; | ||
314 | |||
315 | bfa_sm_send_event(scn, SCNSM_EVENT_TIMEOUT); | ||
316 | } | ||
317 | |||
318 | |||
319 | |||
320 | /** | ||
321 | * fcs_scn_public FCS state change notification public interfaces | ||
322 | */ | ||
323 | |||
324 | /* | ||
325 | * Functions called by port/fab | ||
326 | */ | ||
327 | void | ||
328 | bfa_fcs_port_scn_init(struct bfa_fcs_port_s *port) | ||
329 | { | ||
330 | struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); | ||
331 | |||
332 | scn->port = port; | ||
333 | bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); | ||
334 | } | ||
335 | |||
336 | void | ||
337 | bfa_fcs_port_scn_offline(struct bfa_fcs_port_s *port) | ||
338 | { | ||
339 | struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); | ||
340 | |||
341 | scn->port = port; | ||
342 | bfa_sm_send_event(scn, SCNSM_EVENT_PORT_OFFLINE); | ||
343 | } | ||
344 | |||
345 | void | ||
346 | bfa_fcs_port_scn_online(struct bfa_fcs_port_s *port) | ||
347 | { | ||
348 | struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); | ||
349 | |||
350 | scn->port = port; | ||
351 | bfa_sm_send_event(scn, SCNSM_EVENT_PORT_ONLINE); | ||
352 | } | ||
353 | |||
354 | static void | ||
355 | bfa_fcs_port_scn_portid_rscn(struct bfa_fcs_port_s *port, u32 rpid) | ||
356 | { | ||
357 | struct bfa_fcs_rport_s *rport; | ||
358 | |||
359 | bfa_trc(port->fcs, rpid); | ||
360 | |||
361 | /** | ||
362 | * If this is an unknown device, then it just came online. | ||
363 | * Otherwise let rport handle the RSCN event. | ||
364 | */ | ||
365 | rport = bfa_fcs_port_get_rport_by_pid(port, rpid); | ||
366 | if (rport == NULL) { | ||
367 | /* | ||
368 | * If min cfg mode is enabled, we donot need to | ||
369 | * discover any new rports. | ||
370 | */ | ||
371 | if (!__fcs_min_cfg(port->fcs)) | ||
372 | rport = bfa_fcs_rport_create(port, rpid); | ||
373 | } else { | ||
374 | bfa_fcs_rport_scn(rport); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * rscn format based PID comparison | ||
380 | */ | ||
381 | #define __fc_pid_match(__c0, __c1, __fmt) \ | ||
382 | (((__fmt) == FC_RSCN_FORMAT_FABRIC) || \ | ||
383 | (((__fmt) == FC_RSCN_FORMAT_DOMAIN) && \ | ||
384 | ((__c0)[0] == (__c1)[0])) || \ | ||
385 | (((__fmt) == FC_RSCN_FORMAT_AREA) && \ | ||
386 | ((__c0)[0] == (__c1)[0]) && \ | ||
387 | ((__c0)[1] == (__c1)[1]))) | ||
388 | |||
389 | static void | ||
390 | bfa_fcs_port_scn_multiport_rscn(struct bfa_fcs_port_s *port, | ||
391 | enum fc_rscn_format format, u32 rscn_pid) | ||
392 | { | ||
393 | struct bfa_fcs_rport_s *rport; | ||
394 | struct list_head *qe, *qe_next; | ||
395 | u8 *c0, *c1; | ||
396 | |||
397 | bfa_trc(port->fcs, format); | ||
398 | bfa_trc(port->fcs, rscn_pid); | ||
399 | |||
400 | c0 = (u8 *) &rscn_pid; | ||
401 | |||
402 | list_for_each_safe(qe, qe_next, &port->rport_q) { | ||
403 | rport = (struct bfa_fcs_rport_s *)qe; | ||
404 | c1 = (u8 *) &rport->pid; | ||
405 | if (__fc_pid_match(c0, c1, format)) | ||
406 | bfa_fcs_rport_scn(rport); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | void | ||
411 | bfa_fcs_port_scn_process_rscn(struct bfa_fcs_port_s *port, struct fchs_s *fchs, | ||
412 | u32 len) | ||
413 | { | ||
414 | struct fc_rscn_pl_s *rscn = (struct fc_rscn_pl_s *) (fchs + 1); | ||
415 | int num_entries; | ||
416 | u32 rscn_pid; | ||
417 | bfa_boolean_t nsquery = BFA_FALSE; | ||
418 | int i = 0; | ||
419 | |||
420 | num_entries = | ||
421 | (bfa_os_ntohs(rscn->payldlen) - | ||
422 | sizeof(u32)) / sizeof(rscn->event[0]); | ||
423 | |||
424 | bfa_trc(port->fcs, num_entries); | ||
425 | |||
426 | port->stats.num_rscn++; | ||
427 | |||
428 | bfa_fcs_port_scn_send_ls_acc(port, fchs); | ||
429 | |||
430 | for (i = 0; i < num_entries; i++) { | ||
431 | rscn_pid = rscn->event[i].portid; | ||
432 | |||
433 | bfa_trc(port->fcs, rscn->event[i].format); | ||
434 | bfa_trc(port->fcs, rscn_pid); | ||
435 | |||
436 | switch (rscn->event[i].format) { | ||
437 | case FC_RSCN_FORMAT_PORTID: | ||
438 | if (rscn->event[i].qualifier == FC_QOS_RSCN_EVENT) { | ||
439 | /* | ||
440 | * Ignore this event. f/w would have processed | ||
441 | * it | ||
442 | */ | ||
443 | bfa_trc(port->fcs, rscn_pid); | ||
444 | } else { | ||
445 | port->stats.num_portid_rscn++; | ||
446 | bfa_fcs_port_scn_portid_rscn(port, rscn_pid); | ||
447 | } | ||
448 | break; | ||
449 | |||
450 | case FC_RSCN_FORMAT_FABRIC: | ||
451 | if (rscn->event[i].qualifier == | ||
452 | FC_FABRIC_NAME_RSCN_EVENT) { | ||
453 | bfa_fcs_port_ms_fabric_rscn(port); | ||
454 | break; | ||
455 | } | ||
456 | /* | ||
457 | * !!!!!!!!! Fall Through !!!!!!!!!!!!! | ||
458 | */ | ||
459 | |||
460 | case FC_RSCN_FORMAT_AREA: | ||
461 | case FC_RSCN_FORMAT_DOMAIN: | ||
462 | nsquery = BFA_TRUE; | ||
463 | bfa_fcs_port_scn_multiport_rscn(port, | ||
464 | rscn->event[i].format, | ||
465 | rscn_pid); | ||
466 | break; | ||
467 | |||
468 | default: | ||
469 | bfa_assert(0); | ||
470 | nsquery = BFA_TRUE; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | /** | ||
475 | * If any of area, domain or fabric RSCN is received, do a fresh discovery | ||
476 | * to find new devices. | ||
477 | */ | ||
478 | if (nsquery) | ||
479 | bfa_fcs_port_ns_query(port); | ||
480 | } | ||
481 | |||
482 | |||