// SPDX-License-Identifier: GPL-2.0 /* Huawei Hifc PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * */ #include "unf_exchg.h" #include "unf_log.h" #include "unf_rport.h" #include "unf_exchg.h" #include "unf_service.h" #include "unf_portman.h" #include "unf_npiv.h" static void unf_flogi_callback(void *v_lport, void *v_rport, void *v_xchg); static void unf_fdisc_callback(void *v_lport, void *v_rport, void *v_xchg); static void unf_plogi_callback(void *v_lport, void *v_rport, void *v_xchg); static unsigned int unf_rec_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static void unf_gid_ft_callback(void *v_lport, void *v_rport, void *v_xchg); static void unf_gid_pt_callback(void *v_lport, void *v_rport, void *v_xchg); static void unf_process_rport_after_logo(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport); static unsigned int unf_flogi_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_plogi_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_prli_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_prlo_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_rscn_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_logo_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_echo_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_pdisc_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_adisc_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_rrq_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_rls_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg); static unsigned int unf_send_els_rjt_by_rport( struct unf_lport_s *v_lport, struct unf_xchg_s *v_xchg, struct unf_rport_s *v_rport, struct unf_rjt_info_s *v_rjt_info); unsigned int max_frame_size = UNF_DEFAULT_FRAME_SIZE; #define FCP_XFER_RDY_IU 0x05 #define FCP_RSP_IU 0x07 #define FCP_DATA_IU 0x01 #define UNF_GID_LAST_PORT_ID 0x80 #define UNF_LOWLEVEL_BBCREDIT 0x6 #define UNF_DEFAULT_BB_SC_N 0 #define UNF_INIT_DISC 0x1 /* first time DISC */ #define UNF_RSCN_DISC 0x2 /* RSCN Port Addr DISC */ /* Reference from FCP-4 Table33 RR_TOV: REC_TOV + 2*R_A_TOV + 1S, * REC_TOV = E_D_TOV + 1s */ #define UNF_CALC_LPORT_RRTOV(v_lport) \ (((v_lport)->ed_tov + 1000) + (2 * (v_lport)->ra_tov + 1000)) #define UNF_GID_CONTROL(v_nport_id) ((v_nport_id) >> 24) #define UNF_ECHO_PLD_DATA 0x1234567890ABCDEF #define UNF_ECHO_REQ_SIZE 0 #define UNF_GET_PORT_OPTIONS(v_fc4feature) ((v_fc4feature) >> 20) #define UNF_GET_DOMAIN_ID(x) (((x) & 0xFF0000) >> 16) /* domain id */ #define UNF_GET_AREA_ID(x) (((x) & 0x00FF00) >> 8) /* area id */ #define UNF_SERVICE_GET_NPORTID_FORM_GID_PAGE(v_port_id_page) \ (((unsigned int)(v_port_id_page)->port_id_domain << 16) | \ ((unsigned int)(v_port_id_page)->port_id_area << 8) | \ ((unsigned int)(v_port_id_page)->port_id_port)) #define UNF_GNN_GFF_ID_RJT_REASON(rjt_reason) \ ((((rjt_reason) & UNF_CTIU_RJT_MASK) == \ UNF_CTIU_RJT_UNABLE_PERFORM) && \ ((((rjt_reason) & UNF_CTIU_RJT_EXP_MASK) == \ UNF_CTIU_RJT_EXP_PORTID_NO_REG) || \ (((rjt_reason) & UNF_CTIU_RJT_EXP_MASK) == \ UNF_CTIU_RJT_EXP_PORTNAME_NO_REG) || \ (((rjt_reason) & UNF_CTIU_RJT_EXP_MASK) == \ UNF_CTIU_RJT_EXP_NODENAME_NO_REG))) #define UNF_NEED_BIG_RESPONSE_BUFF(cmnd_code) \ (((cmnd_code) == ELS_ECHO) || ((cmnd_code) == NS_GID_PT) || \ ((cmnd_code) == NS_GID_FT)) #define NEED_REFRESH_NPORTID(pkg) ((((pkg)->cmnd == ELS_PLOGI) || \ ((pkg)->cmnd == ELS_PDISC) || \ ((pkg)->cmnd == ELS_ADISC))) struct unf_els_handler_table { unsigned int cmnd; unsigned int (*pfn_els_cmnd_handler)(struct unf_lport_s *, unsigned int, struct unf_xchg_s *); }; #define UNF_SERVICE_COLLECT(service_collect, item) \ do { \ if ((item) < UNF_SERVICE_BUTT) { \ service_collect.service_cnt[(item)]++; \ } \ } while (0) struct unf_els_handler_table els_handle[] = { { ELS_PLOGI, unf_plogi_handler }, { ELS_FLOGI, unf_flogi_handler }, { ELS_LOGO, unf_logo_handler }, { ELS_ECHO, unf_echo_handler }, { ELS_RRQ, unf_rrq_handler }, { ELS_REC, unf_rec_handler }, { ELS_PRLI, unf_prli_handler }, { ELS_PRLO, unf_prlo_handler }, { ELS_PDISC, unf_pdisc_handler }, { ELS_ADISC, unf_adisc_handler }, { ELS_RSCN, unf_rscn_handler }, { ELS_RLS, unf_rls_handler } }; static void unf_check_rport_need_delay_prli(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, unsigned int v_port_feature) { unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3300, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3301, UNF_TRUE, v_rport, return); v_port_feature &= UNF_PORT_MODE_BOTH; /* Used for: L_Port has INI mode & R_Port is not SW */ if (v_rport->nport_id < UNF_FC_FID_DOM_MGR) { /* * 1. immediately: R_Port only with TGT, or * L_Port only with INI & R_Port has TGT mode, * send PRLI immediately */ if (((v_port_feature == UNF_PORT_MODE_TGT) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) || ((v_port_feature & UNF_PORT_MODE_TGT) == UNF_PORT_MODE_TGT)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) Rport(0x%x) with feature(0x%x) send PRLI", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_port_feature); /* Send PRLI to remote port */ ret = unf_send_prli(v_lport, v_rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) Rport(0x%x) with feature(0x%x) send PRLI failed", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_port_feature); /* Do R_Port recovery */ unf_rport_error_recovery(v_rport); } } else if (v_port_feature != UNF_PORT_MODE_INI) { /* 2. R_Port has BOTH mode or unknown, * Delay to send PRLI */ /* Prevent: PRLI done before PLOGI */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) Rport(0x%x) with feature(0x%x) delay to send PRLI", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_port_feature); /* Delay to send PRLI to R_Port */ unf_rport_delay_login(v_rport); } else { /* 3. R_Port only with INI mode: wait for R_Port's * PRLI: Do not care */ /* Cancel recovery(timer) work */ if (delayed_work_pending(&v_rport->recovery_work)) { if (cancel_delayed_work( &v_rport->recovery_work)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]LOGIN: Port(0x%x_0x%x) Rport(0x%x) with feature(0x%x) is pure INI", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_port_feature); unf_rport_ref_dec(v_rport); } } /* Server: R_Port only support INI, * do not care this case */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) Rport(0x%x) with feature(0x%x) wait for PRLI", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_port_feature); } } } static unsigned int unf_low_level_bb_credit(struct unf_lport_s *v_lport) { struct unf_lport_s *lport = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int bb_credit = UNF_LOWLEVEL_BBCREDIT; if (unlikely(!v_lport)) return bb_credit; lport = v_lport; if (unlikely(!lport->low_level_func.port_mgr_op.pfn_ll_port_config_get)) return bb_credit; ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_get( (void *)lport->fc_port, UNF_PORT_CFG_GET_WORKBALE_BBCREDIT, (void *)&bb_credit); if (unlikely(ret != RETURN_OK)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_INFO, "[warn]Port(0x%x) get BB_Credit failed, use default value(%d)", lport->port_id, UNF_LOWLEVEL_BBCREDIT); bb_credit = UNF_LOWLEVEL_BBCREDIT; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) with BB_Credit(%u)", lport->port_id, bb_credit); return bb_credit; } unsigned int unf_low_level_bbscn(struct unf_lport_s *v_lport) { struct unf_lport_s *lport = v_lport; struct unf_low_level_port_mgr_op_s *port_mgr = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int bb_scn = UNF_DEFAULT_BB_SC_N; if (unlikely(!v_lport)) return bb_scn; port_mgr = &lport->low_level_func.port_mgr_op; if (unlikely(!port_mgr->pfn_ll_port_config_get)) return bb_scn; ret = port_mgr->pfn_ll_port_config_get((void *)lport->fc_port, UNF_PORT_CFG_GET_WORKBALE_BBSCN, (void *)&bb_scn); if (unlikely(ret != RETURN_OK)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_INFO, "[warn]Port(0x%x) get bbscn failed, use default value(%d)", lport->port_id, UNF_DEFAULT_BB_SC_N); bb_scn = UNF_DEFAULT_BB_SC_N; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x)'s bbscn(%d)", lport->port_id, bb_scn); return bb_scn; } static unsigned int unf_els_cmnd_send(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg, struct unf_xchg_s *v_xchg) { unsigned int ret = UNF_RETURN_ERROR; unsigned long time_out = 0; UNF_CHECK_VALID(0x3302, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3303, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3304, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); if (unlikely(!v_lport->low_level_func.service_op.pfn_unf_els_send)) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) ELS send function is NULL", v_lport->port_id); return ret; } /* Add ELS command/response (Exchange) timeout timer */ time_out = UNF_GET_ELS_SFS_XCHG_TIMER(v_lport); if (v_xchg->cmnd_code == ELS_RRQ) { time_out = ((unsigned long) UNF_GET_ELS_SFS_XCHG_TIMER(v_lport) > UNF_RRQ_MIN_TIMEOUT_INTERVAL) ? (unsigned long) UNF_GET_ELS_SFS_XCHG_TIMER(v_lport) : UNF_RRQ_MIN_TIMEOUT_INTERVAL; } else if (v_xchg->cmnd_code == ELS_LOGO) { time_out = UNF_LOGO_TIMEOUT_INTERVAL; } v_lport->xchg_mgr_temp.pfn_unf_xchg_add_timer((void *)v_xchg, time_out, UNF_TIMER_TYPE_SFS); v_pkg->private[PKG_PRIVATE_XCHG_TIMEER] = (unsigned int)UNF_GET_ELS_SFS_XCHG_TIMER(v_lport); v_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME] = v_xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]; /* Send ELS command/response */ ret = v_lport->low_level_func.service_op.pfn_unf_els_send( v_lport->fc_port, v_pkg); if (unlikely(ret != RETURN_OK)) { /* Cancel timer if send failed */ v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer( (void *)v_xchg); } return ret; } static unsigned int unf_gs_cmnd_send(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg, struct unf_xchg_s *v_xchg) { unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3305, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3306, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3307, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); if (unlikely(!v_lport->low_level_func.service_op.pfn_unf_gs_send)) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) GS send function is NULL", v_lport->port_id); return ret; } /* Add GS command timeout timer */ v_lport->xchg_mgr_temp.pfn_unf_xchg_add_timer( (void *)v_xchg, (unsigned long)UNF_GET_GS_SFS_XCHG_TIMER(v_lport), UNF_TIMER_TYPE_SFS); v_pkg->private[PKG_PRIVATE_XCHG_TIMEER] = (unsigned int) UNF_GET_GS_SFS_XCHG_TIMER(v_lport); v_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME] = v_xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]; /* Send GS command */ ret = v_lport->low_level_func.service_op.pfn_unf_gs_send( v_lport->fc_port, v_pkg); if (unlikely(ret != RETURN_OK)) /* Cancel timer if send failed */ v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer( (void *)v_xchg); return ret; } static unsigned int unf_bls_cmnd_send(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg, struct unf_xchg_s *v_xchg) { UNF_CHECK_VALID(0x3308, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3309, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3310, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_pkg->private[PKG_PRIVATE_XCHG_TIMEER] = (unsigned int)UNF_GET_BLS_SFS_XCHG_TIMER(v_lport); v_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME] = v_xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]; return v_lport->low_level_func.service_op.pfn_unf_bls_send( v_lport->fc_port, v_pkg); } static void unf_fill_package(struct unf_frame_pkg_s *v_pkg, struct unf_xchg_s *v_xchg, struct unf_rport_s *v_rport) { /* v_rport maybe NULL */ UNF_CHECK_VALID(0x3311, UNF_TRUE, v_pkg, return); UNF_CHECK_VALID(0x3312, UNF_TRUE, v_xchg, return); v_pkg->cmnd = v_xchg->cmnd_code; v_pkg->fcp_cmnd = &v_xchg->fcp_cmnd; v_pkg->frame_head.csctl_sid = v_xchg->sid; v_pkg->frame_head.rctl_did = v_xchg->did; v_pkg->frame_head.oxid_rxid = ((unsigned int)v_xchg->ox_id << 16 | v_xchg->rx_id); v_pkg->xchg_contex = v_xchg; UNF_CHECK_VALID(0x3313, UNF_TRUE, v_xchg->lport, return); v_pkg->private[PKG_PRIVATE_XCHG_VP_INDEX] = v_xchg->lport->vp_index; if (!v_rport) { v_pkg->private[PKG_PRIVATE_XCHG_RPORT_INDEX] = UNF_RPORT_INVALID_INDEX; v_pkg->private[PKG_PRIVATE_RPORT_RX_SIZE] = INVALID_VALUE32; } else { v_pkg->private[PKG_PRIVATE_XCHG_RPORT_INDEX] = v_rport->rport_index; v_pkg->private[PKG_PRIVATE_RPORT_RX_SIZE] = v_rport->max_frame_size; } v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = v_xchg->hot_pool_tag; v_pkg->private[PKG_PRIVATE_LOWLEVEL_XCHG_ADD] = v_xchg->private[PKG_PRIVATE_LOWLEVEL_XCHG_ADD]; v_pkg->unf_cmnd_pload_bl.buffer_ptr = (unsigned char *) v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; v_pkg->unf_cmnd_pload_bl.buf_dma_addr = v_xchg->fcp_sfs_union.sfs_entry.sfs_buff_phy_addr; /* Low level need to know payload length if send ECHO response */ v_pkg->unf_cmnd_pload_bl.length = v_xchg->fcp_sfs_union.sfs_entry.cur_offset; } static struct unf_xchg_s *unf_get_sfs_free_xchg_and_init( struct unf_lport_s *v_lport, unsigned int v_did, struct unf_rport_s *v_rport, union unf_sfs_u **v_fc_entry) { struct unf_xchg_s *xchg = NULL; union unf_sfs_u *fc_entry = NULL; xchg = unf_cm_get_free_xchg(v_lport, UNF_XCHG_TYPE_SFS); if (!xchg) return NULL; xchg->did = v_did; xchg->sid = v_lport->nport_id; xchg->oid = xchg->sid; xchg->lport = v_lport; xchg->rport = v_rport; xchg->disc_rport = NULL; if (v_lport->low_level_func.xchg_mgr_type == UNF_LOW_LEVEL_MGR_TYPE_PASSTIVE) xchg->ox_id = xchg->hot_pool_tag; xchg->pfn_callback = NULL; xchg->pfn_ob_callback = NULL; fc_entry = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, xchg); return NULL; } *v_fc_entry = fc_entry; return xchg; } static void unf_scr_callback(void *v_lport, void *v_rport, void *v_xchg) { /* Callback function for SCR response: Send GID_PT with INI mode */ struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_disc_s *disc = &lport->disc; struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg; struct unf_els_acc_s *els_acc = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned long lport_flag = 0; unsigned long disc_flag = 0; unsigned int cmnd = 0; UNF_CHECK_VALID(0x3694, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3695, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(v_rport); UNF_REFERNCE_VAR(ret); if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) return; els_acc = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->els_acc; if (xchg->byte_orders & UNF_BIT_2) cmnd = be32_to_cpu(els_acc->cmnd); else cmnd = (els_acc->cmnd); if ((cmnd & UNF_ELS_CMND_HIGH_MASK) == UNF_ELS_CMND_ACC) { /* About ELS_CMND ACC */ spin_lock_irqsave(&lport->lport_state_lock, lport_flag); /* Check L_Port state: SCR_WAIT */ if (lport->en_states != UNF_LPORT_ST_SCR_WAIT) { spin_unlock_irqrestore(&lport->lport_state_lock, lport_flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) receive SCR ACC with error state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); return; } /* Update L_Port state machine: Ready */ /* LPort: SCR_WAIT --> READY */ unf_lport_stat_ma(lport, UNF_EVENT_LPORT_REMOTE_ACC); if (lport->en_states == UNF_LPORT_ST_READY) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) enter READY state when received SCR response", lport->port_id, lport->nport_id); } /* Start to Discovery with INI mode: GID_PT */ if ((lport->options & UNF_PORT_MODE_INI) == UNF_PORT_MODE_INI) { spin_unlock_irqrestore(&lport->lport_state_lock, lport_flag); if (lport->disc.unf_disc_temp.pfn_unf_disc_start) { spin_lock_irqsave(&disc->rport_busy_pool_lock, disc_flag); lport->disc.disc_option = UNF_INIT_DISC; disc->last_disc_jiff = jiffies; spin_unlock_irqrestore( &disc->rport_busy_pool_lock, disc_flag); ret = lport->disc.unf_disc_temp.pfn_unf_disc_start(lport); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]LOGIN: Port(0x%x) DISC %s with INI mode", lport->port_id, (ret != RETURN_OK) ? "failed" : "succeed"); } UNF_REFERNCE_VAR(ret); return; } /* TGT mode: Do not care */ spin_unlock_irqrestore(&lport->lport_state_lock, lport_flag); /* NOTE: set state with UNF_DISC_ST_END used for RSCN process */ spin_lock_irqsave(&disc->rport_busy_pool_lock, disc_flag); lport->disc.en_states = UNF_DISC_ST_END; spin_unlock_irqrestore(&disc->rport_busy_pool_lock, disc_flag); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) is TGT mode, no need to discovery", lport->port_id); return; } /* About ELS_CMND response: RJT */ unf_lport_error_recovery(lport); UNF_REFERNCE_VAR(ret); } static void unf_scr_ob_callback(struct unf_xchg_s *v_xchg) { /* Callback fucnion for exception: Do L_Port error recovery */ struct unf_lport_s *lport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3692, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3693, UNF_TRUE, lport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send SCR failed and do port recovery", lport->port_id); unf_lport_error_recovery(lport); } unsigned int unf_send_scr(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* after RCVD RFF_ID ACC */ struct unf_scr_s *scr = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3314, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3315, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); /* Get free exchange for SCR */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, NULL, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for SCR", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_SCR; /* SCR */ ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_callback = unf_scr_callback; xchg->pfn_ob_callback = unf_scr_ob_callback; /* Fill command/response package */ unf_fill_package(&pkg, xchg, v_rport); scr = &fc_entry->scr; memset(scr, 0, sizeof(struct unf_scr_s)); scr->payload[0] = (UNF_GS_CMND_SCR); /* SCR is 0x62 */ scr->payload[1] = (UNF_FABRIC_FULL_REG); /* Full registration */ /* Send SCR command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: SCR send %s. Port(0x%x_0x%x)--->rport(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_rec_pld(struct unf_rec_pld_s *v_rec_pld, unsigned int v_sid, unsigned short v_oxid) { UNF_CHECK_VALID(0x3339, UNF_TRUE, v_rec_pld, return); v_rec_pld->rec_cmnd = UNF_ELS_CMND_REC; v_rec_pld->xchg_org_sid = v_sid; v_rec_pld->ox_id = v_oxid; v_rec_pld->rx_id = INVALID_VALUE16; } unsigned int unf_send_rec(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_io_xchg) { struct unf_rec_pld_s *rec_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3324, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3325, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3325, UNF_TRUE, v_io_xchg, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); /* Get & Set new free exchange */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for PLOGI", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_REC; ox_id = xchg->ox_id; unf_fill_package(&pkg, xchg, v_rport); rec_pld = &fc_entry->rec.rec_pld; memset(rec_pld, 0, sizeof(struct unf_rec_pld_s)); unf_fill_rec_pld(rec_pld, v_lport->nport_id, v_io_xchg->ox_id); /* Start to Send REC command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]LOGIN: Send REC %s. Port(0x%x_0x%x_0x%llx)--->RPort(0x%x_0x%llx) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_lport->port_name, v_rport->nport_id, v_rport->port_name, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_flogi_pld(struct unf_flogi_payload_s *v_flogi_pld, struct unf_lport_s *v_lport) { struct unf_fabric_parms_s *fabric_parms = NULL; UNF_CHECK_VALID(0x3316, UNF_TRUE, v_flogi_pld, return); UNF_CHECK_VALID(0x3317, UNF_TRUE, v_lport, return); fabric_parms = &v_flogi_pld->fabric_parms; if ((v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT) || (v_lport->en_act_topo == UNF_TOP_P2P_MASK)) { /* Fabric or P2P topology */ fabric_parms->co_parms.bb_credit = unf_low_level_bb_credit(v_lport); fabric_parms->co_parms.lowest_version = UNF_PLOGI_VERSION_LOWER; fabric_parms->co_parms.highest_version = UNF_PLOGI_VERSION_UPPER; fabric_parms->co_parms.bb_receive_data_field_size = (v_lport->max_frame_size); fabric_parms->co_parms.bb_scn = unf_low_level_bbscn(v_lport); } else { /* Loop topology here */ fabric_parms->co_parms.clean_address = UNF_CLEAN_ADDRESS_DEFAULT; fabric_parms->co_parms.bb_credit = UNF_BBCREDIT_LPORT; fabric_parms->co_parms.lowest_version = UNF_PLOGI_VERSION_LOWER; fabric_parms->co_parms.highest_version = UNF_PLOGI_VERSION_UPPER; fabric_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_LPORT; /* :1 */ fabric_parms->co_parms.bb_receive_data_field_size = (v_lport->max_frame_size); } if (v_lport->low_level_func.support_max_npiv_num != 0) fabric_parms->co_parms.clean_address = 1; /* support NPIV */ fabric_parms->cl_parms[2].valid = UNF_CLASS_VALID; fabric_parms->cl_parms[2].priority = UNF_PRIORITY_DISABLE; fabric_parms->cl_parms[2].sequential_delivery = UNF_SEQUEN_DELIVERY_REQ; fabric_parms->cl_parms[2].received_data_field_size = (v_lport->max_frame_size); fabric_parms->high_node_name = UNF_GET_NAME_HIGH_WORD(v_lport->node_name); fabric_parms->low_node_name = UNF_GET_NAME_LOW_WORD(v_lport->node_name); fabric_parms->high_port_name = UNF_GET_NAME_HIGH_WORD(v_lport->port_name); fabric_parms->low_port_name = UNF_GET_NAME_LOW_WORD(v_lport->port_name); } static void unf_flogi_ob_callback(struct unf_xchg_s *v_xchg) { /* Send FLOGI failed & Do L_Port recovery */ struct unf_lport_s *lport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3644, UNF_TRUE, v_xchg, return); /* Get L_port from exchange context */ spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3645, UNF_TRUE, lport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send FLOGI failed", lport->port_id); /* Check L_Port state */ spin_lock_irqsave(&lport->lport_state_lock, flag); if (lport->en_states != UNF_LPORT_ST_FLOGI_WAIT) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send FLOGI failed with state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); spin_unlock_irqrestore(&lport->lport_state_lock, flag); return; } spin_unlock_irqrestore(&lport->lport_state_lock, flag); /* Do L_Port error recovery */ unf_lport_error_recovery(lport); } unsigned int unf_send_flogi(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_xchg_s *xchg = NULL; struct unf_flogi_payload_s *flogi_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3318, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3319, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); /* Get & Set New free Exchange Context */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for FLOGI", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_FLOGI; /* FLOGI */ ox_id = xchg->ox_id; /* Set callback function */ /* for rcvd flogi acc/rjt processer */ xchg->pfn_callback = unf_flogi_callback; /* for send flogi failed processer */ xchg->pfn_ob_callback = unf_flogi_ob_callback; /* Fill package: Exchange --to-->> Package */ unf_fill_package(&pkg, xchg, v_rport); /* Fill Flogi Payload */ flogi_pld = &fc_entry->flogi.flogi_payload; memset(flogi_pld, 0, sizeof(struct unf_flogi_payload_s)); unf_fill_flogi_pld(flogi_pld, v_lport); flogi_pld->cmnd = (UNF_ELS_CMND_FLOGI); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Begin to send FLOGI. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x)", v_lport->port_id, v_rport->nport_id, ox_id); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, flogi_pld, sizeof(struct unf_flogi_payload_s)); /* Start to send FLOGI command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_ERR, "[warn]LOGIN: Send FLOGI failed. Port(0x%x)--->rport(0x%x)", v_lport->port_id, v_rport->nport_id); unf_cm_free_xchg((void *)v_lport, (void *)xchg); } UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fdisc_ob_callback(struct unf_xchg_s *v_xchg) { /* Do recovery */ struct unf_lport_s *lport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3638, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: FDISC send failed"); UNF_CHECK_VALID(0x3639, UNF_TRUE, NULL != lport, return); /* Do L_Port error recovery */ unf_lport_error_recovery(lport); } unsigned int unf_send_fdisc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_xchg_s *exch = NULL; struct unf_flogi_payload_s *fdisc_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3320, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3321, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); exch = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!exch) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for FDISC", v_lport->port_id); return ret; } exch->cmnd_code = ELS_FDISC; /* FDISC */ ox_id = exch->ox_id; /* Set callback function */ exch->pfn_callback = unf_fdisc_callback; exch->pfn_ob_callback = unf_fdisc_ob_callback; unf_fill_package(&pkg, exch, v_rport); /* Fill FDISC entry(payload) */ fdisc_pld = &fc_entry->fdisc.fdisc_payload; memset(fdisc_pld, 0, sizeof(struct unf_flogi_payload_s)); unf_fill_flogi_pld(fdisc_pld, v_lport); fdisc_pld->cmnd = UNF_ELS_CMND_FDISC; /* update cmnd type */ /* Start to send FDISC */ ret = unf_els_cmnd_send(v_lport, &pkg, exch); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)exch); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: FDISC send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_plogi_pld(struct unf_plogi_payload_s *v_plogi_pld, struct unf_lport_s *v_lport) { struct unf_lgn_parms_s *login_parms = NULL; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x3322, UNF_TRUE, v_plogi_pld, return); UNF_CHECK_VALID(0x3323, UNF_TRUE, v_lport, return); lport = v_lport->root_lport; v_plogi_pld->cmnd = (UNF_ELS_CMND_PLOGI); login_parms = &v_plogi_pld->parms; if ((v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { /* P2P or Fabric mode */ login_parms->co_parms.bb_credit = (unf_low_level_bb_credit(v_lport)); login_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_NFPORT; /* 0 */ login_parms->co_parms.bb_scn = (v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) ? 0 : unf_low_level_bbscn(v_lport); } else { /* Public loop & Private loop mode */ login_parms->co_parms.bb_credit = UNF_BBCREDIT_LPORT; /* 0 */ login_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_LPORT; /* 1 */ } login_parms->co_parms.lowest_version = UNF_PLOGI_VERSION_LOWER; login_parms->co_parms.highest_version = UNF_PLOGI_VERSION_UPPER; login_parms->co_parms.continuously_increasing = UNF_CONTIN_INCREASE_SUPPORT; login_parms->co_parms.bb_receive_data_field_size = (v_lport->max_frame_size); login_parms->co_parms.nport_total_concurrent_sequences = (UNF_PLOGI_CONCURRENT_SEQ); login_parms->co_parms.relative_offset = (UNF_PLOGI_RO_CATEGORY); login_parms->co_parms.e_d_tov = UNF_DEFAULT_EDTOV; if (lport->b_priority == UNF_PRIORITY_ENABLE) login_parms->cl_parms[2].priority = UNF_PRIORITY_ENABLE; else login_parms->cl_parms[2].priority = UNF_PRIORITY_DISABLE; login_parms->cl_parms[2].valid = UNF_CLASS_VALID; /* for class_3 */ login_parms->cl_parms[2].received_data_field_size = (v_lport->max_frame_size); login_parms->cl_parms[2].concurrent_sequences = (UNF_PLOGI_CONCURRENT_SEQ); login_parms->cl_parms[2].open_sequences_per_exchange = (UNF_PLOGI_SEQ_PER_XCHG); login_parms->high_node_name = UNF_GET_NAME_HIGH_WORD(v_lport->node_name); login_parms->low_node_name = UNF_GET_NAME_LOW_WORD(v_lport->node_name); login_parms->high_port_name = UNF_GET_NAME_HIGH_WORD(v_lport->port_name); login_parms->low_port_name = UNF_GET_NAME_LOW_WORD(v_lport->port_name); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_plogi_pld, sizeof(struct unf_plogi_payload_s)); } static void unf_plogi_ob_callback(struct unf_xchg_s *v_xchg) { /* Do L_Port or R_Port recovery */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3656, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; rport = v_xchg->rport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3657, UNF_TRUE, lport, return); UNF_CHECK_VALID(0x3734, UNF_TRUE, rport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send PLOGI(0x%x_0x%x) to RPort(%p:0x%x_0x%x) failed", lport->port_id, lport->nport_id, v_xchg->ox_id, v_xchg->rx_id, rport, rport->rport_index, rport->nport_id); /* Start to recovery */ if (rport->nport_id > UNF_FC_FID_DOM_MGR) { /* with Name server: R_Port is fabric --->>> * L_Port error recovery */ unf_lport_error_recovery(lport); } else { /* R_Port is not fabric --->>> R_Port error recovery */ unf_rport_error_recovery(rport); } } unsigned int unf_send_plogi(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_plogi_payload_s *plogi_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3324, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3325, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); /* Get & Set new free exchange */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for PLOGI", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_PLOGI; /* PLOGI */ ox_id = xchg->ox_id; /* Set callback function */ /* for rcvd plogi acc/rjt processer */ xchg->pfn_callback = unf_plogi_callback; /* for send plogi failed processer */ xchg->pfn_ob_callback = unf_plogi_ob_callback; unf_fill_package(&pkg, xchg, v_rport); /* Fill PLOGI payload */ plogi_pld = &fc_entry->plogi.payload; memset(plogi_pld, 0, sizeof(struct unf_plogi_payload_s)); unf_fill_plogi_pld(plogi_pld, v_lport); /* Start to Send PLOGI command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Send PLOGI %s. Port(0x%x_0x%x_0x%llx)--->rport(0x%x_0x%llx) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_lport->port_name, v_rport->nport_id, v_rport->port_name, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_logo_pld(struct unf_logo_payload_s *v_logo_pld, struct unf_lport_s *v_lport) { UNF_CHECK_VALID(0x3326, UNF_TRUE, v_logo_pld, return); UNF_CHECK_VALID(0x3327, UNF_TRUE, v_lport, return); v_logo_pld->cmnd = UNF_ELS_CMND_LOGO; v_logo_pld->nport_id = (v_lport->nport_id); v_logo_pld->high_port_name = UNF_GET_NAME_HIGH_WORD(v_lport->port_name); v_logo_pld->low_port_name = UNF_GET_NAME_LOW_WORD(v_lport->port_name); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_logo_pld, sizeof(struct unf_logo_payload_s)); } static void unf_logo_ob_callback(struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport; struct unf_rport_s *rport; struct unf_rport_s *old_rport; struct unf_xchg_s *xchg; unsigned int nport_id = 0; unsigned int logo_retry = 0; UNF_CHECK_VALID(0x3675, UNF_TRUE, NULL, return); xchg = v_xchg; old_rport = xchg->rport; logo_retry = old_rport->logo_retries; if (old_rport->nport_id != INVALID_VALUE32) unf_rport_enter_closing(old_rport); lport = xchg->lport; if (unf_is_lport_valid(lport) != RETURN_OK) return; /* Get R_Port by exchange info: Init state */ nport_id = xchg->did; rport = unf_get_rport_by_nport_id(lport, nport_id); rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_INIT, nport_id); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) cannot allocate RPort", lport->port_id); return; } rport->logo_retries = logo_retry; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_WARN, "[info]LOGIN: Port(0x%x) received LOGO RSP timeout topo(0x%x) retries(%u)", lport->port_id, lport->en_act_topo, rport->logo_retries); /* RCVD LOGO/PRLO & SEND LOGO: the same process */ if (rport->logo_retries < UNF_MAX_RETRY_COUNT) { /* <: retry (LOGIN or LOGO) if necessary */ unf_process_rport_after_logo(lport, rport); } else { /* >=: Link down */ unf_rport_immediate_linkdown(lport, rport); } } static void unf_logo_callback(void *v_lport, void *v_rport, void *v_xchg) { /* RCVD LOGO ACC/RJT: retry(LOGIN/LOGO) or link down immediately */ struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_rport_s *rport = NULL; struct unf_rport_s *old_rport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_els_rjt_s *els_acc_rjt = NULL; unsigned int cmnd = 0; unsigned int nport_id = 0; unsigned int logo_retry = 0; UNF_CHECK_VALID(0x3675, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(v_rport); xchg = (struct unf_xchg_s *)v_xchg; old_rport = xchg->rport; logo_retry = old_rport->logo_retries; if (old_rport->nport_id != INVALID_VALUE32) unf_rport_enter_closing(old_rport); if (unf_is_lport_valid(v_lport) != RETURN_OK) return; if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) return; /* Get R_Port by exchange info: Init state */ els_acc_rjt = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->els_rjt; nport_id = xchg->did; rport = unf_get_rport_by_nport_id(lport, nport_id); rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_INIT, nport_id); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) cannot allocate RPort", lport->port_id); return; } rport->logo_retries = logo_retry; cmnd = be32_to_cpu(els_acc_rjt->cmnd); UNF_REFERNCE_VAR(cmnd); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x) received LOGO RSP(0x%x), topo(0x%x) Port options(0x%x) RPort options(0x%x) retries(%d)", lport->port_id, (cmnd & UNF_ELS_CMND_HIGH_MASK), lport->en_act_topo, lport->options, rport->options, rport->logo_retries); /* RCVD LOGO/PRLO & SEND LOGO: the same process */ if (rport->logo_retries < UNF_MAX_RETRY_COUNT) /* <: retry (LOGIN or LOGO) if necessary */ unf_process_rport_after_logo(lport, rport); else /* >=: Link down */ unf_rport_immediate_linkdown(lport, rport); } unsigned int unf_send_logo(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_logo_payload_s *logo_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; struct unf_frame_pkg_s pkg = { 0 }; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3328, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for LOGO", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_LOGO; /* LOGO */ ox_id = xchg->ox_id; /* Set callback function */ /* retry or link down immediately */ xchg->pfn_callback = unf_logo_callback; xchg->pfn_ob_callback = unf_logo_ob_callback; /* do nothing */ unf_fill_package(&pkg, xchg, v_rport); /* Fill LOGO entry(payload) */ logo_pld = &fc_entry->logo.payload; memset(logo_pld, 0, sizeof(struct unf_logo_payload_s)); unf_fill_logo_pld(logo_pld, v_lport); /* Start to send LOGO command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); v_rport->logo_retries++; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]LOGIN: LOGO send %s. Port(0x%x)--->rport(0x%x) OXID(0x%x) Retries(%d)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, v_rport->logo_retries); UNF_REFERNCE_VAR(ox_id); return ret; } unsigned int unf_send_logo_by_did(struct unf_lport_s *v_lport, unsigned int v_did) { /* Has non R_Port */ struct unf_logo_payload_s *logo_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; struct unf_frame_pkg_s pkg = { 0 }; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3329, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_did, NULL, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for LOGO", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_LOGO; /* LOGO */ ox_id = xchg->ox_id; unf_fill_package(&pkg, xchg, NULL); /* Fill LOGO entry(payload) */ logo_pld = &fc_entry->logo.payload; memset(logo_pld, 0, sizeof(struct unf_logo_payload_s)); unf_fill_logo_pld(logo_pld, v_lport); /* Start to send LOGO now */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: LOGO send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_did, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void *unf_get_one_big_sfs_buf(struct unf_xchg_s *v_xchg) { struct unf_big_sfs_s *big_sfs = NULL; struct list_head *list_head = NULL; struct unf_xchg_mgr_s *xchg_mgr = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3330, UNF_TRUE, v_xchg, return NULL); xchg_mgr = v_xchg->xchg_mgr; UNF_CHECK_VALID(0x3331, UNF_TRUE, xchg_mgr, return NULL); spin_lock_irqsave(&xchg_mgr->st_big_sfs_pool.big_sfs_pool_lock, flag); if (!list_empty(&xchg_mgr->st_big_sfs_pool.list_free_pool)) { /* from free to busy */ list_head = (&xchg_mgr->st_big_sfs_pool.list_free_pool)->next; list_del(list_head); xchg_mgr->st_big_sfs_pool.free_count--; list_add_tail(list_head, &xchg_mgr->st_big_sfs_pool.list_busy_pool); big_sfs = list_entry(list_head, struct unf_big_sfs_s, entry_big_sfs); } else { spin_unlock_irqrestore( &xchg_mgr->st_big_sfs_pool.big_sfs_pool_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Allocate big sfs buf failed, count(0x%x) exchange(0x%p) command(0x%x)", xchg_mgr->st_big_sfs_pool.free_count, v_xchg, v_xchg->cmnd_code); return NULL; } spin_unlock_irqrestore(&xchg_mgr->st_big_sfs_pool.big_sfs_pool_lock, flag); v_xchg->big_sfs_buf = big_sfs; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Allocate one address(0x%p) of big sfs buffer, remaining count(0x%x) exchange(0x%p) command(0x%x)", big_sfs->vaddr, xchg_mgr->st_big_sfs_pool.free_count, v_xchg, v_xchg->cmnd_code); return big_sfs->vaddr; } static void unf_echo_callback(void *v_lport, void *v_rport, void *v_xchg) { struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_rport_s *rport = (struct unf_rport_s *)v_rport; struct unf_xchg_s *xchg = NULL; struct unf_echo_payload_s *echo_rsp_pld = NULL; unsigned int cmnd = 0; unsigned int mag_ver_local = 0; unsigned int mag_ver_remote = 0; UNF_CHECK_VALID(0x3332, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3333, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3334, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(lport); UNF_REFERNCE_VAR(rport); xchg = (struct unf_xchg_s *)v_xchg; if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) return; echo_rsp_pld = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo_acc.echo_pld; UNF_CHECK_VALID(0x3335, UNF_TRUE, NULL != echo_rsp_pld, return); if (xchg->byte_orders & UNF_BIT_2) { unf_big_end_to_cpu((unsigned char *)echo_rsp_pld, sizeof(struct unf_echo_payload_s)); cmnd = echo_rsp_pld->cmnd; } else { cmnd = echo_rsp_pld->cmnd; } mag_ver_local = echo_rsp_pld->data[0]; mag_ver_remote = echo_rsp_pld->data[1]; /* Print info */ if ((cmnd & UNF_ELS_CMND_HIGH_MASK) == UNF_ELS_CMND_ACC) { if ((mag_ver_local == ECHO_MG_VERSION_LOCAL) && (mag_ver_remote == ECHO_MG_VERSION_REMOTE)) { /* both side are 1822 */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "LPort(0x%x) send ECHO to RPort(0x%x), received ACC. local snd echo:(0x%x), remote rcv echo:(0x%x), remote snd echo acc:(0x%x), local rcv echo acc:(0x%x)", lport->port_id, rport->nport_id, xchg->private[PKG_PRIVATE_ECHO_CMD_SND_TIME], xchg->private[PKG_PRIVATE_ECHO_CMD_RCV_TIME], xchg->private[PKG_PRIVATE_ECHO_RSP_SND_TIME], xchg->private[PKG_PRIVATE_ECHO_ACC_RCV_TIME]); } else if ((mag_ver_local == ECHO_MG_VERSION_LOCAL) && (mag_ver_remote != ECHO_MG_VERSION_REMOTE)) { /* the peer don't supprt smartping, only local snd * and rcv rsp time stamp */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "LPort(0x%x) send ECHO to RPort(0x%x), received ACC. local snd echo:(0x%x), local rcv echo acc:(0x%x)", lport->port_id, rport->nport_id, xchg->private[PKG_PRIVATE_ECHO_CMD_SND_TIME], xchg->private[PKG_PRIVATE_ECHO_ACC_RCV_TIME]); } else if ((mag_ver_local != ECHO_MG_VERSION_LOCAL) && (mag_ver_remote != ECHO_MG_VERSION_REMOTE)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "LPort(0x%x) send ECHO to RPort(0x%x), received ACC. local and remote is not IN300", lport->port_id, rport->nport_id); } } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ECHO to RPort(0x%x) and received RJT", lport->port_id, rport->nport_id); } xchg->echo_info.echo_result = UNF_ELS_ECHO_RESULT_OK; xchg->echo_info.response_time = jiffies - xchg->echo_info.response_time; /* wake up semaphore */ up(&xchg->echo_info.echo_sync_sema); } static void unf_echo_ob_callback(struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; UNF_CHECK_VALID(0x3336, UNF_TRUE, v_xchg, return); lport = v_xchg->lport; UNF_CHECK_VALID(0x3337, UNF_TRUE, lport, return); rport = v_xchg->rport; UNF_CHECK_VALID(0x3338, UNF_TRUE, rport, return); UNF_REFERNCE_VAR(lport); UNF_REFERNCE_VAR(rport); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ECHO to RPort(0x%x) but timeout", lport->port_id, rport->nport_id); v_xchg->echo_info.echo_result = UNF_ELS_ECHO_RESULT_FAIL; /* wake up semaphore */ up(&v_xchg->echo_info.echo_sync_sema); } unsigned int unf_send_echo(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, unsigned int *v_time) { struct unf_echo_payload_s *echo_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; struct unf_frame_pkg_s pkg = { 0 }; unsigned int ret = UNF_RETURN_ERROR; unsigned long delay = 0; unsigned short ox_id = 0; dma_addr_t phy_echo_addr; UNF_CHECK_VALID(0x3340, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3341, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3342, UNF_TRUE, v_time, return UNF_RETURN_ERROR); delay = 2 * (unsigned long)(v_lport->ra_tov); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for ECHO", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_ECHO; /* ECHO */ xchg->fcp_sfs_union.sfs_entry.cur_offset = UNF_ECHO_REQ_SIZE; ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_callback = unf_echo_callback; /* wake up semaphore */ xchg->pfn_ob_callback = unf_echo_ob_callback; /* wake up semaphore */ unf_fill_package(&pkg, xchg, v_rport); /* Fill ECHO entry(payload) */ echo_pld = (struct unf_echo_payload_s *)unf_get_one_big_sfs_buf(xchg); if (!echo_pld) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't allocate buffer for ECHO", v_lport->port_id); unf_cm_free_xchg(v_lport, xchg); return UNF_RETURN_ERROR; } fc_entry->echo.echo_pld = echo_pld; phy_echo_addr = pci_map_single(v_lport->low_level_func.dev, echo_pld, UNF_ECHO_PAYLOAD_LEN, DMA_BIDIRECTIONAL); if (pci_dma_mapping_error( v_lport->low_level_func.dev, phy_echo_addr)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) pci map err", v_lport->port_id); unf_cm_free_xchg(v_lport, xchg); return UNF_RETURN_ERROR; } fc_entry->echo.phy_echo_addr = phy_echo_addr; memset(echo_pld, 0, sizeof(struct unf_echo_payload_s)); echo_pld->cmnd = (UNF_ELS_CMND_ECHO); echo_pld->data[0] = ECHO_MG_VERSION_LOCAL; ret = unf_xchg_ref_inc(xchg, SEND_ELS); UNF_CHECK_VALID(0x3343, UNF_TRUE, (ret == RETURN_OK), return UNF_RETURN_ERROR); /* Start to send ECHO command */ xchg->echo_info.response_time = jiffies; ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) { unf_cm_free_xchg((void *)v_lport, (void *)xchg); } else { if (down_timeout(&xchg->echo_info.echo_sync_sema, (long) msecs_to_jiffies((unsigned int)delay))) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]ECHO send %s. Port(0x%x)--->rport(0x%x) but response timeout with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id); xchg->echo_info.echo_result = UNF_ELS_ECHO_RESULT_FAIL; } if (xchg->echo_info.echo_result == UNF_ELS_ECHO_RESULT_FAIL) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "Echo send fail or timeout"); ret = UNF_RETURN_ERROR; } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "echo acc rsp,echo_cmd_snd(0x%xus)-->echo_cmd_rcv(0x%xus)-->echo_acc_snd(0x%xus)-->echo_acc_rcv(0x%xus).", xchg->private[PKG_PRIVATE_ECHO_CMD_SND_TIME], xchg->private[PKG_PRIVATE_ECHO_CMD_RCV_TIME], xchg->private[PKG_PRIVATE_ECHO_RSP_SND_TIME], xchg->private[PKG_PRIVATE_ECHO_ACC_RCV_TIME]); *v_time = ( xchg->private[PKG_PRIVATE_ECHO_ACC_RCV_TIME] - xchg->private[PKG_PRIVATE_ECHO_CMD_SND_TIME]) - (xchg->private[PKG_PRIVATE_ECHO_RSP_SND_TIME] - xchg->private[PKG_PRIVATE_ECHO_CMD_RCV_TIME]); } } pci_unmap_single(v_lport->low_level_func.dev, phy_echo_addr, UNF_ECHO_PAYLOAD_LEN, DMA_BIDIRECTIONAL); fc_entry->echo.phy_echo_addr = 0; unf_xchg_ref_dec(xchg, SEND_ELS); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_prli_pld(struct unf_pril_payload_s *v_prli_pld, struct unf_lport_s *v_lport) { unsigned int pld_len = 0; UNF_CHECK_VALID(0x3344, UNF_TRUE, v_prli_pld, return); UNF_CHECK_VALID(0x3345, UNF_TRUE, v_lport, return); pld_len = sizeof(struct unf_pril_payload_s) - UNF_PRLI_SIRT_EXTRA_SIZE; v_prli_pld->cmnd = (UNF_ELS_CMND_PRLI | ((unsigned int)UNF_FC4_FRAME_PAGE_SIZE << UNF_FC4_FRAME_PAGE_SIZE_SHIFT) | ((unsigned int)pld_len)); v_prli_pld->parms[0] = (UNF_FC4_FRAME_PARM_0_FCP | UNF_FC4_FRAME_PARM_0_I_PAIR); v_prli_pld->parms[1] = UNF_NOT_MEANINGFUL; v_prli_pld->parms[2] = UNF_NOT_MEANINGFUL; /* About Read Xfer_rdy disable */ v_prli_pld->parms[3] = (UNF_FC4_FRAME_PARM_3_R_XFER_DIS | v_lport->options); /* About FCP confirm */ if (v_lport->low_level_func.lport_cfg_items.fcp_conf == UNF_TRUE) v_prli_pld->parms[3] |= UNF_FC4_FRAME_PARM_3_CONF_ALLOW; /* About Tape support */ if (v_lport->low_level_func.lport_cfg_items.tape_support) { v_prli_pld->parms[3] |= (UNF_FC4_FRAME_PARM_3_REC_SUPPORT | UNF_FC4_FRAME_PARM_3_RETRY_SUPPORT | UNF_FC4_FRAME_PARM_3_TASK_RETRY_ID_SUPPORT | UNF_FC4_FRAME_PARM_3_CONF_ALLOW); } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x)'s PRLI payload: options(0x%x) parameter-3(0x%x)", v_lport->port_id, v_lport->options, v_prli_pld->parms[3]); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_prli_pld, sizeof(struct unf_pril_payload_s)); } static void unf_prli_callback(void *v_lport, void *v_rport, void *v_xchg) { /* RCVD PRLI RSP: ACC or RJT --->>> SCSI Link Up */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_pril_payload_s *prli_acc_pld = NULL; unsigned long flag = 0; unsigned int cmnd = 0; unsigned int options = 0; unsigned int fcp_conf = 0; unsigned int rec_support = 0; unsigned int task_retry_support = 0; unsigned int retry_support = 0; unsigned int tape_support = 0; enum unf_rport_login_state_e rport_state = UNF_RPORT_ST_INIT; UNF_CHECK_VALID(0x3679, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3680, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3681, UNF_TRUE, v_xchg, return); lport = (struct unf_lport_s *)v_lport; rport = (struct unf_rport_s *)v_rport; xchg = (struct unf_xchg_s *)v_xchg; if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange(%p) entry is NULL", lport->port_id, xchg); return; } /* Get PRLI ACC payload */ prli_acc_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->prli_acc.payload; if (xchg->byte_orders & UNF_BIT_2) { /* Change to little End, About INI/TGT mode & confirm info */ options = be32_to_cpu(prli_acc_pld->parms[3]) & (UNF_FC4_FRAME_PARM_3_TGT | UNF_FC4_FRAME_PARM_3_INI); cmnd = be32_to_cpu(prli_acc_pld->cmnd); fcp_conf = be32_to_cpu(prli_acc_pld->parms[3]) & UNF_FC4_FRAME_PARM_3_CONF_ALLOW; rec_support = be32_to_cpu(prli_acc_pld->parms[3]) & UNF_FC4_FRAME_PARM_3_REC_SUPPORT; task_retry_support = be32_to_cpu(prli_acc_pld->parms[3]) & UNF_FC4_FRAME_PARM_3_TASK_RETRY_ID_SUPPORT; retry_support = be32_to_cpu(prli_acc_pld->parms[3]) & UNF_FC4_FRAME_PARM_3_RETRY_SUPPORT; } else { options = (prli_acc_pld->parms[3]) & (UNF_FC4_FRAME_PARM_3_TGT | UNF_FC4_FRAME_PARM_3_INI); cmnd = (prli_acc_pld->cmnd); fcp_conf = prli_acc_pld->parms[3] & UNF_FC4_FRAME_PARM_3_CONF_ALLOW; rec_support = prli_acc_pld->parms[3] & UNF_FC4_FRAME_PARM_3_REC_SUPPORT; task_retry_support = prli_acc_pld->parms[3] & UNF_FC4_FRAME_PARM_3_TASK_RETRY_ID_SUPPORT; retry_support = prli_acc_pld->parms[3] & UNF_FC4_FRAME_PARM_3_RETRY_SUPPORT; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: PRLI RSP: RPort(0x%x) parameter-3(0x%x) option(0x%x) cmd(0x%x) rec support:%u", rport->nport_id, prli_acc_pld->parms[3], options, cmnd, rec_support); /* PRLI ACC: R_Port READY & Report R_Port Link Up */ if ((cmnd & UNF_ELS_CMND_HIGH_MASK) == UNF_ELS_CMND_ACC) { /* Update R_Port options(INI/TGT/BOTH) */ rport->options = options; unf_update_port_feature(rport->port_name, rport->options); /* NOTE: R_Port only with INI mode, send LOGO */ if (rport->options == UNF_PORT_MODE_INI) { /* Update R_Port state: LOGO */ spin_lock_irqsave(&rport->rport_state_lock, flag); unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* NOTE: Start to Send LOGO */ unf_rport_enter_logo(lport, rport); return; } /* About confirm */ if (fcp_conf && (lport->low_level_func.lport_cfg_items.fcp_conf != UNF_FALSE)) { rport->fcp_conf_needed = UNF_TRUE; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) FCP config is need for RPort(0x%x)", lport->port_id, lport->nport_id, rport->nport_id); } tape_support = (rec_support && task_retry_support && retry_support); if (tape_support && (lport->low_level_func.lport_cfg_items.tape_support != UNF_FALSE)) { rport->tape_support_needed = UNF_TRUE; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]Port(0x%x_0x%x) Rec is enabled for RPort(0x%x)", lport->port_id, lport->nport_id, rport->nport_id); } /* Update R_Port state: READY */ spin_lock_irqsave(&rport->rport_state_lock, flag); unf_rport_state_ma(rport, UNF_EVENT_RPORT_READY); rport_state = rport->rp_state; spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Report R_Port online (Link Up) event to SCSI */ if (rport_state == UNF_RPORT_ST_READY) { rport->logo_retries = 0; unf_update_lport_state_by_linkup_event( lport, rport, rport->options); } } else { /* PRLI RJT: Do R_Port error recovery */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x)<---LS_RJT(DID:0x%x SID:0x%x) for PRLI. RPort(0x%p) OX_ID(0x%x)", lport->port_id, lport->nport_id, rport->nport_id, rport, xchg->ox_id); unf_rport_error_recovery(rport); } } static void unf_prli_ob_callback(struct unf_xchg_s *v_xchg) { /* Do R_Port recovery */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3676, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(lport); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; rport = v_xchg->rport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3677, UNF_TRUE, lport, return); UNF_CHECK_VALID(0x3678, UNF_TRUE, rport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) RPort(0x%x) send PRLI failed and do recovery", lport->port_id, lport->nport_id, rport->nport_id); /* Start to do R_Port error recovery */ unf_rport_error_recovery(rport); UNF_REFERNCE_VAR(lport); } unsigned int unf_send_prli(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_pril_payload_s *prli_pal = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3346, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3347, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); /* Get & Set new free exchange */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for PRLI", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_PRLI; // PRLI ox_id = xchg->ox_id; /* Set callback function */ /* for rcvd prli acc/rjt processer */ xchg->pfn_callback = unf_prli_callback; /* for send prli failed processer */ xchg->pfn_ob_callback = unf_prli_ob_callback; unf_fill_package(&pkg, xchg, v_rport); /* Fill PRLI payload */ prli_pal = &fc_entry->prli.payload; memset(prli_pal, 0, sizeof(struct unf_pril_payload_s)); unf_fill_prli_pld(prli_pal, v_lport); /* Start to Send RPLI ELS CMND */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: PRLI send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_prlo_pld(struct unf_pril_payload_s *v_prlo_pld, struct unf_lport_s *v_lport) { UNF_CHECK_VALID(0x3348, UNF_TRUE, v_prlo_pld, return); UNF_CHECK_VALID(0x3349, UNF_TRUE, v_lport, return); v_prlo_pld->cmnd = (UNF_ELS_CMND_PRLO); v_prlo_pld->parms[0] = (UNF_FC4_FRAME_PARM_0_FCP); v_prlo_pld->parms[1] = UNF_NOT_MEANINGFUL; v_prlo_pld->parms[2] = UNF_NOT_MEANINGFUL; v_prlo_pld->parms[3] = UNF_NO_SERVICE_PARAMS; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_prlo_pld, sizeof(struct unf_pril_payload_s)); } unsigned int unf_send_prlo(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_pril_payload_s *prlo_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3350, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3351, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); /* Get free exchange for PRLO */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for PRLO", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_PRLO; /* PRLO */ ox_id = xchg->ox_id; unf_fill_package(&pkg, xchg, v_rport); /* Fill PRLO entry(payload) */ prlo_pld = &fc_entry->prlo.payload; memset(prlo_pld, 0, sizeof(struct unf_pril_payload_s)); unf_fill_prlo_pld(prlo_pld, v_lport); /* Start to send PRLO command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: PRLO send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_rrq_pld(struct unf_rrq_s *v_rrq_pld, struct unf_xchg_s *v_xchg) { UNF_CHECK_VALID(0x3360, UNF_TRUE, v_rrq_pld, return); UNF_CHECK_VALID(0x3361, UNF_TRUE, v_xchg, return); v_rrq_pld->cmnd = UNF_ELS_CMND_RRQ; v_rrq_pld->sid = v_xchg->sid; v_rrq_pld->oxid_rxid = ((unsigned int)v_xchg->ox_id << 16 | v_xchg->rx_id); } static void unf_rrq_callback(void *v_lport, void *v_rport, void *v_xchg) { /* Release I/O */ struct unf_lport_s *lport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_els_acc_s *els_acc = NULL; unsigned int cmnd = 0; struct unf_xchg_s *io_xchg = NULL; UNF_CHECK_VALID(0x3696, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3697, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3698, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(v_rport); lport = (struct unf_lport_s *)v_lport; UNF_REFERNCE_VAR(lport); xchg = (struct unf_xchg_s *)v_xchg; if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) exchange(0x%p) SfsEntryPtr is NULL", lport->port_id, xchg); return; } io_xchg = (struct unf_xchg_s *)xchg->io_xchg; if (!io_xchg) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) IO exchange is NULL. RRQ cb sfs xchg(0x%p) tag(0x%x)", lport->port_id, xchg, xchg->hot_pool_tag); return; } UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR, "[info]Port(0x%x) release IO exch(0x%p) tag(0x%x). RRQ cb sfs xchg(0x%p) tag(0x%x)", lport->port_id, xchg->io_xchg, io_xchg->hot_pool_tag, xchg, xchg->hot_pool_tag); /* NOTE: release I/O exchange resource */ unf_xchg_ref_dec(io_xchg, XCHG_ALLOC); els_acc = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->els_acc; if (xchg->byte_orders & UNF_BIT_2) cmnd = be32_to_cpu(els_acc->cmnd); else cmnd = (els_acc->cmnd); } static void unf_rrq_ob_callback(struct unf_xchg_s *v_xchg) { /* Release I/O */ struct unf_xchg_s *xchg = NULL; struct unf_xchg_s *io_xchg = NULL; xchg = (struct unf_xchg_s *)v_xchg; if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Exchange can't be NULL"); return; } io_xchg = (struct unf_xchg_s *)xchg->io_xchg; if (!io_xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]IO exchange can't be NULL with Sfs exch(0x%p) tag(0x%x)", xchg, xchg->hot_pool_tag); return; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]send RRQ failed: SFS exch(0x%p) tag(0x%x) exch(0x%p) tag(0x%x) OXID_RXID(0x%x_0x%x) SID_DID(0x%x_0x%x)", xchg, xchg->hot_pool_tag, io_xchg, io_xchg->hot_pool_tag, io_xchg->ox_id, io_xchg->rx_id, io_xchg->sid, io_xchg->did); /* NOTE: Free I/O exchange resource */ unf_xchg_ref_dec(io_xchg, XCHG_ALLOC); } unsigned int unf_send_rrq(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { /* after ABTS Done */ struct unf_rrq_s *rrq_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; struct unf_frame_pkg_s pkg = { 0 }; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3362, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3363, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3364, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); if (v_xchg->rport_bind_jifs != v_rport->rport_alloc_jifs || (v_rport->nport_id == INVALID_VALUE32)) return ret; /* Get & Set New free Exchange for RRQ */ xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for RRQ", v_lport->port_id); return ret; } xchg->cmnd_code = ELS_RRQ; // RRQ /* Set callback function */ xchg->pfn_callback = unf_rrq_callback; // release I/O exchange context /* release I/O exchange context */ xchg->pfn_ob_callback = unf_rrq_ob_callback; xchg->io_xchg = v_xchg; // pointer to IO XCHG ox_id = xchg->ox_id; unf_fill_package(&pkg, xchg, v_rport); /* Fill RRQ entry(payload) */ rrq_pld = &fc_entry->rrq; memset(rrq_pld, 0, sizeof(struct unf_rrq_s)); unf_fill_rrq_pld(rrq_pld, v_xchg); /* Start to send RRQ command to remote port */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]RRQ send %s. Port(0x%x)--->rport(0x%x) free old exchange(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, v_xchg->hot_pool_tag, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_gff_id_pld(struct unf_gffid_s *v_gff_id, unsigned int v_nport_id) { UNF_CHECK_VALID(0x3365, UNF_TRUE, v_gff_id, return); v_gff_id->ctiu_pream.rev_inid = (UNF_REV_NPORTID_INIT); v_gff_id->ctiu_pream.gstype_gssub_options = (UNF_FSTYPE_OPT_INIT); v_gff_id->ctiu_pream.cmnd_rsp_size = (UNF_FSTYPE_GFF_ID); v_gff_id->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; v_gff_id->nport_id = v_nport_id; } static void unf_gff_id_ob_callback(struct unf_xchg_s *v_xchg) { /* Send PLOGI */ struct unf_lport_s *lport = NULL; struct unf_lport_s *root_lport = NULL; struct unf_rport_s *rport = NULL; unsigned long flag = 0; unsigned int ret = UNF_RETURN_ERROR; unsigned int nport_id = 0; UNF_CHECK_VALID(0x3611, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; nport_id = v_xchg->disc_port_id; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3612, UNF_TRUE, NULL != lport, return); root_lport = (struct unf_lport_s *)lport->root_lport; atomic_inc(&root_lport->disc.disc_thread_info.disc_contrl_size); wake_up_process(root_lport->disc.disc_thread_info.data_thread); /* Get (safe) R_Port */ rport = unf_get_rport_by_nport_id(lport, nport_id); rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, nport_id); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't allocate new RPort(0x%x)", lport->port_id, nport_id); return; } UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) send GFF_ID(0x%x_0x%x) to RPort(0x%x_0x%x) abnormal", lport->port_id, lport->nport_id, v_xchg->ox_id, v_xchg->rx_id, rport->rport_index, rport->nport_id); /* Update R_Port state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = nport_id; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* NOTE: Start to send PLOGI */ ret = unf_send_plogi(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send PLOGI failed, enter recovry", lport->port_id); /* Do R_Port recovery */ unf_rport_error_recovery(rport); } } static void unf_check_rport_need_delay_plogi(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, unsigned int v_port_feature) { /* * Called by: * 1. Private loop * 2. RCVD GFF_ID ACC */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; unsigned long flag = 0; unsigned int nport_id = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3613, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3614, UNF_TRUE, v_rport, return); nport_id = rport->nport_id; /* * Send GFF_ID means L_Port has INI attribute ** * When to send PLOGI: * 1. R_Port has TGT mode (COM or TGT), send PLOGI immediately * 2. R_Port only with INI, send LOGO immediately * 3. R_Port with unknown attribute, delay to send PLOGI */ if ((v_port_feature & UNF_PORT_MODE_TGT) || (lport->enhanced_features & UNF_LPORT_ENHANCED_FEATURE_ENHANCED_GFF)) { /* R_Port has TGT mode: send PLOGI immediately */ rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY, nport_id); UNF_CHECK_VALID(0x3615, UNF_TRUE, rport, return); /* Update R_Port state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = nport_id; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Start to send PLOGI */ ret = unf_send_plogi(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send PLOGI to RPort(0x%x) failed", lport->port_id, lport->nport_id, nport_id); unf_rport_error_recovery(rport); } } else if (v_port_feature == UNF_PORT_MODE_INI) { /* R_Port only with INI mode: can't send PLOGI --->>> * LOGO/nothing */ spin_lock_irqsave(&rport->rport_state_lock, flag); if (rport->rp_state == UNF_RPORT_ST_INIT) { spin_unlock_irqrestore(&rport->rport_state_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send LOGO to RPort(0x%x) which only with INI mode", lport->port_id, lport->nport_id, nport_id); /* Enter Closing state */ unf_rport_enter_logo(lport, rport); } else { spin_unlock_irqrestore(&rport->rport_state_lock, flag); } } else { /* Unknown R_Port attribute: Delay to send PLOGI */ rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY, nport_id); UNF_CHECK_VALID(0x3616, UNF_TRUE, rport, return); /* Update R_Port state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = nport_id; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); unf_rport_delay_login(rport); } } static void unf_rcv_gff_id_acc(struct unf_lport_s *v_lport, struct unf_gffid_rsp_s *v_gff_id_rsp_pld, unsigned int v_nport_id) { /* Delay to LOGIN */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; struct unf_gffid_rsp_s *gff_id_rsp_pld = v_gff_id_rsp_pld; unsigned int fc4feature = 0; unsigned long flag = 0; UNF_CHECK_VALID(0x3617, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3618, UNF_TRUE, v_gff_id_rsp_pld, return); fc4feature = gff_id_rsp_pld->fc_4_feature[1]; if ((UNF_GFF_ACC_MASK & fc4feature) == 0) fc4feature = be32_to_cpu(gff_id_rsp_pld->fc_4_feature[1]); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) RPort(0x%x) received GFF_ID ACC. FC4 feature is 0x%x(1:TGT,2:INI,3:COM)", lport->port_id, lport->nport_id, v_nport_id, fc4feature); /* Check (& Get new) R_Port */ rport = unf_get_rport_by_nport_id(lport, v_nport_id); if (rport) rport = unf_find_rport(lport, v_nport_id, rport->port_name); if ((rport) || (UNF_GET_PORT_OPTIONS(fc4feature) != UNF_PORT_MODE_INI)) { rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, v_nport_id); UNF_CHECK_VALID(0x3619, UNF_TRUE, NULL != rport, return); } else { return; } if ((fc4feature & UNF_GFF_ACC_MASK) != 0) { spin_lock_irqsave(&rport->rport_state_lock, flag); rport->options = UNF_GET_PORT_OPTIONS(fc4feature); spin_unlock_irqrestore(&rport->rport_state_lock, flag); } else if (rport->port_name != INVALID_WWPN) { spin_lock_irqsave(&rport->rport_state_lock, flag); rport->options = unf_get_port_feature(rport->port_name); spin_unlock_irqrestore(&rport->rport_state_lock, flag); } /* NOTE: Send PLOGI if necessary */ unf_check_rport_need_delay_plogi(lport, rport, rport->options); } static void unf_rcv_gff_id_rjt(struct unf_lport_s *v_lport, struct unf_gffid_rsp_s *v_gff_id_rsp_pld, unsigned int v_nport_id) { /* Delay LOGIN or LOGO */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; struct unf_gffid_rsp_s *gff_id_rsp_pld = v_gff_id_rsp_pld; unsigned int rjt_reason = 0; unsigned long flag = 0; UNF_CHECK_VALID(0x3620, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3621, UNF_TRUE, v_gff_id_rsp_pld, return); /* Check (& Get new) R_Port */ rport = unf_get_rport_by_nport_id(lport, v_nport_id); if (rport) rport = unf_find_rport(lport, v_nport_id, rport->port_name); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) get RPort by N_Port_ID(0x%x) failed and alloc new", lport->port_id, v_nport_id); rport = unf_rport_get_free_and_init(lport, UNF_PORT_TYPE_FC, v_nport_id); UNF_CHECK_VALID(0x3622, UNF_TRUE, NULL != rport, return); spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = v_nport_id; spin_unlock_irqrestore(&rport->rport_state_lock, flag); } rjt_reason = gff_id_rsp_pld->ctiu_pream.frag_reason_exp_vend; UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send GFF_ID for RPort(0x%x) but was rejected. Reason code(0x%x)", lport->port_id, v_nport_id, rjt_reason); if (!UNF_GNN_GFF_ID_RJT_REASON(rjt_reason)) { rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY, v_nport_id); UNF_CHECK_VALID(0x3623, UNF_TRUE, NULL != rport, return); /* Update R_Port state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = v_nport_id; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Delay to send PLOGI */ unf_rport_delay_login(rport); } else { spin_lock_irqsave(&rport->rport_state_lock, flag); if (rport->rp_state == UNF_RPORT_ST_INIT) { spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Enter closing state */ unf_rport_enter_logo(lport, rport); } else { spin_unlock_irqrestore(&rport->rport_state_lock, flag); } } } static void unf_gff_id_callback(void *v_lport, void *v_sns_port, void *v_xchg) { struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_lport_s *root_lport = NULL; struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg; struct unf_gffid_rsp_s *gff_id_rsp_pld = NULL; unsigned int cmnd_rsp_size = 0; unsigned int nport_id = 0; UNF_CHECK_VALID(0x3626, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3627, UNF_TRUE, v_sns_port, return); UNF_CHECK_VALID(0x3628, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(v_sns_port); nport_id = xchg->disc_port_id; gff_id_rsp_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->gff_id_rsp; cmnd_rsp_size = (gff_id_rsp_pld->ctiu_pream.cmnd_rsp_size); root_lport = (struct unf_lport_s *)lport->root_lport; atomic_inc(&root_lport->disc.disc_thread_info.disc_contrl_size); wake_up_process(root_lport->disc.disc_thread_info.data_thread); if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { /* Case for GFF_ID ACC: (Delay)PLOGI */ unf_rcv_gff_id_acc(lport, gff_id_rsp_pld, nport_id); } else if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_REJECT) { /* Case for GFF_ID RJT: Delay PLOGI or LOGO directly */ unf_rcv_gff_id_rjt(lport, gff_id_rsp_pld, nport_id); } else { /* Send PLOGI */ unf_rcv_gff_id_rsp_unknown(lport, nport_id); } } unsigned int unf_send_gff_id(struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, unsigned int v_nport_id) { struct unf_gffid_s *gff_id = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3367, UNF_TRUE, v_sns_port, return UNF_RETURN_ERROR); if (unf_is_lport_valid(v_lport) != RETURN_OK) /* Lport is invalid, no retry or handle required, return ok */ return RETURN_OK; root_lport = (struct unf_lport_s *)v_lport->root_lport; memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_sns_port->nport_id, v_sns_port, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for GFF_ID", v_lport->port_id); return unf_get_and_post_disc_event(v_lport, v_sns_port, v_nport_id, UNF_DISC_GET_FEATURE); } xchg->cmnd_code = NS_GFF_ID; /* GFF_ID */ xchg->disc_port_id = v_nport_id; /* Set callback function */ xchg->pfn_ob_callback = unf_gff_id_ob_callback; /* send PLOGI */ xchg->pfn_callback = unf_gff_id_callback; /* send PLOGI or LOGO */ ox_id = xchg->ox_id; unf_fill_package(&pkg, xchg, v_sns_port); /* Fill GFF_ID payload(entry) */ gff_id = &fc_entry->gff_id; /* GFF_ID */ memset(gff_id, 0, sizeof(struct unf_gffid_s)); unf_fill_gff_id_pld(gff_id, v_nport_id); /* Send GFF_ID GS command now */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); else atomic_dec( &root_lport->disc.disc_thread_info.disc_contrl_size); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: GFF_ID send %s. Port(0x%x)--->rport(0x%x). Inquire RPort(0x%x) OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_sns_port->nport_id, v_nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_gnn_id_pld(struct unf_gnnid_s *v_gnn_id_pld, unsigned int v_nport_id) { /* Inquiry R_Port node name from SW */ UNF_CHECK_VALID(0x3368, UNF_TRUE, v_gnn_id_pld, return); v_gnn_id_pld->ctiu_pream.rev_inid = UNF_REV_NPORTID_INIT; v_gnn_id_pld->ctiu_pream.gstype_gssub_options = UNF_FSTYPE_OPT_INIT; v_gnn_id_pld->ctiu_pream.cmnd_rsp_size = UNF_FSTYPE_GNN_ID; v_gnn_id_pld->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; v_gnn_id_pld->nport_id = v_nport_id; } /* * Function Name : unf_gnn_id_ob_callback * Function Description: Callback for sending GNN_ID abnormal * Input Parameters : struct unf_xchg_s *v_xchg * Output Parameters : N/A * Return Type : void */ static void unf_gnn_id_ob_callback(struct unf_xchg_s *v_xchg) { /* Send GFF_ID */ struct unf_lport_s *lport = NULL; struct unf_rport_s *sns_port = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int nport_id = 0; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3597, UNF_TRUE, v_xchg, return); lport = v_xchg->lport; UNF_CHECK_VALID(0x3598, UNF_TRUE, lport, return); sns_port = v_xchg->rport; UNF_CHECK_VALID(0x3599, UNF_TRUE, sns_port, return); nport_id = v_xchg->disc_port_id; UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send GNN_ID failed to inquire RPort(0x%x)", lport->port_id, nport_id); root_lport = (struct unf_lport_s *)lport->root_lport; atomic_inc(&root_lport->disc.disc_thread_info.disc_contrl_size); wake_up_process(root_lport->disc.disc_thread_info.data_thread); /* NOTE: continue next stage */ ret = unf_get_and_post_disc_event(lport, sns_port, nport_id, UNF_DISC_GET_FEATURE); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", lport->port_id, UNF_DISC_GET_FEATURE, nport_id); unf_rcv_gff_id_rsp_unknown(lport, nport_id); // send PLOGI } } static void unf_rcv_gnn_id_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, struct unf_gnnid_rsp_s *v_gnn_id_rsp_pld, unsigned int v_nport_id) { /* Send GFF_ID or Link down immediately */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *sns_port = v_sns_port; struct unf_gnnid_rsp_s *gnn_id_rsp_pld = v_gnn_id_rsp_pld; struct unf_rport_s *rport = NULL; unsigned long long node_name = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3600, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3601, UNF_TRUE, v_sns_port, return); UNF_CHECK_VALID(0x3602, UNF_TRUE, v_gnn_id_rsp_pld, return); node_name = ((unsigned long long)(gnn_id_rsp_pld->node_name[0]) << 32) | ((unsigned long long)(gnn_id_rsp_pld->node_name[1])); if (node_name == lport->node_name) { /* R_Port & L_Port with same Node Name */ rport = unf_get_rport_by_nport_id(lport, v_nport_id); if (rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]Port(0x%x) has the same node name(0x%llx) with RPort(0x%x), linkdown it", lport->port_id, node_name, v_nport_id); /* Destroy immediately */ unf_rport_immediate_linkdown(lport, rport); } } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x) got RPort(0x%x) with node name(0x%llx) by GNN_ID", lport->port_id, v_nport_id, node_name); /* Start to Send GFF_ID */ ret = unf_get_and_post_disc_event(lport, sns_port, v_nport_id, UNF_DISC_GET_FEATURE); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", lport->port_id, UNF_DISC_GET_FEATURE, v_nport_id); unf_rcv_gff_id_rsp_unknown(lport, v_nport_id); } } } static void unf_rcv_gnn_id_rjt(struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, struct unf_gnnid_rsp_s *v_gnn_id_rsp_pld, unsigned int v_nport_id) { /* Send GFF_ID */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *sns_port = v_sns_port; struct unf_gnnid_rsp_s *gnn_id_rsp_pld = v_gnn_id_rsp_pld; unsigned int rjt_reason = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3603, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3604, UNF_TRUE, v_sns_port, return); UNF_CHECK_VALID(0x3605, UNF_TRUE, v_gnn_id_rsp_pld, return); rjt_reason = (gnn_id_rsp_pld->ctiu_pream.frag_reason_exp_vend); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) GNN_ID was rejected with reason code(0x%x)", lport->port_id, lport->nport_id, rjt_reason); if (!UNF_GNN_GFF_ID_RJT_REASON(rjt_reason)) { /* Node existence: Continue next stage */ ret = unf_get_and_post_disc_event(lport, sns_port, v_nport_id, UNF_DISC_GET_FEATURE); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", lport->port_id, UNF_DISC_GET_FEATURE, v_nport_id); unf_rcv_gff_id_rsp_unknown(lport, v_nport_id); } } } static void unf_gnn_id_callback(void *v_lport, void *v_sns_port, void *v_xchg) { struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_rport_s *sns_port = (struct unf_rport_s *)v_sns_port; struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg; struct unf_gnnid_rsp_s *gnn_id_rsp_pld = NULL; unsigned int cmnd_rsp_size = 0; unsigned int nport_id = 0; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3608, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3609, UNF_TRUE, v_sns_port, return); UNF_CHECK_VALID(0x3610, UNF_TRUE, v_xchg, return); nport_id = xchg->disc_port_id; gnn_id_rsp_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->gnn_id_rsp; cmnd_rsp_size = (gnn_id_rsp_pld->ctiu_pream.cmnd_rsp_size); root_lport = (struct unf_lport_s *)lport->root_lport; atomic_inc(&root_lport->disc.disc_thread_info.disc_contrl_size); wake_up_process(root_lport->disc.disc_thread_info.data_thread); if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { /* Case ACC: send GFF_ID or Link down immediately */ unf_rcv_gnn_id_acc(lport, sns_port, gnn_id_rsp_pld, nport_id); } else if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_REJECT) { /* Case RJT: send GFF_ID */ unf_rcv_gnn_id_rjt(lport, sns_port, gnn_id_rsp_pld, nport_id); } else { /* NOTE: continue next stage */ /* Case unknown: send GFF_ID */ unf_rcv_gnn_id_rsp_unknown(lport, sns_port, nport_id); } } unsigned int unf_send_gnn_id(struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, unsigned int v_nport_id) { /* from DISC stop/re-login */ struct unf_gnnid_s *gnn_id_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3370, UNF_TRUE, v_sns_port, return UNF_RETURN_ERROR); if (unf_is_lport_valid(v_lport) != RETURN_OK) /* Lport is invalid, no retry or handle required, return ok */ return RETURN_OK; root_lport = (struct unf_lport_s *)v_lport->root_lport; memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_sns_port->nport_id, v_sns_port, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) exchange can't be NULL for GNN_ID", v_lport->port_id); return unf_get_and_post_disc_event(v_lport, v_sns_port, v_nport_id, UNF_DISC_GET_NODE_NAME); } xchg->cmnd_code = NS_GNN_ID; /* GNN_ID */ xchg->disc_port_id = v_nport_id; ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_ob_callback = unf_gnn_id_ob_callback; /* send GFF_ID */ xchg->pfn_callback = unf_gnn_id_callback; /* send GFF_ID */ unf_fill_package(&pkg, xchg, v_sns_port); /* Fill GNN_ID entry(payload) */ gnn_id_pld = &fc_entry->gnn_id; /* GNNID payload */ memset(gnn_id_pld, 0, sizeof(struct unf_gnnid_s)); unf_fill_gnn_id_pld(gnn_id_pld, v_nport_id); /* Start to send GNN_ID GS command */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); else atomic_dec( &root_lport->disc.disc_thread_info.disc_contrl_size); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: GNN_ID send %s. Port(0x%x_0x%x)--->rport(0x%x) inquire Nportid(0x%x) OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_sns_port->nport_id, v_nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_gpn_id_pld(struct unf_gpnid_s *v_gpn_id_pld, unsigned int v_nport_id) { UNF_CHECK_VALID(0x3371, UNF_TRUE, v_gpn_id_pld, return); v_gpn_id_pld->ctiu_pream.rev_inid = UNF_REV_NPORTID_INIT; v_gpn_id_pld->ctiu_pream.gstype_gssub_options = UNF_FSTYPE_OPT_INIT; v_gpn_id_pld->ctiu_pream.cmnd_rsp_size = UNF_FSTYPE_GPN_ID; v_gpn_id_pld->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; /* Inquiry WWN from SW */ v_gpn_id_pld->nport_id = v_nport_id; } unsigned int unf_rport_relogin(struct unf_lport_s *v_lport, unsigned int v_nport_id) { /* Send GNN_ID */ struct unf_rport_s *sns_port = NULL; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3563, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); /* Get SNS R_Port */ sns_port = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_DIR_SERV); if (!sns_port) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find fabric Port", v_lport->nport_id); return UNF_RETURN_ERROR; } /* Send GNN_ID now to SW */ ret = unf_get_and_post_disc_event(v_lport, sns_port, v_nport_id, UNF_DISC_GET_NODE_NAME); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", v_lport->nport_id, UNF_DISC_GET_NODE_NAME, v_nport_id); /* NOTE: Continue to next stage */ unf_rcv_gnn_id_rsp_unknown(v_lport, sns_port, v_nport_id); } return ret; } static void unf_rcv_gpn_id_acc(struct unf_lport_s *v_lport, unsigned int v_nport_id, unsigned long long v_port_name) { /* then PLOGI or re-login */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; unsigned long flag = 0; unsigned int ret = UNF_RETURN_ERROR; rport = unf_find_valid_rport(lport, v_port_name, v_nport_id); if (rport) { /* R_Port with TGT mode & L_Port with INI mode: * send PLOGI with INIT state */ if ((rport->options & UNF_PORT_MODE_TGT) == UNF_PORT_MODE_TGT) { rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_INIT, v_nport_id); UNF_CHECK_VALID(0x3630, UNF_TRUE, rport, return); /* Update R_Port state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = v_nport_id; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Start to send PLOGI */ ret = unf_send_plogi(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send PLOGI failed for 0x%x, enter recovry", lport->port_id, lport->nport_id, v_nport_id); unf_rport_error_recovery(rport); } } else { spin_lock_irqsave(&rport->rport_state_lock, flag); if ((rport->rp_state != UNF_RPORT_ST_PLOGI_WAIT) && (rport->rp_state != UNF_RPORT_ST_PRLI_WAIT) && (rport->rp_state != UNF_RPORT_ST_READY)) { unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO); spin_unlock_irqrestore( &rport->rport_state_lock, flag); /* Do LOGO operation */ unf_rport_enter_logo(lport, rport); } else { spin_unlock_irqrestore( &rport->rport_state_lock, flag); } } } else { /* Send GNN_ID */ (void)unf_rport_relogin(lport, v_nport_id); } } static void unf_rcv_gpn_id_rjt(struct unf_lport_s *v_lport, unsigned int v_nport_id) { struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; UNF_CHECK_VALID(0x3631, UNF_TRUE, v_lport, return); rport = unf_get_rport_by_nport_id(lport, v_nport_id); if (rport) unf_rport_linkdown(lport, rport); /* Do R_Port Link down */ } /* * Function Name : unf_rcv_gpn_id_rsp_unknown * Function Description: Process unknown type of GPN_ID response * Input Parameters : struct unf_lport_s *v_lport * : unsigned int v_nport_id * Output Parameters : N/A * Return Type : void */ void unf_rcv_gpn_id_rsp_unknown(struct unf_lport_s *v_lport, unsigned int v_nport_id) { struct unf_lport_s *lport = v_lport; UNF_CHECK_VALID(0x3632, UNF_TRUE, v_lport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) wrong response of GPN_ID with RPort(0x%x)", lport->port_id, v_nport_id); /* NOTE: go to next stage */ (void)unf_rport_relogin(lport, v_nport_id); } static void unf_gpn_id_callback(void *v_lport, void *v_sns_port, void *v_xchg) { struct unf_lport_s *lport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_gpnid_rsp_s *gpn_id_rsp_pld = NULL; unsigned long long port_name = 0; unsigned int cmnd_rsp_size = 0; unsigned int nport_id = 0; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3635, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3636, UNF_TRUE, v_sns_port, return); UNF_CHECK_VALID(0x3637, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(v_sns_port); lport = (struct unf_lport_s *)v_lport; xchg = (struct unf_xchg_s *)v_xchg; nport_id = xchg->disc_port_id; root_lport = (struct unf_lport_s *)lport->root_lport; atomic_inc(&root_lport->disc.disc_thread_info.disc_contrl_size); wake_up_process(root_lport->disc.disc_thread_info.data_thread); gpn_id_rsp_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->gpn_id_rsp; cmnd_rsp_size = gpn_id_rsp_pld->ctiu_pream.cmnd_rsp_size; if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { /* GPN_ID ACC */ port_name = ((unsigned long long) (gpn_id_rsp_pld->port_name[0]) << 32) | ((unsigned long long) (gpn_id_rsp_pld->port_name[1])); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x) GPN_ID ACC with WWN(0x%llx) RPort NPort ID(0x%x)", lport->port_id, port_name, nport_id); /* Send PLOGI or LOGO or GNN_ID */ unf_rcv_gpn_id_acc(lport, nport_id, port_name); } else if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_REJECT) { /* GPN_ID RJT: Link Down */ unf_rcv_gpn_id_rjt(lport, nport_id); } else { /* GPN_ID response type unknown: Send GNN_ID */ unf_rcv_gpn_id_rsp_unknown(lport, nport_id); } } static void unf_gpn_id_ob_callback(struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport = NULL; unsigned int nport_id = 0; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3633, UNF_TRUE, v_xchg, return); lport = v_xchg->lport; nport_id = v_xchg->disc_port_id; UNF_CHECK_VALID(0x3634, UNF_TRUE, lport, return); root_lport = (struct unf_lport_s *)lport->root_lport; atomic_inc(&root_lport->disc.disc_thread_info.disc_contrl_size); wake_up_process(root_lport->disc.disc_thread_info.data_thread); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send GPN_ID failed to inquire RPort(0x%x)", lport->port_id, nport_id); /* NOTE: go to next stage */ (void)unf_rport_relogin(lport, nport_id); } unsigned int unf_send_gpn_id(struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, unsigned int v_nport_id) { struct unf_gpnid_s *gpn_id_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; struct unf_lport_s *root_lport = NULL; UNF_CHECK_VALID(0x3374, UNF_TRUE, v_sns_port, return UNF_RETURN_ERROR); if (unf_is_lport_valid(v_lport) != RETURN_OK) { /* Lport is invalid, no retry or handle required, return ok */ return RETURN_OK; } root_lport = (struct unf_lport_s *)v_lport->root_lport; memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_sns_port->nport_id, v_sns_port, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for GPN_ID", v_lport->port_id); return unf_get_and_post_disc_event(v_lport, v_sns_port, v_nport_id, UNF_DISC_GET_PORT_NAME); } xchg->cmnd_code = NS_GPN_ID; // GPN_ID xchg->disc_port_id = v_nport_id; ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_callback = unf_gpn_id_callback; /* re-login --->>> GNN_ID */ xchg->pfn_ob_callback = unf_gpn_id_ob_callback; unf_fill_package(&pkg, xchg, v_sns_port); /* Fill GPN_ID entry(payload) */ gpn_id_pld = &fc_entry->gpn_id; /* GPN_ID payload */ memset(gpn_id_pld, 0, sizeof(struct unf_gpnid_s)); unf_fill_gpn_id_pld(gpn_id_pld, v_nport_id); /* Send GPN_ID GS command */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); else atomic_dec( &root_lport->disc.disc_thread_info.disc_contrl_size); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: GPN_ID send %s. Port(0x%x)--->rport(0x%x). Inquire RPort(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_sns_port->nport_id, v_nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_gid_ft_pld(struct unf_gid_s *v_gid_pld) { UNF_CHECK_VALID(0x3376, UNF_TRUE, v_gid_pld, return); v_gid_pld->ctiu_pream.rev_inid = UNF_REV_NPORTID_INIT; v_gid_pld->ctiu_pream.gstype_gssub_options = UNF_FSTYPE_OPT_INIT; v_gid_pld->ctiu_pream.cmnd_rsp_size = UNF_FSTYPE_GID_FT; v_gid_pld->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; v_gid_pld->scope_type = UNF_GID_FT_TYPE; } static void unf_gid_ft_ob_callback(struct unf_xchg_s *v_xchg) { /* Do recovery */ struct unf_lport_s *lport = NULL; union unf_sfs_u *sfs_ptr = NULL; struct unf_disc_s *disc = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3589, UNF_TRUE, v_xchg, return); sfs_ptr = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!sfs_ptr) return; spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); if (!lport) return; disc = &lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_FAILED); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* Do DISC recovery operation */ unf_disc_error_recovery(lport); } unsigned int unf_send_gid_ft(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { struct unf_gid_s *gid_pld = NULL; struct unf_gid_rsp_s *gid_rsp = NULL; struct unf_gif_acc_pld_s *gid_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3377, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3378, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for GID_FT", v_lport->port_id); return ret; } xchg->cmnd_code = NS_GID_FT; // GID_FT ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_ob_callback = unf_gid_ft_ob_callback; // do DISC recovery xchg->pfn_callback = unf_gid_ft_callback; unf_fill_package(&pkg, xchg, v_rport); /* Fill GID_FT entry(payload) */ gid_pld = &fc_entry->get_id.gid_req; /* GID req payload */ unf_fill_gid_ft_pld(gid_pld); gid_rsp = &fc_entry->get_id.gid_rsp; /* GID rsp payload */ /* Get GID_FT Response payload */ gid_acc_pld = (struct unf_gif_acc_pld_s *)unf_get_one_big_sfs_buf(xchg); if (!gid_acc_pld) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) allocate GID_FT response buffer failed", v_lport->port_id); unf_cm_free_xchg(v_lport, xchg); return UNF_RETURN_ERROR; } memset(gid_acc_pld, 0, sizeof(struct unf_gif_acc_pld_s)); gid_rsp->gid_acc_pld = gid_acc_pld; /* Send GID_FT GS commmand now */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: GID_FT send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_gid_pt_pld(struct unf_gid_s *v_gid_pld, struct unf_lport_s *v_lport) { UNF_CHECK_VALID(0x3379, UNF_TRUE, v_gid_pld, return); UNF_CHECK_VALID(0x3380, UNF_TRUE, v_lport, return); v_gid_pld->ctiu_pream.rev_inid = (UNF_REV_NPORTID_INIT); v_gid_pld->ctiu_pream.gstype_gssub_options = (UNF_FSTYPE_OPT_INIT); v_gid_pld->ctiu_pream.cmnd_rsp_size = (UNF_FSTYPE_GID_PT); v_gid_pld->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; /* 0x7F000000 means NX_Port */ v_gid_pld->scope_type = UNF_GID_PT_TYPE; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_gid_pld, sizeof(struct unf_gid_s)); } static void unf_gid_pt_ob_callback(struct unf_xchg_s *v_xchg) { /* Do recovery */ struct unf_lport_s *lport = NULL; union unf_sfs_u *sfs_ptr = NULL; struct unf_disc_s *disc = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3593, UNF_TRUE, v_xchg, return); sfs_ptr = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!sfs_ptr) return; spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); if (!lport) return; disc = &lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_FAILED); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* Do DISC recovery operation */ unf_disc_error_recovery(lport); } unsigned int unf_send_gid_pt(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* from DISC start */ struct unf_gid_s *gid_pld = NULL; struct unf_gid_rsp_s *gid_rsp = NULL; struct unf_gif_acc_pld_s *gid_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3381, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3382, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for GID_PT", v_lport->port_id); return ret; } xchg->cmnd_code = NS_GID_PT; /* GID_PT */ ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_ob_callback = unf_gid_pt_ob_callback; /* do DISC recovery */ xchg->pfn_callback = unf_gid_pt_callback; unf_fill_package(&pkg, xchg, v_rport); /* Fill GID_PT entry(payload) */ gid_pld = &fc_entry->get_id.gid_req; /* GID req payload */ unf_fill_gid_pt_pld(gid_pld, v_lport); gid_rsp = &fc_entry->get_id.gid_rsp; /* GID rsp payload */ /* Get GID_PT response payload */ gid_acc_pld = (struct unf_gif_acc_pld_s *)unf_get_one_big_sfs_buf(xchg); if (!gid_acc_pld) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%0x) Allocate GID_PT response buffer failed", v_lport->port_id); unf_cm_free_xchg(v_lport, xchg); return UNF_RETURN_ERROR; } memset(gid_acc_pld, 0, sizeof(struct unf_gif_acc_pld_s)); gid_rsp->gid_acc_pld = gid_acc_pld; /* Send GID_PT GS command to SW */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: GID_PT send %s. Port(0x%x_0x%x)--->rport(0x%x) with OXID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_rft_id_pld(struct unf_rftid_s *v_rft_id_pld, struct unf_lport_s *v_lport) { unsigned int i = 1; UNF_CHECK_VALID(0x3383, UNF_TRUE, v_rft_id_pld, return); UNF_CHECK_VALID(0x3384, UNF_TRUE, v_lport, return); v_rft_id_pld->ctiu_pream.rev_inid = UNF_REV_NPORTID_INIT; v_rft_id_pld->ctiu_pream.gstype_gssub_options = UNF_FSTYPE_OPT_INIT; v_rft_id_pld->ctiu_pream.cmnd_rsp_size = UNF_FSTYPE_RFT_ID; v_rft_id_pld->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; v_rft_id_pld->nport_id = (v_lport->nport_id); v_rft_id_pld->fc_4_types[0] = (UNF_FC4_SCSI_BIT8); for (i = 1; i < 8; i++) v_rft_id_pld->fc_4_types[i] = 0; } static void unf_rft_id_ob_callback(struct unf_xchg_s *v_xchg) { /* Do recovery */ struct unf_lport_s *lport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3687, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3688, UNF_TRUE, lport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send RFT_ID failed", lport->port_id, lport->nport_id); /* Do L_Port recovery operation */ unf_lport_error_recovery(lport); } static void unf_rft_id_callback(void *v_lport, void *v_rport, void *v_xchg) { /* RFT_ID --->>> RFF_ID */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_ctiu_prem_s *ctiu_prem = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int cmnd_rsp_size = 0; unsigned long flag = 0; UNF_CHECK_VALID(0x3689, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3690, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3691, UNF_TRUE, v_xchg, return); lport = (struct unf_lport_s *)v_lport; rport = (struct unf_rport_s *)v_rport; xchg = (struct unf_xchg_s *)v_xchg; if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) SFS entry is NULL with state(0x%x)", lport->port_id, lport->en_states); return; } ctiu_prem = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rft_id_rsp.ctiu_pream; cmnd_rsp_size = ctiu_prem->cmnd_rsp_size; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) RFT_ID response is (0x%x)", (cmnd_rsp_size & UNF_CT_IU_RSP_MASK), lport->port_id, lport->nport_id); if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { /* Case for RFT_ID ACC: send RFF_ID */ spin_lock_irqsave(&lport->lport_state_lock, flag); if (lport->en_states != UNF_LPORT_ST_RFT_ID_WAIT) { spin_unlock_irqrestore(&lport->lport_state_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) receive RFT_ID ACC in state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); return; } /* LPort: RFT_ID_WAIT --> RFF_ID_WAIT */ unf_lport_stat_ma(lport, UNF_EVENT_LPORT_REMOTE_ACC); spin_unlock_irqrestore(&lport->lport_state_lock, flag); /* Start to send RFF_ID GS command */ ret = unf_send_rff_id(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send RFF_ID failed", lport->port_id, lport->nport_id); /* Do L_Port recovery */ unf_lport_error_recovery(lport); } } else { /* Case for RFT_ID RJT: do recovery */ UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) receive RFT_ID RJT with reason_code(0x%x) explanation(0x%x)", lport->port_id, lport->nport_id, (ctiu_prem->frag_reason_exp_vend) & UNF_CT_IU_REASON_MASK, (ctiu_prem->frag_reason_exp_vend) & UNF_CT_IU_EXPLAN_MASK); /* Do L_Port recovery */ unf_lport_error_recovery(lport); } } unsigned int unf_send_rft_id(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* After PLOGI process */ struct unf_rftid_s *rft_id = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3385, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3386, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for RFT_ID", v_lport->port_id); return ret; } xchg->cmnd_code = NS_RFT_ID; /* RFT_ID */ ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_callback = unf_rft_id_callback; xchg->pfn_ob_callback = unf_rft_id_ob_callback; /* Do L_Port recovery */ unf_fill_package(&pkg, xchg, v_rport); /* Fill RFT_ID entry(payload) */ rft_id = &fc_entry->rft_id; memset(rft_id, 0, sizeof(struct unf_rftid_s)); unf_fill_rft_id_pld(rft_id, v_lport); /* Send RFT_ID GS command */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: RFT_ID send %s. Port(0x%x_0x%x)--->rport(0x%x). rport(0x%p) wwpn(0x%llx) OX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_rport, v_rport->port_name, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_fill_rff_id_pld(struct unf_rffid_s *v_rff_id_pld, struct unf_lport_s *v_lport) { UNF_CHECK_VALID(0x3387, UNF_TRUE, v_rff_id_pld, return); UNF_CHECK_VALID(0x3388, UNF_TRUE, v_lport, return); v_rff_id_pld->ctiu_pream.rev_inid = UNF_REV_NPORTID_INIT; v_rff_id_pld->ctiu_pream.gstype_gssub_options = UNF_FSTYPE_OPT_INIT; v_rff_id_pld->ctiu_pream.cmnd_rsp_size = UNF_FSTYPE_RFF_ID; v_rff_id_pld->ctiu_pream.frag_reason_exp_vend = UNF_FRAG_REASON_VENDOR; v_rff_id_pld->nport_id = v_lport->nport_id; v_rff_id_pld->fc_4_feature = UNF_FC4_FCP_TYPE | (v_lport->options << 4); } static void unf_rff_id_callback(void *v_lport, void *v_rport, void *v_xchg) { /* RFF_ID --->>> SCR(for INI mode) */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_ctiu_prem_s *ctiu_prem = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int cmnd_rsp_size = 0; unsigned long flag = 0; UNF_CHECK_VALID(0x3684, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3685, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3686, UNF_TRUE, v_xchg, return); lport = (struct unf_lport_s *)v_lport; xchg = (struct unf_xchg_s *)v_xchg; if (unlikely(!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr)) return; /* SCR send to 0xfffffd(not 0xfffffc), need to get new R_Port */ UNF_REFERNCE_VAR(v_rport); rport = unf_get_rport_by_nport_id(lport, UNF_FC_FID_FCTRL); // 0xfffffd rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, UNF_FC_FID_FCTRL); if (unlikely(!rport)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't allocate RPort(0x%x)", lport->port_id, UNF_FC_FID_FCTRL); return; } rport->nport_id = UNF_FC_FID_FCTRL; ctiu_prem = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rff_id_rsp.ctiu_pream; cmnd_rsp_size = ctiu_prem->cmnd_rsp_size; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]LOGIN: Port(0x%x_0x%x) RFF_ID rsp is (0x%x)", lport->port_id, lport->nport_id, (cmnd_rsp_size & UNF_CT_IU_RSP_MASK)); /* RSP Type check: some SW not support RFF_ID, go to next stage also */ if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) receive RFF ACC(0x%x) in state(0x%x)", lport->port_id, lport->nport_id, (cmnd_rsp_size & UNF_CT_IU_RSP_MASK), lport->en_states); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) receive RFF RJT(0x%x) in state(0x%x) with RJT reason code(0x%x) explanation(0x%x)", lport->port_id, lport->nport_id, (cmnd_rsp_size & UNF_CT_IU_RSP_MASK), lport->en_states, (ctiu_prem->frag_reason_exp_vend) & UNF_CT_IU_REASON_MASK, (ctiu_prem->frag_reason_exp_vend) & UNF_CT_IU_EXPLAN_MASK); } /* L_Port state check */ spin_lock_irqsave(&lport->lport_state_lock, flag); if (lport->en_states != UNF_LPORT_ST_RFF_ID_WAIT) { spin_unlock_irqrestore(&lport->lport_state_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) receive RFF reply in state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); return; } /* Update L_Port state & Send SCR to remote port */ /* LPort: RFF_ID_WAIT --> SCR_WAIT */ unf_lport_stat_ma(lport, UNF_EVENT_LPORT_REMOTE_ACC); spin_unlock_irqrestore(&lport->lport_state_lock, flag); /* Start to send SCR command */ ret = unf_send_scr(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send SCR failed", lport->port_id, lport->nport_id); /* Do L_Port recovery */ unf_lport_error_recovery(lport); } } static void unf_rff_id_ob_callback(struct unf_xchg_s *v_xchg) { /* Do recovery */ struct unf_lport_s *lport = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3682, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flag); lport = v_xchg->lport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag); UNF_CHECK_VALID(0x3683, UNF_TRUE, NULL != lport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send RFF_ID failed", lport->port_id, lport->nport_id); /* Do L_Port recovery */ unf_lport_error_recovery(lport); } unsigned int unf_send_rff_id(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* from RFT_ID, then Send SCR */ struct unf_rffid_s *rff_id = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_CHECK_VALID(0x3389, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3390, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "%s Enter", __func__); xchg = unf_get_sfs_free_xchg_and_init(v_lport, v_rport->nport_id, v_rport, &fc_entry); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for RFF_ID", v_lport->port_id); return ret; } xchg->cmnd_code = NS_RFF_ID; // RFF_ID ox_id = xchg->ox_id; /* Set callback function */ xchg->pfn_callback = unf_rff_id_callback; xchg->pfn_ob_callback = unf_rff_id_ob_callback; /* Do L_Port recovery */ unf_fill_package(&pkg, xchg, v_rport); /* Fill RFF_ID entry(payload) */ rff_id = &fc_entry->rff_id; memset(rff_id, 0, sizeof(struct unf_rffid_s)); unf_fill_rff_id_pld(rff_id, v_lport); /* Send RFF_ID GS command */ ret = unf_gs_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: RFF_ID feature 0x%x(10:TGT,20:INI,30:COM) send %s. Port(0x%x_0x%x)--->pstRPortid(0x%x) rport(0x%p) OX_ID(0x%x)", v_lport->options, (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_rport, ox_id); UNF_REFERNCE_VAR(ox_id); return ret; } static void unf_login_with_rport_in_n2n(struct unf_lport_s *v_lport, unsigned long long v_remote_port_name, unsigned long long v_remote_nort_name) { /* * Call by (P2P): * 1. RCVD FLOGI ACC * 2. Send FLOGI ACC succeed ** * Compare WWN, larger is master, then send PLOGI */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; unsigned long lport_flag = 0; unsigned long rport_flag = 0; unsigned long long port_name = 0; unsigned long long node_name = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3539, UNF_TRUE, v_lport, return); spin_lock_irqsave(&lport->lport_state_lock, lport_flag); /* LPort: FLOGI_WAIT --> READY */ unf_lport_stat_ma(lport, UNF_EVENT_LPORT_READY); spin_unlock_irqrestore(&lport->lport_state_lock, lport_flag); port_name = v_remote_port_name; node_name = v_remote_nort_name; if (lport->port_name > port_name) { /* Master case: send PLOGI */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x)'s WWN(0x%llx) is larger than rport(0x%llx), should be master", lport->port_id, lport->port_name, port_name); /* Update N_Port_ID now: 0xEF */ lport->nport_id = UNF_P2P_LOCAL_NPORT_ID; rport = unf_find_valid_rport(v_lport, port_name, UNF_P2P_REMOTE_NPORT_ID); // 0xD6 rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY, UNF_P2P_REMOTE_NPORT_ID); if (rport) { rport->node_name = node_name; rport->port_name = port_name; rport->nport_id = UNF_P2P_REMOTE_NPORT_ID; // 0xD6 rport->local_nport_id = UNF_P2P_LOCAL_NPORT_ID; // 0xEF spin_lock_irqsave(&rport->rport_state_lock, rport_flag); if ((rport->rp_state == UNF_RPORT_ST_PLOGI_WAIT) || (rport->rp_state == UNF_RPORT_ST_PRLI_WAIT) || (rport->rp_state == UNF_RPORT_ST_READY)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x) Rport(0x%x) have sent PLOGI or PRLI with state(0x%x)", lport->port_id, rport->nport_id, rport->rp_state); spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); return; } /* Update L_Port State: PLOGI_WAIT */ unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); /* P2P with master: Start to Send PLOGI */ ret = unf_send_plogi(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) with WWN(0x%llx) send PLOGI to(0x%llx) failed", lport->port_id, lport->port_name, port_name); unf_rport_error_recovery(rport); } } else { /* Get/Alloc R_Port failed */ UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) with WWN(0x%llx) allocate RPort(ID:0x%x,WWPN:0x%llx) failed", lport->port_id, lport->port_name, UNF_P2P_REMOTE_NPORT_ID, port_name); } } else { /* Slave case: L_Port's Port Name is smaller than R_Port */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) with WWN(0x%llx) is smaller than rport(0x%llx), do nothing", lport->port_id, lport->port_name, port_name); } } static void unf_flogi_acc_ob_callback(struct unf_xchg_s *v_xchg) { /* Callback for Sending FLOGI ACC succeed */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; unsigned long flags = 0; unsigned long long port_name = 0; unsigned long long node_name = 0; UNF_CHECK_VALID(0x3457, UNF_TRUE, v_xchg, return); UNF_CHECK_VALID(0x3458, UNF_TRUE, v_xchg->lport, return); UNF_CHECK_VALID(0x3459, UNF_TRUE, v_xchg->rport, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flags); lport = v_xchg->lport; rport = v_xchg->rport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags); spin_lock_irqsave(&rport->rport_state_lock, flags); port_name = rport->port_name; node_name = rport->node_name; /* Swap case: Set WWPN & WWNN with zero */ rport->port_name = 0; rport->node_name = 0; spin_unlock_irqrestore(&rport->rport_state_lock, flags); /* Enter PLOGI stage: after send FLOGI ACC succeed */ unf_login_with_rport_in_n2n(lport, port_name, node_name); } unsigned int unf_send_flogi_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_flogi_payload_s *flogi_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; unsigned short rx_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3393, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3394, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3395, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_FLOGI); v_xchg->did = 0; /* D_ID must be 0 */ v_xchg->sid = UNF_FC_FID_FLOGI; /* S_ID must be 0xfffffe */ v_xchg->oid = v_xchg->sid; v_xchg->pfn_callback = NULL; v_xchg->lport = v_lport; v_xchg->rport = v_rport; /* call back for sending FLOGI response */ v_xchg->pfn_ob_callback = unf_flogi_acc_ob_callback; fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } unf_fill_package(&pkg, v_xchg, v_rport); /* Fill FLOGI ACC payload */ memset(fc_entry, 0, sizeof(union unf_sfs_u)); flogi_acc_pld = &fc_entry->flogi_acc.flogi_payload; flogi_acc_pld->cmnd = (UNF_ELS_CMND_ACC); unf_fill_flogi_pld(flogi_acc_pld, v_lport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; /* Send FLOGI ACC to remote port */ ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR, "[info]LOGIN: FLOGI ACC send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static void unf_fill_plogi_acc_pld(struct unf_plogi_payload_s *v_plogi_acc_pld, struct unf_lport_s *v_lport) { struct unf_lgn_parms_s *login_parms = NULL; UNF_CHECK_VALID(0x3396, UNF_TRUE, v_plogi_acc_pld, return); UNF_CHECK_VALID(0x3397, UNF_TRUE, v_lport, return); v_plogi_acc_pld->cmnd = (UNF_ELS_CMND_ACC); login_parms = &v_plogi_acc_pld->parms; if ((v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { login_parms->co_parms.bb_credit = unf_low_level_bb_credit(v_lport); login_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_NFPORT; /* 0 */ login_parms->co_parms.bb_scn = (v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) ? 0 : unf_low_level_bbscn(v_lport); } else { login_parms->co_parms.bb_credit = UNF_BBCREDIT_LPORT; login_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_LPORT; /* 1 */ } login_parms->co_parms.lowest_version = UNF_PLOGI_VERSION_LOWER; login_parms->co_parms.highest_version = UNF_PLOGI_VERSION_UPPER; login_parms->co_parms.continuously_increasing = UNF_CONTIN_INCREASE_SUPPORT; login_parms->co_parms.bb_receive_data_field_size = v_lport->max_frame_size; login_parms->co_parms.nport_total_concurrent_sequences = UNF_PLOGI_CONCURRENT_SEQ; login_parms->co_parms.relative_offset = (UNF_PLOGI_RO_CATEGORY); login_parms->co_parms.e_d_tov = (v_lport->ed_tov); login_parms->cl_parms[2].valid = UNF_CLASS_VALID; /* class-3 */ login_parms->cl_parms[2].received_data_field_size = v_lport->max_frame_size; login_parms->cl_parms[2].concurrent_sequences = UNF_PLOGI_CONCURRENT_SEQ; login_parms->cl_parms[2].open_sequences_per_exchange = UNF_PLOGI_SEQ_PER_XCHG; login_parms->high_node_name = UNF_GET_NAME_HIGH_WORD(v_lport->node_name); login_parms->low_node_name = UNF_GET_NAME_LOW_WORD(v_lport->node_name); login_parms->high_port_name = UNF_GET_NAME_HIGH_WORD(v_lport->port_name); login_parms->low_port_name = UNF_GET_NAME_LOW_WORD(v_lport->port_name); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_plogi_acc_pld, sizeof(struct unf_plogi_payload_s)); } static void unf_schedule_open_work(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* Used for L_Port port only with TGT, or R_Port only with INI */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; unsigned long delay = 0; unsigned long flag = 0; unsigned int ret = 0; unsigned int port_feature = INVALID_VALUE32; UNF_CHECK_VALID(0x3452, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3453, UNF_TRUE, v_rport, return); delay = (unsigned long)lport->ed_tov; port_feature = rport->options & UNF_PORT_MODE_BOTH; if ((lport->options == UNF_PORT_MODE_TGT) || (port_feature == UNF_PORT_MODE_INI)) { spin_lock_irqsave(&rport->rport_state_lock, flag); ret = unf_rport_ref_inc(rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) RPort(0x%x) abnormal, no need open", lport->port_id, lport->nport_id, rport->nport_id); spin_unlock_irqrestore(&rport->rport_state_lock, flag); return; } /* Delay work pending check */ if (delayed_work_pending(&rport->open_work)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) RPort(0x%x) open work is running, no need re-open", lport->port_id, lport->nport_id, rport->nport_id); spin_unlock_irqrestore(&rport->rport_state_lock, flag); unf_rport_ref_dec(rport); return; } /* start open work */ if (queue_delayed_work( unf_work_queue, &rport->open_work, (unsigned long) msecs_to_jiffies((unsigned int)delay))) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) RPort(0x%x) start open work", lport->port_id, lport->nport_id, rport->nport_id); (void)unf_rport_ref_inc(rport); } spin_unlock_irqrestore(&rport->rport_state_lock, flag); unf_rport_ref_dec(rport); } } static void unf_plogi_acc_ob_callback(struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; unsigned long flags = 0; UNF_CHECK_VALID(0x3454, UNF_TRUE, v_xchg, return); spin_lock_irqsave(&v_xchg->xchg_state_lock, flags); lport = v_xchg->lport; rport = v_xchg->rport; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags); UNF_CHECK_VALID(0x3455, UNF_TRUE, lport, return); UNF_CHECK_VALID(0x3456, UNF_TRUE, rport, return); /* * 1. According to FC-LS 4.2.7.1: * after RCVD PLOGI or sending PLOGI ACC, need to termitate open EXCH */ unf_cm_xchg_mgr_abort_io_by_id(lport, rport, rport->nport_id, lport->nport_id, 0); /* 2. Send PLOGI ACC fail */ if (v_xchg->ob_callback_sts != UNF_IO_SUCCESS) { /* Do R_Port recovery */ unf_rport_error_recovery(rport); /* Do not care: Just used for L_Port only is * TGT mode or R_Port only is INI mode */ unf_schedule_open_work(lport, rport); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x_0x%x) send PLOGI ACC failed(0x%x) with RPort(0x%x) feature(0x%x)", lport->port_id, lport->nport_id, lport->options, v_xchg->ob_callback_sts, rport->nport_id, rport->options); /* NOTE: return */ return; } /* 3. Private Loop: check whether or not need to send PRLI */ spin_lock_irqsave(&rport->rport_state_lock, flags); if ((lport->en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP) && ((rport->rp_state == UNF_RPORT_ST_PRLI_WAIT) || (rport->rp_state == UNF_RPORT_ST_READY))) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) RPort(0x%x) with State(0x%x) return directly", lport->port_id, lport->nport_id, rport->nport_id, rport->rp_state); /* Do nothing */ spin_unlock_irqrestore(&rport->rport_state_lock, flags); return; } unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PRLI); // PRLI_WAIT spin_unlock_irqrestore(&rport->rport_state_lock, flags); /* 4. Set Port Feature with BOTH: cancel */ if ((rport->options == UNF_PORT_MODE_UNKNOWN) && (rport->port_name != INVALID_WWPN)) rport->options = unf_get_port_feature(rport->port_name); /* * 5. Check whether need to send PRLI delay * Call by: RCVD PLOGI ACC or callback for sending PLOGI ACC succeed */ unf_check_rport_need_delay_prli(lport, rport, rport->options); /* 6. Do not care: Just used for L_Port only is * TGT mode or R_Port only is INI mode */ unf_schedule_open_work(lport, rport); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x_0x%x) send PLOGI ACC succeed with RPort(0x%x) feature(0x%x)", lport->port_id, lport->nport_id, lport->options, rport->nport_id, rport->options); } unsigned int unf_send_plogi_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_plogi_payload_s *plogi_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; unsigned short rx_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3398, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3399, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3400, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_PLOGI); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->pfn_callback = NULL; v_xchg->lport = v_lport; v_xchg->rport = v_rport; /* call back for sending PLOGI ACC */ v_xchg->pfn_ob_callback = unf_plogi_acc_ob_callback; unf_fill_package(&pkg, v_xchg, v_rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } /* Fill PLOGI ACC payload */ memset(fc_entry, 0, sizeof(union unf_sfs_u)); plogi_acc_pld = &fc_entry->plogi_acc.payload; unf_fill_plogi_acc_pld(plogi_acc_pld, v_lport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; /* Start to Send PLOGI ACC now */ ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) /* NOTE: free exchange */ unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); if ((v_rport->nport_id < UNF_FC_FID_DOM_MGR) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: PLOGI ACC send %s. Port(0x%x_0x%x_0x%llx)--->rport(0x%x_0x%llx) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_lport->nport_id, v_lport->port_name, v_rport->nport_id, v_rport->port_name, ox_id, rx_id); } UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static void unf_fill_rjt_pld(struct unf_els_rjt_s *v_els_rjt, unsigned int v_reason_code, unsigned int v_reason_explanation) { UNF_CHECK_VALID(0x3401, UNF_TRUE, v_els_rjt, return); v_els_rjt->cmnd = UNF_ELS_CMND_RJT; v_els_rjt->reason_code = (v_reason_code | v_reason_explanation); } static void unf_fill_prli_acc_pld(struct unf_pril_payload_s *v_prli_acc_pld, struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { unsigned int port_mode = UNF_FC4_FRAME_PARM_3_TGT; UNF_CHECK_VALID(0x3402, UNF_TRUE, v_prli_acc_pld, return); UNF_CHECK_VALID(0x3403, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3404, UNF_TRUE, v_rport, return); v_prli_acc_pld->cmnd = ( UNF_ELS_CMND_ACC | ((unsigned int)UNF_FC4_FRAME_PAGE_SIZE << UNF_FC4_FRAME_PAGE_SIZE_SHIFT) | ((unsigned int)(sizeof(struct unf_pril_payload_s) - UNF_PRLI_SIRT_EXTRA_SIZE))); v_prli_acc_pld->parms[0] = (UNF_FC4_FRAME_PARM_0_FCP | UNF_FC4_FRAME_PARM_0_I_PAIR | UNF_FC4_FRAME_PARM_0_GOOD_RSP_CODE); v_prli_acc_pld->parms[1] = UNF_NOT_MEANINGFUL; v_prli_acc_pld->parms[2] = UNF_NOT_MEANINGFUL; /* About INI/TGT mode */ if (v_rport->nport_id < UNF_FC_FID_DOM_MGR) /* return INI (0x20): R_Port has TGT mode, * L_Port has INI mode */ port_mode = UNF_FC4_FRAME_PARM_3_INI; else port_mode = v_lport->options; /* About Read xfer_rdy disable */ v_prli_acc_pld->parms[3] = (UNF_FC4_FRAME_PARM_3_R_XFER_DIS | port_mode); /* 0x2 */ /* About Tape support */ if (v_rport->tape_support_needed) { v_prli_acc_pld->parms[3] |= (UNF_FC4_FRAME_PARM_3_REC_SUPPORT | UNF_FC4_FRAME_PARM_3_RETRY_SUPPORT | UNF_FC4_FRAME_PARM_3_TASK_RETRY_ID_SUPPORT | UNF_FC4_FRAME_PARM_3_CONF_ALLOW); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "PRLI ACC tape support"); } /* About confirm */ if (v_lport->low_level_func.lport_cfg_items.fcp_conf == UNF_TRUE) /* 0x80 */ v_prli_acc_pld->parms[3] |= UNF_FC4_FRAME_PARM_3_CONF_ALLOW; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_prli_acc_pld, sizeof(struct unf_pril_payload_s)); } static void unf_prli_acc_ob_callback(struct unf_xchg_s *v_xchg) { /* Report R_Port scsi Link Up */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; unsigned long flags = 0; enum unf_rport_login_state_e rport_state = UNF_RPORT_ST_INIT; UNF_CHECK_VALID(0x3449, UNF_TRUE, v_xchg, return); lport = v_xchg->lport; rport = v_xchg->rport; UNF_CHECK_VALID(0x3450, UNF_TRUE, lport, return); UNF_CHECK_VALID(0x3451, UNF_TRUE, rport, return); /* Update & Report Link Up */ spin_lock_irqsave(&rport->rport_state_lock, flags); unf_rport_state_ma(rport, UNF_EVENT_RPORT_READY); // READY rport_state = rport->rp_state; if (rport->nport_id < UNF_FC_FID_DOM_MGR) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[event]LOGIN: Port(0x%x) RPort(0x%x) state(0x%x) WWN(0x%llx) prliacc", lport->port_id, rport->nport_id, rport->rp_state, rport->port_name); } spin_unlock_irqrestore(&rport->rport_state_lock, flags); if (rport_state == UNF_RPORT_ST_READY) { rport->logo_retries = 0; unf_update_lport_state_by_linkup_event(lport, rport, rport->options); } } unsigned int unf_send_prli_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_pril_payload_s *prli_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; unsigned short rx_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3405, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3406, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3407, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_PRLI); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; v_xchg->pfn_callback = NULL; /* callback when send succeed */ v_xchg->pfn_ob_callback = unf_prli_acc_ob_callback; /* Fill common package */ unf_fill_package(&pkg, v_xchg, v_rport); /* Get FC entry (alloc when create exchange) */ fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } /* Fill FRLI Payload */ memset(fc_entry, 0, sizeof(union unf_sfs_u)); prli_acc_pld = &fc_entry->prli_acc.payload; unf_fill_prli_acc_pld(prli_acc_pld, v_lport, v_rport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; /* Send ELS (RPLI) RSP */ ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); if ((v_rport->nport_id < UNF_FC_FID_DOM_MGR) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: PRLI ACC send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); } UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_send_rec_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { /* Reserved */ UNF_REFERNCE_VAR(v_lport); UNF_REFERNCE_VAR(v_rport); UNF_REFERNCE_VAR(v_xchg); unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); return RETURN_OK; } static void unf_rrq_acc_ob_callback(struct unf_xchg_s *v_xchg) { UNF_CHECK_VALID(0x3408, UNF_TRUE, v_xchg, return); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_IO_ATT, UNF_MAJOR, "[info]RRQ ACC Xchg(0x%p) tag(0x%x)", v_xchg, v_xchg->hot_pool_tag); UNF_REFERNCE_VAR(v_xchg); } static void unf_fill_els_acc_pld(struct unf_els_acc_s *v_els_acc_pld) { UNF_CHECK_VALID(0x3420, UNF_TRUE, v_els_acc_pld, return); v_els_acc_pld->cmnd = UNF_ELS_CMND_ACC; } static void unf_rscn_acc_ob_callback(struct unf_xchg_s *v_xchg) { UNF_REFERNCE_VAR(v_xchg); } static unsigned int unf_send_rscn_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_els_acc_s *rscn_acc = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3421, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3422, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3423, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_RSCN); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; /* Set call back function */ v_xchg->pfn_callback = NULL; v_xchg->pfn_ob_callback = unf_rscn_acc_ob_callback; // do nothing unf_fill_package(&pkg, v_xchg, v_rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } memset(fc_entry, 0, sizeof(union unf_sfs_u)); rscn_acc = &fc_entry->els_acc; unf_fill_els_acc_pld(rscn_acc); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: RSCN ACC send %s. Port(0x%x)--->rport(0x%x) with OXID(0x%x) RXID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static void unf_logo_acc_ob_callback(struct unf_xchg_s *v_xchg) { UNF_REFERNCE_VAR(v_xchg); } unsigned int unf_send_logo_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_els_acc_s *logo_acc = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3424, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3425, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3426, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_LOGO); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; v_xchg->pfn_callback = NULL; v_xchg->pfn_ob_callback = unf_logo_acc_ob_callback; // do nothing unf_fill_package(&pkg, v_xchg, v_rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } memset(fc_entry, 0, sizeof(union unf_sfs_u)); logo_acc = &fc_entry->els_acc; unf_fill_els_acc_pld(logo_acc); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); if (v_rport->nport_id < UNF_FC_FID_DOM_MGR) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: LOGO ACC send %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); } UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_send_rrq_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_els_acc_s *rrq_acc = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg = { 0 }; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3427, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3428, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3429, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; v_xchg->pfn_callback = NULL; // do noting fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); return UNF_RETURN_ERROR; } memset(fc_entry, 0, sizeof(union unf_sfs_u)); rrq_acc = &fc_entry->els_acc; v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_RRQ); v_xchg->pfn_ob_callback = unf_rrq_acc_ob_callback; // do noting unf_fill_els_acc_pld(rrq_acc); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; unf_fill_package(&pkg, v_xchg, v_rport); ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]RRQ ACC send %s. Port(0x%x)--->rport(0x%x) with Xchg(0x%p) OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, v_xchg, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static void unf_fill_pdisc_acc_pld(struct unf_plogi_payload_s *v_pdisc_acc_pld, struct unf_lport_s *v_lport) { struct unf_lgn_parms_s *login_parms = NULL; UNF_CHECK_VALID(0x3430, UNF_TRUE, v_pdisc_acc_pld, return); UNF_CHECK_VALID(0x3431, UNF_TRUE, v_lport, return); v_pdisc_acc_pld->cmnd = UNF_ELS_CMND_ACC; login_parms = &v_pdisc_acc_pld->parms; if ((v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { login_parms->co_parms.bb_credit = unf_low_level_bb_credit(v_lport); login_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_NFPORT; login_parms->co_parms.bb_scn = (v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) ? 0 : unf_low_level_bbscn(v_lport); } else { login_parms->co_parms.bb_credit = UNF_BBCREDIT_LPORT; login_parms->co_parms.alternate_bb_credit_mgmt = UNF_BBCREDIT_MANAGE_LPORT; } login_parms->co_parms.lowest_version = UNF_PLOGI_VERSION_LOWER; login_parms->co_parms.highest_version = UNF_PLOGI_VERSION_UPPER; login_parms->co_parms.continuously_increasing = UNF_CONTIN_INCREASE_SUPPORT; login_parms->co_parms.bb_receive_data_field_size = v_lport->max_frame_size; login_parms->co_parms.nport_total_concurrent_sequences = UNF_PLOGI_CONCURRENT_SEQ; login_parms->co_parms.relative_offset = UNF_PLOGI_RO_CATEGORY; login_parms->co_parms.e_d_tov = v_lport->ed_tov; login_parms->cl_parms[2].valid = UNF_CLASS_VALID; // class-3 login_parms->cl_parms[2].received_data_field_size = v_lport->max_frame_size; login_parms->cl_parms[2].concurrent_sequences = UNF_PLOGI_CONCURRENT_SEQ; login_parms->cl_parms[2].open_sequences_per_exchange = UNF_PLOGI_SEQ_PER_XCHG; login_parms->high_node_name = UNF_GET_NAME_HIGH_WORD(v_lport->node_name); login_parms->low_node_name = UNF_GET_NAME_LOW_WORD(v_lport->node_name); login_parms->high_port_name = UNF_GET_NAME_HIGH_WORD(v_lport->port_name); login_parms->low_port_name = UNF_GET_NAME_LOW_WORD(v_lport->port_name); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_pdisc_acc_pld, sizeof(struct unf_plogi_payload_s)); } static void unf_pdisc_acc_ob_callback(struct unf_xchg_s *v_xchg) { UNF_REFERNCE_VAR(v_xchg); } unsigned int unf_send_pdisc_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_plogi_payload_s *pdisc_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3432, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3433, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3434, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_PDISC); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; /* Set call back function */ v_xchg->pfn_callback = NULL; v_xchg->pfn_ob_callback = unf_pdisc_acc_ob_callback; // do nothing unf_fill_package(&pkg, v_xchg, v_rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } memset(fc_entry, 0, sizeof(union unf_sfs_u)); pdisc_acc_pld = &fc_entry->pdisc_acc.payload; unf_fill_pdisc_acc_pld(pdisc_acc_pld, v_lport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Send PDISC ACC %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static void unf_fill_adisc_acc_pld(struct unf_adisc_payload_s *v_adisc_acc_pld, struct unf_lport_s *v_lport) { UNF_CHECK_VALID(0x3435, UNF_TRUE, v_adisc_acc_pld, return); UNF_CHECK_VALID(0x3436, UNF_TRUE, v_lport, return); v_adisc_acc_pld->cmnd = (UNF_ELS_CMND_ACC); v_adisc_acc_pld->hard_address = (v_lport->nport_id & UNF_ALPA_MASK); v_adisc_acc_pld->high_node_name = UNF_GET_NAME_HIGH_WORD(v_lport->node_name); v_adisc_acc_pld->low_node_name = UNF_GET_NAME_LOW_WORD(v_lport->node_name); v_adisc_acc_pld->high_port_name = UNF_GET_NAME_HIGH_WORD(v_lport->port_name); v_adisc_acc_pld->low_port_name = UNF_GET_NAME_LOW_WORD(v_lport->port_name); v_adisc_acc_pld->nport_id = v_lport->nport_id; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, v_adisc_acc_pld, sizeof(struct unf_adisc_payload_s)); } static void unf_adisc_acc_ob_callback(struct unf_xchg_s *v_xchg) { UNF_REFERNCE_VAR(v_xchg); } static unsigned int unf_send_adisc_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_adisc_payload_s *adisc_acc_pld = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg = { 0 }; unsigned short ox_id = 0; unsigned short rx_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3437, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3438, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3439, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_ADISC); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; v_xchg->pfn_callback = NULL; v_xchg->pfn_ob_callback = unf_adisc_acc_ob_callback; // do nothing unf_fill_package(&pkg, v_xchg, v_rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } memset(fc_entry, 0, sizeof(union unf_sfs_u)); adisc_acc_pld = &fc_entry->adisc_acc.adisc_payl; unf_fill_adisc_acc_pld(adisc_acc_pld, v_lport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Send ADISC ACC %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static void unf_fill_prlo_acc_pld(struct unf_prli_prlo_s *v_prlo_acc, struct unf_lport_s *v_lport) { struct unf_pril_payload_s *prlo_acc_pld = NULL; UNF_CHECK_VALID(0x3440, UNF_TRUE, v_prlo_acc, return); prlo_acc_pld = &v_prlo_acc->payload; prlo_acc_pld->cmnd = (UNF_ELS_CMND_ACC | ((unsigned int)UNF_FC4_FRAME_PAGE_SIZE << UNF_FC4_FRAME_PAGE_SIZE_SHIFT) | ((unsigned int) sizeof(struct unf_pril_payload_s))); prlo_acc_pld->parms[0] = UNF_FC4_FRAME_PARM_0_FCP | UNF_FC4_FRAME_PARM_0_GOOD_RSP_CODE; prlo_acc_pld->parms[1] = 0; prlo_acc_pld->parms[2] = 0; prlo_acc_pld->parms[3] = 0; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, prlo_acc_pld, sizeof(struct unf_pril_payload_s)); } static unsigned int unf_send_prlo_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { struct unf_prli_prlo_s *prlo_acc = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3441, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3442, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3443, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_PRLO); v_xchg->did = v_rport->nport_id; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->rport = v_rport; v_xchg->pfn_callback = NULL; // do nothing v_xchg->pfn_ob_callback = NULL; // do nothing unf_fill_package(&pkg, v_xchg, v_rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } memset(fc_entry, 0, sizeof(union unf_sfs_u)); prlo_acc = &fc_entry->prlo_acc; unf_fill_prlo_acc_pld(prlo_acc, v_lport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Send PRLO ACC %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } unsigned int unf_send_abts(struct unf_lport_s *v_lport, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; unsigned int ret = UNF_RETURN_ERROR; struct unf_frame_pkg_s pkg; UNF_CHECK_VALID(0x3444, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3445, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); rport = v_xchg->rport; UNF_CHECK_VALID(0x3446, UNF_TRUE, rport, return UNF_RETURN_ERROR); /* set pkg info */ memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); pkg.type = UNF_PKG_BLS_REQ; pkg.frame_head.csctl_sid = v_xchg->sid; pkg.frame_head.rctl_did = v_xchg->did; pkg.frame_head.oxid_rxid = (unsigned int)v_xchg->ox_id << 16 | v_xchg->rx_id; pkg.xchg_contex = v_xchg; pkg.unf_cmnd_pload_bl.buffer_ptr = (unsigned char *) v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; pkg.unf_cmnd_pload_bl.buf_dma_addr = v_xchg->fcp_sfs_union.sfs_entry.sfs_buff_phy_addr; pkg.private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = v_xchg->hot_pool_tag; UNF_SET_XCHG_ALLOC_TIME(&pkg, v_xchg); UNF_SET_ABORT_INFO_IOTYPE(&pkg, v_xchg); pkg.private[PKG_PRIVATE_XCHG_RPORT_INDEX] = v_xchg->private[PKG_PRIVATE_XCHG_RPORT_INDEX]; /* Send ABTS frame to target */ ret = unf_bls_cmnd_send(v_lport, &pkg, v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_IO_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) send ABTS %s. Abort exch(0x%p) Cmdsn:0x%lx, tag(0x%x) iotype(0x%x)", v_lport->port_id, v_lport->nport_id, (ret == UNF_RETURN_ERROR) ? "failed" : "succeed", v_xchg, (unsigned long)v_xchg->cmnd_sn, v_xchg->hot_pool_tag, v_xchg->data_direction); UNF_REFERNCE_VAR(rport); return ret; } unsigned int unf_release_rport_res(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { unsigned int ret = UNF_RETURN_ERROR; struct unf_rport_info_s rport_info; UNF_CHECK_VALID(0x3447, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3448, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); memset(&rport_info, 0, sizeof(struct unf_rport_info_s)); rport_info.rport_index = v_rport->rport_index; rport_info.nport_id = v_rport->nport_id; rport_info.port_name = v_rport->port_name; /* 2. release R_Port(parent context/Session) resource */ if (!v_lport->low_level_func.service_op.pfn_unf_release_rport_res) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) release rport resource function can't be NULL", v_lport->port_id); return ret; } ret = v_lport->low_level_func.service_op.pfn_unf_release_rport_res( v_lport->fc_port, &rport_info); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) rport_index(0x%x, %p) send release session CMND failed", v_lport->port_id, rport_info.rport_index, v_rport); return ret; } static inline unsigned char unf_determin_bbscn(unsigned char local_bbscn, unsigned char remote_bbscn) { if ((remote_bbscn == 0) || (local_bbscn == 0)) local_bbscn = 0; else local_bbscn = local_bbscn > remote_bbscn ? local_bbscn : remote_bbscn; return local_bbscn; } static void unf_cfg_lowlevel_fabric_params( struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_fabric_parms_s *v_login_parms) { struct unf_port_login_parms_s login_co_parms = { 0 }; unsigned int remote_edtov = 0; unsigned int ret = 0; unsigned char remote_edtov_resolution = 0; /* 0:ms; 1:ns */ if (!v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) return; login_co_parms.remote_rttov_tag = (unsigned char)UNF_GET_RT_TOV_FROM_PARAMS(v_login_parms); login_co_parms.remote_edtov_tag = 0; login_co_parms.remote_bbcredit = (unsigned short) UNF_GET_BB_CREDIT_FROM_PARAMS(v_login_parms); login_co_parms.compared_bbscn = (unsigned int)unf_determin_bbscn( (unsigned char) v_lport->low_level_func.lport_cfg_items.bb_scn, (unsigned char) UNF_GET_BB_SC_N_FROM_PARAMS(v_login_parms)); remote_edtov_resolution = (unsigned char) UNF_GET_E_D_TOV_RESOLUTION_FROM_PARAMS(v_login_parms); remote_edtov = UNF_GET_E_D_TOV_FROM_PARAMS(v_login_parms); login_co_parms.compared_edtov_val = remote_edtov_resolution ? (remote_edtov / 1000000) : remote_edtov; login_co_parms.compared_ratov_val = UNF_GET_RA_TOV_FROM_PARAMS(v_login_parms); login_co_parms.els_cmnd_code = ELS_FLOGI; if (v_lport->en_act_topo & UNF_TOP_P2P_MASK) { login_co_parms.en_act_topo = (v_login_parms->co_parms.n_port == UNF_F_PORT) ? UNF_ACT_TOP_P2P_FABRIC : UNF_ACT_TOP_P2P_DIRECT; } else { login_co_parms.en_act_topo = v_lport->en_act_topo; } ret = v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_set( (void *)v_lport->fc_port, UNF_PORT_CFG_UPDATE_FABRIC_PARAM, (void *)&login_co_parms); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Lowlevel unsupport fabric config"); } static unsigned int unf_check_flogi_params( struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_fabric_parms_s *v_fabric_parms) { unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3460, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3461, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3462, UNF_TRUE, v_fabric_parms, return UNF_RETURN_ERROR); UNF_REFERNCE_VAR(v_lport); UNF_REFERNCE_VAR(v_rport); if (v_fabric_parms->cl_parms[2].valid == UNF_CLASS_INVALID) { /* Discard directly */ UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) NPort_ID(0x%x) FLOGI not support class3", v_lport->port_id, v_rport->nport_id); return UNF_RETURN_ERROR; } return ret; } static void unf_save_fabric_params(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_fabric_parms_s *v_fabric_parms) { unsigned long long fabric_node_name = 0; UNF_CHECK_VALID(0x3463, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3464, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3465, UNF_TRUE, v_fabric_parms, return); UNF_REFERNCE_VAR(v_lport); fabric_node_name = (unsigned long long) (((unsigned long long) (v_fabric_parms->high_node_name) << 32) | ((unsigned long long) (v_fabric_parms->low_node_name))); /* R_Port for 0xfffffe is used for FLOGI, not need to save WWN */ if (v_fabric_parms->co_parms.bb_receive_data_field_size > UNF_MAX_FRAME_SIZE) v_rport->max_frame_size = UNF_MAX_FRAME_SIZE; // 2112 else v_rport->max_frame_size = v_fabric_parms->co_parms.bb_receive_data_field_size; /* with Fabric attribute */ if (v_fabric_parms->co_parms.n_port == UNF_F_PORT) { v_rport->ed_tov = v_fabric_parms->co_parms.e_d_tov; v_rport->ra_tov = v_fabric_parms->co_parms.r_a_tov; v_lport->ed_tov = v_fabric_parms->co_parms.e_d_tov; v_lport->ra_tov = v_fabric_parms->co_parms.r_a_tov; v_lport->rr_tov = UNF_CALC_LPORT_RRTOV(v_lport); v_lport->fabric_node_name = fabric_node_name; } /* Configure info from FLOGI to chip */ unf_cfg_lowlevel_fabric_params(v_lport, v_rport, v_fabric_parms); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) Rport(0x%x) login parameter: E_D_TOV = %u. LPort E_D_TOV = %u. fabric nodename: 0x%x%x", v_lport->port_id, v_rport->nport_id, (v_fabric_parms->co_parms.e_d_tov), v_lport->ed_tov, v_fabric_parms->high_node_name, v_fabric_parms->low_node_name); } static unsigned int unf_flogi_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; struct unf_flogi_fdisc_acc_s *flogi_frame = NULL; struct unf_fabric_parms_s *fabric_login_parms = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned long flag = 0; unsigned long long wwpn = 0; unsigned long long wwnn = 0; UNF_CHECK_VALID(0x3466, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3467, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_REFERNCE_VAR(v_sid); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x)<---RPort(0x%x) Receive FLOGI with OX_ID(0x%x)", v_lport->port_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_FLOGI); /* Check L_Port state: Offline */ if (v_lport->en_states >= UNF_LPORT_ST_OFFLINE) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) with state(0x%x) not need to handle FLOGI", v_lport->port_id, v_lport->en_states); unf_cm_free_xchg(v_lport, v_xchg); return ret; } flogi_frame = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->flogi; fabric_login_parms = &flogi_frame->flogi_payload.fabric_parms; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, &flogi_frame->flogi_payload, sizeof(struct unf_flogi_payload_s)); wwpn = (unsigned long long) (((unsigned long long) (fabric_login_parms->high_port_name) << 32) | ((unsigned long long)fabric_login_parms->low_port_name)); wwnn = (unsigned long long) (((unsigned long long) (fabric_login_parms->high_node_name) << 32) | ((unsigned long long)fabric_login_parms->low_node_name)); /* Get (new) R_Port: reuse only */ rport = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_FLOGI); rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY, UNF_FC_FID_FLOGI); if (unlikely(!rport)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) has no RPort. do nothing", v_lport->port_id); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } /* Update R_Port info */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->port_name = wwpn; rport->node_name = wwnn; spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Check RCVD FLOGI parameters: only for class-3 */ ret = unf_check_flogi_params(v_lport, rport, fabric_login_parms); if (ret != RETURN_OK) { /* Discard directly */ unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } /* P2P fabric */ unf_lport_update_topo(v_lport, UNF_ACT_TOP_P2P_DIRECT); /* Save fabric parameters */ unf_save_fabric_params(v_lport, rport, fabric_login_parms); /* Send ACC for FLOGI */ ret = unf_send_flogi_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send FLOGI ACC failed and do recover", v_lport->port_id); /* Do L_Port recovery */ unf_lport_error_recovery(v_lport); } return ret; } static void unf_cfg_lowlevel_port_params(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_lgn_parms_s *v_login_parms, unsigned int v_cmd_type) { struct unf_port_login_parms_s login_co_parms = { 0 }; unsigned int ret = 0; if (!v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) return; login_co_parms.rport_index = v_rport->rport_index; login_co_parms.seq_cnt = 0; login_co_parms.ed_tov = 0; login_co_parms.ed_tov_timer_val = v_lport->ed_tov; login_co_parms.tx_mfs = v_rport->max_frame_size; login_co_parms.remote_rttov_tag = (unsigned char)UNF_GET_RT_TOV_FROM_PARAMS(v_login_parms); login_co_parms.remote_edtov_tag = 0; login_co_parms.remote_bbcredit = (unsigned short)UNF_GET_BB_CREDIT_FROM_PARAMS(v_login_parms); login_co_parms.els_cmnd_code = v_cmd_type; if (v_lport->en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP) { login_co_parms.compared_bbscn = 0; } else { login_co_parms.compared_bbscn = (unsigned int)unf_determin_bbscn( (unsigned char) v_lport->low_level_func.lport_cfg_items.bb_scn, (unsigned char) UNF_GET_BB_SC_N_FROM_PARAMS(v_login_parms)); } login_co_parms.compared_edtov_val = v_lport->ed_tov; login_co_parms.compared_ratov_val = v_lport->ra_tov; ret = v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_set( (void *)v_lport->fc_port, UNF_PORT_CFG_UPDATE_PLOGI_PARAM, (void *)&login_co_parms); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) Lowlevel unsupport port config", v_lport->port_id); } unsigned int unf_check_plogi_params(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_lgn_parms_s *v_login_parms) { unsigned int ret = RETURN_OK; unsigned long flag = 0; UNF_CHECK_VALID(0x3468, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3469, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3470, UNF_TRUE, v_login_parms, return UNF_RETURN_ERROR); /* Parameters check: Class-type */ if ((v_login_parms->cl_parms[2].valid == UNF_CLASS_INVALID) || (v_login_parms->co_parms.bb_receive_data_field_size == 0)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) RPort N_Port_ID(0x%x) with PLOGI parameters invalid: class3(%u), BBReceiveDataFieldSize(0x%x), send LOGO", v_lport->port_id, v_rport->nport_id, v_login_parms->cl_parms[2].valid, v_login_parms->co_parms.bb_receive_data_field_size); spin_lock_irqsave(&v_rport->rport_state_lock, flag); /* --->>> LOGO */ unf_rport_state_ma(v_rport, UNF_EVENT_RPORT_LOGO); spin_unlock_irqrestore(&v_rport->rport_state_lock, flag); /* Enter LOGO stage */ unf_rport_enter_logo(v_lport, v_rport); return UNF_RETURN_ERROR; } /* 16G FC Brocade SW, Domain Controller's * PLOGI both support CLASS-1 & CLASS-2 */ if ((v_login_parms->cl_parms[0].valid == UNF_CLASS_VALID) || (v_login_parms->cl_parms[1].valid == UNF_CLASS_VALID)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) get PLOGI class1(%u) class2(%u) from N_Port_ID(0x%x)", v_lport->port_id, v_login_parms->cl_parms[0].valid, v_login_parms->cl_parms[1].valid, v_rport->nport_id); } return ret; } static void unf_save_plogi_params(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_lgn_parms_s *v_login_parms, unsigned int v_cmd_code) { #define UNF_DELAY_TIME 100 /* WWPN smaller delay to send PRLI with COM mode */ unsigned long long wwpn = INVALID_VALUE64; unsigned long long wwnn = INVALID_VALUE64; unsigned int ed_tov = 0; unsigned int remote_edtov = 0; if (v_login_parms->co_parms.bb_receive_data_field_size > UNF_MAX_FRAME_SIZE) v_rport->max_frame_size = UNF_MAX_FRAME_SIZE; // 2112 else v_rport->max_frame_size = v_login_parms->co_parms.bb_receive_data_field_size; wwnn = (unsigned long long) (((unsigned long long) (v_login_parms->high_node_name) << 32) | ((unsigned long long)v_login_parms->low_node_name)); wwpn = (unsigned long long) (((unsigned long long) (v_login_parms->high_port_name) << 32) | ((unsigned long long)v_login_parms->low_port_name)); remote_edtov = v_login_parms->co_parms.e_d_tov; ed_tov = v_login_parms->co_parms.e_d_tov_resolution ? (remote_edtov / 1000000) : remote_edtov; v_rport->port_name = wwpn; v_rport->node_name = wwnn; v_rport->local_nport_id = v_lport->nport_id; if ((v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT) || (v_lport->en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP)) { /* P2P or Private Loop */ v_lport->ed_tov = (v_lport->ed_tov > ed_tov) ? v_lport->ed_tov : ed_tov; v_lport->ra_tov = 2 * v_lport->ed_tov; // 2 * E_D_TOV v_lport->rr_tov = UNF_CALC_LPORT_RRTOV(v_lport); if (ed_tov != 0) v_rport->ed_tov = ed_tov; else v_rport->ed_tov = UNF_DEFAULT_EDTOV; } else { /* SAN: E_D_TOV updated by FLOGI */ v_rport->ed_tov = v_lport->ed_tov; } /* WWPN smaller: delay to send PRLI */ if (v_rport->port_name > v_lport->port_name) v_rport->ed_tov += UNF_DELAY_TIME; // 100ms /* Configure port parameters to low level (chip) */ unf_cfg_lowlevel_port_params(v_lport, v_rport, v_login_parms, v_cmd_code); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) RPort(0x%x) with WWPN(0x%llx) WWNN(0x%llx) login: ED_TOV(%u) Port: ED_TOV(%u)", v_lport->port_id, v_rport->nport_id, v_rport->port_name, v_rport->node_name, ed_tov, v_lport->ed_tov); } static int unf_check_bbscn_is_enabled(unsigned char local_bbscn, unsigned char remote_bbscn) { return unf_determin_bbscn(local_bbscn, remote_bbscn) ? UNF_TRUE : UNF_FALSE; } static unsigned int unf_irq_process_switch_2_thread(void *v_lport, struct unf_xchg_s *v_xchg, unf_evt_task v_evt_task) { struct unf_cm_event_report *event = NULL; struct unf_xchg_s *xchg = NULL; unsigned int ret = 0; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x1996, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x1996, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); lport = v_lport; xchg = v_xchg; if (unlikely((!lport->event_mgr.pfn_unf_get_free_event) || (!lport->event_mgr.pfn_unf_post_event) || (!lport->event_mgr.pfn_unf_release_event))) { UNF_TRACE(0x2065, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) event function is NULL", lport->port_id); return UNF_RETURN_ERROR; } ret = unf_xchg_ref_inc(xchg, SFS_RESPONSE); UNF_CHECK_VALID(0x3343, UNF_TRUE, (ret == RETURN_OK), return UNF_RETURN_ERROR); event = lport->event_mgr.pfn_unf_get_free_event((void *)v_lport); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, event, return UNF_RETURN_ERROR); event->lport = lport; event->event_asy_flag = UNF_EVENT_ASYN; event->pfn_unf_event_task = v_evt_task; event->para_in = v_xchg; lport->event_mgr.pfn_unf_post_event(lport, event); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) start to switch thread process now", lport->port_id); return ret; } static unsigned int unf_plogi_handler_com_process(struct unf_xchg_s *v_xchg) { struct unf_xchg_s *xchg = v_xchg; struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_plogi_pdisc_s *plogi_frame = NULL; struct unf_lgn_parms_s *login_parms = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned long flag = 0; UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg, return UNF_RETURN_ERROR); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->rport, return UNF_RETURN_ERROR); lport = xchg->lport; rport = xchg->rport; plogi_frame = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->plogi; login_parms = &plogi_frame->payload.parms; unf_save_plogi_params(lport, rport, login_parms, ELS_PLOGI); /* Update state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = xchg->sid; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Send PLOGI ACC to remote port */ ret = unf_send_plogi_acc(lport, rport, xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send PLOGI ACC failed", lport->port_id); /* NOTE: exchange has been freed inner(before) */ unf_rport_error_recovery(rport); return ret; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]LOGIN: Port(0x%x) send PLOGI ACC to Port(0x%x) succeed", lport->port_id, rport->nport_id); return ret; } static int unf_plogi_async_handle(void *v_argc_in, void *v_argc_out) { struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_argc_in; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x2267, UNF_TRUE, xchg, return UNF_RETURN_ERROR); ret = unf_plogi_handler_com_process(xchg); unf_xchg_ref_dec(xchg, SFS_RESPONSE); return (int)ret; } static unsigned int unf_send_els_rjt_by_did(struct unf_lport_s *v_lport, struct unf_xchg_s *v_xchg, unsigned int v_did, struct unf_rjt_info_s *v_rjt_info) { struct unf_els_rjt_s *els_rjt = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = v_xchg; struct unf_frame_pkg_s pkg = { 0 }; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3503, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3504, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); xchg->cmnd_code = UNF_SET_ELS_RJT_TYPE(v_rjt_info->els_cmnd_code); xchg->did = v_did; xchg->sid = v_lport->nport_id; xchg->oid = xchg->sid; xchg->lport = v_lport; xchg->rport = NULL; xchg->disc_rport = NULL; xchg->pfn_callback = NULL; xchg->pfn_ob_callback = NULL; unf_fill_package(&pkg, xchg, NULL); fc_entry = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, xchg); return UNF_RETURN_ERROR; } els_rjt = &fc_entry->els_rjt; memset(els_rjt, 0, sizeof(struct unf_els_rjt_s)); unf_fill_rjt_pld(els_rjt, v_rjt_info->reason_code, v_rjt_info->reason_explanation); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Send LS_RJT %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_did, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_plogi_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_xchg_s *xchg = v_xchg; struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; struct unf_plogi_pdisc_s *plogi_frame = NULL; struct unf_lgn_parms_s *login_parms = NULL; struct unf_rjt_info_s rjt_info = { 0 }; unsigned long long wwpn = INVALID_VALUE64; unsigned int ret = UNF_RETURN_ERROR; int bbscn_enabled = UNF_FALSE; int switch_2_thread = UNF_FALSE; UNF_CHECK_VALID(0x3474, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3475, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); /* 1. Maybe: PLOGI is sent by Name server */ if ((v_sid < UNF_FC_FID_DOM_MGR) || (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Receive PLOGI. Port(0x%x_0x%x)<---RPort(0x%x) with OX_ID(0x%x)", v_lport->port_id, v_lport->nport_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_PLOGI); /* 2. State check: Offline */ if (lport->en_states >= UNF_LPORT_ST_OFFLINE) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) received PLOGI with state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); unf_cm_free_xchg(lport, xchg); return UNF_RETURN_ERROR; } /* * 3. According to FC-LS 4.2.7.1: * After RCVD PLogi or send Plogi ACC, need to termitate open EXCH */ unf_cm_xchg_mgr_abort_io_by_id(lport, rport, v_sid, lport->nport_id, 0); /* Get R_Port by WWpn */ plogi_frame = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->plogi; login_parms = &plogi_frame->payload.parms; UNF_PRINT_SFS_LIMIT(UNF_INFO, lport->port_id, &plogi_frame->payload, sizeof(struct unf_plogi_payload_s)); wwpn = (unsigned long long) (((unsigned long long) (login_parms->high_port_name) << 32) | ((unsigned long long)login_parms->low_port_name)); /* 4. Get (new) R_Port (by wwpn) */ rport = unf_find_rport(lport, v_sid, wwpn); rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, v_sid); if (!rport) { memset(&rjt_info, 0, sizeof(struct unf_rjt_info_s)); rjt_info.els_cmnd_code = ELS_PLOGI; rjt_info.reason_code = UNF_LS_RJT_BUSY; rjt_info.reason_explanation = UNF_LS_RJT_INSUFFICIENT_RESOURCES; /* R_Port is NULL: Send ELS RJT for PLOGI */ (void)unf_send_els_rjt_by_did(lport, xchg, v_sid, &rjt_info); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) has no RPort and send PLOGI reject", lport->port_id); /* NOTE: exchange has been freed inner(before) */ return UNF_RETURN_ERROR; } /* 5. Cancel recovery timer work after RCVD PLOGI */ if (cancel_delayed_work(&rport->recovery_work)) atomic_dec(&rport->rport_ref_cnt); /* * 6. Plogi parameters check * Call by: (RCVD) PLOGI handler & callback function for RCVD PLOGI_ACC */ ret = unf_check_plogi_params(lport, rport, login_parms); if (ret != RETURN_OK) { unf_cm_free_xchg(lport, xchg); return UNF_RETURN_ERROR; } xchg->lport = v_lport; xchg->rport = rport; xchg->sid = v_sid; /* 7. About bbscn for context change */ bbscn_enabled = unf_check_bbscn_is_enabled( (unsigned char)lport->low_level_func.lport_cfg_items.bb_scn, (unsigned char)UNF_GET_BB_SC_N_FROM_PARAMS(login_parms)); if ((lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT) && (bbscn_enabled == UNF_TRUE)) { switch_2_thread = UNF_TRUE; lport->b_bbscn_support = UNF_TRUE; } /* 8. Process PLOGI Frame: switch to thread if necessary */ if ((switch_2_thread == UNF_TRUE) && (lport->root_lport == lport)) /* Wait for LR complete sync */ ret = unf_irq_process_switch_2_thread(lport, xchg, unf_plogi_async_handle); else ret = unf_plogi_handler_com_process(xchg); return ret; } static void unf_obtain_tape_capacity(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, unsigned int tape_parm) { unsigned int rec_support = 0; unsigned int task_retry_support = 0; unsigned int retry_support = 0; rec_support = tape_parm & UNF_FC4_FRAME_PARM_3_REC_SUPPORT; task_retry_support = tape_parm & UNF_FC4_FRAME_PARM_3_TASK_RETRY_ID_SUPPORT; retry_support = tape_parm & UNF_FC4_FRAME_PARM_3_RETRY_SUPPORT; if ((v_lport->low_level_func.lport_cfg_items.tape_support) && rec_support && task_retry_support && retry_support) { v_rport->tape_support_needed = UNF_TRUE; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) FC_tape is needed for RPort(0x%x)", v_lport->port_id, v_lport->nport_id, v_rport->nport_id); } if ((tape_parm & UNF_FC4_FRAME_PARM_3_CONF_ALLOW) && (v_lport->low_level_func.lport_cfg_items.fcp_conf != UNF_FALSE)) { v_rport->fcp_conf_needed = UNF_TRUE; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) FCP confirm is needed for RPort(0x%x)", v_lport->port_id, v_lport->nport_id, v_rport->nport_id); } } unsigned int unf_prli_handler_com_process(struct unf_xchg_s *v_xchg) { struct unf_prli_prlo_s *prli = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned long flags = 0; unsigned int uisid = 0; struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_xchg_s *xchg = NULL; xchg = v_xchg; UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg, return UNF_RETURN_ERROR); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->lport, return UNF_RETURN_ERROR); lport = xchg->lport; uisid = v_xchg->sid; UNF_SERVICE_COLLECT(lport->link_service_info, UNF_SERVICE_ITEM_PRLI); /* 1. Get R_Port: for each R_Port from rport_busy_list */ rport = unf_get_rport_by_nport_id(lport, uisid); if (!rport) { /* non session (R_Port) existence */ (void)unf_send_logo_by_did(lport, uisid); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) received PRLI but no RPort SID(0x%x) OX_ID(0x%x)", lport->port_id, lport->nport_id, uisid, v_xchg->ox_id); unf_cm_free_xchg(lport, v_xchg); return ret; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]LOGIN: Receive PRLI. Port(0x%x)<---RPort(0x%x) with S_ID(0x%x)", lport->port_id, rport->nport_id, uisid); /* 2. Get PRLI info */ prli = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->prli; if ((uisid < UNF_FC_FID_DOM_MGR) || (lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Receive PRLI. Port(0x%x_0x%x)<---RPort(0x%x) parameter-3(0x%x) OX_ID(0x%x)", lport->port_id, lport->nport_id, uisid, prli->payload.parms[3], v_xchg->ox_id); } UNF_PRINT_SFS_LIMIT(UNF_INFO, lport->port_id, &prli->payload, sizeof(struct unf_pril_payload_s)); spin_lock_irqsave(&rport->rport_state_lock, flags); /* 3. Increase R_Port ref_cnt */ ret = unf_rport_ref_inc(rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) RPort(0x%x_0x%p) is removing and do nothing", lport->port_id, rport->nport_id, rport); spin_unlock_irqrestore(&rport->rport_state_lock, flags); unf_cm_free_xchg(lport, v_xchg); return RETURN_OK; } /* 4. Cancel R_Port Open work */ if (cancel_delayed_work(&rport->open_work)) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) RPort(0x%x) cancel open work succeed", lport->port_id, lport->nport_id, rport->nport_id); /* This is not the last counter */ atomic_dec(&rport->rport_ref_cnt); } /* 5. Check R_Port state */ if ((rport->rp_state != UNF_RPORT_ST_PRLI_WAIT) && (rport->rp_state != UNF_RPORT_ST_READY)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) RPort(0x%x) with state(0x%x) when received PRLI, send LOGO", lport->port_id, lport->nport_id, rport->nport_id, rport->rp_state); unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO); // LOGO spin_unlock_irqrestore(&rport->rport_state_lock, flags); /* NOTE: Start to send LOGO */ unf_rport_enter_logo(lport, rport); unf_cm_free_xchg(lport, v_xchg); unf_rport_ref_dec(rport); return ret; } spin_unlock_irqrestore(&rport->rport_state_lock, flags); /* 6. Update R_Port options(INI/TGT/BOTH) */ rport->options = prli->payload.parms[3] & (UNF_FC4_FRAME_PARM_3_TGT | UNF_FC4_FRAME_PARM_3_INI); unf_update_port_feature(rport->port_name, rport->options); /* for Confirm */ rport->fcp_conf_needed = UNF_FALSE; unf_obtain_tape_capacity(lport, rport, prli->payload.parms[3]); if ((prli->payload.parms[3] & UNF_FC4_FRAME_PARM_3_CONF_ALLOW) && (lport->low_level_func.lport_cfg_items.fcp_conf != UNF_FALSE)) { rport->fcp_conf_needed = UNF_TRUE; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) FCP confirm is needed for RPort(0x%x)", lport->port_id, lport->nport_id, rport->nport_id); } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x_0x%x) RPort(0x%x) parameter-3(0x%x) options(0x%x)", lport->port_id, lport->nport_id, rport->nport_id, prli->payload.parms[3], rport->options); /* 7. Send PRLI ACC */ ret = unf_send_prli_acc(lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) RPort(0x%x) send PRLI ACC failed", lport->port_id, lport->nport_id, rport->nport_id); /* NOTE: exchange has been freed inner(before) */ unf_rport_error_recovery(rport); } /* 8. Decrease R_Port ref_cnt */ unf_rport_ref_dec(rport); return ret; } static int unf_prli_async_handle(void *v_argc_in, void *v_argc_out) { struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_argc_in; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x2267, UNF_TRUE, xchg, return UNF_RETURN_ERROR); ret = unf_prli_handler_com_process(xchg); unf_xchg_ref_dec(xchg, SFS_RESPONSE); return (int)ret; } static unsigned int unf_prli_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { unsigned int ret = UNF_RETURN_ERROR; int switch_2_thread = UNF_FALSE; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x3476, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3477, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); v_xchg->sid = v_sid; v_xchg->lport = v_lport; lport = v_lport; if ((v_lport->b_bbscn_support) && (v_lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) switch_2_thread = UNF_TRUE; if ((switch_2_thread == UNF_TRUE) && (lport->root_lport == lport)) /* Wait for LR done sync */ ret = unf_irq_process_switch_2_thread(v_lport, v_xchg, unf_prli_async_handle); else ret = unf_prli_handler_com_process(v_xchg); return ret; } static void unf_save_rscn_port_id( struct unf_rscn_mg_s *v_rscn_mg, struct unf_rscn_port_id_page_s *v_rscn_port_id) { struct unf_port_id_page_s *exit_port_id_page = NULL; struct unf_port_id_page_s *new_port_id_page = NULL; struct list_head *node = NULL; struct list_head *next_node = NULL; unsigned long flag = 0; enum int_e repeat = UNF_FALSE; UNF_CHECK_VALID(0x3478, UNF_TRUE, v_rscn_mg, return); UNF_CHECK_VALID(0x3479, UNF_TRUE, v_rscn_port_id, return); /* 1. check new RSCN Port_ID (RSNC_Page) * whether within RSCN_Mgr or not */ spin_lock_irqsave(&v_rscn_mg->rscn_id_list_lock, flag); if (list_empty(&v_rscn_mg->list_using_rscn_page)) { repeat = UNF_FALSE; } else { /* Check repeat: for each exist RSCN page * form RSCN_Mgr Page list */ list_for_each_safe(node, next_node, &v_rscn_mg->list_using_rscn_page) { exit_port_id_page = list_entry(node, struct unf_port_id_page_s, list_node_rscn); if ((exit_port_id_page->port_id_port == v_rscn_port_id->port_id_port) && (exit_port_id_page->port_id_area == v_rscn_port_id->port_id_area) && (exit_port_id_page->port_id_domain == v_rscn_port_id->port_id_domain)) { repeat = UNF_TRUE; break; } } } spin_unlock_irqrestore(&v_rscn_mg->rscn_id_list_lock, flag); UNF_CHECK_VALID(0x3480, UNF_TRUE, v_rscn_mg->pfn_unf_get_free_rscn_node, return); /* 2. Get & add free RSNC Node --->>> RSCN_Mgr */ if (repeat == UNF_FALSE) { new_port_id_page = v_rscn_mg->pfn_unf_get_free_rscn_node(v_rscn_mg); if (!new_port_id_page) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Get free RSCN node failed"); return; } new_port_id_page->uc_addr_format = v_rscn_port_id->addr_format; new_port_id_page->uc_event_qualifier = v_rscn_port_id->event_qualifier; new_port_id_page->uc_reserved = v_rscn_port_id->reserved; new_port_id_page->port_id_domain = v_rscn_port_id->port_id_domain; new_port_id_page->port_id_area = v_rscn_port_id->port_id_area; new_port_id_page->port_id_port = v_rscn_port_id->port_id_port; /* Add entry to list: using_rscn_page */ spin_lock_irqsave(&v_rscn_mg->rscn_id_list_lock, flag); list_add_tail(&new_port_id_page->list_node_rscn, &v_rscn_mg->list_using_rscn_page); spin_unlock_irqrestore(&v_rscn_mg->rscn_id_list_lock, flag); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) has repeat RSCN node with domain(0x%x) area(0x%x)", v_rscn_port_id->port_id_domain, v_rscn_port_id->port_id_area, v_rscn_port_id->port_id_port); } } static unsigned int unf_analysis_rscn_payload(struct unf_lport_s *v_lport, struct unf_rscn_pld_s *v_rscn_pld) { #define UNF_OS_DISC_REDISC_TIME 10000 struct unf_rscn_port_id_page_s *rscn_port_id = NULL; struct unf_disc_s *disc = NULL; struct unf_rscn_mg_s *rscn_mgr = NULL; unsigned int i = 0; unsigned int pld_len = 0; unsigned int port_id_page_cnt = 0; unsigned int ret = RETURN_OK; unsigned long flag = 0; enum int_e need_disc_flag = UNF_FALSE; UNF_CHECK_VALID(0x3481, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3482, UNF_TRUE, v_rscn_pld, return UNF_RETURN_ERROR); /* This field is the length in bytes of the entire Payload, * inclusive of the word 0 */ pld_len = UNF_GET_RSCN_PLD_LEN(v_rscn_pld->cmnd); pld_len -= sizeof(v_rscn_pld->cmnd); port_id_page_cnt = pld_len / UNF_RSCN_PAGE_LEN; /* Pages within payload is nor more than 255 */ if (port_id_page_cnt > UNF_RSCN_PAGE_SUM) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x_0x%x) page num(0x%x) exceed 255 in RSCN", v_lport->port_id, v_lport->nport_id, port_id_page_cnt); return UNF_RETURN_ERROR; } /* L_Port-->Disc-->Rscn_Mgr */ disc = &v_lport->disc; rscn_mgr = &disc->rscn_mgr; /* for each ID from RSCN_Page: check whether need to Disc or not */ while (i < port_id_page_cnt) { rscn_port_id = &v_rscn_pld->port_id_page[i]; if (unf_lookup_lport_by_nport_id(v_lport, *(unsigned int *)rscn_port_id)) { /* Prevent to create session with L_Port which have the same N_Port_ID */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) find local N_Port_ID(0x%x) within RSCN payload", ((struct unf_lport_s *) (v_lport->root_lport))->nport_id, *(unsigned int *)rscn_port_id); } else { /* New RSCN_Page ID find, save it to RSCN_Mgr */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x_0x%x) save RSCN N_Port_ID(0x%x)", v_lport->port_id, v_lport->nport_id, *(unsigned int *)rscn_port_id); /* 1. new RSCN_Page ID find, save it to RSCN_Mgr */ unf_save_rscn_port_id(rscn_mgr, rscn_port_id); need_disc_flag = UNF_TRUE; unf_report_io_dm_event(v_lport, ELS_RSCN, *(unsigned int *)rscn_port_id); } i++; } if (need_disc_flag != UNF_TRUE) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR, "[info]Port(0x%x) find all N_Port_ID and do not need to disc", ((struct unf_lport_s *)(v_lport->root_lport))->nport_id); return RETURN_OK; } /* 2. Do/Start Disc: Check & do Disc (GID_PT) process */ if (!disc->unf_disc_temp.pfn_unf_disc_start) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) DISC start function is NULL", v_lport->nport_id, v_lport->nport_id); return UNF_RETURN_ERROR; } spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); if ((disc->en_states == UNF_DISC_ST_END) || ((jiffies - disc->last_disc_jiff) > msecs_to_jiffies(UNF_OS_DISC_REDISC_TIME))) { disc->disc_option = UNF_RSCN_DISC; disc->last_disc_jiff = jiffies; spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); ret = disc->unf_disc_temp.pfn_unf_disc_start(v_lport); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_ABNORMAL, UNF_INFO, "[info]Port(0x%x_0x%x) DISC state(0x%x) with last time(%llu) and don't do DISC", v_lport->port_id, v_lport->nport_id, disc->en_states, disc->last_disc_jiff); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); } return ret; } static unsigned int unf_rscn_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { /* * A RSCN ELS shall be sent to registered Nx_Ports * when an event occurs that may have affected the state of * one or more Nx_Ports, or the ULP state within the Nx_Port. * * The Payload of a RSCN Request includes a list * containing the addresses of the affected Nx_Ports. * * Each affected Port_ID page contains the ID of the Nx_Port, * Fabric Controller, E_Port, domain, or area for * which the event was detected. */ struct unf_rscn_pld_s *rscn_pld = NULL; struct unf_rport_s *rport = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int pld_len = 0; UNF_REFERNCE_VAR(pld_len); UNF_CHECK_VALID(0x3483, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3484, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Receive RSCN Port(0x%x_0x%x)<---RPort(0x%x) OX_ID(0x%x)", v_lport->port_id, v_lport->nport_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_RSCN); /* 1. Get R_Port by S_ID */ rport = unf_get_rport_by_nport_id(v_lport, v_sid); // rport busy_list if (!rport) { rport = unf_rport_get_free_and_init(v_lport, UNF_PORT_TYPE_FC, v_sid); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) received RSCN but has no RPort(0x%x) with OX_ID(0x%x)", v_lport->port_id, v_lport->nport_id, v_sid, v_xchg->ox_id); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } rport->nport_id = v_sid; } rscn_pld = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rscn.rscn_pld; UNF_CHECK_VALID(0x3485, UNF_TRUE, NULL != rscn_pld, return UNF_RETURN_ERROR); pld_len = UNF_GET_RSCN_PLD_LEN(rscn_pld->cmnd); UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, rscn_pld, pld_len); /* 2. NOTE: Analysis RSCN payload(save & disc if necessary) */ ret = unf_analysis_rscn_payload(v_lport, rscn_pld); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) analysis RSCN failed", v_lport->port_id, v_lport->nport_id); /* 3. send rscn_acc after analysis payload */ ret = unf_send_rscn_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) send RSCN response failed", v_lport->port_id, v_lport->nport_id); return UNF_RETURN_ERROR; } UNF_REFERNCE_VAR(pld_len); return ret; } static void unf_analysis_pdisc_pld(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_plogi_pdisc_s *v_pdisc) { struct unf_lgn_parms_s *pdisc_params = NULL; unsigned long long wwpn = INVALID_VALUE64; unsigned long long wwnn = INVALID_VALUE64; UNF_CHECK_VALID(0x3486, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3487, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3488, UNF_TRUE, v_pdisc, return); UNF_REFERNCE_VAR(v_lport); pdisc_params = &v_pdisc->payload.parms; if (pdisc_params->co_parms.bb_receive_data_field_size > UNF_MAX_FRAME_SIZE) v_rport->max_frame_size = UNF_MAX_FRAME_SIZE; // 2112 else v_rport->max_frame_size = pdisc_params->co_parms.bb_receive_data_field_size; wwnn = (unsigned long long) (((unsigned long long) (pdisc_params->high_node_name) << 32) | ((unsigned long long)pdisc_params->low_node_name)); wwpn = (unsigned long long) (((unsigned long long)(pdisc_params->high_port_name) << 32) | ((unsigned long long)pdisc_params->low_port_name)); v_rport->port_name = wwpn; v_rport->node_name = wwnn; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) save PDISC parameters to Rport(0x%x) WWPN(0x%llx) WWNN(0x%llx)", v_lport->port_id, v_rport->nport_id, v_rport->port_name, v_rport->node_name); } static unsigned int unf_send_pdisc_rjt(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_xchg_s *v_xchg) { unsigned int ret = UNF_RETURN_ERROR; struct unf_rjt_info_s rjt_info; UNF_CHECK_VALID(0x3432, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3433, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3434, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); memset(&rjt_info, 0, sizeof(struct unf_rjt_info_s)); rjt_info.els_cmnd_code = ELS_PDISC; rjt_info.reason_code = UNF_LS_RJT_LOGICAL_ERROR; rjt_info.reason_explanation = UNF_LS_RJT_NO_ADDITIONAL_INFO; ret = unf_send_els_rjt_by_rport(v_lport, v_xchg, v_rport, &rjt_info); return ret; } static unsigned int unf_pdisc_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_plogi_pdisc_s *pdisc = NULL; struct unf_rport_s *rport = NULL; unsigned long flags = 0; unsigned int ret = RETURN_OK; unsigned long long wwpn = 0; UNF_CHECK_VALID(0x3489, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3490, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Receive PDISC. Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", v_lport->port_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_PDISC); pdisc = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->pdisc; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, &pdisc->payload, sizeof(struct unf_plogi_payload_s)); wwpn = (unsigned long long) (((unsigned long long) (pdisc->payload.parms.high_port_name) << 32) | ((unsigned long long)pdisc->payload.parms.low_port_name)); rport = unf_find_rport(v_lport, v_sid, wwpn); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find RPort by NPort ID(0x%x). Free exchange and send LOGO", v_lport->port_id, v_sid); unf_cm_free_xchg(v_lport, v_xchg); (void)unf_send_logo_by_did(v_lport, v_sid); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MINOR, "[info]Port(0x%x) get exist RPort(0x%x) when receive PDISC with S_Id(0x%x)", v_lport->port_id, rport->nport_id, v_sid); if (v_sid >= UNF_FC_FID_DOM_MGR) return unf_send_pdisc_rjt(v_lport, rport, v_xchg); unf_analysis_pdisc_pld(v_lport, rport, pdisc); /* State: READY */ spin_lock_irqsave(&rport->rport_state_lock, flags); if (rport->rp_state == UNF_RPORT_ST_READY) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) find RPort(0x%x) state is READY when receiving PDISC", v_lport->port_id, v_sid); spin_unlock_irqrestore(&rport->rport_state_lock, flags); ret = unf_send_pdisc_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) handle PDISC failed", v_lport->port_id); return ret; } /* Report Down/Up event to scsi */ unf_update_lport_state_by_linkup_event(v_lport, rport, rport->options); } /* State: Closing */ else if ((rport->rp_state == UNF_RPORT_ST_CLOSING) && (rport->session)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find RPort(0x%x) state is 0x%x when receiving PDISC", v_lport->port_id, v_sid, rport->rp_state); spin_unlock_irqrestore(&rport->rport_state_lock, flags); unf_cm_free_xchg(v_lport, v_xchg); (void)unf_send_logo_by_did(v_lport, v_sid); } /* State: PRLI_WAIT */ else if (rport->rp_state == UNF_RPORT_ST_PRLI_WAIT) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) find RPort(0x%x) state is 0x%x when receiving PDISC", v_lport->port_id, v_sid, rport->rp_state); spin_unlock_irqrestore(&rport->rport_state_lock, flags); ret = unf_send_pdisc_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) handle PDISC failed", v_lport->port_id); return ret; } } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find RPort(0x%x) state is 0x%x when receiving PDISC, send LOGO", v_lport->port_id, v_sid, rport->rp_state); unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO); spin_unlock_irqrestore(&rport->rport_state_lock, flags); unf_rport_enter_logo(v_lport, rport); unf_cm_free_xchg(v_lport, v_xchg); } } return ret; } static void unf_analysis_adisc_pld(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_adisc_payload_s *v_adisc_pld) { unsigned long long wwpn = INVALID_VALUE64; unsigned long long wwnn = INVALID_VALUE64; UNF_CHECK_VALID(0x3491, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3492, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3493, UNF_TRUE, v_adisc_pld, return); UNF_REFERNCE_VAR(v_lport); wwnn = (unsigned long long) (((unsigned long long)(v_adisc_pld->high_node_name) << 32) | ((unsigned long long)v_adisc_pld->low_node_name)); wwpn = (unsigned long long) (((unsigned long long)(v_adisc_pld->high_port_name) << 32) | ((unsigned long long)v_adisc_pld->low_port_name)); v_rport->port_name = wwpn; v_rport->node_name = wwnn; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) save ADISC parameters to RPort(0x%x), WWPN(0x%llx) WWNN(0x%llx) NPort ID(0x%x)", v_lport->port_id, v_rport->nport_id, v_rport->port_name, v_rport->node_name, v_adisc_pld->nport_id); } static unsigned int unf_adisc_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; struct unf_adisc_payload_s *adisc_pld = NULL; unsigned long flags = 0; unsigned long long wwpn = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3494, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3495, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Receive ADISC. Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", v_lport->port_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_ADISC); adisc_pld = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->adisc.adisc_payl; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, adisc_pld, sizeof(struct unf_adisc_payload_s)); wwpn = (unsigned long long) (((unsigned long long)(adisc_pld->high_port_name) << 32) | ((unsigned long long)adisc_pld->low_port_name)); rport = unf_find_rport(v_lport, v_sid, wwpn); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find RPort by NPort ID(0x%x). Free exchange and send LOGO", v_lport->port_id, v_sid); unf_cm_free_xchg(v_lport, v_xchg); (void)unf_send_logo_by_did(v_lport, v_sid); return ret; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MINOR, "[info]Port(0x%x) get exist RPort(0x%x) when receive ADISC with S_ID(0x%x)", v_lport->port_id, rport->nport_id, v_sid); unf_analysis_adisc_pld(v_lport, rport, adisc_pld); /* State: READY */ spin_lock_irqsave(&rport->rport_state_lock, flags); if (rport->rp_state == UNF_RPORT_ST_READY) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) find RPort(0x%x) state is READY when receiving ADISC", v_lport->port_id, v_sid); spin_unlock_irqrestore(&rport->rport_state_lock, flags); /* Return ACC directly */ ret = unf_send_adisc_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ADISC ACC failed", v_lport->port_id); return ret; } /* Report Down/Up event to SCSI */ unf_update_lport_state_by_linkup_event(v_lport, rport, rport->options); } /* State: Closing */ else if ((rport->rp_state == UNF_RPORT_ST_CLOSING) && (rport->session)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find RPort(0x%x) state is 0x%x when receiving ADISC", v_lport->port_id, v_sid, rport->rp_state); spin_unlock_irqrestore(&rport->rport_state_lock, flags); rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_RECOVER, rport->nport_id); if (rport) { spin_lock_irqsave(&rport->rport_state_lock, flags); rport->nport_id = v_sid; spin_unlock_irqrestore(&rport->rport_state_lock, flags); ret = unf_send_adisc_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ADISC ACC failed", v_lport->port_id); return ret; } unf_update_lport_state_by_linkup_event(v_lport, rport, rport->options); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find RPort by NPort_ID(0x%x). Free exchange and send LOGO", v_lport->port_id, v_sid); unf_cm_free_xchg(v_lport, v_xchg); (void)unf_send_logo_by_did(v_lport, v_sid); } } /* State: PRLI_WAIT */ else if (rport->rp_state == UNF_RPORT_ST_PRLI_WAIT) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) find RPort(0x%x) state is 0x%x when receiving ADISC", v_lport->port_id, v_sid, rport->rp_state); spin_unlock_irqrestore(&rport->rport_state_lock, flags); ret = unf_send_adisc_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ADISC ACC failed", v_lport->port_id); return ret; } } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find RPort(0x%x) state is 0x%x when receiving ADISC, send LOGO", v_lport->port_id, v_sid, rport->rp_state); unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO); spin_unlock_irqrestore(&rport->rport_state_lock, flags); unf_rport_enter_logo(v_lport, rport); unf_cm_free_xchg(v_lport, v_xchg); } return ret; } static unsigned int unf_rec_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3496, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3497, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_REFERNCE_VAR(v_sid); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x) receive REC", v_lport->port_id); /* Send rec acc */ ret = unf_send_rec_acc(v_lport, rport, v_xchg); // discard directly return ret; } static unsigned int unf_rrq_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; struct unf_rrq_s *rrq = NULL; struct unf_xchg_s *xchg_reused = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; unsigned int sid = 0; unsigned long flags = 0; struct unf_rjt_info_s rjt_info = { 0 }; struct unf_xchg_hot_pool_s *hot_pool = NULL; UNF_CHECK_VALID(0x3498, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3499, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_REFERNCE_VAR(rx_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_RRQ); rrq = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rrq; ox_id = (unsigned short)(rrq->oxid_rxid >> 16); rx_id = (unsigned short)(rrq->oxid_rxid); sid = rrq->sid & UNF_NPORTID_MASK; UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_KEVENT, "[warn]Receive RRQ. Port(0x%x)<---RPort(0x%x) sfsXchg(0x%p) OX_ID(0x%x,0x%x) RX_ID(0x%x)", v_lport->port_id, v_sid, v_xchg, ox_id, v_xchg->ox_id, rx_id); /* Get R_Port */ rport = unf_get_rport_by_nport_id(v_lport, v_sid); if (!rport) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) receive RRQ but has no RPort(0x%x)", v_lport->port_id, v_sid); /* NOTE: send LOGO */ ret = unf_send_logo_by_did(v_lport, sid); unf_cm_free_xchg(v_lport, v_xchg); return ret; } /* Get Target (Abort I/O) exchange context */ /* UNF_FindXchgByOxId */ xchg_reused = unf_cm_lookup_xchg_by_id(v_lport, ox_id, sid); if (!xchg_reused) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) cannot find exchange with OX_ID(0x%x) RX_ID(0x%x) S_ID(0x%x)", v_lport->port_id, ox_id, rx_id, sid); rjt_info.els_cmnd_code = ELS_RRQ; rjt_info.reason_code = FCXLS_BA_RJT_LOGICAL_ERROR | FCXLS_LS_RJT_INVALID_OXID_RXID; /* NOTE: send ELS RJT */ if (unf_send_els_rjt_by_rport(v_lport, v_xchg, rport, &rjt_info) != RETURN_OK) { unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } return RETURN_OK; } hot_pool = xchg_reused->hot_pool; if (unlikely(!hot_pool)) { UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR, "Port(0x%x) OxId(0x%x) Rxid(0x%x) Sid(0x%x) Hot Pool is NULL.", v_lport->port_id, ox_id, rx_id, sid); return ret; } spin_lock_irqsave(&hot_pool->xchg_hot_pool_lock, flags); xchg_reused->ox_id = INVALID_VALUE16; xchg_reused->rx_id = INVALID_VALUE16; spin_unlock_irqrestore(&hot_pool->xchg_hot_pool_lock, flags); /* NOTE: release I/O exchange context */ unf_xchg_ref_dec(xchg_reused, SFS_RESPONSE); /* Send RRQ ACC */ ret = unf_send_rrq_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) can not send RRQ rsp. Xchg(0x%p) Ioxchg(0x%p) OX_RX_ID(0x%x 0x%x) S_ID(0x%x)", v_lport->port_id, v_xchg, xchg_reused, ox_id, rx_id, sid); unf_cm_free_xchg(v_lport, v_xchg); return ret; } UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_send_els_rjt_by_rport(struct unf_lport_s *v_lport, struct unf_xchg_s *v_xchg, struct unf_rport_s *v_rport, struct unf_rjt_info_s *v_rjt_info) { struct unf_els_rjt_s *els_rjt = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = v_xchg; struct unf_frame_pkg_s pkg = { 0 }; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3500, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3501, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3502, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); xchg->cmnd_code = UNF_SET_ELS_RJT_TYPE(v_rjt_info->els_cmnd_code); xchg->did = v_rport->nport_id; xchg->sid = v_lport->nport_id; xchg->oid = xchg->sid; xchg->lport = v_lport; xchg->rport = v_rport; xchg->disc_rport = NULL; xchg->pfn_callback = NULL; xchg->pfn_ob_callback = NULL; unf_fill_package(&pkg, xchg, v_rport); fc_entry = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, xchg); return UNF_RETURN_ERROR; } els_rjt = &fc_entry->els_rjt; memset(els_rjt, 0, sizeof(struct unf_els_rjt_s)); unf_fill_rjt_pld(els_rjt, v_rjt_info->reason_code, v_rjt_info->reason_explanation); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Send LS_RJT for 0x%x %s. Port(0x%x)--->rport(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", v_rjt_info->els_cmnd_code, (ret != RETURN_OK) ? "failed" : "succeed", v_lport->port_id, v_rport->nport_id, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_els_cmnd_default_handler(struct unf_lport_s *v_lport, struct unf_xchg_s *v_xchg, unsigned int v_sid, unsigned int v_els_cmnd_code) { #define ELS_LCB 0X81 #define ELS_RDP 0X18 struct unf_rport_s *rport = NULL; struct unf_rjt_info_s rjt_info = { 0 }; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3505, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3506, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); if ((v_els_cmnd_code != ELS_LCB) && (v_els_cmnd_code != ELS_RDP)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_ABNORMAL, UNF_KEVENT, "[info]Receive Unknown ELS command(0x%x). Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", v_els_cmnd_code, v_lport->port_id, v_sid, v_xchg->ox_id); } memset(&rjt_info, 0, sizeof(struct unf_rjt_info_s)); rjt_info.els_cmnd_code = v_els_cmnd_code; rjt_info.reason_code = UNF_LS_RJT_NOT_SUPPORTED; rport = unf_get_rport_by_nport_id(v_lport, v_sid); if (rport) ret = unf_send_els_rjt_by_rport(v_lport, v_xchg, rport, &rjt_info); else ret = unf_send_els_rjt_by_did(v_lport, v_xchg, v_sid, &rjt_info); return ret; } static struct unf_xchg_s *unf_alloc_xchg_for_rcv_cmnd( struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg) { struct unf_xchg_s *xchg = NULL; unsigned long flags = 0; unsigned int i = 0; unsigned int offset = 0; unsigned char *cmnd_pld = NULL; unsigned int first_dword = 0; unsigned int alloc_time = 0; UNF_CHECK_VALID(0x3508, UNF_TRUE, v_lport, return NULL); UNF_CHECK_VALID(0x3509, UNF_TRUE, v_pkg, return NULL); if (!v_pkg->xchg_contex) { xchg = unf_cm_get_free_xchg(v_lport, UNF_XCHG_TYPE_SFS); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[warn]Port(0x%x) get new exchange failed", v_lport->port_id); return NULL; } offset = (xchg->fcp_sfs_union.sfs_entry.cur_offset); cmnd_pld = (unsigned char *)xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rscn.rscn_pld; first_dword = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->sfs_common.frame_head.rctl_did; if ((cmnd_pld) || (first_dword != 0) || (offset != 0)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) exchange(0x%p) abnormal, maybe data overrun, start(%llu) command(0x%x)", v_lport->port_id, xchg, xchg->alloc_jif, v_pkg->cmnd); UNF_PRINT_SFS(UNF_INFO, v_lport->port_id, xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr, sizeof(union unf_sfs_u)); } memset(xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr, 0, sizeof(union unf_sfs_u)); v_pkg->xchg_contex = (void *)xchg; spin_lock_irqsave(&xchg->xchg_state_lock, flags); xchg->fcp_sfs_union.sfs_entry.cur_offset = 0; alloc_time = xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]; for (i = 0; i < PKG_MAX_PRIVATE_DATA_SIZE; i++) xchg->private[i] = v_pkg->private[i]; xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME] = alloc_time; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } else { xchg = (struct unf_xchg_s *)v_pkg->xchg_contex; } if (!xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) { unf_cm_free_xchg((void *)v_lport, (void *)xchg); return NULL; } return xchg; } static unsigned char *unf_calc_big_cmnd_pld_buffer(struct unf_xchg_s *v_xchg, unsigned int v_cmnd_code) { unsigned char *cmnd_pld = NULL; void *buf = NULL; unsigned char *dest = NULL; UNF_CHECK_VALID(0x3510, UNF_TRUE, v_xchg, return NULL); if (v_cmnd_code == ELS_RSCN) cmnd_pld = (unsigned char *)v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rscn.rscn_pld; else cmnd_pld = (unsigned char *)v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo.echo_pld; if (!cmnd_pld) { buf = unf_get_one_big_sfs_buf(v_xchg); if (!buf) return NULL; if (v_cmnd_code == ELS_RSCN) { memset(buf, 0, sizeof(struct unf_rscn_pld_s)); v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->rscn.rscn_pld = buf; } else { memset(buf, 0, sizeof(struct unf_echo_payload_s)); v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo.echo_pld = buf; } dest = (unsigned char *)buf; } else { dest = (unsigned char *) (cmnd_pld + v_xchg->fcp_sfs_union.sfs_entry.cur_offset); } return dest; } static unsigned char *unf_calc_other_pld_buffer(struct unf_xchg_s *v_xchg) { unsigned char *dest = NULL; unsigned int offset = 0; UNF_CHECK_VALID(0x3511, UNF_TRUE, v_xchg, return NULL); offset = (sizeof(struct unf_fchead_s)) + (v_xchg->fcp_sfs_union.sfs_entry.cur_offset); dest = (unsigned char *) ((unsigned char *) (v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) + offset); return dest; } static struct unf_xchg_s *unf_mv_data_2_xchg(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg) { struct unf_xchg_s *xchg = NULL; unsigned char *dest = NULL; unsigned int length = 0; unsigned long flags = 0; UNF_CHECK_VALID(0x3512, UNF_TRUE, v_lport, return NULL); UNF_CHECK_VALID(0x3513, UNF_TRUE, v_pkg, return NULL); xchg = unf_alloc_xchg_for_rcv_cmnd(v_lport, v_pkg); if (!xchg) return NULL; spin_lock_irqsave(&xchg->xchg_state_lock, flags); memcpy(&xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->sfs_common.frame_head, &v_pkg->frame_head, sizeof(v_pkg->frame_head)); if ((v_pkg->cmnd == ELS_RSCN) || (v_pkg->cmnd == ELS_ECHO)) dest = unf_calc_big_cmnd_pld_buffer(xchg, v_pkg->cmnd); else dest = unf_calc_other_pld_buffer(xchg); if (!dest) { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); unf_cm_free_xchg((void *)v_lport, (void *)xchg); return NULL; } if (((xchg->fcp_sfs_union.sfs_entry.cur_offset + v_pkg->unf_cmnd_pload_bl.length) > (unsigned int)sizeof(union unf_sfs_u)) && (v_pkg->cmnd != ELS_RSCN) && (v_pkg->cmnd != ELS_ECHO)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) excange(0x%p) command(0x%x,0x%x) copy payload overrun(0x%x:0x%x:0x%x)", v_lport->port_id, xchg, v_pkg->cmnd, xchg->hot_pool_tag, xchg->fcp_sfs_union.sfs_entry.cur_offset, v_pkg->unf_cmnd_pload_bl.length, (unsigned int)sizeof(union unf_sfs_u)); spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); unf_cm_free_xchg((void *)v_lport, (void *)xchg); return NULL; } length = v_pkg->unf_cmnd_pload_bl.length; if (length > 0) memcpy(dest, v_pkg->unf_cmnd_pload_bl.buffer_ptr, length); xchg->fcp_sfs_union.sfs_entry.cur_offset += length; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); return xchg; } static unsigned int unf_logo_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; struct unf_rport_s *logo_rport = NULL; struct unf_logo_s *logo = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int nport_id = 0; struct unf_rjt_info_s rjt_info = { 0 }; UNF_REFERNCE_VAR(logo); UNF_CHECK_VALID(0x3514, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3515, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_LOGO); logo = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->logo; nport_id = logo->payload.nport_id & UNF_NPORTID_MASK; if (v_sid < UNF_FC_FID_DOM_MGR) { /* R_Port is not fabric port */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]LOGIN: Receive LOGO. Port(0x%x)<---RPort(0x%x) NPort_ID(0x%x) OXID(0x%x)", v_lport->port_id, v_sid, nport_id, v_xchg->ox_id); } UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, &logo->payload, sizeof(struct unf_logo_payload_s)); /* * 1. S_ID unequal to NPort_ID: * link down Rport find by NPort_ID immediately */ if (nport_id != v_sid) { logo_rport = unf_get_rport_by_nport_id(v_lport, nport_id); if (logo_rport) unf_rport_immediate_linkdown(v_lport, logo_rport); } /* 2. Get R_Port by S_ID (frame header) */ rport = unf_get_rport_by_nport_id(v_lport, v_sid); rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_INIT, v_sid); // INIT if (!rport) { memset(&rjt_info, 0, sizeof(struct unf_rjt_info_s)); rjt_info.els_cmnd_code = ELS_LOGO; rjt_info.reason_code = UNF_LS_RJT_LOGICAL_ERROR; rjt_info.reason_explanation = UNF_LS_RJT_NO_ADDITIONAL_INFO; ret = unf_send_els_rjt_by_did(v_lport, v_xchg, v_sid, &rjt_info); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) receive LOGO but has no RPort(0x%x)", v_lport->port_id, v_sid); return ret; } /* * 3. I/O resource release: set ABORT tag * * Call by: R_Port remove; RCVD LOGO; RCVD PLOGI; send PLOGI ACC */ unf_cm_xchg_mgr_abort_io_by_id(v_lport, rport, v_sid, v_lport->nport_id, INI_IO_STATE_LOGO); /* 4. Send LOGO ACC */ ret = unf_send_logo_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send LOGO failed", v_lport->port_id); /* * 5. Do same operations with RCVD LOGO/PRLO & Send LOGO: * retry (LOGIN or LOGO) or link down immediately */ unf_process_rport_after_logo(v_lport, rport); return ret; } static unsigned int unf_prlo_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; struct unf_prli_prlo_s *prlo = NULL; unsigned int ret = UNF_RETURN_ERROR; UNF_REFERNCE_VAR(prlo); UNF_CHECK_VALID(0x3516, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3517, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Receive PRLO. Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", v_lport->port_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_LOGO); /* Get (new) R_Port */ rport = unf_get_rport_by_nport_id(v_lport, v_sid); rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_INIT, v_sid); /* INIT */ if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) receive PRLO but has no RPort", v_lport->port_id); /* Discard directly */ unf_cm_free_xchg(v_lport, v_xchg); return ret; } prlo = &v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->prlo; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, &prlo->payload, sizeof(struct unf_pril_payload_s)); /* Send PRLO ACC to remote */ ret = unf_send_prlo_acc(v_lport, rport, v_xchg); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send PRLO ACC failed", v_lport->port_id); /* Enter Enhanced action after LOGO (retry LOGIN or LOGO) */ unf_process_rport_after_logo(v_lport, rport); UNF_REFERNCE_VAR(prlo); return ret; } static void unf_fill_echo_acc_pld(struct unf_echo_s *v_echo_acc) { struct unf_echo_payload_s *echo_acc_pld = NULL; UNF_CHECK_VALID(0x3518, UNF_TRUE, v_echo_acc, return); echo_acc_pld = v_echo_acc->echo_pld; UNF_CHECK_VALID(0x3519, UNF_TRUE, echo_acc_pld, return); echo_acc_pld->cmnd = UNF_ELS_CMND_ACC; } static void unf_echo_acc_callback(struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport; UNF_CHECK_VALID(0x3517, UNF_TRUE, v_xchg, return); lport = v_xchg->lport; UNF_CHECK_VALID(0x3517, UNF_TRUE, lport, return); if (v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo_acc.phy_echo_addr) { pci_unmap_single( lport->low_level_func.dev, v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo_acc.phy_echo_addr, UNF_ECHO_PAYLOAD_LEN, DMA_BIDIRECTIONAL); v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo_acc.phy_echo_addr = 0; } } static unsigned int unf_send_echo_acc(struct unf_lport_s *v_lport, unsigned int v_did, struct unf_xchg_s *v_xchg) { struct unf_echo_s *echo_acc = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg; dma_addr_t phy_echo_acc_addr; struct unf_rjt_info_s rjt_info = { 0 }; UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); UNF_CHECK_VALID(0x3520, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3521, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_ECHO); v_xchg->did = v_did; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->pfn_callback = NULL; v_xchg->pfn_ob_callback = unf_echo_acc_callback; unf_fill_package(&pkg, v_xchg, v_xchg->rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } echo_acc = &fc_entry->echo_acc; unf_fill_echo_acc_pld(echo_acc); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; phy_echo_acc_addr = pci_map_single(v_lport->low_level_func.dev, echo_acc->echo_pld, UNF_ECHO_PAYLOAD_LEN, DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(v_lport->low_level_func.dev, phy_echo_acc_addr)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) pci map err", v_lport->port_id); unf_cm_free_xchg(v_lport, v_xchg); return UNF_RETURN_ERROR; } echo_acc->phy_echo_addr = phy_echo_acc_addr; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); if (ret != RETURN_OK) { pci_unmap_single(v_lport->low_level_func.dev, phy_echo_acc_addr, UNF_ECHO_PAYLOAD_LEN, DMA_BIDIRECTIONAL); echo_acc->phy_echo_addr = 0; if (ret == UNF_RETURN_NOT_SUPPORT) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]Port(0x%x) send ECHO reject to RPort(0x%x) with OX_ID(0x%x) RX_ID(0x%x)", v_lport->port_id, v_did, ox_id, rx_id); rjt_info.els_cmnd_code = ELS_ECHO; rjt_info.reason_code = UNF_LS_RJT_NOT_SUPPORTED; unf_send_els_rjt_by_rport(v_lport, v_xchg, v_xchg->rport, &rjt_info); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ECHO ACC to RPort(0x%x) with OX_ID(0x%x) RX_ID(0x%x) failed", v_lport->port_id, v_did, ox_id, rx_id); unf_cm_free_xchg((void *)v_lport, (void *)v_xchg); } } UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_echo_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_echo_payload_s *echo_pld = NULL; struct unf_rport_s *rport = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int data_len = 0; UNF_CHECK_VALID(0x3522, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3523, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); data_len = v_xchg->fcp_sfs_union.sfs_entry.cur_offset; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Receive ECHO. Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x))", v_lport->port_id, v_sid, v_xchg->ox_id); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_ECHO); echo_pld = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo.echo_pld; UNF_PRINT_SFS_LIMIT(UNF_INFO, v_lport->port_id, echo_pld, data_len); rport = unf_get_rport_by_nport_id(v_lport, v_sid); v_xchg->rport = rport; ret = unf_send_echo_acc(v_lport, v_sid, v_xchg); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send ECHO ACC failed", v_lport->port_id); UNF_REFERNCE_VAR(echo_pld); UNF_REFERNCE_VAR(data_len); return ret; } static unsigned int unf_check_els_cmnd_valid(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg, struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport = v_lport; struct unf_frame_pkg_s *ppkg = v_fra_pkg; struct unf_xchg_s *xchg = v_xchg; struct unf_rjt_info_s rjt_info = { 0 }; struct unf_lport_s *vport = NULL; unsigned int sid = 0; unsigned int did = 0; sid = (ppkg->frame_head.csctl_sid) & UNF_NPORTID_MASK; did = (ppkg->frame_head.rctl_did) & UNF_NPORTID_MASK; memset(&rjt_info, 0, sizeof(struct unf_rjt_info_s)); rjt_info.reason_code = UNF_LS_RJT_NOT_SUPPORTED; if ((ppkg->cmnd == ELS_FLOGI) && (lport->en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) receive FLOGI in top (0x%x) and send LS_RJT", lport->port_id, lport->en_act_topo); rjt_info.els_cmnd_code = ELS_FLOGI; (void)unf_send_els_rjt_by_did(lport, xchg, sid, &rjt_info); return UNF_RETURN_ERROR; } if ((ppkg->cmnd == ELS_PLOGI) && (did >= UNF_FC_FID_DOM_MGR)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x)receive PLOGI with wellknown address(0x%x) and Send LS_RJT", lport->port_id, did); rjt_info.els_cmnd_code = ELS_PLOGI; (void)unf_send_els_rjt_by_did(lport, xchg, sid, &rjt_info); return UNF_RETURN_ERROR; } if (((lport->nport_id == 0) || (lport->nport_id == INVALID_VALUE32)) && (NEED_REFRESH_NPORTID(ppkg))) { lport->nport_id = did; } else if ((did != lport->nport_id) && (ppkg->cmnd != ELS_FLOGI)) { vport = unf_cm_lookup_vport_by_did(lport, did); if (!vport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) receive ELS cmd(0x%x) with abnormal D_ID(0x%x)", lport->nport_id, ppkg->cmnd, did); unf_cm_free_xchg(lport, xchg); return UNF_RETURN_ERROR; } } return RETURN_OK; } static unsigned int unf_rcv_els_cmnd_req(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_xchg_s *xchg = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int i = 0; unsigned int sid = 0; unsigned int did = 0; struct unf_lport_s *vport = NULL; unsigned int (*pfn_els_cmnd_handler)(struct unf_lport_s *, unsigned int, struct unf_xchg_s *) = NULL; sid = (v_fra_pkg->frame_head.csctl_sid) & UNF_NPORTID_MASK; did = (v_fra_pkg->frame_head.rctl_did) & UNF_NPORTID_MASK; xchg = unf_mv_data_2_xchg(v_lport, v_fra_pkg); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) receive ElsCmnd(0x%x), exchange is NULL", v_lport->port_id, v_fra_pkg->cmnd); return UNF_RETURN_ERROR; } if (v_fra_pkg->last_pkg_flag != UNF_TRUE) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Exchange(%u) waiting for last WQE", xchg->hot_pool_tag); return RETURN_OK; } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Exchange(%u) get last WQE", xchg->hot_pool_tag); if (v_lport->low_level_func.xchg_mgr_type == UNF_LOW_LEVEL_MGR_TYPE_PASSTIVE) { xchg->ox_id = UNF_GET_OXID(v_fra_pkg); xchg->abort_oxid = xchg->ox_id; xchg->rx_id = xchg->hot_pool_tag; } xchg->cmnd_code = v_fra_pkg->cmnd; ret = unf_check_els_cmnd_valid(v_lport, v_fra_pkg, xchg); if (ret != RETURN_OK) { /* NOTE: exchange has been released */ return UNF_RETURN_ERROR; } if ((did != v_lport->nport_id) && (v_fra_pkg->cmnd != ELS_FLOGI)) { vport = unf_cm_lookup_vport_by_did(v_lport, did); if (!vport) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) received unknown ELS command with S_ID(0x%x) D_ID(0x%x))", v_lport->port_id, sid, did); return UNF_RETURN_ERROR; } v_lport = vport; UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_INFO, "[info]VPort(0x%x) received ELS command with S_ID(0x%x) D_ID(0x%x)", v_lport->port_id, sid, did); } do { if ((v_fra_pkg->cmnd) == els_handle[i].cmnd) { pfn_els_cmnd_handler = els_handle[i].pfn_els_cmnd_handler; break; } i++; } while (i < (sizeof(els_handle) / sizeof(struct unf_els_handler_table))); if (pfn_els_cmnd_handler) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) receive ELS(0x%x) from RPort(0x%x) and process it", v_lport->port_id, v_fra_pkg->cmnd, sid); ret = pfn_els_cmnd_handler(v_lport, sid, xchg); } else { ret = unf_els_cmnd_default_handler(v_lport, xchg, sid, v_fra_pkg->cmnd); } return ret; } static unsigned int unf_send_els_rsp_succ(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_xchg_s *xchg = NULL; unsigned int ret = RETURN_OK; unsigned short hot_pool_tag = 0; unsigned long flags = 0; void (*pfn_ob_callback)(struct unf_xchg_s *) = NULL; UNF_CHECK_VALID(0x3529, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3530, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); if (!v_lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) lookup exchange by tag function is NULL", v_lport->port_id); return UNF_RETURN_ERROR; } hot_pool_tag = (unsigned short) (v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]); xchg = (struct unf_xchg_s *) (v_lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag( (void *)v_lport, hot_pool_tag)); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) find exhange by tag(0x%x) failed", v_lport->port_id, hot_pool_tag); return UNF_RETURN_ERROR; } v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)xchg); spin_lock_irqsave(&xchg->xchg_state_lock, flags); if ((xchg->pfn_ob_callback) && (!(xchg->io_state & TGT_IO_STATE_ABORT))) { pfn_ob_callback = xchg->pfn_ob_callback; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) with exchange(0x%p) tag(%u) do callback", v_lport->port_id, xchg, hot_pool_tag); pfn_ob_callback(xchg); } else { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } unf_cm_free_xchg((void *)v_lport, (void *)xchg); return ret; } static unsigned char *unf_calc_big_resp_pld_buffer(struct unf_xchg_s *v_xchg, unsigned int v_cmnd_code) { unsigned char *resp_pld = NULL; unsigned char *dest = NULL; UNF_CHECK_VALID(0x3510, UNF_TRUE, v_xchg, return NULL); if (v_cmnd_code == ELS_ECHO) resp_pld = (unsigned char *) v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->echo.echo_pld; else resp_pld = (unsigned char *) v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->get_id.gid_rsp.gid_acc_pld; if (resp_pld) dest = (unsigned char *) (resp_pld + v_xchg->fcp_sfs_union.sfs_entry.cur_offset); return dest; } static unsigned char *unf_calc_other_resp_pld_buffer(struct unf_xchg_s *v_xchg) { unsigned char *dest = NULL; unsigned int offset = 0; UNF_CHECK_VALID(0x3511, UNF_TRUE, v_xchg, return NULL); offset = v_xchg->fcp_sfs_union.sfs_entry.cur_offset; dest = (unsigned char *)((unsigned char *) (v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr) + offset); return dest; } static unsigned int unf_mv_resp_2_xchg(struct unf_xchg_s *v_xchg, struct unf_frame_pkg_s *v_pkg) { unsigned char *dest = NULL; unsigned int length = 0; unsigned int offset = 0; unsigned int max_frame_len = 0; unsigned long flags = 0; spin_lock_irqsave(&v_xchg->xchg_state_lock, flags); if (UNF_NEED_BIG_RESPONSE_BUFF(v_xchg->cmnd_code)) { dest = unf_calc_big_resp_pld_buffer(v_xchg, v_xchg->cmnd_code); offset = 0; max_frame_len = sizeof(struct unf_gif_acc_pld_s); } else if (v_xchg->cmnd_code == NS_GA_NXT || v_xchg->cmnd_code == NS_GIEL) { dest = unf_calc_big_resp_pld_buffer(v_xchg, v_xchg->cmnd_code); offset = 0; max_frame_len = v_xchg->fcp_sfs_union.sfs_entry.sfs_buff_len; } else { dest = unf_calc_other_resp_pld_buffer(v_xchg); offset = sizeof(struct unf_fchead_s); max_frame_len = sizeof(union unf_sfs_u); } if (!dest) { spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags); return UNF_RETURN_ERROR; } if (v_xchg->fcp_sfs_union.sfs_entry.cur_offset == 0) { v_xchg->fcp_sfs_union.sfs_entry.cur_offset += offset; dest = dest + offset; } length = v_pkg->unf_cmnd_pload_bl.length; if ((v_xchg->fcp_sfs_union.sfs_entry.cur_offset + length) > max_frame_len) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Exchange(0x%p) command(0x%x) hotpooltag(0x%x) OX_RX_ID(0x%x) S_ID(0x%x) D_ID(0x%x) copy payload overrun(0x%x:0x%x:0x%x)", v_xchg, v_xchg->cmnd_code, v_xchg->hot_pool_tag, v_pkg->frame_head.oxid_rxid, v_pkg->frame_head.csctl_sid & UNF_NPORTID_MASK, v_pkg->frame_head.rctl_did & UNF_NPORTID_MASK, v_xchg->fcp_sfs_union.sfs_entry.cur_offset, v_pkg->unf_cmnd_pload_bl.length, max_frame_len); length = max_frame_len - v_xchg->fcp_sfs_union.sfs_entry.cur_offset; } if (length > 0) memcpy(dest, v_pkg->unf_cmnd_pload_bl.buffer_ptr, length); v_xchg->fcp_sfs_union.sfs_entry.cur_offset += length; spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags); return RETURN_OK; } static unsigned int unf_send_els_cmnd_succ(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_xchg_s *xchg = NULL; unsigned int ret = RETURN_OK; unsigned short hot_pool_tag = 0; unsigned long flags = 0; void (*pfn_callback)(void *, void *, void *) = NULL; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x3531, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3532, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); lport = v_lport; if (!lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) lookup exchange by tag function can't be NULL", lport->port_id); return UNF_RETURN_ERROR; } hot_pool_tag = (unsigned short) (v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]); xchg = (struct unf_xchg_s *) (lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag((void *)lport, hot_pool_tag)); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) find exchange by tag(0x%x) failed", lport->port_id, lport->nport_id, hot_pool_tag); return UNF_RETURN_ERROR; } UNF_CHECK_ALLOCTIME_VALID( lport, hot_pool_tag, xchg, v_fra_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME], xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]); if (((v_fra_pkg->frame_head.csctl_sid) & UNF_NPORTID_MASK) != xchg->did) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find exhange invalid, package S_ID(0x%x) exchange S_ID(0x%x) D_ID(0x%x)", lport->port_id, v_fra_pkg->frame_head.csctl_sid, xchg->sid, xchg->did); return UNF_RETURN_ERROR; } if (v_fra_pkg->last_pkg_flag == UNF_PKG_NOT_LAST_RESPONSE) { ret = unf_mv_resp_2_xchg(xchg, v_fra_pkg); return ret; } xchg->byte_orders = v_fra_pkg->byte_orders; lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)xchg); spin_lock_irqsave(&xchg->xchg_state_lock, flags); if ((xchg->pfn_callback) && ((xchg->cmnd_code == ELS_RRQ) || (xchg->cmnd_code == ELS_LOGO) || (!(xchg->io_state & TGT_IO_STATE_ABORT)))) { pfn_callback = xchg->pfn_callback; if ((xchg->cmnd_code == ELS_FLOGI) || (xchg->cmnd_code == ELS_FDISC)) xchg->sid = v_fra_pkg->frame_head.rctl_did & UNF_NPORTID_MASK; if (xchg->cmnd_code == ELS_ECHO) { xchg->private[PKG_PRIVATE_ECHO_CMD_RCV_TIME] = v_fra_pkg->private[PKG_PRIVATE_ECHO_CMD_RCV_TIME]; xchg->private[PKG_PRIVATE_ECHO_RSP_SND_TIME] = v_fra_pkg->private[PKG_PRIVATE_ECHO_RSP_SND_TIME]; xchg->private[PKG_PRIVATE_ECHO_CMD_SND_TIME] = v_fra_pkg->private[PKG_PRIVATE_ECHO_CMD_SND_TIME]; xchg->private[PKG_PRIVATE_ECHO_ACC_RCV_TIME] = v_fra_pkg->private[PKG_PRIVATE_ECHO_ACC_RCV_TIME]; } spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); /* Do callback */ pfn_callback(xchg->lport, xchg->rport, xchg); } else { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } unf_cm_free_xchg((void *)lport, (void *)xchg); return ret; } static unsigned int unf_send_els_cmnd_failed(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_xchg_s *xchg = NULL; unsigned int ret = RETURN_OK; unsigned short hot_pool_tag = 0; unsigned long flags = 0; void (*pfn_ob_callback)(struct unf_xchg_s *) = NULL; UNF_CHECK_VALID(0x3533, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3534, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); if (!v_lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) lookup exchange by tag function can't be NULL", v_lport->port_id); return UNF_RETURN_ERROR; } hot_pool_tag = (unsigned short) (v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]); xchg = (struct unf_xchg_s *) (v_lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag( (void *)v_lport, hot_pool_tag)); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) find exhange by tag(0x%x) failed", v_lport->port_id, v_lport->nport_id, hot_pool_tag); return UNF_RETURN_ERROR; } UNF_CHECK_ALLOCTIME_VALID( v_lport, hot_pool_tag, xchg, v_fra_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME], xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]); v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)xchg); spin_lock_irqsave(&xchg->xchg_state_lock, flags); if ((xchg->pfn_ob_callback) && ((xchg->cmnd_code == ELS_RRQ) || (xchg->cmnd_code == ELS_LOGO) || (!(xchg->io_state & TGT_IO_STATE_ABORT)))) { pfn_ob_callback = xchg->pfn_ob_callback; xchg->ob_callback_sts = v_fra_pkg->status; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); pfn_ob_callback(xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) exchange(0x%p) tag(0x%x) do callback", v_lport->port_id, xchg, hot_pool_tag); } else { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } unf_cm_free_xchg((void *)v_lport, (void *)xchg); return ret; } static unsigned int unf_rcv_els_cmnd_reply(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3535, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3536, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); if ((v_fra_pkg->status == UNF_IO_SUCCESS) || (v_fra_pkg->status == UNF_IO_UNDER_FLOW)) ret = unf_send_els_cmnd_succ(v_lport, v_fra_pkg); else ret = unf_send_els_cmnd_failed(v_lport, v_fra_pkg); return ret; } void unf_lport_enter_msn_plogi(struct unf_lport_s *v_lport) { /* Fabric or Public Loop Mode: Login with Name server */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; unsigned long flag = 0; unsigned int ret = UNF_RETURN_ERROR; struct unf_plogi_payload_s *plogi_pld = NULL; union unf_sfs_u *fc_entry = NULL; struct unf_xchg_s *xchg = NULL; struct unf_frame_pkg_s pkg; UNF_CHECK_VALID(0x1811, UNF_TRUE, v_lport, return); /* Get (safe) R_Port */ rport = unf_rport_get_free_and_init(v_lport, UNF_PORT_TYPE_FC, UNF_FC_FID_MGMT_SERV); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) allocate RPort failed", v_lport->port_id); return; } spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = UNF_FC_FID_MGMT_SERV; // 0xfffffa spin_unlock_irqrestore(&rport->rport_state_lock, flag); memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); /* Get & Set new free exchange */ xchg = unf_cm_get_free_xchg(v_lport, UNF_XCHG_TYPE_SFS); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) exchange can't be NULL for PLOGI", v_lport->port_id); return; } xchg->cmnd_code = ELS_PLOGI; // PLOGI xchg->did = rport->nport_id; xchg->sid = v_lport->nport_id; xchg->oid = xchg->sid; xchg->lport = lport; xchg->rport = rport; if (v_lport->low_level_func.xchg_mgr_type == UNF_LOW_LEVEL_MGR_TYPE_PASSTIVE) xchg->ox_id = xchg->hot_pool_tag; /* Set callback function */ xchg->pfn_callback = NULL; // for rcvd plogi acc/rjt processer xchg->pfn_ob_callback = NULL; // for send plogi failed processer unf_fill_package(&pkg, xchg, rport); /* Fill PLOGI payload */ fc_entry = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, xchg->hot_pool_tag); unf_cm_free_xchg(v_lport, xchg); return; } plogi_pld = &fc_entry->plogi.payload; memset(plogi_pld, 0, sizeof(struct unf_plogi_payload_s)); unf_fill_plogi_pld(plogi_pld, v_lport); /* Start to Send PLOGI command */ ret = unf_els_cmnd_send(v_lport, &pkg, xchg); if (ret != RETURN_OK) unf_cm_free_xchg((void *)v_lport, (void *)xchg); } static void unf_register_to_switch(struct unf_lport_s *v_lport) { /* Register to Fabric, used for: FABRIC & PUBLI LOOP */ unsigned long flag = 0; UNF_CHECK_VALID(0x3542, UNF_TRUE, v_lport, return); spin_lock_irqsave(&v_lport->lport_state_lock, flag); /* LPort: FLOGI_WAIT --> PLOGI_WAIT */ unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_REMOTE_ACC); spin_unlock_irqrestore(&v_lport->lport_state_lock, flag); /* Login with Name server: PLOGI */ unf_lport_enter_sns_plogi(v_lport); unf_lport_enter_msn_plogi(v_lport); if ((v_lport->root_lport == v_lport) &&/* Physical Port */ (v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC)) { unf_linkup_all_vports(v_lport); } } void unf_login_with_loop_node(struct unf_lport_s *v_lport, unsigned int v_alpa) { /* Only used for Private Loop LOGIN */ struct unf_rport_s *rport = NULL; unsigned long rport_flag = 0; unsigned int port_feature = 0; unsigned int ret; /* Check AL_PA validity */ if (v_lport->nport_id == v_alpa) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) is the same as RPort with AL_PA(0x%x), do nothing", v_lport->port_id, v_alpa); return; } if (v_alpa == 0) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) RPort(0x%x) is fabric, do nothing", v_lport->port_id, v_alpa); return; } /* Get & set R_Port: reuse only */ rport = unf_get_rport_by_nport_id(v_lport, v_alpa); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: Port(0x%x_0x%x) RPort(0x%x_0x%p) login with private loop", v_lport->port_id, v_lport->nport_id, v_alpa, rport); rport = unf_get_safe_rport(v_lport, rport, UNF_RPORT_REUSE_ONLY, v_alpa); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) allocate new RPort(0x%x) failed", v_lport->port_id, v_lport->nport_id, v_alpa); return; } /* Update R_Port state & N_Port_ID */ spin_lock_irqsave(&rport->rport_state_lock, rport_flag); rport->nport_id = v_alpa; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); // PLOGI_WAIT spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); /* Private Loop: check whether need delay to send PLOGI or not */ port_feature = rport->options; /* check Rport and Lport feature */ if ((port_feature == UNF_PORT_MODE_UNKNOWN) && (v_lport->options == UNF_PORT_MODE_INI)) { /* Start to send PLOGI */ ret = unf_send_plogi(v_lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) send PLOGI to RPort(0x%x) failed", v_lport->port_id, v_lport->nport_id, rport->nport_id); unf_rport_error_recovery(rport); } } else { unf_check_rport_need_delay_plogi(v_lport, rport, port_feature); } } unsigned int unf_receive_els_pkg(void *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_lport_s *lport = NULL; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3543, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3544, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); lport = (struct unf_lport_s *)v_lport; switch (v_fra_pkg->type) { case UNF_PKG_ELS_REQ_DONE: ret = unf_rcv_els_cmnd_reply(lport, v_fra_pkg); break; case UNF_PKG_ELS_REQ: ret = unf_rcv_els_cmnd_req(lport, v_fra_pkg); break; default: UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) with exchange type(0x%x) abnormal", lport->port_id, lport->nport_id, v_fra_pkg->type); break; } return ret; } unsigned int unf_send_els_done(void *v_lport, struct unf_frame_pkg_s *v_pkg) { unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3545, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3546, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); if (v_pkg->type == UNF_PKG_ELS_REPLY_DONE) { if ((v_pkg->status == UNF_IO_SUCCESS) || (v_pkg->status == UNF_IO_UNDER_FLOW)) ret = unf_send_els_rsp_succ(v_lport, v_pkg); else ret = unf_send_els_cmnd_failed(v_lport, v_pkg); } return ret; } static unsigned int unf_rcv_gs_cmnd_reply(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_xchg_s *xchg = NULL; unsigned long flags = 0; unsigned short hot_pool_tag = 0; unsigned int ret = RETURN_OK; struct unf_lport_s *lport = NULL; void (*pfn_callback)(void *, void *, void *) = NULL; UNF_CHECK_VALID(0x3553, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3554, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); lport = v_lport; hot_pool_tag = (unsigned short) (v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]); xchg = (struct unf_xchg_s *)unf_cm_lookup_xchg_by_tag( (void *)lport, hot_pool_tag); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find exhange by tag(0x%x) failed", lport->port_id, hot_pool_tag); return UNF_RETURN_ERROR; } UNF_CHECK_ALLOCTIME_VALID( lport, hot_pool_tag, xchg, v_fra_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME], xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]); if (v_fra_pkg->last_pkg_flag == UNF_PKG_NOT_LAST_RESPONSE) { ret = unf_mv_resp_2_xchg(xchg, v_fra_pkg); return ret; } lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)xchg); spin_lock_irqsave(&xchg->xchg_state_lock, flags); if ((xchg->pfn_callback) && (!(xchg->io_state & TGT_IO_STATE_ABORT))) { pfn_callback = xchg->pfn_callback; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); pfn_callback(xchg->lport, xchg->rport, xchg); } else { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } unf_cm_free_xchg((void *)lport, (void *)xchg); return ret; } static unsigned int unf_send_gs_cmnd_failed(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_xchg_s *xchg = NULL; unsigned int ret = RETURN_OK; unsigned short hot_pool_tag = 0; unsigned long flags = 0; void (*pfn_ob_callback)(struct unf_xchg_s *) = NULL; UNF_CHECK_VALID(0x3555, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3556, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); if (!v_lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) loopup exchange by tag function can't be NULL", v_lport->port_id); return UNF_RETURN_ERROR; } hot_pool_tag = (unsigned short) (v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]); xchg = (struct unf_xchg_s *) (v_lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag( (void *)v_lport, hot_pool_tag)); if (!xchg) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find exhange by tag(0x%x)", v_lport->port_id, hot_pool_tag); return UNF_RETURN_ERROR; } UNF_CHECK_ALLOCTIME_VALID( v_lport, hot_pool_tag, xchg, v_fra_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME], xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]); v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)xchg); spin_lock_irqsave(&xchg->xchg_state_lock, flags); if ((xchg->pfn_ob_callback) && (!(xchg->io_state & TGT_IO_STATE_ABORT))) { pfn_ob_callback = xchg->pfn_ob_callback; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); pfn_ob_callback(xchg); } else { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } unf_cm_free_xchg((void *)v_lport, (void *)xchg); return ret; } unsigned int unf_receive_gs_pkg(void *v_lport, struct unf_frame_pkg_s *v_fra_pkg) { struct unf_lport_s *lport = NULL; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3557, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3558, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR); lport = (struct unf_lport_s *)v_lport; if ((v_fra_pkg->type) == UNF_PKG_GS_REQ_DONE) { if ((v_fra_pkg->status == UNF_IO_SUCCESS) || (v_fra_pkg->status == UNF_IO_UNDER_FLOW) || (v_fra_pkg->status == UNF_IO_OVER_FLOW)) ret = unf_rcv_gs_cmnd_reply(lport, v_fra_pkg); else ret = unf_send_gs_cmnd_failed(lport, v_fra_pkg); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) exchange type(0x%x) mismatch", lport->port_id, v_fra_pkg->type); return UNF_RETURN_ERROR; } return ret; } static void unf_handle_init_gid_acc(struct unf_gif_acc_pld_s *v_gid_acc_pld, struct unf_lport_s *v_lport) { /* * from SCR ACC callback * NOTE: inquiry disc R_Port used for NPIV */ struct unf_disc_rport_s *disc_rport = NULL; struct unf_disc_s *disc = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned int gid_port_id = 0; unsigned int nport_id = 0; unsigned int i = 0; unsigned char control = 0; UNF_CHECK_VALID(0x3559, UNF_TRUE, v_gid_acc_pld, return); UNF_CHECK_VALID(0x3560, UNF_TRUE, v_lport, return); /* * 1. Find & Check & Get (new) R_Port from list_disc_rports_pool * then, Add to R_Port Disc_busy_list */ while (i < UNF_GID_PORT_CNT) { gid_port_id = (v_gid_acc_pld->gid_port_id[i]); nport_id = UNF_NPORTID_MASK & gid_port_id; control = UNF_GID_CONTROL(gid_port_id); /* for each N_Port_ID from GID_ACC payload */ if ((nport_id != v_lport->nport_id) && (nport_id != 0) && (!unf_lookup_lport_by_nport_id(v_lport, nport_id))) { /* for New Port, not L_Port */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) get nportid(0x%x) from GID_ACC", v_lport->port_id, v_lport->nport_id, nport_id); /* Get R_Port from list of RPort Disc Pool */ disc_rport = unf_rport_get_free_and_init(v_lport, UNF_PORT_TYPE_DISC, nport_id); if (!disc_rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) can't allocate new rport(0x%x) from disc pool", v_lport->port_id, v_lport->nport_id, nport_id); i++; continue; } } if ((control & UNF_GID_LAST_PORT_ID) == UNF_GID_LAST_PORT_ID) break; i++; } /* * 2. Do port disc stop operation: * NOTE: Do DISC & release R_Port from * busy_list back to list_disc_rports_pool */ disc = &v_lport->disc; if (!disc->unf_disc_temp.pfn_unf_disc_stop) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) disc stop function is NULL", v_lport->port_id, v_lport->nport_id); return; } ret = disc->unf_disc_temp.pfn_unf_disc_stop(v_lport); if (ret != RETURN_OK) UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) do disc stop failed", v_lport->port_id, v_lport->nport_id); } void unf_rport_immediate_linkdown(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* Swap case: Report Link Down immediately & release R_Port */ unsigned long flags = 0; struct unf_disc_s *disc = NULL; UNF_CHECK_VALID(0x3561, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3562, UNF_TRUE, v_rport, return); spin_lock_irqsave(&v_rport->rport_state_lock, flags); /* 1. Inc R_Port ref_cnt */ if (unf_rport_ref_inc(v_rport) != RETURN_OK) { spin_unlock_irqrestore(&v_rport->rport_state_lock, flags); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) Rport(0x%p,0x%x) is removing and no need process", v_lport->port_id, v_rport, v_rport->nport_id); return; } /* 2. R_PORT state update: Link Down Event --->>> closing state */ unf_rport_state_ma(v_rport, UNF_EVENT_RPORT_LINK_DOWN); spin_unlock_irqrestore(&v_rport->rport_state_lock, flags); /* 3. Put R_Port from busy to destroy list */ disc = &v_lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, flags); list_del_init(&v_rport->entry_rport); list_add_tail(&v_rport->entry_rport, &disc->list_destroy_rports); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flags); /* 4. Schedule Closing work (Enqueuing workqueue) */ unf_schedule_closing_work(v_lport, v_rport); unf_rport_ref_dec(v_rport); } static unsigned int unf_rport_check_wwn(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* Send GPN_ID */ struct unf_rport_s *sns_port = NULL; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3564, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3565, UNF_TRUE, v_rport, return UNF_RETURN_ERROR); /* Get SNS R_Port */ sns_port = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_DIR_SERV); if (!sns_port) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find fabric Port", v_lport->nport_id); return UNF_RETURN_ERROR; } /* Send GPN_ID to SW */ ret = unf_get_and_post_disc_event(v_lport, sns_port, v_rport->nport_id, UNF_DISC_GET_PORT_NAME); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", v_lport->nport_id, UNF_DISC_GET_PORT_NAME, v_rport->nport_id); unf_rcv_gpn_id_rsp_unknown(v_lport, v_rport->nport_id); } return ret; } static unsigned int unf_handle_rscn_port_not_in_disc( struct unf_lport_s *v_lport, unsigned int v_rscn_nport_id) { /* RSCN Port_ID not in GID_ACC payload table: Link Down */ struct unf_rport_s *rport = NULL; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3566, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); /* from R_Port busy list by N_Port_ID */ rport = unf_get_rport_by_nport_id(v_lport, v_rscn_nport_id); if (rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]Port(0x%x) RPort(0x%x) wwpn(0x%llx) has been removed and link down it", v_lport->port_id, v_rscn_nport_id, rport->port_name); unf_rport_linkdown(v_lport, rport); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) has no RPort(0x%x) and do nothing", v_lport->nport_id, v_rscn_nport_id); } return ret; } static unsigned int unf_handle_rscn_port_in_disc(struct unf_lport_s *v_lport, unsigned int v_rscn_nport_id) { /* Send GPN_ID or re-login(GNN_ID) */ struct unf_rport_s *rport = NULL; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3567, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); /* from R_Port busy list by N_Port_ID */ rport = unf_get_rport_by_nport_id(v_lport, v_rscn_nport_id); if (rport) { /* R_Port exist: send GPN_ID */ ret = unf_rport_check_wwn(v_lport, rport); } else { if ((v_lport->options & UNF_PORT_MODE_INI) == UNF_PORT_MODE_INI) { /* Re-LOGIN with INI mode: Send GNN_ID */ ret = unf_rport_relogin(v_lport, v_rscn_nport_id); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) with no INI feature. Do nothing", v_lport->nport_id); } } return ret; } static unsigned int unf_handle_rscn_port_addr( struct unf_port_id_page_s *v_port_id_page, struct unf_gif_acc_pld_s *v_gid_acc_pld, struct unf_lport_s *v_lport) { /* * Input parameters: * 1. Port_ID_page: saved from RSCN payload * 2. GID_ACC_payload: back from GID_ACC (GID_PT or GID_FT) ** * Do work: check whether RSCN Port_ID within GID_ACC payload or not * then, re-login or link down rport */ unsigned int rscn_nport_id = 0; unsigned int gid_port_id = 0; unsigned int nport_id = 0; unsigned int i = 0; unsigned char control = 0; unsigned int ret = RETURN_OK; enum int_e have_same_id = UNF_FALSE; UNF_CHECK_VALID(0x3568, UNF_TRUE, v_port_id_page, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3569, UNF_TRUE, v_gid_acc_pld, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3570, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); /* 1. get RSCN_NPort_ID from (L_Port->Disc->RSCN_Mgr)->RSCN_Port_ID_Page */ rscn_nport_id = UNF_SERVICE_GET_NPORTID_FORM_GID_PAGE(v_port_id_page); /* * 2. for RSCN_NPort_ID * check whether RSCN_NPort_ID within GID_ACC_Payload or not */ while (i < UNF_GID_PORT_CNT) { /* 4k */ gid_port_id = (v_gid_acc_pld->gid_port_id[i]); nport_id = UNF_NPORTID_MASK & gid_port_id; control = UNF_GID_CONTROL(gid_port_id); if ((v_lport->nport_id != nport_id) && (nport_id != 0)) { /* is not L_Port */ if (rscn_nport_id == nport_id) { /* RSCN Port_ID within GID_ACC payload */ have_same_id = UNF_TRUE; break; } } if ((control & UNF_GID_LAST_PORT_ID) == UNF_GID_LAST_PORT_ID) break; i++; } /* 3. RSCN_Port_ID not within GID_ACC payload table */ if (have_same_id == UNF_FALSE) { /* rport has been removed */ UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_INFO, "[warn]Port(0x%x_0x%x) find RSCN N_Port_ID(0x%x) in GID_ACC table failed", v_lport->port_id, v_lport->nport_id, rscn_nport_id); /* Link down rport */ ret = unf_handle_rscn_port_not_in_disc(v_lport, rscn_nport_id); } else { /* 4. RSCN_Port_ID within GID_ACC payload table */ UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x_0x%x) find RSCN N_Port_ID(0x%x) in GID_ACC table succeed", v_lport->port_id, v_lport->nport_id, rscn_nport_id); /* Re-login with INI mode */ ret = unf_handle_rscn_port_in_disc(v_lport, rscn_nport_id); } return ret; } static void unf_check_rport_rscn_process( struct unf_rport_s *v_rport, struct unf_port_id_page_s *v_port_id_page) { struct unf_rport_s *rport = v_rport; struct unf_port_id_page_s *port_id_page = v_port_id_page; unsigned char format = port_id_page->uc_addr_format; switch (format) { /* domain+area */ case UNF_RSCN_AREA_ADDR_GROUP: if (UNF_GET_DOMAIN_ID(rport->nport_id) == port_id_page->port_id_domain && UNF_GET_AREA_ID(rport->nport_id) == port_id_page->port_id_area) { rport->rscn_position = UNF_RPORT_NEED_PROCESS; } break; /* domain */ case UNF_RSCN_DOMAIN_ADDR_GROUP: if (UNF_GET_DOMAIN_ID(rport->nport_id) == port_id_page->port_id_domain) rport->rscn_position = UNF_RPORT_NEED_PROCESS; break; /* all */ case UNF_RSCN_FABRIC_ADDR_GROUP: rport->rscn_position = UNF_RPORT_NEED_PROCESS; break; default: break; } } static void unf_set_rport_rscn_position( struct unf_lport_s *v_lport, struct unf_port_id_page_s *v_port_id_page) { struct unf_rport_s *rport = NULL; struct list_head *node = NULL; struct list_head *next_node = NULL; struct unf_disc_s *disc = NULL; unsigned long disc_flag = 0; unsigned long rport_flag = 0; UNF_CHECK_VALID(0x3571, UNF_TRUE, v_lport, return); disc = &v_lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, disc_flag); list_for_each_safe(node, next_node, &disc->list_busy_rports) { rport = list_entry(node, struct unf_rport_s, entry_rport); spin_lock_irqsave(&rport->rport_state_lock, rport_flag); if (rport->nport_id < UNF_FC_FID_DOM_MGR) { if (rport->rscn_position == UNF_RPORT_NOT_NEED_PROCESS) unf_check_rport_rscn_process(rport, v_port_id_page); } else { rport->rscn_position = UNF_RPORT_NOT_NEED_PROCESS; } spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); } spin_unlock_irqrestore(&disc->rport_busy_pool_lock, disc_flag); } static void unf_set_rport_rscn_position_local(struct unf_lport_s *v_lport) { struct unf_rport_s *rport = NULL; struct list_head *node = NULL; struct list_head *next_node = NULL; struct unf_disc_s *disc = NULL; unsigned long disc_flag = 0; unsigned long rport_flag = 0; UNF_CHECK_VALID(0x3572, UNF_TRUE, v_lport, return); disc = &v_lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, disc_flag); list_for_each_safe(node, next_node, &disc->list_busy_rports) { rport = list_entry(node, struct unf_rport_s, entry_rport); spin_lock_irqsave(&rport->rport_state_lock, rport_flag); if (rport->nport_id < UNF_FC_FID_DOM_MGR) { if (rport->rscn_position == UNF_RPORT_NEED_PROCESS) rport->rscn_position = UNF_RPORT_ONLY_IN_LOCAL_PROCESS; } else { rport->rscn_position = UNF_RPORT_NOT_NEED_PROCESS; } spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); } spin_unlock_irqrestore(&disc->rport_busy_pool_lock, disc_flag); } static void unf_reset_rport_rscn_setting(struct unf_lport_s *v_lport) { struct unf_rport_s *rport = NULL; struct list_head *node = NULL; struct list_head *next_node = NULL; struct unf_disc_s *disc = NULL; unsigned long rport_flag = 0; UNF_CHECK_VALID(0x3573, UNF_TRUE, v_lport, return); disc = &v_lport->disc; list_for_each_safe(node, next_node, &disc->list_busy_rports) { rport = list_entry(node, struct unf_rport_s, entry_rport); spin_lock_irqsave(&rport->rport_state_lock, rport_flag); rport->rscn_position = UNF_RPORT_NOT_NEED_PROCESS; spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); } } static void unf_compare_nport_id_with_rport_list( struct unf_lport_s *v_lport, unsigned int v_nport_id, struct unf_port_id_page_s *v_port_id_page) { struct unf_rport_s *rport = NULL; unsigned long rport_flag = 0; unsigned char format = v_port_id_page->uc_addr_format; UNF_CHECK_VALID(0x3574, UNF_TRUE, v_lport, return); switch (format) { /* domain+area */ case UNF_RSCN_AREA_ADDR_GROUP: if ((UNF_GET_DOMAIN_ID(v_nport_id) != v_port_id_page->port_id_domain) || (UNF_GET_AREA_ID(v_nport_id) != v_port_id_page->port_id_area)) return; break; /* domain */ case UNF_RSCN_DOMAIN_ADDR_GROUP: if (UNF_GET_DOMAIN_ID(v_nport_id) != v_port_id_page->port_id_domain) return; break; /* all */ case UNF_RSCN_FABRIC_ADDR_GROUP: break; /* can't enter this branch guarantee by outer */ default: break; } rport = unf_get_rport_by_nport_id(v_lport, v_nport_id); if (!rport) { if ((v_lport->options & UNF_PORT_MODE_INI) == UNF_PORT_MODE_INI) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[event]Port(0x%x) Find Rport(0x%x) by RSCN", v_lport->nport_id, v_nport_id); unf_rport_relogin(v_lport, v_nport_id); } } else { spin_lock_irqsave(&rport->rport_state_lock, rport_flag); if (rport->rscn_position == UNF_RPORT_NEED_PROCESS) rport->rscn_position = UNF_RPORT_IN_DISC_AND_LOCAL_PROCESS; spin_unlock_irqrestore(&rport->rport_state_lock, rport_flag); } } static void unf_compare_disc_with_local_rport( struct unf_lport_s *v_lport, struct unf_gif_acc_pld_s *v_gid_acc_pld, struct unf_port_id_page_s *v_port_id_page) { unsigned int gid_port_id = 0; unsigned int nport_id = 0; unsigned int i = 0; unsigned char control = 0; UNF_CHECK_VALID(0x3575, UNF_TRUE, v_gid_acc_pld, return); UNF_CHECK_VALID(0x3576, UNF_TRUE, v_lport, return); while (i < UNF_GID_PORT_CNT) { gid_port_id = (v_gid_acc_pld->gid_port_id[i]); nport_id = UNF_NPORTID_MASK & gid_port_id; control = UNF_GID_CONTROL(gid_port_id); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]Port(0x%x) DISC N_Port_ID(0x%x)", v_lport->nport_id, nport_id); if ((nport_id != 0) && (!unf_lookup_lport_by_nport_id(v_lport, nport_id))) unf_compare_nport_id_with_rport_list(v_lport, nport_id, v_port_id_page); if ((UNF_GID_LAST_PORT_ID & control) == UNF_GID_LAST_PORT_ID) break; i++; } unf_set_rport_rscn_position_local(v_lport); } static unsigned int unf_process_each_rport_after_rscn( struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, struct unf_rport_s *v_rport) { unsigned long rport_flag = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3577, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3578, UNF_TRUE, v_sns_port, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3579, UNF_TRUE, v_sns_port, return UNF_RETURN_ERROR); UNF_REFERNCE_VAR(v_sns_port); spin_lock_irqsave(&v_rport->rport_state_lock, rport_flag); if (v_rport->rscn_position == UNF_RPORT_IN_DISC_AND_LOCAL_PROCESS) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[info]Port(0x%x_0x%x) RPort(0x%x) rescan position(0x%x), check wwpn", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_rport->rscn_position); v_rport->rscn_position = UNF_RPORT_NOT_NEED_PROCESS; spin_unlock_irqrestore(&v_rport->rport_state_lock, rport_flag); ret = unf_rport_check_wwn(v_lport, v_rport); } else if (v_rport->rscn_position == UNF_RPORT_ONLY_IN_LOCAL_PROCESS) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT, "[event]Port(0x%x_0x%x) RPort(0x%x) rescan position(0x%x), linkdown it", v_lport->port_id, v_lport->nport_id, v_rport->nport_id, v_rport->rscn_position); v_rport->rscn_position = UNF_RPORT_NOT_NEED_PROCESS; spin_unlock_irqrestore(&v_rport->rport_state_lock, rport_flag); unf_rport_linkdown(v_lport, v_rport); } else { spin_unlock_irqrestore(&v_rport->rport_state_lock, rport_flag); } return ret; } static unsigned int unf_process_local_rport_after_rscn( struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port) { struct unf_rport_s *rport = NULL; struct list_head *node = NULL; struct unf_disc_s *disc = NULL; unsigned long disc_flag = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3580, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3581, UNF_TRUE, v_sns_port, return UNF_RETURN_ERROR); disc = &v_lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, disc_flag); if (list_empty(&disc->list_busy_rports)) { spin_unlock_irqrestore(&disc->rport_busy_pool_lock, disc_flag); return UNF_RETURN_ERROR; } node = (&disc->list_busy_rports)->next; do { rport = list_entry(node, struct unf_rport_s, entry_rport); if (rport->rscn_position == UNF_RPORT_NOT_NEED_PROCESS) { node = node->next; continue; } else { spin_unlock_irqrestore(&disc->rport_busy_pool_lock, disc_flag); ret = unf_process_each_rport_after_rscn(v_lport, v_sns_port, rport); spin_lock_irqsave(&disc->rport_busy_pool_lock, disc_flag); node = (&disc->list_busy_rports)->next; } } while (node != &disc->list_busy_rports); unf_reset_rport_rscn_setting(v_lport); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, disc_flag); return ret; } static unsigned int unf_handle_rscn_group_addr( struct unf_port_id_page_s *v_port_id_page, struct unf_gif_acc_pld_s *v_gid_acc_pld, struct unf_lport_s *v_lport) { struct unf_rport_s *sns_port = NULL; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3582, UNF_TRUE, v_port_id_page, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3583, UNF_TRUE, v_gid_acc_pld, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3584, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_REFERNCE_VAR(v_port_id_page); sns_port = unf_get_rport_by_nport_id(v_lport, UNF_FC_FID_DIR_SERV); if (!sns_port) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) find fabric port failed", v_lport->port_id); return UNF_RETURN_ERROR; } unf_set_rport_rscn_position(v_lport, v_port_id_page); unf_compare_disc_with_local_rport(v_lport, v_gid_acc_pld, v_port_id_page); ret = unf_process_local_rport_after_rscn(v_lport, sns_port); return ret; } static void unf_handle_rscn_gid_acc(struct unf_gif_acc_pld_s *v_gid_acc_pld, struct unf_lport_s *v_lport) { /* for N_Port_ID table return from RSCN */ struct unf_port_id_page_s *port_id_page = NULL; struct unf_rscn_mg_s *rscn_mgr = NULL; struct list_head *list_node = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3585, UNF_TRUE, v_gid_acc_pld, return); UNF_CHECK_VALID(0x3586, UNF_TRUE, v_lport, return); rscn_mgr = &v_lport->disc.rscn_mgr; spin_lock_irqsave(&rscn_mgr->rscn_id_list_lock, flag); while (!list_empty(&rscn_mgr->list_using_rscn_page)) { /* * for each RSCN_Using_Page(NPortID) * for each L_Port->Disc->RSCN_Mgr-> * RSCN_Using_Page(Port_ID_Page) * NOTE: * check using_page_port_id whether within * GID_ACC payload or not */ list_node = (&rscn_mgr->list_using_rscn_page)->next; port_id_page = list_entry(list_node, struct unf_port_id_page_s, list_node_rscn); /* NOTE: here delete node (from RSCN using Page) */ list_del(list_node); spin_unlock_irqrestore(&rscn_mgr->rscn_id_list_lock, flag); switch (port_id_page->uc_addr_format) { /* each page of RSNC corresponding one of N_Port_ID */ case UNF_RSCN_PORT_ADDR: (void)unf_handle_rscn_port_addr(port_id_page, v_gid_acc_pld, v_lport); break; /* each page of RSNC corresponding address group */ case UNF_RSCN_AREA_ADDR_GROUP: case UNF_RSCN_DOMAIN_ADDR_GROUP: case UNF_RSCN_FABRIC_ADDR_GROUP: (void)unf_handle_rscn_group_addr(port_id_page, v_gid_acc_pld, v_lport); break; default: break; } /* NOTE: release this RSCN_Node */ rscn_mgr->pfn_unf_release_rscn_node(rscn_mgr, port_id_page); /* go to next */ spin_lock_irqsave(&rscn_mgr->rscn_id_list_lock, flag); } spin_unlock_irqrestore(&rscn_mgr->rscn_id_list_lock, flag); } static void unf_gid_acc_handle(struct unf_gif_acc_pld_s *v_gid_acc_pld, struct unf_lport_s *v_lport) { #define UNF_NONE_DISC 0X0 /* before enter DISC */ struct unf_disc_s *disc = NULL; unsigned long flag = 0; UNF_CHECK_VALID(0x3587, UNF_TRUE, v_gid_acc_pld, return); UNF_CHECK_VALID(0x3588, UNF_TRUE, v_lport, return); disc = &v_lport->disc; spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); switch (disc->disc_option) { case UNF_INIT_DISC: // from SCR callback with INI mode disc->disc_option = UNF_NONE_DISC; spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* R_Port from Disc_list */ unf_handle_init_gid_acc(v_gid_acc_pld, v_lport); break; case UNF_RSCN_DISC: /* from RSCN payload parse(analysis) */ disc->disc_option = UNF_NONE_DISC; spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* R_Port from busy_list */ unf_handle_rscn_gid_acc(v_gid_acc_pld, v_lport); break; default: spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x)'s disc option(0x%x) is abnormal", v_lport->port_id, v_lport->nport_id, disc->disc_option); break; } } static void unf_gid_ft_callback(void *v_lport, void *v_rport, void *v_xchg) { struct unf_lport_s *lport = NULL; struct unf_disc_s *disc = NULL; struct unf_gif_acc_pld_s *gid_acc_pld = NULL; struct unf_xchg_s *xchg = NULL; union unf_sfs_u *sfs_ptr = NULL; unsigned int cmnd_rsp_size = 0; unsigned int rjt_reason = 0; unsigned long flag = 0; UNF_CHECK_VALID(0x3590, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3591, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3592, UNF_TRUE, v_xchg, return); UNF_REFERNCE_VAR(v_rport); lport = (struct unf_lport_s *)v_lport; xchg = (struct unf_xchg_s *)v_xchg; disc = &lport->disc; sfs_ptr = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; gid_acc_pld = sfs_ptr->get_id.gid_rsp.gid_acc_pld; if (!gid_acc_pld) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) GID_FT response payload is NULL", lport->port_id); return; } cmnd_rsp_size = (gid_acc_pld->ctiu_pream.cmnd_rsp_size); if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_SUCCESS); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* Process GID_FT ACC */ unf_gid_acc_handle(gid_acc_pld, lport); } else if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_REJECT) { rjt_reason = (gid_acc_pld->ctiu_pream.frag_reason_exp_vend); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) GID_FT was rejected with reason code(0x%x)", lport->port_id, rjt_reason); if ((rjt_reason & UNF_CTIU_RJT_EXP_MASK) == UNF_CTIU_RJT_EXP_FC4TYPE_NO_REG) { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_SUCCESS); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); unf_gid_acc_handle(gid_acc_pld, lport); } else { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_SUCCESS); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); } } else { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_FAILED); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* Do DISC recovery operation */ unf_disc_error_recovery(lport); } } static void unf_gid_pt_callback(void *v_lport, void *v_rport, void *v_xchg) { struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_disc_s *disc = NULL; struct unf_gif_acc_pld_s *gid_acc_pld = NULL; struct unf_xchg_s *xchg = NULL; union unf_sfs_u *sfs_ptr = NULL; unsigned int cmnd_rsp_size = 0; unsigned int rjt_reason = 0; unsigned long flag = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3594, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3595, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3596, UNF_TRUE, v_xchg, return); lport = (struct unf_lport_s *)v_lport; rport = (struct unf_rport_s *)v_rport; disc = &lport->disc; xchg = (struct unf_xchg_s *)v_xchg; sfs_ptr = xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; gid_acc_pld = sfs_ptr->get_id.gid_rsp.gid_acc_pld; if (!gid_acc_pld) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) GID_PT response payload is NULL", lport->port_id); return; } cmnd_rsp_size = (gid_acc_pld->ctiu_pream.cmnd_rsp_size); if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_ACCEPT) { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_SUCCESS); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); unf_gid_acc_handle(gid_acc_pld, lport); } else if ((cmnd_rsp_size & UNF_CT_IU_RSP_MASK) == UNF_CT_IU_REJECT) { rjt_reason = (gid_acc_pld->ctiu_pream.frag_reason_exp_vend); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) GID_PT was rejected with reason code(0x%x)", lport->port_id, lport->nport_id, rjt_reason); if (UNF_CTIU_RJT_EXP_PORTTYPE_NO_REG == (rjt_reason & UNF_CTIU_RJT_EXP_MASK)) { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_SUCCESS); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); unf_gid_acc_handle(gid_acc_pld, lport); } else { ret = unf_send_gid_ft(lport, rport); if (ret != RETURN_OK) { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_FAILED); spin_unlock_irqrestore( &disc->rport_busy_pool_lock, flag); /* Do DISC recovery */ unf_disc_error_recovery(lport); } } } else { spin_lock_irqsave(&disc->rport_busy_pool_lock, flag); unf_disc_state_ma(lport, UNF_EVENT_DISC_FAILED); spin_unlock_irqrestore(&disc->rport_busy_pool_lock, flag); /* Do DISC recovery */ unf_disc_error_recovery(lport); } } void unf_rcv_gnn_id_rsp_unknown(struct unf_lport_s *v_lport, struct unf_rport_s *v_sns_port, unsigned int v_nport_id) { /* Send GFF_ID */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *sns_port = v_sns_port; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3606, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3607, UNF_TRUE, v_sns_port, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x_0x%x) Rportid(0x%x) GNN_ID response is unknown. Sending GFF_ID", lport->port_id, lport->nport_id, v_nport_id); ret = unf_get_and_post_disc_event(lport, sns_port, v_nport_id, UNF_DISC_GET_FEATURE); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", lport->port_id, UNF_DISC_GET_FEATURE, v_nport_id); /* NOTE: go to next stage */ unf_rcv_gff_id_rsp_unknown(lport, v_nport_id); // send PLOGI } } void unf_rcv_gff_id_rsp_unknown(struct unf_lport_s *v_lport, unsigned int v_nport_id) { /* Send PLOGI */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; unsigned long flag = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3624, UNF_TRUE, v_lport, return); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send GFF_ID for RPort(0x%x) but response is unknown", lport->port_id, v_nport_id); /* Get (Safe) R_Port & Set State */ rport = unf_get_rport_by_nport_id(lport, v_nport_id); if (rport) rport = unf_find_rport(lport, v_nport_id, rport->port_name); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) can't get RPort by NPort ID(0x%x), allocate new RPort", lport->port_id, lport->nport_id, v_nport_id); rport = unf_rport_get_free_and_init(lport, UNF_PORT_TYPE_FC, v_nport_id); UNF_CHECK_VALID(0x3619, UNF_TRUE, NULL != rport, return); spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = v_nport_id; spin_unlock_irqrestore(&rport->rport_state_lock, flag); } rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, v_nport_id); UNF_CHECK_VALID(0x3625, UNF_TRUE, rport, return); /* Update R_Port state: PLOGI_WAIT */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = v_nport_id; unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Start to send PLOGI */ ret = unf_send_plogi(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) can not send PLOGI for RPort(0x%x), enter recovery", lport->port_id, v_nport_id); unf_rport_error_recovery(rport); } } static void unf_lport_update_nport_id(struct unf_lport_s *v_lport, unsigned int v_nport_id) { unsigned long flag = 0; UNF_CHECK_VALID(0x3646, UNF_TRUE, v_lport, return); spin_lock_irqsave(&v_lport->lport_state_lock, flag); v_lport->nport_id = v_nport_id; spin_unlock_irqrestore(&v_lport->lport_state_lock, flag); } static void unf_lport_update_time_params( struct unf_lport_s *v_lport, struct unf_flogi_payload_s *v_flogi_payload) { unsigned long flag = 0; unsigned int ed_tov = 0; unsigned int ra_tov = 0; UNF_CHECK_VALID(0x3647, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3648, UNF_TRUE, v_flogi_payload, return); ed_tov = v_flogi_payload->fabric_parms.co_parms.e_d_tov; ra_tov = v_flogi_payload->fabric_parms.co_parms.r_a_tov; spin_lock_irqsave(&v_lport->lport_state_lock, flag); /* FC-FS-3: 21.3.4, 21.3.5 */ if ((v_lport->en_act_topo == UNF_ACT_TOP_P2P_FABRIC) || (v_lport->en_act_topo == UNF_ACT_TOP_PUBLIC_LOOP)) { v_lport->ed_tov = ed_tov; v_lport->ra_tov = ra_tov; } else { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR, "[info]Port(0x%x_0x%x) with topo(0x%x) no need to save time parameters", v_lport->port_id, v_lport->nport_id, v_lport->en_act_topo); } spin_unlock_irqrestore(&v_lport->lport_state_lock, flag); } static void unf_fdisc_callback(void *v_lport, void *v_rport, void *v_xchg) { /* Register to Name Server or Do recovery */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_xchg_s *xchg = NULL; struct unf_flogi_payload_s *fdisc_pld = NULL; unsigned long flag = 0; unsigned int cmd = 0; lport = (struct unf_lport_s *)v_lport; rport = (struct unf_rport_s *)v_rport; xchg = (struct unf_xchg_s *)v_xchg; UNF_CHECK_VALID(0x3640, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3641, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3642, UNF_TRUE, v_xchg, return); UNF_CHECK_VALID(0x3643, UNF_TRUE, xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr, return); fdisc_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->fdisc_acc.fdisc_payload; if (xchg->byte_orders & UNF_BIT_2) unf_big_end_to_cpu((unsigned char *)fdisc_pld, sizeof(struct unf_flogi_payload_s)); cmd = fdisc_pld->cmnd; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: FDISC response is (0x%x). Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", cmd, lport->port_id, rport->nport_id, xchg->ox_id); rport = unf_get_rport_by_nport_id(lport, UNF_FC_FID_FLOGI); rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, UNF_FC_FID_FLOGI); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) has no Rport", lport->port_id); return; } spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = UNF_FC_FID_FLOGI; spin_unlock_irqrestore(&rport->rport_state_lock, flag); if ((cmd & UNF_ELS_CMND_HIGH_MASK) == UNF_ELS_CMND_ACC) { /* Case for ACC */ spin_lock_irqsave(&lport->lport_state_lock, flag); if (lport->en_states != UNF_LPORT_ST_FLOGI_WAIT) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) receive Flogi/Fdisc ACC in state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); spin_unlock_irqrestore(&lport->lport_state_lock, flag); return; } spin_unlock_irqrestore(&lport->lport_state_lock, flag); unf_lport_update_nport_id(lport, xchg->sid); unf_lport_update_time_params(lport, fdisc_pld); unf_register_to_switch(lport); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: FDISC response is (0x%x). Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", cmd, lport->port_id, rport->nport_id, xchg->ox_id); /* Case for RJT: Do L_Port recovery */ unf_lport_error_recovery(lport); } } static void unf_rcv_flogi_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_flogi_payload_s *v_flogi_pld, unsigned int v_nport_id, struct unf_xchg_s *v_xchg) { /* PLOGI to Name server or remote port */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; struct unf_flogi_payload_s *flogi_pld = v_flogi_pld; struct unf_fabric_parms_s *fabric_params = NULL; unsigned long long port_name = 0; unsigned long long node_name = 0; unsigned long flag = 0; UNF_CHECK_VALID(0x3649, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3650, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3651, UNF_TRUE, v_flogi_pld, return); /* Check L_Port state: FLOGI_WAIT */ spin_lock_irqsave(&lport->lport_state_lock, flag); if (lport->en_states != UNF_LPORT_ST_FLOGI_WAIT) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_WARN, "[info]Port(0x%x_0x%x) receive FLOGI ACC with state(0x%x)", lport->port_id, lport->nport_id, lport->en_states); spin_unlock_irqrestore(&lport->lport_state_lock, flag); return; } spin_unlock_irqrestore(&lport->lport_state_lock, flag); fabric_params = &flogi_pld->fabric_parms; node_name = (unsigned long long) (((unsigned long long)(fabric_params->high_node_name) << 32) | ((unsigned long long)(fabric_params->low_node_name))); port_name = (unsigned long long) (((unsigned long long)(fabric_params->high_port_name) << 32) | ((unsigned long long)(fabric_params->low_port_name))); /* flogi acc pyload class 3 service priority value */ lport->b_priority = UNF_PRIORITY_DISABLE; /* Save Flogi parameters */ unf_save_fabric_params(lport, rport, fabric_params); if (UNF_CHECK_NPORT_FPORT_BIT(flogi_pld) == UNF_N_PORT) { /* P2P Mode */ unf_lport_update_topo(lport, UNF_ACT_TOP_P2P_DIRECT); unf_login_with_rport_in_n2n(lport, port_name, node_name); } else { /* for: UNF_ACT_TOP_PUBLIC_LOOP * /UNF_ACT_TOP_P2P_FABRIC/UNF_TOP_P2P_MASK */ if (lport->en_act_topo != UNF_ACT_TOP_PUBLIC_LOOP) unf_lport_update_topo(lport, UNF_ACT_TOP_P2P_FABRIC); unf_lport_update_nport_id(lport, v_nport_id); unf_lport_update_time_params(lport, flogi_pld); /* Save process both for Public loop & Fabric */ unf_register_to_switch(lport); } } static void unf_flogi_acc_com_process(struct unf_xchg_s *v_xchg) { /* Maybe within interrupt or thread context */ struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_flogi_payload_s *flogi_pld = NULL; unsigned int nport_id = 0; unsigned int cmnd = 0; unsigned long flags = 0; struct unf_xchg_s *xchg = v_xchg; UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg, return); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->lport, return); lport = xchg->lport; rport = xchg->rport; flogi_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->flogi_acc.flogi_payload; cmnd = flogi_pld->cmnd; /* Get N_Port_ID & R_Port */ /* Others: 0xFFFFFE */ rport = unf_get_rport_by_nport_id(lport, UNF_FC_FID_FLOGI); nport_id = UNF_FC_FID_FLOGI; /* Get Safe R_Port: reuse only */ rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, nport_id); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can not allocate new Rport", lport->port_id); return; } /* Update R_Port N_Port_ID */ spin_lock_irqsave(&rport->rport_state_lock, flags); /* Others: 0xFFFFFE */ rport->nport_id = UNF_FC_FID_FLOGI; spin_unlock_irqrestore(&rport->rport_state_lock, flags); /* Process FLOGI ACC or RJT */ if ((cmnd & UNF_ELS_CMND_HIGH_MASK) == UNF_ELS_CMND_ACC) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: FLOGI response is(0x%x). Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", cmnd, lport->port_id, rport->nport_id, xchg->ox_id); /* Case for ACC */ unf_rcv_flogi_acc(lport, rport, flogi_pld, xchg->sid, xchg); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: FLOGI response is(0x%x). Port(0x%x)<---RPort(0x%x) with OX_ID(0x%x)", cmnd, lport->port_id, rport->nport_id, xchg->ox_id); /* Case for RJT: do L_Port error recovery */ unf_lport_error_recovery(lport); } } static int unf_rcv_flogi_acc_async_callback(void *v_arg_in, void *v_arg_out) { struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_arg_in; UNF_CHECK_VALID(0x2267, UNF_TRUE, xchg, return UNF_RETURN_ERROR); unf_flogi_acc_com_process(xchg); unf_xchg_ref_dec(xchg, SFS_RESPONSE); return RETURN_OK; } static void unf_flogi_callback(void *v_lport, void *v_rport, void *v_xchg) { /* Callback function for FLOGI ACC or RJT */ struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg; struct unf_flogi_payload_s *flogi_pld = NULL; int bbscn_enabled = UNF_FALSE; enum unf_act_topo_e act_topo = UNF_ACT_TOP_UNKNOWN; int switch_2_thread = UNF_FALSE; UNF_CHECK_VALID(0x3652, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3653, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3654, UNF_TRUE, v_xchg, return); UNF_CHECK_VALID(0x3655, UNF_TRUE, xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr, return); xchg->lport = v_lport; flogi_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->flogi_acc.flogi_payload; if (xchg->byte_orders & UNF_BIT_2) unf_big_end_to_cpu((unsigned char *)flogi_pld, sizeof(struct unf_flogi_payload_s)); if ((lport->en_act_topo != UNF_ACT_TOP_PUBLIC_LOOP) && (UNF_CHECK_NPORT_FPORT_BIT(flogi_pld) == UNF_F_PORT)) /* Get Top Mode (P2P_F) --->>> used for BBSCN */ act_topo = UNF_ACT_TOP_P2P_FABRIC; bbscn_enabled = unf_check_bbscn_is_enabled( (unsigned char) lport->low_level_func.lport_cfg_items.bb_scn, (unsigned char) UNF_GET_BB_SC_N_FROM_PARAMS(&flogi_pld->fabric_parms)); if ((act_topo == UNF_ACT_TOP_P2P_FABRIC) && (bbscn_enabled == UNF_TRUE)) { /* BBSCN Enable or not --->>> used for Context change */ lport->b_bbscn_support = UNF_TRUE; switch_2_thread = UNF_TRUE; } if ((switch_2_thread == UNF_TRUE) && (lport->root_lport == lport)) { /* Wait for LR done sync: for Root Port */ (void)unf_irq_process_switch_2_thread( lport, xchg, unf_rcv_flogi_acc_async_callback); } else { /* Process FLOGI response directly */ unf_flogi_acc_com_process(xchg); } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_ALL, "[info]Port(0x%x) process FLOGI response: switch(%d) to thread done", lport->port_id, switch_2_thread); } struct unf_rport_s *unf_find_rport(struct unf_lport_s *v_lport, unsigned int v_rport_nport_id, unsigned long long v_port_name) { struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = NULL; UNF_CHECK_VALID(0x3658, UNF_TRUE, v_lport, return NULL); if (v_rport_nport_id >= UNF_FC_FID_DOM_MGR) // N_Port_ID <---> SID /* R_Port is Fabric: by N_Port_ID */ rport = unf_get_rport_by_nport_id(lport, v_rport_nport_id); else /* Others: by WWPN & N_Port_ID */ rport = unf_find_valid_rport(lport, v_port_name, v_rport_nport_id); return rport; } static void unf_rcv_plogi_acc(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport, struct unf_lgn_parms_s *v_login_parms) { /* PLOGI ACC: PRLI(non fabric) or RFT_ID(fabric) */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; struct unf_lgn_parms_s *login_parms = v_login_parms; unsigned long long node_name = 0; unsigned long long port_name = 0; unsigned long flag = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3659, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3660, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3661, UNF_TRUE, v_login_parms, return); node_name = (unsigned long long) (((unsigned long long)(login_parms->high_node_name) << 32) | ((unsigned long long)(login_parms->low_node_name))); port_name = (unsigned long long) (((unsigned long long)(login_parms->high_port_name) << 32) | ((unsigned long long)(login_parms->low_port_name))); /* ACC & Case for: R_Port is fabric (RFT_ID) */ if (rport->nport_id >= UNF_FC_FID_DOM_MGR) { /* Check L_Port state */ spin_lock_irqsave(&lport->lport_state_lock, flag); if (lport->en_states != UNF_LPORT_ST_PLOGI_WAIT) { spin_unlock_irqrestore(&lport->lport_state_lock, flag); UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) receive PLOGI ACC with error state(0x%x)", v_lport->port_id, lport->en_states); return; } /* PLOGI_WAIT --> RFT_ID_WAIT */ unf_lport_stat_ma(lport, UNF_EVENT_LPORT_REMOTE_ACC); spin_unlock_irqrestore(&lport->lport_state_lock, flag); /* PLOGI parameters save */ unf_save_plogi_params(lport, rport, login_parms, ELS_ACC); /* Update R_Port WWPN & WWNN */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->node_name = node_name; rport->port_name = port_name; spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Start to Send RFT_ID */ ret = unf_send_rft_id(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send RFT_ID failed", v_lport->port_id); unf_lport_error_recovery(lport); } } else { /* ACC & Case for: R_Port is not fabric */ if ((rport->options == UNF_PORT_MODE_UNKNOWN) && (rport->port_name != INVALID_WWPN)) rport->options = unf_get_port_feature(port_name); /* Set Port Feature with BOTH: cancel */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->node_name = node_name; rport->port_name = port_name; UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO, "[info]LOGIN: Port(0x%x)<---LS_ACC(DID:0x%x SID:0x%x) for PLOGI ACC with RPort state(0x%x) NodeName(0x%llx) E_D_TOV(%d)", lport->port_id, lport->nport_id, rport->nport_id, rport->rp_state, rport->node_name, rport->ed_tov); if ((lport->en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP) && ((rport->rp_state == UNF_RPORT_ST_PRLI_WAIT) || (rport->rp_state == UNF_RPORT_ST_READY))) { /* Do nothing, return directly */ spin_unlock_irqrestore(&rport->rport_state_lock, flag); return; } /* PRLI_WAIT */ unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PRLI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* PLOGI parameters save */ unf_save_plogi_params(lport, rport, login_parms, ELS_ACC); /* * Need Delay to Send PRLI or not * Used for: L_Port with INI mode & R_Port is not Fabric */ unf_check_rport_need_delay_prli(lport, rport, rport->options); /* Do not care: Just used for L_Port only is * TGT mode or R_Port only is INI mode */ unf_schedule_open_work(lport, rport); } } static void unf_plogi_acc_com_process(struct unf_xchg_s *v_xchg) { struct unf_lport_s *lport = NULL; struct unf_rport_s *rport = NULL; struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg; struct unf_plogi_payload_s *plogi_pld = NULL; struct unf_lgn_parms_s *login_parms = NULL; unsigned long flag = 0; unsigned long long port_name = 0; unsigned int rport_nport_id = 0; unsigned int cmnd = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg, return); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->lport, return); UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->rport, return); lport = xchg->lport; rport = xchg->rport; rport_nport_id = rport->nport_id; plogi_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->plogi_acc.payload; login_parms = &plogi_pld->parms; cmnd = (plogi_pld->cmnd); if ((cmnd & UNF_ELS_CMND_HIGH_MASK) == UNF_ELS_CMND_ACC) { /* Case for PLOGI ACC: Go to next stage */ port_name = (unsigned long long) (((unsigned long long)(login_parms->high_port_name) << 32) | ((unsigned long long)(login_parms->low_port_name))); /* Get (new) R_Port: 0xfffffc has same WWN with 0xfffcxx */ rport = unf_find_rport(lport, rport_nport_id, port_name); rport = unf_get_safe_rport(lport, rport, UNF_RPORT_REUSE_ONLY, rport_nport_id); if (unlikely(!rport)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) alloc new RPort with wwpn(0x%llx) failed", lport->port_id, lport->nport_id, port_name); return; } /* PLOGI parameters check */ ret = unf_check_plogi_params(lport, rport, login_parms); if (ret != RETURN_OK) return; /* Update R_Port state */ spin_lock_irqsave(&rport->rport_state_lock, flag); rport->nport_id = rport_nport_id; /* --->>> PLOGI_WAIT */ unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Start to process PLOGI ACC */ unf_rcv_plogi_acc(lport, rport, login_parms); } else { /* Case for PLOGI RJT: L_Port or R_Port recovery */ UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x)<---RPort(0x%p) with LS_RJT(DID:0x%x SID:0x%x) for PLOGI", lport->port_id, rport, lport->nport_id, rport->nport_id); if (rport->nport_id >= UNF_FC_FID_DOM_MGR) /* for Name server */ unf_lport_error_recovery(lport); else unf_rport_error_recovery(rport); } UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]LOGIN: PLOGI response(0x%x). Port(0x%x_0x%x)<---RPort(0x%x_0x%p) wwpn(0x%llx) OX_ID(0x%x)", cmnd, lport->port_id, lport->nport_id, rport->nport_id, rport, port_name, xchg->ox_id); } static int unf_rcv_plogi_acc_async_callback(void *v_argc_in, void *v_argc_out) { struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_argc_in; UNF_CHECK_VALID(0x2267, UNF_TRUE, xchg, return UNF_RETURN_ERROR); unf_plogi_acc_com_process(xchg); unf_xchg_ref_dec(xchg, SFS_RESPONSE); return RETURN_OK; } static void unf_plogi_callback(void *v_lport, void *v_rport, void *v_xchg) { struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg; struct unf_plogi_payload_s *plogi_pld = NULL; struct unf_lgn_parms_s *login_parms = NULL; int bbscn_enabled = UNF_FALSE; int switch_2_thread = UNF_FALSE; UNF_CHECK_VALID(0x3662, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3663, UNF_TRUE, v_rport, return); UNF_CHECK_VALID(0x3664, UNF_TRUE, v_xchg, return); UNF_CHECK_VALID(0x3665, UNF_TRUE, xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr, return); plogi_pld = &xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr->plogi_acc.payload; login_parms = &plogi_pld->parms; xchg->lport = v_lport; if (xchg->byte_orders & UNF_BIT_2) unf_big_end_to_cpu((unsigned char *)plogi_pld, sizeof(struct unf_plogi_payload_s)); bbscn_enabled = unf_check_bbscn_is_enabled( (unsigned char)lport->low_level_func.lport_cfg_items.bb_scn, (unsigned char)UNF_GET_BB_SC_N_FROM_PARAMS(login_parms)); if ((bbscn_enabled == UNF_TRUE) && (lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) { switch_2_thread = UNF_TRUE; lport->b_bbscn_support = UNF_TRUE; } if ((switch_2_thread == UNF_TRUE) && (lport->root_lport == lport)) { /* Wait for LR done sync: just for ROOT Port */ (void)unf_irq_process_switch_2_thread( lport, xchg, unf_rcv_plogi_acc_async_callback); } else { unf_plogi_acc_com_process(xchg); } } static void unf_process_logo_in_pri_loop(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* Send PLOGI or LOGO */ struct unf_rport_s *rport = v_rport; unsigned long flag = 0; UNF_CHECK_VALID(0x3666, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3667, UNF_TRUE, v_rport, return); spin_lock_irqsave(&rport->rport_state_lock, flag); unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); /* PLOGI WAIT */ spin_unlock_irqrestore(&rport->rport_state_lock, flag); /* Private Loop with INI mode, Avoid COM Mode problem */ unf_rport_delay_login(rport); } static void unf_process_logo_in_n2n(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* Send PLOGI or LOGO */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; unsigned long flag = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3668, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3669, UNF_TRUE, v_rport, return); spin_lock_irqsave(&rport->rport_state_lock, flag); unf_rport_state_ma(rport, UNF_EVENT_RPORT_ENTER_PLOGI); // PLOGI WAIT spin_unlock_irqrestore(&rport->rport_state_lock, flag); if (lport->port_name > rport->port_name) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x)'s WWN(0x%llx) is larger than(0x%llx), should be master", lport->port_id, lport->port_name, rport->port_name); ret = unf_send_plogi(lport, rport); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]LOGIN: Port(0x%x) send PLOGI failed, enter recovery", v_lport->port_id); unf_rport_error_recovery(rport); } } else { unf_rport_enter_logo(lport, rport); } } void unf_process_logo_in_fabric(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* Send GFF_ID or LOGO */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; struct unf_rport_s *sns_port = NULL; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3670, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3671, UNF_TRUE, v_rport, return); /* L_Port with INI Mode: Send GFF_ID */ sns_port = unf_get_rport_by_nport_id(lport, UNF_FC_FID_DIR_SERV); if (!sns_port) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) can't find fabric port", lport->port_id); return; } ret = unf_get_and_post_disc_event(v_lport, sns_port, rport->nport_id, UNF_DISC_GET_FEATURE); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) add discovery event(0x%x) failed Rport(0x%x)", lport->port_id, UNF_DISC_GET_FEATURE, rport->nport_id); unf_rcv_gff_id_rsp_unknown(lport, rport->nport_id); } } static void unf_process_rport_after_logo(struct unf_lport_s *v_lport, struct unf_rport_s *v_rport) { /* * 1. LOGO handler * 2. RPLO handler * 3. LOGO_CALL_BACK (send LOGO ACC) handler */ struct unf_lport_s *lport = v_lport; struct unf_rport_s *rport = v_rport; UNF_CHECK_VALID(0x3672, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x3673, UNF_TRUE, v_rport, return); if (rport->nport_id < UNF_FC_FID_DOM_MGR) { /* R_Port is not fabric port (retry LOGIN or LOGO) */ if (lport->en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP) { /* Private Loop: PLOGI or LOGO */ unf_process_logo_in_pri_loop(lport, rport); } else if (lport->en_act_topo == UNF_ACT_TOP_P2P_DIRECT) { /* Point to Point: LOGIN or LOGO */ unf_process_logo_in_n2n(lport, rport); } else { /* Fabric or Public Loop: GFF_ID or LOGO */ unf_process_logo_in_fabric(lport, rport); } } else { /* Rport is fabric port: link down now */ unf_rport_linkdown(lport, rport); } } static unsigned int unf_rcv_bls_req_done(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg) { /* * About I/O resource: * 1. normal: Release I/O resource during RRQ processer * 2. exception: Release I/O resource immediately */ struct unf_xchg_s *xchg = NULL; unsigned short hot_pool_tag = 0; unsigned long flags = 0; unsigned long time_ms = 0; unsigned int ret = RETURN_OK; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x3723, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3724, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); lport = v_lport; /* 1. BLS Request Response: Hot Pool Tag --->>> OX_ID */ hot_pool_tag = (unsigned short)v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]; xchg = (struct unf_xchg_s *)unf_cm_lookup_xchg_by_tag( (void *)lport, hot_pool_tag); if (!xchg) { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) can't find exchange by tag(0x%x) when receiving ABTS response", lport->port_id, hot_pool_tag); /* return directly */ return UNF_RETURN_ERROR; } /* Consistency check */ UNF_CHECK_ALLOCTIME_VALID(v_lport, hot_pool_tag, xchg, v_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME], xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]); /* 2. Increase ref_cnt for exchange protecting */ ret = unf_xchg_ref_inc(xchg, TGT_ABTS_DONE); /* hold */ UNF_CHECK_VALID(0x3725, UNF_TRUE, (ret == RETURN_OK), return UNF_RETURN_ERROR); /* 3. Exchag I/O State Set & Check: reused */ spin_lock_irqsave(&xchg->xchg_state_lock, flags); xchg->io_state |= INI_IO_STATE_DONE; /* I/O Done */ xchg->abts_state |= ABTS_RESPONSE_RECEIVED; if (!(xchg->io_state & INI_IO_STATE_UPABORT)) { /* NOTE: I/O exchange has been released and used again */ spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x_0x%x) SID(0x%x) exch(0x%p) (0x%x:0x%x:0x%x:0x%x) state(0x%x) is abnormal with cnt(0x%x)", lport->port_id, lport->nport_id, xchg->sid, xchg, xchg->hot_pool_tag, xchg->ox_id, xchg->rx_id, xchg->oid, xchg->io_state, atomic_read(&xchg->ref_cnt)); /* return directly */ /* cancel ref & do nothing */ unf_xchg_ref_dec(xchg, TGT_ABTS_DONE); return UNF_RETURN_ERROR; } spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); /* 4. Exchange Timer check, cancel if necessary */ lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)xchg); /* * 5. Exchage I/O Status check: Succ-> Add RRQ Timer * ***** pkg->status --- to --->>> scsi_cmnd->result ***** * * FAILED: ERR_Code or X_ID is err, or BA_RSP type is err */ spin_lock_irqsave(&xchg->xchg_state_lock, flags); if (v_pkg->status == UNF_IO_SUCCESS) { /* Succeed: PKG status -->> EXCH status -->> scsi status */ UNF_SET_SCSI_CMND_RESULT(xchg, UNF_IO_SUCCESS); xchg->io_state |= INI_IO_STATE_WAIT_RRQ; xchg->rx_id = UNF_GET_RXID(v_pkg); spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); /* Add RRQ timer */ time_ms = (unsigned long)(lport->ra_tov); lport->xchg_mgr_temp.pfn_unf_xchg_add_timer( (void *)xchg, time_ms, UNF_TIMER_TYPE_INI_RRQ); } else { /* Failed: PKG status -->> EXCH status -->> scsi status */ UNF_SET_SCSI_CMND_RESULT(xchg, UNF_IO_FAILED); if (MARKER_STS_RECEIVED & xchg->abts_state) { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); /* NOTE: release I/O resource immediately */ unf_cm_free_xchg(lport, xchg); } else { UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN, "[warn]Port(0x%x) exch(0x%p) OX_RX(0x%x:0x%x) IOstate(0x%x) ABTSstate(0x%x) receive response abnormal ref(0x%x)", lport->port_id, xchg, xchg->ox_id, xchg->rx_id, xchg->io_state, xchg->abts_state, atomic_read(&xchg->ref_cnt)); spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } } /* * 6. If abts response arrived before * marker sts received just wake up abts marker sema */ spin_lock_irqsave(&xchg->xchg_state_lock, flags); if (!(xchg->abts_state & MARKER_STS_RECEIVED)) { xchg->ucode_abts_state = v_pkg->status; spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); /* NOTE: wake up semaphore */ up(&xchg->task_sema); } else { spin_unlock_irqrestore(&xchg->xchg_state_lock, flags); } /* 7. dec exch ref_cnt */ unf_xchg_ref_dec(xchg, TGT_ABTS_DONE); return ret; } static unsigned int unf_rcv_abort_ini_io_done(struct unf_lport_s *v_lport, struct unf_frame_pkg_s *v_pkg) { /* INI mode: do not care */ struct unf_xchg_s *io_xchg = NULL; unsigned short io_pool_tag = 0; unsigned int ret = RETURN_OK; UNF_CHECK_VALID(0x3735, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3736, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); io_pool_tag = UNF_GET_IO_XCHG_TAG(v_pkg); io_xchg = (struct unf_xchg_s *)unf_cm_lookup_xchg_by_tag( (void *)v_lport, io_pool_tag); if (io_xchg) { UNF_CHECK_ALLOCTIME_VALID( v_lport, io_pool_tag, io_xchg, v_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME], io_xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]); /* 1. Timer release */ v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer( (void *)io_xchg); UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR, "[info]Port(0x%x) abort INI IO with status(0x%x) exchange(0x%p) tag(0x%x)", v_lport->port_id, v_pkg->status, io_xchg, io_pool_tag); /* 2. Free I/O Exchange context */ unf_cm_free_xchg((void *)v_lport, (void *)io_xchg); } return ret; } unsigned int unf_receive_bls_pkg(void *v_lport, struct unf_frame_pkg_s *v_pkg) { struct unf_lport_s *lport = NULL; unsigned int ret = UNF_RETURN_ERROR; lport = (struct unf_lport_s *)v_lport; UNF_CHECK_VALID(0x3730, UNF_TRUE, lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3731, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR); if (v_pkg->type == UNF_PKG_BLS_REQ_DONE) { /* INI: RCVD BLS Req Done */ ret = unf_rcv_bls_req_done(v_lport, v_pkg); } else if (v_pkg->type == UNF_PKG_INI_IO) { /* INI: Abort Done (do not care) */ ret = unf_rcv_abort_ini_io_done(v_lport, v_pkg); } else { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR, "[err]Port(0x%x) received BLS packet type(%xh) is error", lport->port_id, v_pkg->type); return UNF_RETURN_ERROR; } UNF_REFERNCE_VAR(lport); return ret; } static void unf_fill_rls_acc_pld(struct unf_rls_acc_s *v_rls_acc, struct unf_lport_s *v_lport) { struct unf_rls_acc_payload_s *rls_acc_pld = NULL; rls_acc_pld = &v_rls_acc->rls; rls_acc_pld->cmnd = UNF_ELS_CMND_ACC; rls_acc_pld->link_failure_count = v_lport->err_code_sum.link_fail_count; rls_acc_pld->loss_of_sync_count = v_lport->err_code_sum.loss_of_sync_count; rls_acc_pld->loss_of_signal_count = v_lport->err_code_sum.loss_of_signal_count; rls_acc_pld->primitive_seq_count = 0; rls_acc_pld->invalid_trans_word_count = 0; rls_acc_pld->invalid_crc_count = v_lport->err_code_sum.bad_crc_count; } static unsigned int unf_send_rls_acc(struct unf_lport_s *v_lport, unsigned int v_did, struct unf_xchg_s *v_xchg) { struct unf_rls_acc_s *rls_acc = NULL; union unf_sfs_u *fc_entry = NULL; unsigned int ret = UNF_RETURN_ERROR; unsigned short ox_id = 0; unsigned short rx_id = 0; struct unf_frame_pkg_s pkg; memset(&pkg, 0, sizeof(struct unf_frame_pkg_s)); v_xchg->cmnd_code = UNF_SET_ELS_ACC_TYPE(ELS_RLS); v_xchg->did = v_did; v_xchg->sid = v_lport->nport_id; v_xchg->oid = v_xchg->sid; v_xchg->lport = v_lport; v_xchg->pfn_callback = NULL; v_xchg->pfn_ob_callback = NULL; unf_fill_package(&pkg, v_xchg, v_xchg->rport); fc_entry = v_xchg->fcp_sfs_union.sfs_entry.fc_sfs_entry_ptr; if (!fc_entry) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) entry can't be NULL with tag(0x%x)", v_lport->port_id, v_xchg->hot_pool_tag); return UNF_RETURN_ERROR; } rls_acc = &fc_entry->rls_acc; unf_fill_rls_acc_pld(rls_acc, v_lport); ox_id = v_xchg->ox_id; rx_id = v_xchg->rx_id; ret = unf_els_cmnd_send(v_lport, &pkg, v_xchg); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR, "[info]Port(0x%x) send Rls acc %s to RPort(0x%x) with OX_ID(0x%x) RX_ID(0x%x).", v_lport->port_id, (ret != RETURN_OK) ? "failed" : "succeed", v_did, ox_id, rx_id); UNF_REFERNCE_VAR(ox_id); UNF_REFERNCE_VAR(rx_id); return ret; } static unsigned int unf_rls_handler(struct unf_lport_s *v_lport, unsigned int v_sid, struct unf_xchg_s *v_xchg) { struct unf_rport_s *rport = NULL; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x3483, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); UNF_CHECK_VALID(0x3484, UNF_TRUE, v_xchg, return UNF_RETURN_ERROR); UNF_SERVICE_COLLECT(v_lport->link_service_info, UNF_SERVICE_ITEM_RLS); rport = unf_get_rport_by_nport_id(v_lport, v_sid); if (!rport) { UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn] Port(0x%x_0x%x) can`t find RPort by sid(0x%x) OX_ID(0x%x)", v_lport->port_id, v_lport->nport_id, v_sid, v_xchg->ox_id); unf_cm_free_xchg(v_lport, v_xchg); return ret; } v_xchg->rport = rport; ret = unf_send_rls_acc(v_lport, v_sid, v_xchg); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN, "[warn]Port(0x%x) send RLS ACC failed", v_lport->port_id); unf_cm_free_xchg(v_lport, v_xchg); } return ret; }