| /* | |
| * Copyright (c) 2005-2014 Brocade Communications Systems, Inc. | |
| * Copyright (c) 2014- QLogic Corporation. | |
| * All rights reserved | |
| * www.qlogic.com | |
| * | |
| * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter. | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License (GPL) Version 2 as | |
| * published by the Free Software Foundation | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * General Public License for more details. | |
| */ | |
| /* | |
| * fcpim.c - FCP initiator mode i-t nexus state machine | |
| */ | |
| #include "bfad_drv.h" | |
| #include "bfa_fcs.h" | |
| #include "bfa_fcbuild.h" | |
| #include "bfad_im.h" | |
| BFA_TRC_FILE(FCS, FCPIM); | |
| /* | |
| * forward declarations | |
| */ | |
| static void bfa_fcs_itnim_timeout(void *arg); | |
| static void bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim); | |
| static void bfa_fcs_itnim_send_prli(void *itnim_cbarg, | |
| struct bfa_fcxp_s *fcxp_alloced); | |
| static void bfa_fcs_itnim_prli_response(void *fcsarg, | |
| struct bfa_fcxp_s *fcxp, void *cbarg, | |
| bfa_status_t req_status, u32 rsp_len, | |
| u32 resid_len, struct fchs_s *rsp_fchs); | |
| static void bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_itnim_aen_event event); | |
| static void bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static void bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event); | |
| static struct bfa_sm_table_s itnim_sm_table[] = { | |
| {BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE}, | |
| {BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND}, | |
| {BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT}, | |
| {BFA_SM(bfa_fcs_itnim_sm_prli_retry), BFA_ITNIM_PRLI_RETRY}, | |
| {BFA_SM(bfa_fcs_itnim_sm_hcb_online), BFA_ITNIM_HCB_ONLINE}, | |
| {BFA_SM(bfa_fcs_itnim_sm_online), BFA_ITNIM_ONLINE}, | |
| {BFA_SM(bfa_fcs_itnim_sm_hcb_offline), BFA_ITNIM_HCB_OFFLINE}, | |
| {BFA_SM(bfa_fcs_itnim_sm_initiator), BFA_ITNIM_INITIATIOR}, | |
| }; | |
| /* | |
| * fcs_itnim_sm FCS itnim state machine | |
| */ | |
| static void | |
| bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_FCS_ONLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); | |
| itnim->prli_retries = 0; | |
| bfa_fcs_itnim_send_prli(itnim, NULL); | |
| break; | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_INITIATOR: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_FRMSENT: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli); | |
| break; | |
| case BFA_FCS_ITNIM_SM_INITIATOR: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); | |
| bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_RSP_OK: | |
| if (itnim->rport->scsi_function == BFA_RPORT_INITIATOR) | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); | |
| else | |
| bfa_sm_set_state(itnim, | |
| bfa_fcs_itnim_sm_hal_rport_online); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_RSP_ERROR: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_retry); | |
| bfa_timer_start(itnim->fcs->bfa, &itnim->timer, | |
| bfa_fcs_itnim_timeout, itnim, | |
| BFA_FCS_RETRY_TIMEOUT); | |
| break; | |
| case BFA_FCS_ITNIM_SM_RSP_NOT_SUPP: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| break; | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcxp_discard(itnim->fcxp); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_INITIATOR: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); | |
| bfa_fcxp_discard(itnim->fcxp); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcxp_discard(itnim->fcxp); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_HAL_ONLINE: | |
| if (!itnim->bfa_itnim) | |
| itnim->bfa_itnim = bfa_itnim_create(itnim->fcs->bfa, | |
| itnim->rport->bfa_rport, itnim); | |
| if (itnim->bfa_itnim) { | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online); | |
| bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec); | |
| } else { | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_DELETE); | |
| } | |
| break; | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_TIMEOUT: | |
| if (itnim->prli_retries < BFA_FCS_RPORT_MAX_RETRIES) { | |
| itnim->prli_retries++; | |
| bfa_trc(itnim->fcs, itnim->prli_retries); | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); | |
| bfa_fcs_itnim_send_prli(itnim, NULL); | |
| } else { | |
| /* invoke target offline */ | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP); | |
| } | |
| break; | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_timer_stop(&itnim->timer); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_INITIATOR: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); | |
| bfa_timer_stop(&itnim->timer); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_timer_stop(&itnim->timer); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad; | |
| char lpwwn_buf[BFA_STRING_32]; | |
| char rpwwn_buf[BFA_STRING_32]; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_HCB_ONLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_online); | |
| bfa_fcb_itnim_online(itnim->itnim_drv); | |
| wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port)); | |
| wwn2str(rpwwn_buf, itnim->rport->pwwn); | |
| BFA_LOG(KERN_INFO, bfad, bfa_log_level, | |
| "Target (WWN = %s) is online for initiator (WWN = %s)\n", | |
| rpwwn_buf, lpwwn_buf); | |
| bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); | |
| bfa_itnim_offline(itnim->bfa_itnim); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad; | |
| char lpwwn_buf[BFA_STRING_32]; | |
| char rpwwn_buf[BFA_STRING_32]; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); | |
| bfa_fcb_itnim_offline(itnim->itnim_drv); | |
| bfa_itnim_offline(itnim->bfa_itnim); | |
| wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port)); | |
| wwn2str(rpwwn_buf, itnim->rport->pwwn); | |
| if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE) { | |
| BFA_LOG(KERN_ERR, bfad, bfa_log_level, | |
| "Target (WWN = %s) connectivity lost for " | |
| "initiator (WWN = %s)\n", rpwwn_buf, lpwwn_buf); | |
| bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT); | |
| } else { | |
| BFA_LOG(KERN_INFO, bfad, bfa_log_level, | |
| "Target (WWN = %s) offlined by initiator (WWN = %s)\n", | |
| rpwwn_buf, lpwwn_buf); | |
| bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE); | |
| } | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_HCB_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| /* | |
| * This state is set when a discovered rport is also in intiator mode. | |
| * This ITN is marked as no_op and is not active and will not be truned into | |
| * online state. | |
| */ | |
| static void | |
| bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_fcs_itnim_event event) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_trc(itnim->fcs, event); | |
| switch (event) { | |
| case BFA_FCS_ITNIM_SM_OFFLINE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE); | |
| break; | |
| /* | |
| * fcs_online is expected here for well known initiator ports | |
| */ | |
| case BFA_FCS_ITNIM_SM_FCS_ONLINE: | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE); | |
| break; | |
| case BFA_FCS_ITNIM_SM_RSP_ERROR: | |
| case BFA_FCS_ITNIM_SM_INITIATOR: | |
| break; | |
| case BFA_FCS_ITNIM_SM_DELETE: | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| bfa_fcs_itnim_free(itnim); | |
| break; | |
| default: | |
| bfa_sm_fault(itnim->fcs, event); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, | |
| enum bfa_itnim_aen_event event) | |
| { | |
| struct bfa_fcs_rport_s *rport = itnim->rport; | |
| struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad; | |
| struct bfa_aen_entry_s *aen_entry; | |
| /* Don't post events for well known addresses */ | |
| if (BFA_FCS_PID_IS_WKA(rport->pid)) | |
| return; | |
| bfad_get_aen_entry(bfad, aen_entry); | |
| if (!aen_entry) | |
| return; | |
| aen_entry->aen_data.itnim.vf_id = rport->port->fabric->vf_id; | |
| aen_entry->aen_data.itnim.ppwwn = bfa_fcs_lport_get_pwwn( | |
| bfa_fcs_get_base_port(itnim->fcs)); | |
| aen_entry->aen_data.itnim.lpwwn = bfa_fcs_lport_get_pwwn(rport->port); | |
| aen_entry->aen_data.itnim.rpwwn = rport->pwwn; | |
| /* Send the AEN notification */ | |
| bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq, | |
| BFA_AEN_CAT_ITNIM, event); | |
| } | |
| static void | |
| bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = itnim_cbarg; | |
| struct bfa_fcs_rport_s *rport = itnim->rport; | |
| struct bfa_fcs_lport_s *port = rport->port; | |
| struct fchs_s fchs; | |
| struct bfa_fcxp_s *fcxp; | |
| int len; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| fcxp = fcxp_alloced ? fcxp_alloced : | |
| bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE); | |
| if (!fcxp) { | |
| itnim->stats.fcxp_alloc_wait++; | |
| bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe, | |
| bfa_fcs_itnim_send_prli, itnim, BFA_TRUE); | |
| return; | |
| } | |
| itnim->fcxp = fcxp; | |
| len = fc_prli_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), | |
| itnim->rport->pid, bfa_fcs_lport_get_fcid(port), 0); | |
| bfa_fcxp_send(fcxp, rport->bfa_rport, port->fabric->vf_id, port->lp_tag, | |
| BFA_FALSE, FC_CLASS_3, len, &fchs, | |
| bfa_fcs_itnim_prli_response, (void *)itnim, | |
| FC_MAX_PDUSZ, FC_ELS_TOV); | |
| itnim->stats.prli_sent++; | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_FRMSENT); | |
| } | |
| static void | |
| bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, | |
| bfa_status_t req_status, u32 rsp_len, | |
| u32 resid_len, struct fchs_s *rsp_fchs) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg; | |
| struct fc_els_cmd_s *els_cmd; | |
| struct fc_prli_s *prli_resp; | |
| struct fc_ls_rjt_s *ls_rjt; | |
| struct fc_prli_params_s *sparams; | |
| bfa_trc(itnim->fcs, req_status); | |
| /* | |
| * Sanity Checks | |
| */ | |
| if (req_status != BFA_STATUS_OK) { | |
| itnim->stats.prli_rsp_err++; | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); | |
| return; | |
| } | |
| els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); | |
| if (els_cmd->els_code == FC_ELS_ACC) { | |
| prli_resp = (struct fc_prli_s *) els_cmd; | |
| if (fc_prli_rsp_parse(prli_resp, rsp_len) != FC_PARSE_OK) { | |
| bfa_trc(itnim->fcs, rsp_len); | |
| /* | |
| * Check if this r-port is also in Initiator mode. | |
| * If so, we need to set this ITN as a no-op. | |
| */ | |
| if (prli_resp->parampage.servparams.initiator) { | |
| bfa_trc(itnim->fcs, prli_resp->parampage.type); | |
| itnim->rport->scsi_function = | |
| BFA_RPORT_INITIATOR; | |
| itnim->stats.prli_rsp_acc++; | |
| itnim->stats.initiator++; | |
| bfa_sm_send_event(itnim, | |
| BFA_FCS_ITNIM_SM_RSP_OK); | |
| return; | |
| } | |
| itnim->stats.prli_rsp_parse_err++; | |
| return; | |
| } | |
| itnim->rport->scsi_function = BFA_RPORT_TARGET; | |
| sparams = &prli_resp->parampage.servparams; | |
| itnim->seq_rec = sparams->retry; | |
| itnim->rec_support = sparams->rec_support; | |
| itnim->task_retry_id = sparams->task_retry_id; | |
| itnim->conf_comp = sparams->confirm; | |
| itnim->stats.prli_rsp_acc++; | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_OK); | |
| } else { | |
| ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); | |
| bfa_trc(itnim->fcs, ls_rjt->reason_code); | |
| bfa_trc(itnim->fcs, ls_rjt->reason_code_expl); | |
| itnim->stats.prli_rsp_rjt++; | |
| if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) { | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_NOT_SUPP); | |
| return; | |
| } | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); | |
| } | |
| } | |
| static void | |
| bfa_fcs_itnim_timeout(void *arg) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) arg; | |
| itnim->stats.timeout++; | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_TIMEOUT); | |
| } | |
| static void | |
| bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim) | |
| { | |
| if (itnim->bfa_itnim) { | |
| bfa_itnim_delete(itnim->bfa_itnim); | |
| itnim->bfa_itnim = NULL; | |
| } | |
| bfa_fcb_itnim_free(itnim->fcs->bfad, itnim->itnim_drv); | |
| } | |
| /* | |
| * itnim_public FCS ITNIM public interfaces | |
| */ | |
| /* | |
| * Called by rport when a new rport is created. | |
| * | |
| * @param[in] rport - remote port. | |
| */ | |
| struct bfa_fcs_itnim_s * | |
| bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport) | |
| { | |
| struct bfa_fcs_lport_s *port = rport->port; | |
| struct bfa_fcs_itnim_s *itnim; | |
| struct bfad_itnim_s *itnim_drv; | |
| int ret; | |
| /* | |
| * call bfad to allocate the itnim | |
| */ | |
| ret = bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv); | |
| if (ret) { | |
| bfa_trc(port->fcs, rport->pwwn); | |
| return NULL; | |
| } | |
| /* | |
| * Initialize itnim | |
| */ | |
| itnim->rport = rport; | |
| itnim->fcs = rport->fcs; | |
| itnim->itnim_drv = itnim_drv; | |
| itnim->bfa_itnim = NULL; | |
| itnim->seq_rec = BFA_FALSE; | |
| itnim->rec_support = BFA_FALSE; | |
| itnim->conf_comp = BFA_FALSE; | |
| itnim->task_retry_id = BFA_FALSE; | |
| /* | |
| * Set State machine | |
| */ | |
| bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); | |
| return itnim; | |
| } | |
| /* | |
| * Called by rport to delete the instance of FCPIM. | |
| * | |
| * @param[in] rport - remote port. | |
| */ | |
| void | |
| bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pid); | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_DELETE); | |
| } | |
| /* | |
| * Notification from rport that PLOGI is complete to initiate FC-4 session. | |
| */ | |
| void | |
| bfa_fcs_itnim_brp_online(struct bfa_fcs_itnim_s *itnim) | |
| { | |
| itnim->stats.onlines++; | |
| if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid)) | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HAL_ONLINE); | |
| } | |
| /* | |
| * Called by rport to handle a remote device offline. | |
| */ | |
| void | |
| bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim) | |
| { | |
| itnim->stats.offlines++; | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_OFFLINE); | |
| } | |
| /* | |
| * Called by rport when remote port is known to be an initiator from | |
| * PRLI received. | |
| */ | |
| void | |
| bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pid); | |
| itnim->stats.initiator++; | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR); | |
| } | |
| /* | |
| * Called by rport to check if the itnim is online. | |
| */ | |
| bfa_status_t | |
| bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim) | |
| { | |
| bfa_trc(itnim->fcs, itnim->rport->pid); | |
| switch (bfa_sm_to_state(itnim_sm_table, itnim->sm)) { | |
| case BFA_ITNIM_ONLINE: | |
| case BFA_ITNIM_INITIATIOR: | |
| return BFA_STATUS_OK; | |
| default: | |
| return BFA_STATUS_NO_FCPIM_NEXUS; | |
| } | |
| } | |
| /* | |
| * BFA completion callback for bfa_itnim_online(). | |
| */ | |
| void | |
| bfa_cb_itnim_online(void *cbarg) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_ONLINE); | |
| } | |
| /* | |
| * BFA completion callback for bfa_itnim_offline(). | |
| */ | |
| void | |
| bfa_cb_itnim_offline(void *cb_arg) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_OFFLINE); | |
| } | |
| /* | |
| * Mark the beginning of PATH TOV handling. IO completion callbacks | |
| * are still pending. | |
| */ | |
| void | |
| bfa_cb_itnim_tov_begin(void *cb_arg) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| } | |
| /* | |
| * Mark the end of PATH TOV handling. All pending IOs are already cleaned up. | |
| */ | |
| void | |
| bfa_cb_itnim_tov(void *cb_arg) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; | |
| struct bfad_itnim_s *itnim_drv = itnim->itnim_drv; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| itnim_drv->state = ITNIM_STATE_TIMEOUT; | |
| } | |
| /* | |
| * BFA notification to FCS/driver for second level error recovery. | |
| * | |
| * Atleast one I/O request has timedout and target is unresponsive to | |
| * repeated abort requests. Second level error recovery should be initiated | |
| * by starting implicit logout and recovery procedures. | |
| */ | |
| void | |
| bfa_cb_itnim_sler(void *cb_arg) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg; | |
| itnim->stats.sler++; | |
| bfa_trc(itnim->fcs, itnim->rport->pwwn); | |
| bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP); | |
| } | |
| struct bfa_fcs_itnim_s * | |
| bfa_fcs_itnim_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn) | |
| { | |
| struct bfa_fcs_rport_s *rport; | |
| rport = bfa_fcs_rport_lookup(port, rpwwn); | |
| if (!rport) | |
| return NULL; | |
| WARN_ON(rport->itnim == NULL); | |
| return rport->itnim; | |
| } | |
| bfa_status_t | |
| bfa_fcs_itnim_attr_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn, | |
| struct bfa_itnim_attr_s *attr) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = NULL; | |
| itnim = bfa_fcs_itnim_lookup(port, rpwwn); | |
| if (itnim == NULL) | |
| return BFA_STATUS_NO_FCPIM_NEXUS; | |
| attr->state = bfa_sm_to_state(itnim_sm_table, itnim->sm); | |
| attr->retry = itnim->seq_rec; | |
| attr->rec_support = itnim->rec_support; | |
| attr->conf_comp = itnim->conf_comp; | |
| attr->task_retry_id = itnim->task_retry_id; | |
| return BFA_STATUS_OK; | |
| } | |
| bfa_status_t | |
| bfa_fcs_itnim_stats_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn, | |
| struct bfa_itnim_stats_s *stats) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = NULL; | |
| WARN_ON(port == NULL); | |
| itnim = bfa_fcs_itnim_lookup(port, rpwwn); | |
| if (itnim == NULL) | |
| return BFA_STATUS_NO_FCPIM_NEXUS; | |
| memcpy(stats, &itnim->stats, sizeof(struct bfa_itnim_stats_s)); | |
| return BFA_STATUS_OK; | |
| } | |
| bfa_status_t | |
| bfa_fcs_itnim_stats_clear(struct bfa_fcs_lport_s *port, wwn_t rpwwn) | |
| { | |
| struct bfa_fcs_itnim_s *itnim = NULL; | |
| WARN_ON(port == NULL); | |
| itnim = bfa_fcs_itnim_lookup(port, rpwwn); | |
| if (itnim == NULL) | |
| return BFA_STATUS_NO_FCPIM_NEXUS; | |
| memset(&itnim->stats, 0, sizeof(struct bfa_itnim_stats_s)); | |
| return BFA_STATUS_OK; | |
| } | |
| void | |
| bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim, | |
| struct fchs_s *fchs, u16 len) | |
| { | |
| struct fc_els_cmd_s *els_cmd; | |
| bfa_trc(itnim->fcs, fchs->type); | |
| if (fchs->type != FC_TYPE_ELS) | |
| return; | |
| els_cmd = (struct fc_els_cmd_s *) (fchs + 1); | |
| bfa_trc(itnim->fcs, els_cmd->els_code); | |
| switch (els_cmd->els_code) { | |
| case FC_ELS_PRLO: | |
| bfa_fcs_rport_prlo(itnim->rport, fchs->ox_id); | |
| break; | |
| default: | |
| WARN_ON(1); | |
| } | |
| } |