2026-01-21 18:59:54 +08:00

9876 lines
310 KiB
C

// 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;
}