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 28BFA_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 */ 36static void bfa_fcs_port_scn_send_scr(void *scn_cbarg, 37 struct bfa_fcxp_s *fcxp_alloced); 38static 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); 45static void bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, 46 struct fchs_s *rx_fchs); 47static 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 */ 56enum 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 65static void bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, 66 enum port_scn_event event); 67static void bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, 68 enum port_scn_event event); 69static void bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, 70 enum port_scn_event event); 71static void bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, 72 enum port_scn_event event); 73static 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 */ 79static void 80bfa_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_sm_fault(scn->port->fcs, event); 94 } 95} 96 97static void 98bfa_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_sm_fault(scn->port->fcs, event); 113 } 114} 115 116static void 117bfa_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_sm_fault(scn->port->fcs, event); 141 } 142} 143 144static void 145bfa_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_sm_fault(scn->port->fcs, event); 161 } 162} 163 164static void 165bfa_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_sm_fault(scn->port->fcs, event); 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 */ 187static void 188bfa_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_ELS_TOV); 222 223 bfa_sm_send_event(scn, SCNSM_EVENT_SCR_SENT); 224} 225 226static void 227bfa_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 */ 274static void 275bfa_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 */ 310static void 311bfa_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 */ 327void 328bfa_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 336void 337bfa_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 345void 346bfa_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 354static void 355bfa_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 389static void 390bfa_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 410void 411bfa_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