3077 lines
89 KiB
C
3077 lines
89 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Huawei Hifc PCI Express Linux driver
|
|
* Copyright(c) 2017 Huawei Technologies Co., Ltd
|
|
*
|
|
*/
|
|
|
|
#include "unf_log.h"
|
|
#include "unf_common.h"
|
|
#include "hifc_module.h"
|
|
#include "hifc_service.h"
|
|
#include "hifc_io.h"
|
|
#include "hifc_chipitf.h"
|
|
|
|
#define HIFC_RQ_ERROR_FRAME 0x100
|
|
#define HIFC_ELS_SRQ_BUF_NUM 0x9
|
|
|
|
/* Parent SCQ Receive the ELS processing function */
|
|
static unsigned int hifc_scq_rcv_els_cmd(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
static unsigned int hifc_scq_rcv_els_rsp(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
static unsigned int hifc_scq_rcv_els_rsp_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
|
|
/* Parent SCQ Receive the GS RSP processing function */
|
|
static unsigned int hifc_scq_rcv_gs_rsp(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
|
|
/* Parent SCQ Receive the BLS RSP processing function */
|
|
static unsigned int hifc_scq_rcv_abts_rsp(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
|
|
/* Parent SCQ Receive the offload completion processing function */
|
|
static unsigned int hifc_scq_rcv_offload_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
|
|
/* Parent SCQ Receive the flush sq completion processing function */
|
|
static unsigned int hifc_scq_rcv_flush_sq_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
|
|
/* Parent SCQ Receive the bufferclear completion processing function */
|
|
static unsigned int hifc_scq_rcv_buf_clear_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
static unsigned int hifc_scq_rcv_sess_rst_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
static unsigned int hifc_scq_rcv_clear_srq_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
static unsigned int hifc_scq_rcv_marker_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
static unsigned int hifc_scq_rcv_abts_marker_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe);
|
|
|
|
typedef unsigned int (*pfn_scqe_handler)(struct hifc_hba_s *,
|
|
union hifcoe_scqe_u *);
|
|
|
|
struct unf_scqe_handler_table_s {
|
|
unsigned int scqe_type; /* ELS type */
|
|
int reclaim_sq_wpg;
|
|
pfn_scqe_handler pfn_scqe_handle_fun;
|
|
};
|
|
|
|
struct unf_scqe_handler_table_s scqe_handler_table[] = {
|
|
{ /* INI rcvd ELS_CMND */
|
|
HIFC_SCQE_ELS_CMND,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_els_cmd
|
|
},
|
|
{ /* INI rcvd ELS_RSP */
|
|
HIFC_SCQE_ELS_RSP,
|
|
UNF_TRUE,
|
|
hifc_scq_rcv_els_rsp
|
|
},
|
|
{ /* INI rcvd GS_RSP */
|
|
HIFC_SCQE_GS_RSP,
|
|
UNF_TRUE,
|
|
hifc_scq_rcv_gs_rsp
|
|
},
|
|
{ /* INI rcvd BLS_RSP */
|
|
HIFC_SCQE_ABTS_RSP,
|
|
UNF_TRUE,
|
|
hifc_scq_rcv_abts_rsp
|
|
},
|
|
{ /* INI rcvd FCP RSP */
|
|
HIFC_SCQE_FCP_IRSP,
|
|
UNF_TRUE,
|
|
hifc_scq_recv_iresp
|
|
},
|
|
{ /* INI rcvd ELS_RSP STS(Done) */
|
|
HIFC_SCQE_ELS_RSP_STS,
|
|
UNF_TRUE,
|
|
hifc_scq_rcv_els_rsp_sts
|
|
},
|
|
{ /* INI rcvd Session enable STS */
|
|
HIFC_SCQE_SESS_EN_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_offload_sts
|
|
},
|
|
{ /* INI rcvd flush (pending) SQ STS */
|
|
HIFC_SCQE_FLUSH_SQ_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_flush_sq_sts
|
|
},
|
|
{ /* INI rcvd Buffer clear STS */
|
|
HIFC_SCQE_BUF_CLEAR_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_buf_clear_sts
|
|
},
|
|
{ /* INI rcvd session reset STS */
|
|
HIFC_SCQE_SESS_RST_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_sess_rst_sts
|
|
},
|
|
{ /* ELS SRQ */
|
|
HIFC_SCQE_CLEAR_SRQ_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_clear_srq_sts
|
|
},
|
|
{ /* INI rcvd TMF RSP */
|
|
HIFC_SCQE_FCP_ITMF_RSP,
|
|
UNF_TRUE,
|
|
hifc_scq_recv_iresp
|
|
},
|
|
{ /* INI rcvd TMF Marker STS */
|
|
HIFC_SCQE_ITMF_MARKER_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_marker_sts
|
|
},
|
|
{ /* INI rcvd ABTS Marker STS */
|
|
HIFC_SCQE_ABTS_MARKER_STS,
|
|
UNF_FALSE,
|
|
hifc_scq_rcv_abts_marker_sts
|
|
}
|
|
};
|
|
|
|
static unsigned int hifc_get_els_rps_pld_len(unsigned short type,
|
|
unsigned short cmnd,
|
|
unsigned int *v_els_acc_pld_len)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
|
|
UNF_CHECK_VALID(0x4917, UNF_TRUE, v_els_acc_pld_len,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
/* RJT */
|
|
if (type == ELS_RJT) {
|
|
*v_els_acc_pld_len = UNF_ELS_ACC_RJT_LEN;
|
|
return RETURN_OK;
|
|
}
|
|
|
|
/* ACC */
|
|
switch (cmnd) {
|
|
/* uses the same PAYLOAD length as PLOGI. */
|
|
case ELS_FLOGI:
|
|
case ELS_PDISC:
|
|
case ELS_PLOGI:
|
|
*v_els_acc_pld_len = UNF_PLOGI_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_PRLI:
|
|
/* The PRLI ACC payload extends 12 bytes */
|
|
*v_els_acc_pld_len = UNF_PRLI_ACC_PAYLOAD_LEN -
|
|
UNF_PRLI_SIRT_EXTRA_SIZE;
|
|
break;
|
|
|
|
case ELS_LOGO:
|
|
*v_els_acc_pld_len = UNF_LOGO_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_PRLO:
|
|
*v_els_acc_pld_len = UNF_PRLO_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_RSCN:
|
|
*v_els_acc_pld_len = UNF_RSCN_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_ADISC:
|
|
*v_els_acc_pld_len = UNF_ADISC_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_RRQ:
|
|
*v_els_acc_pld_len = UNF_RRQ_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_SCR:
|
|
*v_els_acc_pld_len = UNF_SCR_RSP_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case ELS_ECHO:
|
|
*v_els_acc_pld_len = UNF_ECHO_ACC_PAYLOAD_LEN;
|
|
break;
|
|
case ELS_RLS:
|
|
*v_els_acc_pld_len = UNF_RLS_ACC_PAYLOAD_LEN;
|
|
break;
|
|
case ELS_REC:
|
|
*v_els_acc_pld_len = UNF_REC_ACC_PAYLOAD_LEN;
|
|
break;
|
|
default:
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Unknown ELS command(0x%x)", cmnd);
|
|
ret = UNF_RETURN_ERROR;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct hifc_els_cmd_payload_table_s {
|
|
unsigned short cmnd; /* ELS type */
|
|
unsigned int req_pld_len;
|
|
unsigned int rsp_pld_len;
|
|
};
|
|
|
|
struct hifc_els_cmd_payload_table_s els_pld_table_map[] = {
|
|
{ ELS_FDISC,
|
|
UNF_FDISC_PAYLOAD_LEN,
|
|
UNF_FDISC_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_FLOGI,
|
|
UNF_FLOGI_PAYLOAD_LEN,
|
|
UNF_FLOGI_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_PLOGI,
|
|
UNF_PLOGI_PAYLOAD_LEN,
|
|
UNF_PLOGI_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_SCR,
|
|
UNF_SCR_PAYLOAD_LEN,
|
|
UNF_SCR_RSP_PAYLOAD_LEN
|
|
},
|
|
{ ELS_PDISC,
|
|
UNF_PDISC_PAYLOAD_LEN,
|
|
UNF_PDISC_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_LOGO,
|
|
UNF_LOGO_PAYLOAD_LEN,
|
|
UNF_LOGO_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_PRLO,
|
|
UNF_PRLO_PAYLOAD_LEN,
|
|
UNF_PRLO_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_ADISC,
|
|
UNF_ADISC_PAYLOAD_LEN,
|
|
UNF_ADISC_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_RRQ,
|
|
UNF_RRQ_PAYLOAD_LEN,
|
|
UNF_RRQ_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_RSCN,
|
|
0,
|
|
UNF_RSCN_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_ECHO,
|
|
UNF_ECHO_PAYLOAD_LEN,
|
|
UNF_ECHO_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_RLS,
|
|
UNF_RLS_PAYLOAD_LEN,
|
|
UNF_RLS_ACC_PAYLOAD_LEN
|
|
},
|
|
{ ELS_REC,
|
|
UNF_REC_PAYLOAD_LEN,
|
|
UNF_REC_ACC_PAYLOAD_LEN
|
|
}
|
|
};
|
|
|
|
static unsigned int hifc_get_els_req_and_acc_pld_len(unsigned short cmnd,
|
|
unsigned int *req_pld_len,
|
|
unsigned int *rsp_pld_len)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int i;
|
|
|
|
UNF_CHECK_VALID(0x4917, UNF_TRUE, req_pld_len, return UNF_RETURN_ERROR);
|
|
|
|
for (i = 0; i < (sizeof(els_pld_table_map) /
|
|
sizeof(struct hifc_els_cmd_payload_table_s)); i++) {
|
|
if (els_pld_table_map[i].cmnd == cmnd) {
|
|
*req_pld_len = els_pld_table_map[i].req_pld_len;
|
|
*rsp_pld_len = els_pld_table_map[i].rsp_pld_len;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
switch (cmnd) {
|
|
case ELS_PRLI:
|
|
/* If sirt is enabled, The PRLI ACC payload extends
|
|
* 12 bytes
|
|
*/
|
|
*req_pld_len = HIFC_GET_PRLI_PAYLOAD_LEN;
|
|
*rsp_pld_len = HIFC_GET_PRLI_PAYLOAD_LEN;
|
|
break;
|
|
|
|
default:
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT,
|
|
UNF_ERR, "[err]Unknown ELS_CMD(0x%x)", cmnd);
|
|
ret = UNF_RETURN_ERROR;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Function Name : hifc_get_els_frame_len
|
|
* Function Description: Get ELS Frame length
|
|
* Input Parameters : type,
|
|
* : cmnd
|
|
* Output Parameters : v_frame_len
|
|
* Return Type : unsigned int
|
|
*/
|
|
static unsigned int hifc_get_els_frame_len(unsigned short type,
|
|
unsigned short cmnd,
|
|
unsigned int *v_frame_len)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int hdr_len = sizeof(struct unf_fchead_s);
|
|
unsigned int req_len = 0;
|
|
unsigned int rsp_len = 0;
|
|
|
|
UNF_CHECK_VALID(0x4917, UNF_TRUE, v_frame_len, return UNF_RETURN_ERROR);
|
|
|
|
if (type == ELS_RJT)
|
|
rsp_len = UNF_ELS_ACC_RJT_LEN;
|
|
else
|
|
ret = hifc_get_els_req_and_acc_pld_len(cmnd, &req_len,
|
|
&rsp_len);
|
|
|
|
if (ret == RETURN_OK)
|
|
*v_frame_len = hdr_len + ((type == ELS_ACC || type == ELS_RJT) ?
|
|
rsp_len : req_len);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void hifc_build_els_frame_header(unsigned short v_xid_base,
|
|
unsigned short v_cmnd_type,
|
|
unsigned short els_code,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int fctl = 0;
|
|
unsigned int rctl = 0;
|
|
unsigned int type = 0;
|
|
struct unf_fchead_s *cm_fc_hdr_buf = NULL;
|
|
struct unf_fchead_s *pkg_fc_hdr_info = NULL;
|
|
|
|
pkg_fc_hdr_info = &v_pkg->frame_head;
|
|
cm_fc_hdr_buf = HIFC_GET_CMND_FC_HEADER(v_pkg);
|
|
|
|
if (v_cmnd_type == ELS_CMND) {
|
|
rctl = HIFC_FC_RCTL_ELS_REQ;
|
|
fctl = HIFC_FCTL_REQ;
|
|
|
|
/* If the ELS_CMD frame is sent, Adjusting the oxid */
|
|
cm_fc_hdr_buf->oxid_rxid = pkg_fc_hdr_info->oxid_rxid +
|
|
((unsigned int)v_xid_base << 16);
|
|
} else {
|
|
rctl = HIFC_FC_RCTL_ELS_RSP;
|
|
fctl = HIFC_FCTL_RESP;
|
|
|
|
/* If the ELS_RSP frame is sent, Adjusting the rxid */
|
|
cm_fc_hdr_buf->oxid_rxid = pkg_fc_hdr_info->oxid_rxid +
|
|
v_xid_base;
|
|
}
|
|
|
|
type = HIFC_FC_TYPE_ELS;
|
|
|
|
/* Get SID, DID, OXID, RXID from CM layer */
|
|
cm_fc_hdr_buf->rctl_did = pkg_fc_hdr_info->rctl_did;
|
|
cm_fc_hdr_buf->csctl_sid = pkg_fc_hdr_info->csctl_sid;
|
|
cm_fc_hdr_buf->parameter = 0;
|
|
|
|
/* R_CTL, CS_CTL, TYPE, F_CTL, SEQ_ID, DF_CTL, SEQ_CNT, LL filled */
|
|
UNF_SET_FC_HEADER_RCTL(cm_fc_hdr_buf, rctl);
|
|
UNF_SET_FC_HEADER_CS_CTL(cm_fc_hdr_buf, 0);
|
|
UNF_SET_FC_HEADER_TYPE(cm_fc_hdr_buf, type);
|
|
UNF_SET_FC_HEADER_FCTL(cm_fc_hdr_buf, fctl);
|
|
UNF_SET_FC_HEADER_SEQ_CNT(cm_fc_hdr_buf, 0);
|
|
UNF_SET_FC_HEADER_DF_CTL(cm_fc_hdr_buf, 0);
|
|
UNF_SET_FC_HEADER_SEQ_ID(cm_fc_hdr_buf, 0);
|
|
|
|
UNF_PRINT_SFS(UNF_INFO, 0, cm_fc_hdr_buf, sizeof(struct unf_fchead_s));
|
|
}
|
|
|
|
void hifc_save_login_para_in_sq_info(
|
|
struct hifc_hba_s *v_hba,
|
|
struct unf_port_login_parms_s *v_login_co_parms)
|
|
{
|
|
struct hifc_hba_s *hba = NULL;
|
|
unsigned int rport_index = v_login_co_parms->rport_index;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
if (rport_index >= UNF_HIFC_MAXRPORT_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) save login parms,but uplevel alloc invalid rport index: 0x%x",
|
|
hba->port_cfg.port_id, rport_index);
|
|
|
|
return;
|
|
}
|
|
|
|
sq_info =
|
|
&hba->parent_queue_mgr->parent_queues[rport_index].parent_sq_info;
|
|
|
|
sq_info->plogi_coparams.seq_cnt = v_login_co_parms->seq_cnt;
|
|
sq_info->plogi_coparams.ed_tov = v_login_co_parms->ed_tov;
|
|
sq_info->plogi_coparams.tx_mfs = (v_login_co_parms->tx_mfs <
|
|
HIFC_DEFAULT_TX_MAX_FREAM_SIZE) ? HIFC_DEFAULT_TX_MAX_FREAM_SIZE :
|
|
v_login_co_parms->tx_mfs;
|
|
|
|
sq_info->plogi_coparams.ed_tov_timer_val =
|
|
v_login_co_parms->ed_tov_timer_val;
|
|
}
|
|
|
|
static void hifc_save_default_plogi_param_in_ctx(
|
|
struct hifc_hba_s *v_hba,
|
|
struct hifcoe_parent_context_s *v_ctx,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int tx_mfs = HIFC_DEFAULT_TX_MAX_FREAM_SIZE;
|
|
unsigned int did = 0;
|
|
|
|
did = UNF_GET_DID(v_pkg);
|
|
|
|
if (did == UNF_FC_FID_DIR_SERV)
|
|
tx_mfs = 2048;
|
|
|
|
v_ctx->sw_section.tx_mfs = cpu_to_be16((unsigned short)(tx_mfs));
|
|
}
|
|
|
|
static void hifc_save_plogi_acc_param_in_ctx(
|
|
struct hifc_hba_s *v_hba,
|
|
struct hifcoe_parent_context_s *v_ctx,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
#define HIFC_UCODE_MAX_PKT_SIZE_PER_DISPATCH ((8 * 1024))
|
|
|
|
struct unf_lgn_port_coparms_s *port_co_param = NULL;
|
|
struct unf_plogi_payload_s *plogi_acc_pld = NULL;
|
|
|
|
plogi_acc_pld = UNF_GET_PLOGI_ACC_PAYLOAD(v_pkg);
|
|
port_co_param = &plogi_acc_pld->parms.co_parms;
|
|
|
|
/* e_d_tov and seq_cnt */
|
|
hifc_big_to_cpu32(&v_ctx->sw_section.sw_ctxt_config.pctxt_val1,
|
|
sizeof(unsigned int));
|
|
|
|
v_ctx->sw_section.sw_ctxt_config.dw.e_d_tov =
|
|
port_co_param->e_d_tov_resolution;
|
|
|
|
v_ctx->sw_section.sw_ctxt_config.dw.seq_cnt =
|
|
port_co_param->seq_cnt;
|
|
|
|
hifc_cpu_to_big32(&v_ctx->sw_section.sw_ctxt_config.pctxt_val1,
|
|
sizeof(unsigned int));
|
|
|
|
v_ctx->sw_section.tx_mfs =
|
|
(unsigned short)(v_pkg->private[PKG_PRIVATE_RPORT_RX_SIZE]) <
|
|
HIFC_DEFAULT_TX_MAX_FREAM_SIZE ?
|
|
cpu_to_be16((unsigned short)HIFC_DEFAULT_TX_MAX_FREAM_SIZE) :
|
|
cpu_to_be16 ((unsigned short)
|
|
(v_pkg->private[PKG_PRIVATE_RPORT_RX_SIZE]));
|
|
|
|
v_ctx->sw_section.e_d_tov_timer_val =
|
|
cpu_to_be32(port_co_param->e_d_tov);
|
|
|
|
v_ctx->sw_section.mfs_unaligned_bytes =
|
|
cpu_to_be16(HIFC_UCODE_MAX_PKT_SIZE_PER_DISPATCH %
|
|
port_co_param->bb_receive_data_field_size);
|
|
}
|
|
|
|
static void hifc_recover_offloading_state(
|
|
struct hifc_parent_queue_info_s *v_prntq_info,
|
|
enum hifc_parent_queue_state_e offload_state)
|
|
{
|
|
unsigned long flag = 0;
|
|
|
|
spin_lock_irqsave(&v_prntq_info->parent_queue_state_lock, flag);
|
|
|
|
if (v_prntq_info->offload_state == HIFC_QUEUE_STATE_OFFLOADING)
|
|
v_prntq_info->offload_state = offload_state;
|
|
|
|
spin_unlock_irqrestore(&v_prntq_info->parent_queue_state_lock, flag);
|
|
}
|
|
|
|
static void hifc_save_magic_num_in_ctx(struct hifcoe_parent_context_s *v_ctx,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
/* The CID itself is initialized by the microcode.
|
|
* The driver multiplexes the CID as magicnum and then updates
|
|
* the CID by the microcode.
|
|
*/
|
|
v_ctx->sw_section.cid = cpu_to_be32(UNF_GETXCHGALLOCTIME(v_pkg));
|
|
}
|
|
|
|
static void hifc_save_magic_num_in_nurmal_root_ts(
|
|
struct hifc_root_sqe_s *v_rt_sqe,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
v_rt_sqe->task_section.fc_dw1.magic_num = UNF_GETXCHGALLOCTIME(v_pkg);
|
|
}
|
|
|
|
static int hifc_check_need_delay_offload(
|
|
void *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int rport_idx,
|
|
struct hifc_parent_queue_info_s *v_cur_parent_queue,
|
|
struct hifc_parent_queue_info_s **v_offload_parnt_queue)
|
|
{
|
|
unsigned long flag = 0;
|
|
struct hifc_parent_queue_info_s *offload_parnt_queue = NULL;
|
|
|
|
spin_lock_irqsave(&v_cur_parent_queue->parent_queue_state_lock, flag);
|
|
|
|
if (v_cur_parent_queue->offload_state == HIFC_QUEUE_STATE_OFFLOADING) {
|
|
spin_unlock_irqrestore(
|
|
&v_cur_parent_queue->parent_queue_state_lock, flag);
|
|
|
|
offload_parnt_queue = hifc_find_offload_parent_queue(
|
|
v_hba,
|
|
v_pkg->frame_head.csctl_sid & UNF_NPORTID_MASK,
|
|
v_pkg->frame_head.rctl_did & UNF_NPORTID_MASK,
|
|
rport_idx);
|
|
if (offload_parnt_queue) {
|
|
*v_offload_parnt_queue = offload_parnt_queue;
|
|
|
|
return UNF_TRUE;
|
|
}
|
|
} else {
|
|
spin_unlock_irqrestore(
|
|
&v_cur_parent_queue->parent_queue_state_lock, flag);
|
|
}
|
|
|
|
return UNF_FALSE;
|
|
}
|
|
|
|
static unsigned int hifc_build_service_wqe_root_offload(
|
|
void *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
struct hifc_parent_queue_info_s *v_parnt_qinfo,
|
|
struct hifc_root_sqe_s *v_sqe)
|
|
{
|
|
unsigned int cqm_xid = 0;
|
|
unsigned short els_cmnd_type = UNF_ZERO;
|
|
struct hifc_parent_ctx_s *parnt_ctx = NULL;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
struct hifcoe_parent_context_s *v_ctx = NULL;
|
|
|
|
els_cmnd_type = HIFC_GET_ELS_RSP_TYPE(v_pkg->cmnd);
|
|
cqm_xid = hifc_get_parent_ctx_xid_by_pkg(v_hba, v_pkg);
|
|
|
|
/* An offload request is initiated only when the parent queue is in the
|
|
* initialized state
|
|
*/
|
|
if (v_parnt_qinfo->offload_state == HIFC_QUEUE_STATE_INITIALIZED) {
|
|
/* Obtain Parent Context and set WQE to off_load, GPA_Addr */
|
|
parnt_ctx = hifc_get_parnt_ctx_virt_addr_by_pkg(v_hba, v_pkg);
|
|
|
|
sq_info = hifc_find_parent_sq_by_pkg(v_hba, v_pkg);
|
|
if (unlikely((!parnt_ctx) || (!sq_info) ||
|
|
(cqm_xid == INVALID_VALUE32))) {
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* Fill in ROOT SQE with offload request */
|
|
hifc_build_els_wqe_root_offload(
|
|
v_sqe,
|
|
parnt_ctx->cqm_parent_ctx_obj->paddr,
|
|
cqm_xid);
|
|
|
|
/* If the value is PlogiAcc, parse the FlogiAcc negotiation
|
|
* parameter and fill in Context
|
|
*/
|
|
v_ctx = (struct hifcoe_parent_context_s *)
|
|
parnt_ctx->virt_parent_ctx;
|
|
|
|
if (els_cmnd_type == ELS_ACC)
|
|
hifc_save_plogi_acc_param_in_ctx(
|
|
(struct hifc_hba_s *)v_hba, v_ctx, v_pkg);
|
|
else
|
|
hifc_save_default_plogi_param_in_ctx(
|
|
(struct hifc_hba_s *)v_hba, v_ctx, v_pkg);
|
|
|
|
/* The SID DID parameter is updated to Parent SQ Qinfo */
|
|
sq_info->local_port_id = UNF_GET_SID(v_pkg);
|
|
sq_info->remote_port_id = UNF_GET_DID(v_pkg);
|
|
|
|
/* Transfers the key value to the ucode for offload */
|
|
hifc_big_to_cpu32(v_ctx->key, sizeof(v_ctx->key));
|
|
memcpy(v_ctx->key, &sq_info->local_port_id,
|
|
sizeof(sq_info->local_port_id));
|
|
memcpy((unsigned char *)v_ctx->key +
|
|
sizeof(sq_info->local_port_id),
|
|
&sq_info->remote_port_id,
|
|
sizeof(sq_info->remote_port_id));
|
|
|
|
hifc_cpu_to_big32(v_ctx->key, sizeof(v_ctx->key));
|
|
|
|
/* Update magic num to parent_ctx */
|
|
hifc_save_magic_num_in_ctx(v_ctx, v_pkg);
|
|
|
|
hifc_build_service_wqe_ctx_sge(
|
|
v_sqe, parnt_ctx->parent_ctx,
|
|
sizeof(struct hifcoe_parent_context_s));
|
|
|
|
v_parnt_qinfo->offload_state = HIFC_QUEUE_STATE_OFFLOADING;
|
|
} else {
|
|
/* If the connection is being uninstalled and the plogi is
|
|
* delivered through the root channel, the plogi must be carried
|
|
* to the ucode.
|
|
*/
|
|
v_sqe->task_section.fc_dw4.parent_xid = cqm_xid;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_IO_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) send PLOGI with no offload while parent queue is not initialized status",
|
|
((struct hifc_hba_s *)v_hba)->port_cfg.port_id);
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_send_els_via_root(void *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned short els_cmd_code = UNF_ZERO;
|
|
unsigned short els_cmnd_type = UNF_ZERO;
|
|
unsigned int frame_len = 0;
|
|
unsigned int exch_id = 0;
|
|
unsigned int scq_num = 0;
|
|
unsigned int rport_idx = 0;
|
|
int sqe_delay = UNF_FALSE;
|
|
void *frame_addr = NULL;
|
|
struct hifc_hba_s *hba = NULL;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
struct hifc_parent_queue_info_s *offload_parnt_queue = NULL;
|
|
struct hifc_root_sqe_s *sqe = NULL;
|
|
struct hifc_root_sqe_s local_rt_sqe;
|
|
unsigned long flag = 0;
|
|
enum hifc_parent_queue_state_e last_offload_state =
|
|
HIFC_QUEUE_STATE_INITIALIZED;
|
|
struct hifc_destroy_ctrl_info_s destroy_sqe_info = { 0 };
|
|
unsigned long long frame_phy_addr;
|
|
|
|
/* The ROOT SQE is assembled in local variables and then copied to the
|
|
* queue memory
|
|
*/
|
|
sqe = &local_rt_sqe;
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
memset(sqe, 0, sizeof(local_rt_sqe));
|
|
|
|
/* Determine the ELS type in the pstPkg */
|
|
els_cmnd_type = HIFC_GET_ELS_RSP_TYPE(v_pkg->cmnd);
|
|
if (HIFC_PKG_IS_ELS_RSP(els_cmnd_type)) {
|
|
els_cmd_code = HIFC_GET_ELS_RSP_CODE(v_pkg->cmnd);
|
|
exch_id = UNF_GET_RXID(v_pkg);
|
|
sqe->task_section.fc_dw0.task_type = HIFC_SQE_ELS_RSP;
|
|
} else {
|
|
els_cmd_code = els_cmnd_type;
|
|
els_cmnd_type = ELS_CMND;
|
|
exch_id = UNF_GET_OXID(v_pkg);
|
|
sqe->task_section.fc_dw0.task_type = HIFC_SQE_ELS_CMND;
|
|
}
|
|
if ((els_cmd_code == ELS_ECHO) && (els_cmnd_type != ELS_RJT)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[info]Port(0x%x) RPort(0x%x) send ELS ECHO can't send via root Type(0x%x)",
|
|
hba->port_cfg.port_id, rport_idx, els_cmnd_type);
|
|
|
|
return UNF_RETURN_NOT_SUPPORT;
|
|
}
|
|
exch_id += hba->exit_base;
|
|
|
|
ret = hifc_get_els_frame_len(els_cmnd_type, els_cmd_code, &frame_len);
|
|
if (ret != RETURN_OK) {
|
|
dump_stack();
|
|
return ret;
|
|
}
|
|
|
|
/* Obtains the frame start address */
|
|
frame_addr = HIFC_GET_CMND_HEADER_ADDR(v_pkg);
|
|
frame_phy_addr = v_pkg->unf_cmnd_pload_bl.buf_dma_addr;
|
|
|
|
/* Assemble the frame header and adjust the Paylaod based on the ELS */
|
|
hifc_build_els_frame_header(hba->exit_base, els_cmnd_type,
|
|
els_cmd_code, v_pkg);
|
|
|
|
/* Assembling the Control Section */
|
|
hifc_build_service_wqe_ctrl_section(
|
|
&sqe->ctrl_section,
|
|
HIFC_BYTES_TO_QW_NUM(
|
|
sizeof(struct hifc_root_sqe_task_section_s)),
|
|
HIFC_BYTES_TO_QW_NUM(sizeof(struct hifc_root_sge_s)));
|
|
|
|
/* Fill in Normal Root SQE TS */
|
|
rport_idx = v_pkg->private[PKG_PRIVATE_XCHG_RPORT_INDEX];
|
|
scq_num = hifc_get_rport_maped_cmd_scqn(v_hba, rport_idx);
|
|
hifc_build_service_wqe_root_ts(v_hba, sqe, exch_id, rport_idx, scq_num);
|
|
|
|
/* Upsate magic number into sqe */
|
|
hifc_save_magic_num_in_nurmal_root_ts(sqe, v_pkg);
|
|
|
|
/* Fill in the special part of Normal Root SQE TS and initiate implicit
|
|
* uninstallation
|
|
*/
|
|
if ((els_cmd_code == ELS_PLOGI) && (els_cmnd_type != ELS_RJT)) {
|
|
prnt_qinfo = hifc_find_parent_queue_info_by_pkg(hba, v_pkg);
|
|
if (!prnt_qinfo) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[warn]Port(0x%x) RPort(0x%x) send ELS Type(0x%x) find parent queue fail",
|
|
hba->port_cfg.port_id, rport_idx,
|
|
els_cmnd_type);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
spin_lock_irqsave(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
last_offload_state = prnt_qinfo->offload_state;
|
|
|
|
/* Fill in the special part of Normal Root SQE TS */
|
|
ret = hifc_build_service_wqe_root_offload((void *)hba,
|
|
v_pkg, prnt_qinfo,
|
|
sqe);
|
|
if (ret != RETURN_OK) {
|
|
spin_unlock_irqrestore(
|
|
&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
return ret;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock,
|
|
flag);
|
|
|
|
/* Before the offload, check whether there is a risk of
|
|
* repeated offload
|
|
*/
|
|
sqe_delay = hifc_check_need_delay_offload((void *)hba,
|
|
v_pkg, rport_idx,
|
|
prnt_qinfo,
|
|
&offload_parnt_queue);
|
|
}
|
|
|
|
/* Fill in Normal Root SQE SGE */
|
|
hifc_build_service_wqe_root_sge(sqe, frame_addr, frame_phy_addr,
|
|
frame_len, v_hba);
|
|
|
|
if (sqe_delay == UNF_TRUE) {
|
|
ret = hifc_push_delay_sqe((void *)hba, offload_parnt_queue,
|
|
sqe, v_pkg);
|
|
if (ret == RETURN_OK) {
|
|
hifc_recover_offloading_state(prnt_qinfo,
|
|
last_offload_state);
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Port(0x%x) RPort(0x%x) send ELS Type(0x%x) Code(0x%x) ExchId(0x%x)",
|
|
hba->port_cfg.port_id, rport_idx, els_cmnd_type,
|
|
els_cmd_code, exch_id);
|
|
|
|
ret = hifc_root_sq_enqueue(hba, sqe);
|
|
if ((ret != RETURN_OK) && (prnt_qinfo)) {
|
|
hifc_recover_offloading_state(prnt_qinfo, last_offload_state);
|
|
|
|
spin_lock_irqsave(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
if (prnt_qinfo->parent_sq_info.destroy_sqe.valid ==
|
|
UNF_TRUE) {
|
|
memcpy(&destroy_sqe_info,
|
|
&prnt_qinfo->parent_sq_info.destroy_sqe,
|
|
sizeof(struct hifc_destroy_ctrl_info_s));
|
|
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.valid =
|
|
UNF_FALSE;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock,
|
|
flag);
|
|
|
|
hifc_pop_destroy_parent_queue_sqe((void *)v_hba,
|
|
&destroy_sqe_info);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[warn]Port(0x%x) RPort(0x%x) send ELS Type(0x%x) Code(0x%x) ExchId(0x%x) fail, recover offloadstatus(%u)",
|
|
hba->port_cfg.port_id,
|
|
rport_idx,
|
|
els_cmnd_type,
|
|
els_cmd_code,
|
|
exch_id,
|
|
prnt_qinfo->offload_state);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void *hifc_get_els_frame_addr(struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned short els_cmd_code,
|
|
unsigned short els_cmnd_type,
|
|
unsigned long long *v_phyaddr)
|
|
{
|
|
void *frame_pld_addr;
|
|
dma_addr_t els_frame_addr = 0;
|
|
|
|
if (els_cmd_code == ELS_ECHO) {
|
|
frame_pld_addr = (void *)UNF_GET_ECHO_PAYLOAD(v_pkg);
|
|
els_frame_addr = UNF_GET_ECHO_PAYLOAD_PHYADDR(v_pkg);
|
|
} else if (els_cmd_code == ELS_RSCN) {
|
|
if (els_cmnd_type == ELS_CMND) {
|
|
/* Not Support */
|
|
frame_pld_addr = NULL;
|
|
els_frame_addr = 0;
|
|
} else {
|
|
frame_pld_addr =
|
|
(void *)UNF_GET_RSCN_ACC_PAYLOAD(v_pkg);
|
|
els_frame_addr = v_pkg->unf_cmnd_pload_bl.buf_dma_addr +
|
|
sizeof(struct unf_fchead_s);
|
|
}
|
|
} else {
|
|
frame_pld_addr = (void *)HIFC_GET_CMND_PAYLOAD_ADDR(v_pkg);
|
|
els_frame_addr = v_pkg->unf_cmnd_pload_bl.buf_dma_addr +
|
|
sizeof(struct unf_fchead_s);
|
|
}
|
|
*v_phyaddr = els_frame_addr;
|
|
return frame_pld_addr;
|
|
}
|
|
|
|
static unsigned int hifc_send_els_via_parent(
|
|
void *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
struct hifc_parent_queue_info_s *v_prntq_info)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned short els_cmd_code = UNF_ZERO;
|
|
unsigned short els_cmnd_type = UNF_ZERO;
|
|
unsigned short remote_xid = 0;
|
|
unsigned short local_xid = 0;
|
|
struct hifc_hba_s *hba;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
struct hifcoe_sqe_s sqe;
|
|
void *frame_pld_addr;
|
|
unsigned int frame_pld_len = 0;
|
|
unsigned int acc_pld_len = 0;
|
|
unsigned long long fram_phy_addr = 0;
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
memset(&sqe, 0, sizeof(struct hifcoe_sqe_s));
|
|
|
|
sq_info = &v_prntq_info->parent_sq_info;
|
|
|
|
/* Determine the ELS type in pstPkg */
|
|
els_cmnd_type = HIFC_GET_ELS_CMND_CODE(v_pkg->cmnd);
|
|
if (HIFC_PKG_IS_ELS_RSP(els_cmnd_type)) {
|
|
els_cmd_code = HIFC_GET_ELS_RSP_CODE(v_pkg->cmnd);
|
|
remote_xid = UNF_GET_OXID(v_pkg);
|
|
local_xid = UNF_GET_RXID(v_pkg) + hba->exit_base;
|
|
} else {
|
|
els_cmd_code = els_cmnd_type;
|
|
els_cmnd_type = ELS_CMND;
|
|
local_xid = UNF_GET_OXID(v_pkg) + hba->exit_base;
|
|
remote_xid = UNF_GET_RXID(v_pkg);
|
|
}
|
|
|
|
frame_pld_addr = hifc_get_els_frame_addr(v_hba, v_pkg, els_cmd_code,
|
|
els_cmnd_type, &fram_phy_addr);
|
|
|
|
if (HIFC_PKG_IS_ELS_RSP(els_cmnd_type)) {
|
|
ret = hifc_get_els_rps_pld_len(els_cmnd_type, els_cmd_code,
|
|
&frame_pld_len);
|
|
if (ret != RETURN_OK)
|
|
return ret;
|
|
|
|
hifc_build_els_wqe_ts_rsp(
|
|
&sqe, sq_info, frame_pld_addr,
|
|
els_cmnd_type, els_cmd_code,
|
|
v_prntq_info->parent_sts_scq_info.cqm_queue_id);
|
|
} else {
|
|
/* Fill in HIFCOE_TASK_T_ELS */
|
|
ret = hifc_get_els_req_and_acc_pld_len(els_cmd_code,
|
|
&frame_pld_len,
|
|
&acc_pld_len);
|
|
if (ret != RETURN_OK)
|
|
return ret;
|
|
|
|
hifc_build_els_wqe_ts_req(
|
|
&sqe, sq_info, els_cmd_code,
|
|
v_prntq_info->parent_sts_scq_info.cqm_queue_id,
|
|
frame_pld_addr);
|
|
}
|
|
|
|
/* Assemble the magicnum field of the els */
|
|
hifc_build_els_wqe_ts_magic_num(&sqe, els_cmnd_type,
|
|
UNF_GETXCHGALLOCTIME(v_pkg));
|
|
|
|
/* Assemble the SQE Control Section part */
|
|
hifc_build_service_wqe_ctrl_section(
|
|
&sqe.ctrl_sl,
|
|
HIFC_BYTES_TO_QW_NUM(HIFC_SQE_TS_SIZE),
|
|
HIFC_BYTES_TO_QW_NUM(sizeof(struct hifcoe_variable_sge_s)));
|
|
|
|
/* Assemble the SQE Task Section Els Common part */
|
|
hifc_build_service_wqe_ts_common(&sqe.ts_sl, sq_info->rport_index,
|
|
local_xid, remote_xid,
|
|
HIFC_LSW(frame_pld_len));
|
|
|
|
/* Build SGE */
|
|
hifc_build_els_gs_wqe_sge(&sqe, frame_pld_addr, fram_phy_addr,
|
|
frame_pld_len, sq_info->context_id, v_hba);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) RPort(0x%x) send ELS Type(0x%x) Code(0x%x) ExchId(0x%x)",
|
|
hba->port_cfg.port_id, sq_info->rport_index, els_cmnd_type,
|
|
els_cmd_code, local_xid);
|
|
|
|
ret = hifc_parent_sq_enqueue(sq_info, &sqe);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_send_els_cmnd(void *v_hba, struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned long flag = 0;
|
|
struct hifc_hba_s *hba = NULL;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
unsigned short els_cmd_code = UNF_ZERO;
|
|
unsigned short els_rsp_code = UNF_ZERO;
|
|
union unf_sfs_u *fc_entry = NULL;
|
|
struct unf_rrq_s *rrq_pld = NULL;
|
|
unsigned short ox_id = 0;
|
|
unsigned short rx_id = 0;
|
|
|
|
/* Check Parameters */
|
|
UNF_CHECK_VALID(0x5014, UNF_TRUE, v_hba, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x5015, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x5016, UNF_TRUE, UNF_GET_SFS_ENTRY(v_pkg),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x5017, UNF_TRUE, HIFC_GET_CMND_PAYLOAD_ADDR(v_pkg),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
HIFC_CHECK_PKG_ALLOCTIME(v_pkg);
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
els_cmd_code = HIFC_GET_ELS_CMND_CODE(v_pkg->cmnd);
|
|
els_rsp_code = HIFC_GET_ELS_RSP_CODE(v_pkg->cmnd);
|
|
|
|
/* If RRQ Req, Special processing */
|
|
if (els_cmd_code == ELS_RRQ) {
|
|
fc_entry = UNF_GET_SFS_ENTRY(v_pkg);
|
|
rrq_pld = &fc_entry->rrq;
|
|
ox_id = (unsigned short)(rrq_pld->oxid_rxid >> 16);
|
|
rx_id = (unsigned short)(rrq_pld->oxid_rxid & 0xFFFF);
|
|
ox_id += hba->exit_base;
|
|
rrq_pld->oxid_rxid = ox_id << 16 | rx_id;
|
|
}
|
|
|
|
prnt_qinfo = hifc_find_parent_queue_info_by_pkg(hba, v_pkg);
|
|
if (!prnt_qinfo) {
|
|
HIFC_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
|
|
"Port(0x%x) send ELS SID(0x%x) DID(0x%x) get a null parent queue info, send via root",
|
|
hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
/* If the Rport cannot be found, Send Pkg by Root SQ */
|
|
ret = hifc_send_els_via_root(v_hba, v_pkg);
|
|
return ret;
|
|
}
|
|
|
|
spin_lock_irqsave(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
/* After offload, Send Pkg by Parent SQ */
|
|
if (HIFC_RPORT_OFFLOADED(prnt_qinfo)) {
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock,
|
|
flag);
|
|
|
|
ret = hifc_send_els_via_parent(v_hba, v_pkg, prnt_qinfo);
|
|
} else {
|
|
/* Before offload, Send Pkg by Root SQ */
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock,
|
|
flag);
|
|
|
|
ret = hifc_send_els_via_root(v_hba, v_pkg);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rq_rcv_els_rsp_sts(
|
|
struct hifc_hba_s *v_hba,
|
|
struct hifc_root_rq_complet_info_s *v_cs_info)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int rx_id = (~0);
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
|
|
rx_id = (unsigned int)v_cs_info->exch_id - v_hba->exit_base;
|
|
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] = v_cs_info->magic_num;
|
|
|
|
ret = hifc_rcv_els_rsp_sts(v_hba, &pkg, rx_id);
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_ELS_RSP_STS);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_recv_els_rsp_payload(struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id,
|
|
unsigned char *v_els_pld_buf,
|
|
unsigned int pld_len)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
v_pkg->type = UNF_PKG_ELS_REQ_DONE;
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
|
|
/* Payload Buffer in ROOT SQ Buffer */
|
|
v_pkg->unf_cmnd_pload_bl.buffer_ptr = v_els_pld_buf;
|
|
v_pkg->unf_cmnd_pload_bl.length = pld_len;
|
|
v_pkg->byte_orders |= HIFC_BIT_2;
|
|
|
|
/* Mark as a non-last block */
|
|
v_pkg->last_pkg_flag = UNF_PKG_NOT_LAST_RESPONSE;
|
|
|
|
UNF_LOWLEVEL_RECEIVE_ELS_PKG(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_rq_rcv_els_frame(struct hifc_hba_s *v_hba,
|
|
unsigned char *v_frame,
|
|
unsigned int frame_len,
|
|
unsigned short pkg_flag,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int ox_id = INVALID_VALUE32;
|
|
unsigned int pld_len = 0;
|
|
unsigned char *plg_buf = NULL;
|
|
unsigned long flags = 0;
|
|
|
|
plg_buf = v_frame;
|
|
pld_len = frame_len;
|
|
|
|
v_pkg->status = UNF_IO_SUCCESS;
|
|
|
|
if (UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head) ==
|
|
HIFC_FC_RCTL_ELS_RSP) {
|
|
ox_id = v_pkg->frame_head.oxid_rxid >> 16;
|
|
|
|
if (!(HIFC_XID_IS_VALID(ox_id, (unsigned int)v_hba->exit_base,
|
|
(unsigned int)v_hba->exit_count))) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT,
|
|
UNF_WARN, "[err]Port(0x%x) ExchId(0x%x) isn't in 0x%x~0x%x",
|
|
v_hba->port_cfg.port_id, ox_id,
|
|
v_hba->exit_base,
|
|
v_hba->exit_base + v_hba->exit_count - 1);
|
|
|
|
goto rq_recv_error_els_frame;
|
|
}
|
|
|
|
ox_id -= v_hba->exit_base;
|
|
|
|
ret = hifc_recv_els_rsp_payload(v_hba, v_pkg, ox_id, plg_buf,
|
|
pld_len);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[err]Port(0x%x) receive ESL RSP payload error, OXID(0x%x) RXID(0x%x) PldLen(0x%x)",
|
|
v_hba->port_cfg.port_id, UNF_GET_OXID(v_pkg),
|
|
UNF_GET_RXID(v_pkg), pld_len);
|
|
|
|
HIFC_ERR_IO_STAT(v_hba, HIFCOE_TASK_T_RCV_ELS_RSP);
|
|
}
|
|
|
|
if (HIFC_CHECK_IF_LAST_PKG(pkg_flag)) {
|
|
ret = hifc_rcv_els_rsp(v_hba, v_pkg, ox_id);
|
|
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_RCV_ELS_RSP);
|
|
}
|
|
} else if (UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head) ==
|
|
HIFC_FC_RCTL_ELS_REQ) {
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_RCV_ELS_CMD);
|
|
|
|
if (HIFC_CHECK_IF_FIRST_PKG(pkg_flag))
|
|
v_pkg->xchg_contex = NULL;
|
|
|
|
v_pkg->last_pkg_flag = (HIFC_CHECK_IF_LAST_PKG(pkg_flag)) ?
|
|
UNF_PKG_LAST_REQUEST : UNF_PKG_NOT_LAST_REQUEST;
|
|
|
|
ret = hifc_rcv_els_cmnd(v_hba, v_pkg, plg_buf, pld_len,
|
|
HIFC_CHECK_IF_FIRST_PKG(pkg_flag));
|
|
|
|
spin_lock_irqsave(&v_hba->delay_info.srq_lock, flags);
|
|
if (v_hba->delay_info.srq_delay_flag) {
|
|
v_hba->delay_info.srq_delay_flag = 0;
|
|
|
|
if (!cancel_delayed_work(&v_hba->delay_info.del_work)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN,
|
|
UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) rcvd plogi from srq process delay timer maybe timeout",
|
|
v_hba->port_cfg.port_id);
|
|
}
|
|
spin_unlock_irqrestore(&v_hba->delay_info.srq_lock,
|
|
flags);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT,
|
|
UNF_ERR,
|
|
"[info]Port(0x%x) received els from root rq and send delay plogi to CM",
|
|
v_hba->port_cfg.port_id);
|
|
|
|
hifc_rcv_els_cmnd(
|
|
v_hba, &v_hba->delay_info.pkg,
|
|
v_hba->delay_info.pkg.unf_cmnd_pload_bl.buffer_ptr,
|
|
0, UNF_FALSE);
|
|
} else {
|
|
spin_unlock_irqrestore(&v_hba->delay_info.srq_lock,
|
|
flags);
|
|
}
|
|
|
|
} else {
|
|
goto rq_recv_error_els_frame;
|
|
}
|
|
|
|
return ret;
|
|
|
|
rq_recv_error_els_frame:
|
|
return HIFC_RQ_ERROR_FRAME;
|
|
}
|
|
|
|
static unsigned int hifc_rq_rcv_bls_frame(struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int ox_id = INVALID_VALUE32;
|
|
|
|
v_pkg->status = UNF_IO_SUCCESS;
|
|
|
|
if ((UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head) == HIFC_RCTL_BLS_ACC) ||
|
|
(UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head) == HIFC_RCTL_BLS_RJT)) {
|
|
/* INI Mode */
|
|
ox_id = UNF_GET_FC_HEADER_OXID(&v_pkg->frame_head);
|
|
if ((ox_id < (unsigned int)v_hba->exit_base) ||
|
|
(ox_id >= (unsigned int)(v_hba->exit_base +
|
|
v_hba->exit_count))) {
|
|
goto rq_recv_error_bls_frame;
|
|
}
|
|
ox_id -= v_hba->exit_base;
|
|
|
|
ret = hifc_rcv_bls_rsp(v_hba, v_pkg, ox_id);
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_RCV_ABTS_RSP);
|
|
} else {
|
|
goto rq_recv_error_bls_frame;
|
|
}
|
|
|
|
return ret;
|
|
|
|
rq_recv_error_bls_frame:
|
|
return HIFC_RQ_ERROR_FRAME;
|
|
}
|
|
|
|
static unsigned int hifc_rq_rcv_service_frame(struct hifc_hba_s *v_hba,
|
|
unsigned char *v_frame,
|
|
unsigned int frame_len,
|
|
unsigned short pkg_flag,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned char fc_frame_type = 0;
|
|
|
|
fc_frame_type = UNF_GET_FC_HEADER_TYPE(&v_pkg->frame_head);
|
|
|
|
if (fc_frame_type == HIFC_FC_TYPE_ELS) {
|
|
v_hba->delay_info.root_rq_rcvd_flag = 1;
|
|
ret = hifc_rq_rcv_els_frame(v_hba, v_frame, frame_len,
|
|
pkg_flag, v_pkg);
|
|
} else if (fc_frame_type == HIFC_FC_TYPE_BLS) {
|
|
ret = hifc_rq_rcv_bls_frame(v_hba, v_pkg);
|
|
} else {
|
|
ret = HIFC_RQ_ERROR_FRAME;
|
|
}
|
|
|
|
if (ret == HIFC_RQ_ERROR_FRAME) {
|
|
/* Error statistics are collected when an invalid frame
|
|
* is received
|
|
*/
|
|
HIFC_IO_STAT(v_hba, HIFCOE_TASK_T_BUTT);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[info]Port(0x%x) Receive an unsupported frame, Rctl(0x%x), Type(0x%x), Fctl(0x%x), Sid_Did(0x%x_0x%x),OxId_RxId(0x%x_0x%x), FrameLen(0x%x), drop it",
|
|
v_hba->port_cfg.port_id,
|
|
UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head),
|
|
UNF_GET_FC_HEADER_TYPE(&v_pkg->frame_head),
|
|
UNF_GET_FC_HEADER_FCTL(&v_pkg->frame_head),
|
|
UNF_GET_FC_HEADER_SID(&v_pkg->frame_head),
|
|
UNF_GET_FC_HEADER_DID(&v_pkg->frame_head),
|
|
UNF_GET_FC_HEADER_OXID(&v_pkg->frame_head),
|
|
UNF_GET_FC_HEADER_RXID(&v_pkg->frame_head),
|
|
frame_len);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_service_frame_from_rq(struct hifc_hba_s *v_hba,
|
|
struct hifc_root_rq_info_s
|
|
*v_rq_info,
|
|
struct hifc_root_rq_complet_info_s
|
|
*v_complet_info,
|
|
unsigned short v_rcv_buf_num)
|
|
{
|
|
unsigned short remain_len = 0;
|
|
unsigned short rcv_len = 0;
|
|
unsigned short pkg_flag = 0;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned short pkt_len = 0;
|
|
void *root_rq_rcv_buf = NULL;
|
|
unsigned short ci = 0;
|
|
unsigned int loop = 0;
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct unf_fchead_s *els_frame = NULL;
|
|
unsigned char *pld_buf = NULL;
|
|
unsigned int pld_len = 0;
|
|
|
|
ci = v_rq_info->ci;
|
|
pkt_len = v_complet_info->buf_length;
|
|
memset(&pkg, 0, sizeof(pkg));
|
|
|
|
for (loop = 0; loop < v_rcv_buf_num; loop++) {
|
|
/* Obtain rcv buffer */
|
|
root_rq_rcv_buf =
|
|
(void *)((unsigned long long)v_rq_info->rq_rcv_buff +
|
|
HIFC_ROOT_RQ_RECV_BUFF_SIZE * ci);
|
|
|
|
/* Calculate the frame data address and length */
|
|
els_frame = (struct unf_fchead_s *)root_rq_rcv_buf;
|
|
rcv_len = HIFC_ROOT_RQ_RECV_BUFF_SIZE;
|
|
pkg_flag = 0;
|
|
|
|
if (loop == (v_rcv_buf_num - 1)) {
|
|
pkg_flag |= HIFC_LAST_PKG_FLAG;
|
|
remain_len = pkt_len % HIFC_ROOT_RQ_RECV_BUFF_SIZE;
|
|
rcv_len = (remain_len > 0) ? (remain_len) :
|
|
HIFC_ROOT_RQ_RECV_BUFF_SIZE;
|
|
}
|
|
|
|
/* Calculate the frame data address and length */
|
|
if (loop == 0) {
|
|
pkg_flag |= HIFC_FIRST_PKG_FLAG;
|
|
|
|
memcpy(&pkg.frame_head, els_frame,
|
|
sizeof(pkg.frame_head));
|
|
hifc_big_to_cpu32(&pkg.frame_head,
|
|
sizeof(pkg.frame_head));
|
|
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] =
|
|
v_complet_info->magic_num;
|
|
|
|
pld_buf = (unsigned char *)(els_frame + 1);
|
|
pld_len = rcv_len - sizeof(pkg.frame_head);
|
|
} else {
|
|
pld_buf = (unsigned char *)els_frame;
|
|
pld_len = rcv_len;
|
|
}
|
|
|
|
/* Processing the rqe sent by the FC ucode */
|
|
ret = hifc_rq_rcv_service_frame(v_hba, pld_buf, pld_len,
|
|
pkg_flag, &pkg);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT,
|
|
UNF_INFO,
|
|
"[err]Up layer Process RQE frame or status abnormal(0x%x)",
|
|
ret);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ci = ((ci + 1) < v_rq_info->q_depth) ? (ci + 1) : 0;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_rcv_gs_rsp_payload(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id,
|
|
unsigned char *v_els_pld_buf,
|
|
unsigned int pld_len)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
v_pkg->type = UNF_PKG_GS_REQ_DONE;
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
|
|
/* Convert to small endian */
|
|
hifc_big_to_cpu32(v_els_pld_buf, pld_len);
|
|
|
|
/* Payload Buffer in ROOT SQ Buffer */
|
|
v_pkg->unf_cmnd_pload_bl.buffer_ptr = v_els_pld_buf;
|
|
v_pkg->unf_cmnd_pload_bl.length = pld_len;
|
|
|
|
/* Mark as a non-last block */
|
|
v_pkg->last_pkg_flag = UNF_PKG_NOT_LAST_RESPONSE;
|
|
|
|
UNF_LOWLEVEL_RECEIVE_GS_PKG(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_abts_rsp(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
/* Default path, which is sent from SCQ to the driver */
|
|
unsigned char status = 0;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int ox_id = INVALID_VALUE32;
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct hifcoe_scqe_rcv_abts_rsp_s *abts_rsp = NULL;
|
|
|
|
abts_rsp = &v_scqe->rcv_abts_rsp;
|
|
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] = abts_rsp->magic_num;
|
|
|
|
ox_id = (unsigned int)(abts_rsp->wd0.ox_id);
|
|
|
|
if (unlikely((ox_id < (unsigned int)v_hba->exit_base) ||
|
|
(ox_id >=
|
|
(unsigned int)(v_hba->exit_base + v_hba->exit_count)))) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) has bad OX_ID(0x%x) for bls_rsp",
|
|
v_hba->port_cfg.port_id, ox_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ox_id -= v_hba->exit_base;
|
|
|
|
if (unlikely(HIFC_SCQE_HAS_ERRCODE(v_scqe))) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) BLS response has error code(0x%x) tag(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
HIFC_GET_SCQE_STATUS(v_scqe),
|
|
(unsigned int)(abts_rsp->wd0.ox_id));
|
|
|
|
status = UNF_IO_FAILED;
|
|
} else {
|
|
pkg.frame_head.rctl_did = abts_rsp->wd3.did;
|
|
pkg.frame_head.csctl_sid = abts_rsp->wd4.sid;
|
|
pkg.frame_head.oxid_rxid = (unsigned int)(abts_rsp->wd0.rx_id)
|
|
| ox_id << 16;
|
|
|
|
/* BLS_ACC/BLS_RJT: IO_succeed */
|
|
if (abts_rsp->wd2.fh_rctrl == HIFC_RCTL_BLS_ACC) {
|
|
status = UNF_IO_SUCCESS;
|
|
} else if (abts_rsp->wd2.fh_rctrl == HIFC_RCTL_BLS_RJT) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO,
|
|
UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) ABTS RJT: %08x-%08x-%08x",
|
|
v_hba->port_cfg.port_id,
|
|
abts_rsp->payload[0],
|
|
abts_rsp->payload[1], abts_rsp->payload[2]);
|
|
|
|
status = UNF_IO_SUCCESS;
|
|
} else {
|
|
/* 3. BA_RSP type is err: IO_failed */
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) BLS response RCTL is error",
|
|
v_hba->port_cfg.port_id);
|
|
|
|
HIFC_ERR_IO_STAT(v_hba, HIFC_SCQE_ABTS_RSP);
|
|
|
|
status = UNF_IO_FAILED;
|
|
}
|
|
}
|
|
|
|
/* Set PKG/exchange status & Process BLS_RSP */
|
|
pkg.status = status;
|
|
ret = hifc_rcv_bls_rsp(v_hba, &pkg, ox_id);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) recv ABTS rsp OX_ID(0x%x) RX_ID(0x%x) SID(0x%x) DID(0x%x) %s",
|
|
v_hba->port_cfg.port_id,
|
|
ox_id,
|
|
abts_rsp->wd0.rx_id,
|
|
abts_rsp->wd4.sid,
|
|
abts_rsp->wd3.did,
|
|
(ret == RETURN_OK) ? "OK" : "ERROR");
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rq_rcv_srv_err(struct hifc_hba_s *v_hba,
|
|
struct hifc_root_rq_complet_info_s *v_cs_info)
|
|
{
|
|
UNF_REFERNCE_VAR(v_hba);
|
|
UNF_REFERNCE_VAR(v_cs_info);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]hifc_rq_rcv_srv_err not implemented yet");
|
|
|
|
if (!v_hba)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
if (!v_cs_info)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
unsigned int hifc_rcv_els_cmnd(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned char *v_pld,
|
|
unsigned int pld_len,
|
|
int first_frame)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
/* Convert Payload to small endian */
|
|
hifc_big_to_cpu32(v_pld, pld_len);
|
|
|
|
v_pkg->type = UNF_PKG_ELS_REQ;
|
|
|
|
v_pkg->unf_cmnd_pload_bl.buffer_ptr = v_pld;
|
|
|
|
/* Payload length */
|
|
v_pkg->unf_cmnd_pload_bl.length = pld_len;
|
|
|
|
/* Obtain the Cmnd type from the Paylaod. The Cmnd is in small endian */
|
|
if (first_frame == UNF_TRUE) {
|
|
v_pkg->cmnd = UNF_GET_FC_PAYLOAD_ELS_CMND(
|
|
v_pkg->unf_cmnd_pload_bl.buffer_ptr);
|
|
}
|
|
|
|
/* Errors have been processed in HIFC_RecvElsError */
|
|
v_pkg->status = UNF_IO_SUCCESS;
|
|
|
|
/* Send PKG to the CM layer */
|
|
UNF_LOWLEVEL_RECEIVE_ELS_PKG(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_els_rsp(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
/* Receive CmndReqSts */
|
|
v_pkg->type = UNF_PKG_ELS_REQ_DONE;
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
v_pkg->byte_orders |= HIFC_BIT_2;
|
|
|
|
/* Mark the last block */
|
|
v_pkg->last_pkg_flag = UNF_PKG_LAST_RESPONSE;
|
|
|
|
/* Send PKG to the CM layer */
|
|
UNF_LOWLEVEL_RECEIVE_ELS_PKG(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_els_rsp_sts(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int rx_id)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
v_pkg->type = UNF_PKG_ELS_REPLY_DONE;
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = rx_id;
|
|
|
|
UNF_LOWLEVEL_SEND_ELS_DONE(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_gs_rsp(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
/* Receive CmndReqSts */
|
|
v_pkg->type = UNF_PKG_GS_REQ_DONE;
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
|
|
/* Mark the last block */
|
|
v_pkg->last_pkg_flag = UNF_PKG_LAST_RESPONSE;
|
|
|
|
/* Send PKG to the CM layer */
|
|
UNF_LOWLEVEL_RECEIVE_GS_PKG(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_bls_rsp(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id)
|
|
{
|
|
/*
|
|
* 1. SCQ (normal)
|
|
* 2. from Root RQ (parent no existence)
|
|
**
|
|
* single frame, single sequence
|
|
*/
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
v_pkg->type = UNF_PKG_BLS_REQ_DONE;
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
v_pkg->last_pkg_flag = UNF_PKG_LAST_RESPONSE;
|
|
|
|
UNF_LOWLEVEL_RECEIVE_BLS_PKG(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_tmf_marker_sts(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
|
|
/* Send PKG info to COM */
|
|
UNF_LOWLEVEL_RECEIVE_MARKER_STS(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_rcv_abts_marker_sts(const struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int ox_id)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = ox_id;
|
|
|
|
UNF_LOWLEVEL_RECEIVE_ABTS_MARKER_STS(ret, v_hba->lport, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void hifc_scqe_error_pre_process(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
/* Currently, only printing and statistics collection are performed */
|
|
HIFC_ERR_IO_STAT(v_hba, HIFC_GET_SCQE_TYPE(v_scqe));
|
|
HIFC_SCQ_ERR_TYPE_STAT(v_hba, HIFC_GET_SCQE_STATUS(v_scqe));
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_ABNORMAL, UNF_WARN,
|
|
"[warn]Port(0x%x)-Task_type(%u) SCQE contain error code(%u), additional info(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
v_scqe->common.ch.wd0.task_type,
|
|
v_scqe->common.ch.wd0.err_code,
|
|
v_scqe->common.conn_id);
|
|
}
|
|
|
|
unsigned int hifc_rcv_scqe_entry_from_scq(void *v_hba, void *v_scqe,
|
|
unsigned int scq_idx)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
int do_reclaim = UNF_FALSE;
|
|
unsigned int index = 0;
|
|
unsigned int total_index = 0;
|
|
struct hifc_hba_s *hba = NULL;
|
|
union hifcoe_scqe_u *scqe = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_hba,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_scqe,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, HIFC_TOTAL_SCQ_NUM > scq_idx,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
scqe = (union hifcoe_scqe_u *)v_scqe;
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
HIFC_IO_STAT(hba, HIFC_GET_SCQE_TYPE(scqe));
|
|
|
|
/* 1. error code cheking */
|
|
if (unlikely(HIFC_SCQE_HAS_ERRCODE(scqe))) {
|
|
/* So far, just print & counter */
|
|
hifc_scqe_error_pre_process(hba, scqe);
|
|
}
|
|
|
|
/* 2. Process SCQE by corresponding processer */
|
|
total_index = sizeof(scqe_handler_table) /
|
|
sizeof(struct unf_scqe_handler_table_s);
|
|
while (index < total_index) {
|
|
if (HIFC_GET_SCQE_TYPE(scqe) ==
|
|
scqe_handler_table[index].scqe_type) {
|
|
ret = scqe_handler_table[index].pfn_scqe_handle_fun(
|
|
hba, scqe);
|
|
do_reclaim = scqe_handler_table[index].reclaim_sq_wpg;
|
|
|
|
break;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
/* 3. SCQE type check */
|
|
if (unlikely(index == total_index)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[warn]Unknown SCQE type %d",
|
|
HIFC_GET_SCQE_TYPE(scqe));
|
|
|
|
UNF_PRINT_SFS_LIMIT(UNF_ERR, hba->port_cfg.port_id, scqe,
|
|
sizeof(union hifcoe_scqe_u));
|
|
}
|
|
|
|
/* 4. If SCQE is for SQ-WQE then recovery Link List SQ free page */
|
|
if (do_reclaim == UNF_TRUE) {
|
|
if (HIFC_SCQE_CONN_ID_VALID(scqe)) {
|
|
ret = hifc_reclaim_sq_wqe_page(v_hba, scqe);
|
|
} else {
|
|
/* NOTE: for buffer clear, the SCQE conn_id is 0xFFFF,
|
|
* count with HBA
|
|
*/
|
|
HIFC_HBA_STAT(
|
|
(struct hifc_hba_s *)v_hba,
|
|
HIFC_STAT_SQ_IO_BUFFER_CLEARED);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void *hifc_get_els_buf_by_userid(struct hifc_hba_s *v_hba,
|
|
unsigned short user_id)
|
|
{
|
|
struct hifc_srq_buff_entry_s *buf_entry = NULL;
|
|
struct hifc_srq_info_s *srq_info = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_hba, return NULL);
|
|
|
|
srq_info = &v_hba->els_srq_info;
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
user_id < srq_info->valid_wqe_num, return NULL);
|
|
|
|
buf_entry = &srq_info->els_buff_entry_head[user_id];
|
|
|
|
return buf_entry->buff_addr;
|
|
}
|
|
|
|
static unsigned int hifc_check_srq_buf_valid(struct hifc_hba_s *v_hba,
|
|
unsigned int *v_buf_id,
|
|
unsigned int v_buf_num)
|
|
{
|
|
unsigned int index = 0;
|
|
unsigned int buf_id = 0;
|
|
void *srq_buf = NULL;
|
|
|
|
for (index = 0; index < v_buf_num; index++) {
|
|
buf_id = v_buf_id[index];
|
|
|
|
if (buf_id < v_hba->els_srq_info.valid_wqe_num) {
|
|
srq_buf = hifc_get_els_buf_by_userid(
|
|
v_hba,
|
|
(unsigned short)buf_id);
|
|
} else {
|
|
srq_buf = NULL;
|
|
}
|
|
|
|
if (!srq_buf) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) get srq buffer user id(0x%x) is null",
|
|
v_hba->port_cfg.port_id, buf_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static void hifc_reclaim_srq_buff(struct hifc_hba_s *v_hba,
|
|
unsigned int *v_buf_id,
|
|
unsigned int v_buf_num)
|
|
{
|
|
unsigned int index = 0;
|
|
unsigned int buf_id = 0;
|
|
void *srq_buf = NULL;
|
|
|
|
for (index = 0; index < v_buf_num; index++) {
|
|
buf_id = v_buf_id[index];
|
|
if (buf_id < v_hba->els_srq_info.valid_wqe_num) {
|
|
srq_buf = hifc_get_els_buf_by_userid(
|
|
v_hba,
|
|
(unsigned short)buf_id);
|
|
} else {
|
|
srq_buf = NULL;
|
|
}
|
|
|
|
/* If the value of buffer is NULL, it indicates that the value
|
|
* of buffer is invalid. In this case, exit directly.
|
|
*/
|
|
if (!srq_buf)
|
|
break;
|
|
|
|
hifc_post_els_srq_wqe(&v_hba->els_srq_info,
|
|
(unsigned short)buf_id);
|
|
}
|
|
}
|
|
|
|
static unsigned int hifc_check_els_gs_valid(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe,
|
|
struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int *v_buf_id,
|
|
unsigned int buf_num,
|
|
unsigned int frame_len)
|
|
{
|
|
unsigned int ox_id = INVALID_VALUE32;
|
|
|
|
ox_id = v_pkg->frame_head.oxid_rxid >> 16;
|
|
|
|
/* The ELS CMD returns an error code and discards it directly */
|
|
if ((sizeof(struct hifc_fc_frame_header) > frame_len) ||
|
|
(HIFC_SCQE_HAS_ERRCODE(v_scqe)) ||
|
|
(buf_num > HIFC_ELS_SRQ_BUF_NUM)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO,
|
|
UNF_LOG_LOGIN_ATT, UNF_KEVENT,
|
|
"[event]Port(0x%x) get scqe type(0x%x) payload len(0x%x),scq status(0x%x),user id num(0x%x) abnormal",
|
|
v_hba->port_cfg.port_id,
|
|
HIFC_GET_SCQE_TYPE(v_scqe),
|
|
frame_len,
|
|
HIFC_GET_SCQE_STATUS(v_scqe),
|
|
buf_num);
|
|
|
|
/* ELS RSP Special Processing */
|
|
if (HIFC_GET_SCQE_TYPE(v_scqe) == HIFC_SCQE_ELS_RSP) {
|
|
if (HIFC_SCQE_ERR_TO_CM(v_scqe)) {
|
|
v_pkg->status = UNF_IO_FAILED;
|
|
(void)hifc_rcv_els_rsp(v_hba, v_pkg, ox_id);
|
|
} else {
|
|
HIFC_HBA_STAT(v_hba,
|
|
HIFC_STAT_ELS_RSP_EXCH_REUSE);
|
|
}
|
|
}
|
|
|
|
/* GS RSP Special Processing */
|
|
if (HIFC_GET_SCQE_TYPE(v_scqe) == HIFC_SCQE_GS_RSP) {
|
|
if (HIFC_SCQE_ERR_TO_CM(v_scqe)) {
|
|
v_pkg->status = UNF_IO_FAILED;
|
|
(void)hifc_rcv_gs_rsp(v_hba, v_pkg, ox_id);
|
|
} else {
|
|
HIFC_HBA_STAT(v_hba,
|
|
HIFC_STAT_GS_RSP_EXCH_REUSE);
|
|
}
|
|
}
|
|
|
|
/* Reclaim srq */
|
|
if (buf_num <= HIFC_ELS_SRQ_BUF_NUM)
|
|
hifc_reclaim_srq_buff(v_hba, v_buf_id, buf_num);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* ELS CMD Check the validity of the buffer sent by the ucode */
|
|
if (HIFC_GET_SCQE_TYPE(v_scqe) == HIFC_SCQE_ELS_CMND) {
|
|
if (hifc_check_srq_buf_valid(v_hba, v_buf_id, buf_num) !=
|
|
RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) get els cmnd scqe user id num(0x%x) abnormal, as some srq buff is null",
|
|
v_hba->port_cfg.port_id, buf_num);
|
|
|
|
hifc_reclaim_srq_buff(v_hba, v_buf_id, buf_num);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_els_cmd(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int pld_len = 0;
|
|
unsigned int hdr_len = 0;
|
|
unsigned int frame_len = 0;
|
|
unsigned int rcv_data_len = 0;
|
|
unsigned int max_buf_num = 0;
|
|
unsigned short buf_id = 0;
|
|
unsigned int index = 0;
|
|
unsigned char *pld = NULL;
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct hifcoe_scqe_rcv_els_cmd_s *els_cmd = NULL;
|
|
struct hifc_fc_frame_header *els_frame = NULL;
|
|
struct hifc_fc_frame_header local_fc_frame = { 0 };
|
|
void *els_buf = NULL;
|
|
int first_frame = UNF_FALSE;
|
|
unsigned long flags = 0;
|
|
unsigned char srq_delay_flag = 0;
|
|
|
|
els_cmd = &v_scqe->rcv_els_cmd;
|
|
frame_len = els_cmd->wd3.data_len;
|
|
max_buf_num = els_cmd->wd3.user_id_num;
|
|
|
|
pkg.xchg_contex = NULL;
|
|
pkg.status = UNF_IO_SUCCESS;
|
|
|
|
/* Check the validity of error codes and buff. If an exception occurs,
|
|
* discard the error code
|
|
*/
|
|
ret = hifc_check_els_gs_valid(v_hba, v_scqe, &pkg, els_cmd->user_id,
|
|
max_buf_num, frame_len);
|
|
if (ret != RETURN_OK)
|
|
return RETURN_OK;
|
|
|
|
/* Send data to COM cyclically */
|
|
for (index = 0; index < max_buf_num; index++) {
|
|
/* Exception record, which is not processed currently */
|
|
if (rcv_data_len >= frame_len) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) get els cmd date len(0x%x) is bigger than fream len(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
rcv_data_len, frame_len);
|
|
}
|
|
|
|
buf_id = (unsigned short)els_cmd->user_id[index];
|
|
els_buf = hifc_get_els_buf_by_userid(v_hba, buf_id);
|
|
|
|
/* Obtain playload address */
|
|
pld = (unsigned char *)(els_buf);
|
|
hdr_len = 0;
|
|
first_frame = UNF_FALSE;
|
|
if (index == 0) {
|
|
els_frame = (struct hifc_fc_frame_header *)els_buf;
|
|
pld = (unsigned char *)(els_frame + 1);
|
|
|
|
hdr_len = sizeof(struct hifc_fc_frame_header);
|
|
first_frame = UNF_TRUE;
|
|
|
|
memcpy(&local_fc_frame, els_frame,
|
|
sizeof(struct hifc_fc_frame_header));
|
|
hifc_big_to_cpu32(&local_fc_frame,
|
|
sizeof(struct hifc_fc_frame_header));
|
|
memcpy(&pkg.frame_head, &local_fc_frame,
|
|
sizeof(pkg.frame_head));
|
|
}
|
|
|
|
/* Calculate the playload length */
|
|
pkg.last_pkg_flag = 0;
|
|
pld_len = HIFC_SRQ_ELS_SGE_LEN;
|
|
|
|
if ((rcv_data_len + HIFC_SRQ_ELS_SGE_LEN) >= frame_len) {
|
|
pkg.last_pkg_flag = 1;
|
|
pld_len = frame_len - rcv_data_len;
|
|
|
|
if (unlikely(
|
|
(v_hba->active_topo == UNF_TOP_P2P_MASK) &&
|
|
(v_hba->delay_info.root_rq_rcvd_flag == 0))) {
|
|
/* Only data is pushed for the first time, but
|
|
* the last packet flag is not set
|
|
*/
|
|
pkg.last_pkg_flag = 0;
|
|
srq_delay_flag = 1;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) revd els from srq, and need delay processed, topo(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
v_hba->active_topo);
|
|
}
|
|
}
|
|
|
|
/* Push data to COM */
|
|
if (ret == RETURN_OK) {
|
|
ret = hifc_rcv_els_cmnd(v_hba, &pkg, pld,
|
|
(pld_len - hdr_len),
|
|
first_frame);
|
|
|
|
/* If the plogi arrives before the flogi, the pkg is
|
|
* saved, and the last packet is pushed
|
|
* when the root rq contains content.
|
|
*/
|
|
if (unlikely(srq_delay_flag == 1)) {
|
|
spin_lock_irqsave(&v_hba->delay_info.srq_lock,
|
|
flags);
|
|
memcpy(&v_hba->delay_info.pkg, &pkg,
|
|
sizeof(pkg));
|
|
v_hba->delay_info.srq_delay_flag = 1;
|
|
v_hba->delay_info.pkg.last_pkg_flag = 1;
|
|
|
|
/* Add a 20-ms timer to prevent the root rq
|
|
* from processing data
|
|
*/
|
|
(void)queue_delayed_work(
|
|
v_hba->work_queue,
|
|
&v_hba->delay_info.del_work,
|
|
(unsigned long)
|
|
msecs_to_jiffies((unsigned int)
|
|
HIFC_SRQ_PROCESS_DELAY_MS));
|
|
|
|
spin_unlock_irqrestore(
|
|
&v_hba->delay_info.srq_lock, flags);
|
|
}
|
|
}
|
|
|
|
/* Reclaim srq buffer */
|
|
hifc_post_els_srq_wqe(&v_hba->els_srq_info, buf_id);
|
|
|
|
rcv_data_len += pld_len;
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) recv ELS Type(0x%x) Cmnd(0x%x) OXID(0x%x) RXID(0x%x) SID(0x%x) DID(0x%x) %u",
|
|
v_hba->port_cfg.port_id,
|
|
pkg.type,
|
|
pkg.cmnd,
|
|
els_cmd->wd2.ox_id,
|
|
els_cmd->wd2.rx_id,
|
|
els_cmd->wd1.sid,
|
|
els_cmd->wd0.did,
|
|
ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_get_els_gs_pld_len(struct hifc_hba_s *v_hba,
|
|
unsigned int v_rcv_data_len,
|
|
unsigned int v_frame_len)
|
|
{
|
|
unsigned int pld_len;
|
|
|
|
/* Exception record, which is not processed currently */
|
|
if (v_rcv_data_len >= v_frame_len) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) get els rsp date len(0x%x) is bigger than fream len(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
v_rcv_data_len, v_frame_len);
|
|
}
|
|
|
|
pld_len = HIFC_SRQ_ELS_SGE_LEN;
|
|
if ((v_rcv_data_len + HIFC_SRQ_ELS_SGE_LEN) >= v_frame_len)
|
|
pld_len = v_frame_len - v_rcv_data_len;
|
|
|
|
return pld_len;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_els_rsp(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int pld_len = 0;
|
|
unsigned int hdr_len = 0;
|
|
unsigned int frame_len = 0;
|
|
unsigned int rcv_data_len = 0;
|
|
unsigned int max_buf_num = 0;
|
|
unsigned short buf_id = 0;
|
|
unsigned int index = 0;
|
|
unsigned int ox_id = (~0);
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct hifcoe_scqe_rcv_els_gs_rsp_s *els_rsp;
|
|
struct hifc_fc_frame_header *els_frame = NULL;
|
|
void *els_buf = NULL;
|
|
unsigned char *pld = NULL;
|
|
|
|
els_rsp = &v_scqe->rcv_els_gs_rsp;
|
|
frame_len = els_rsp->wd2.data_len;
|
|
max_buf_num = els_rsp->wd4.user_id_num;
|
|
|
|
ox_id = (unsigned int)(els_rsp->wd1.ox_id) - v_hba->exit_base;
|
|
pkg.frame_head.oxid_rxid = (unsigned int)(els_rsp->wd1.rx_id) |
|
|
ox_id << 16;
|
|
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] = els_rsp->magic_num;
|
|
pkg.frame_head.csctl_sid = els_rsp->wd4.sid;
|
|
pkg.frame_head.rctl_did = els_rsp->wd3.did;
|
|
pkg.status = UNF_IO_SUCCESS;
|
|
|
|
/* Handle the exception first. The ELS RSP returns the error code.
|
|
* Only the OXID can submit the error code to the CM layer.
|
|
*/
|
|
ret = hifc_check_els_gs_valid(v_hba, v_scqe, &pkg,
|
|
els_rsp->user_id, max_buf_num, frame_len);
|
|
if (ret != RETURN_OK)
|
|
return RETURN_OK;
|
|
|
|
/* if this is echo rsp */
|
|
if (els_rsp->wd3.echo_rsp == UNF_TRUE) {
|
|
/* echo time stamp fill in the Els rsp user_id last 4dword */
|
|
pkg.private[PKG_PRIVATE_ECHO_CMD_RCV_TIME] =
|
|
els_rsp->user_id[5];
|
|
pkg.private[PKG_PRIVATE_ECHO_RSP_SND_TIME] =
|
|
els_rsp->user_id[6];
|
|
pkg.private[PKG_PRIVATE_ECHO_CMD_SND_TIME] =
|
|
els_rsp->user_id[7];
|
|
pkg.private[PKG_PRIVATE_ECHO_ACC_RCV_TIME] =
|
|
els_rsp->user_id[8];
|
|
}
|
|
|
|
/* Send data to COM cyclically */
|
|
for (index = 0; index < max_buf_num; index++) {
|
|
/* Obtain buffer address */
|
|
els_buf = NULL;
|
|
buf_id = (unsigned short)els_rsp->user_id[index];
|
|
|
|
els_buf = hifc_get_els_buf_by_userid(v_hba, buf_id);
|
|
|
|
/* If the value of buffer is NULL, the buff id is abnormal and
|
|
* exits directly
|
|
*/
|
|
if (unlikely(!els_buf)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) OXID(0x%x) RXID(0x%x) SID(0x%x) DID(0x%x) Index(0x%x) get els rsp buff user id(0x%x) abnormal",
|
|
v_hba->port_cfg.port_id, ox_id,
|
|
els_rsp->wd1.rx_id, els_rsp->wd4.sid,
|
|
els_rsp->wd3.did, index, buf_id);
|
|
|
|
if (index == 0) {
|
|
pkg.status = UNF_IO_FAILED;
|
|
ret = hifc_rcv_els_rsp(v_hba, &pkg, ox_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
hdr_len = 0;
|
|
pld = (unsigned char *)(els_buf);
|
|
if (index == 0) {
|
|
hdr_len = sizeof(struct hifc_fc_frame_header);
|
|
|
|
els_frame = (struct hifc_fc_frame_header *)els_buf;
|
|
pld = (unsigned char *)(els_frame + 1);
|
|
}
|
|
|
|
/* Calculate the playload length */
|
|
pld_len = hifc_get_els_gs_pld_len(v_hba, rcv_data_len,
|
|
frame_len);
|
|
|
|
/* Push data to COM */
|
|
if (ret == RETURN_OK) {
|
|
ret = hifc_recv_els_rsp_payload(v_hba, &pkg, ox_id, pld,
|
|
(pld_len - hdr_len));
|
|
}
|
|
|
|
/* Reclaim srq buffer */
|
|
hifc_post_els_srq_wqe(&v_hba->els_srq_info, buf_id);
|
|
|
|
rcv_data_len += pld_len;
|
|
}
|
|
|
|
if ((els_rsp->wd3.end_rsp) && (ret == RETURN_OK))
|
|
ret = hifc_rcv_els_rsp(v_hba, &pkg, ox_id);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) receive ELS RSP OXID(0x%x) RXID(0x%x) SID(0x%x) DID(0x%x) end_rsp(0x%x) user_num(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
ox_id,
|
|
els_rsp->wd1.rx_id,
|
|
els_rsp->wd4.sid,
|
|
els_rsp->wd3.did,
|
|
els_rsp->wd3.end_rsp,
|
|
els_rsp->wd4.user_id_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_gs_rsp(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int pld_len = 0;
|
|
unsigned int hdr_len = 0;
|
|
unsigned int frame_len = 0;
|
|
unsigned int rcv_data_len = 0;
|
|
unsigned int max_buf_num = 0;
|
|
unsigned short buf_id = 0;
|
|
unsigned int index = 0;
|
|
unsigned int ox_id = (~0);
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct hifcoe_scqe_rcv_els_gs_rsp_s *gs_rsp = NULL;
|
|
struct hifc_fc_frame_header *gs_frame = NULL;
|
|
void *gs_buf = NULL;
|
|
unsigned char *pld = NULL;
|
|
|
|
gs_rsp = &v_scqe->rcv_els_gs_rsp;
|
|
frame_len = gs_rsp->wd2.data_len;
|
|
max_buf_num = gs_rsp->wd4.user_id_num;
|
|
|
|
ox_id = (unsigned int)(gs_rsp->wd1.ox_id) - v_hba->exit_base;
|
|
pkg.frame_head.oxid_rxid = (unsigned int)(gs_rsp->wd1.rx_id) |
|
|
ox_id << 16;
|
|
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] = gs_rsp->magic_num;
|
|
pkg.frame_head.csctl_sid = gs_rsp->wd4.sid;
|
|
pkg.frame_head.rctl_did = gs_rsp->wd3.did;
|
|
pkg.status = UNF_IO_SUCCESS;
|
|
|
|
if (gs_rsp->wd3.end_rsp)
|
|
HIFC_HBA_STAT(v_hba, HIFC_STAT_LAST_GS_SCQE);
|
|
|
|
/* Exception handling: The GS RSP returns an error code. Only the OXID
|
|
* can submit the error code to the CM layer
|
|
*/
|
|
ret = hifc_check_els_gs_valid(v_hba, v_scqe, &pkg, gs_rsp->user_id,
|
|
max_buf_num, frame_len);
|
|
if (ret != RETURN_OK)
|
|
return RETURN_OK;
|
|
|
|
/* Send data to COM cyclically */
|
|
for (index = 0; index < max_buf_num; index++) {
|
|
/* Obtain buffer address */
|
|
gs_buf = NULL;
|
|
buf_id = (unsigned short)gs_rsp->user_id[index];
|
|
|
|
gs_buf = hifc_get_els_buf_by_userid(v_hba, buf_id);
|
|
|
|
if (unlikely(!gs_buf)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) OXID(0x%x) RXID(0x%x) SID(0x%x) DID(0x%x) Index(0x%x) get gs rsp scqe user id(0x%x) abnormal",
|
|
v_hba->port_cfg.port_id, ox_id,
|
|
gs_rsp->wd1.rx_id, gs_rsp->wd4.sid,
|
|
gs_rsp->wd3.did, index, buf_id);
|
|
|
|
if (index == 0) {
|
|
pkg.status = UNF_IO_FAILED;
|
|
ret = hifc_rcv_gs_rsp(v_hba, &pkg, ox_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Obtain playload address */
|
|
hdr_len = 0;
|
|
pld = (unsigned char *)(gs_buf);
|
|
if (index == 0) {
|
|
hdr_len = sizeof(struct hifc_fc_frame_header);
|
|
|
|
gs_frame = (struct hifc_fc_frame_header *)gs_buf;
|
|
pld = (unsigned char *)(gs_frame + 1);
|
|
}
|
|
|
|
/* Calculate the playload length */
|
|
pld_len = hifc_get_els_gs_pld_len(v_hba, rcv_data_len,
|
|
frame_len);
|
|
|
|
/* Push data to COM */
|
|
if (ret == RETURN_OK)
|
|
ret = hifc_rcv_gs_rsp_payload(v_hba, &pkg, ox_id, pld,
|
|
(pld_len - hdr_len));
|
|
|
|
/* Reclaim srq buffer */
|
|
hifc_post_els_srq_wqe(&v_hba->els_srq_info, buf_id);
|
|
|
|
rcv_data_len += pld_len;
|
|
}
|
|
|
|
if ((gs_rsp->wd3.end_rsp) && (ret == RETURN_OK))
|
|
ret = hifc_rcv_gs_rsp(v_hba, &pkg, ox_id);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) recv GS RSP OXID(0x%x) RXID(0x%x) SID(0x%x) DID(0x%x) end_rsp(0x%x) user_num(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
ox_id,
|
|
gs_rsp->wd1.rx_id,
|
|
gs_rsp->wd4.sid,
|
|
gs_rsp->wd3.did,
|
|
gs_rsp->wd3.end_rsp,
|
|
gs_rsp->wd4.user_id_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_els_rsp_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int rx_id = INVALID_VALUE32;
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct hifcoe_scqe_comm_rsp_sts_s *els_rsp_sts = NULL;
|
|
|
|
els_rsp_sts = &v_scqe->comm_sts;
|
|
rx_id = (unsigned int)els_rsp_sts->wd0.rx_id;
|
|
rx_id = rx_id - v_hba->exit_base;
|
|
|
|
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] = els_rsp_sts->magic_num;
|
|
pkg.frame_head.oxid_rxid = rx_id |
|
|
(unsigned int)(els_rsp_sts->wd0.ox_id) << 16;
|
|
|
|
if (unlikely(HIFC_SCQE_HAS_ERRCODE(v_scqe)))
|
|
pkg.status = UNF_IO_FAILED;
|
|
else
|
|
pkg.status = UNF_IO_SUCCESS;
|
|
|
|
ret = hifc_rcv_els_rsp_sts(v_hba, &pkg, rx_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_check_rport_is_valid(
|
|
const struct hifc_parent_queue_info_s *v_prntq_info,
|
|
unsigned int scqe_xid)
|
|
{
|
|
if (v_prntq_info->parent_ctx.cqm_parent_ctx_obj) {
|
|
if ((v_prntq_info->parent_sq_info.context_id &
|
|
HIFC_CQM_XID_MASK) == (scqe_xid & HIFC_CQM_XID_MASK))
|
|
return RETURN_OK;
|
|
}
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_offload_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int rport_valid = UNF_RETURN_ERROR;
|
|
unsigned int rport_index = 0;
|
|
unsigned int cache_id = 0;
|
|
unsigned int local_ctx_id = 0;
|
|
unsigned long flag = 0;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
struct hifcoe_scqe_sess_sts_s *offload_sts = NULL;
|
|
struct hifc_destroy_ctrl_info_s destroy_sqe_info = { 0 };
|
|
|
|
offload_sts = &v_scqe->sess_sts;
|
|
rport_index = offload_sts->wd1.conn_id;
|
|
cache_id = offload_sts->wd2.cid;
|
|
local_ctx_id = offload_sts->wd0.xid_qpn;
|
|
|
|
if (rport_index >= UNF_HIFC_MAXRPORT_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive an error offload status: rport index(0x%x) is invalid, cache id(0x%x)",
|
|
v_hba->port_cfg.port_id, rport_index, cache_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
prnt_qinfo = &v_hba->parent_queue_mgr->parent_queues[rport_index];
|
|
|
|
rport_valid = hifc_check_rport_is_valid(prnt_qinfo, local_ctx_id);
|
|
if (rport_valid != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive an error offload status: rport index(0x%x), context id(0x%x) is invalid",
|
|
v_hba->port_cfg.port_id, rport_index, local_ctx_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* off_load failed */
|
|
if (HIFC_GET_SCQE_STATUS(v_scqe) != HIFC_COMPLETION_STATUS_SUCCESS) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x), rport(0x%x), context id(0x%x), cache id(0x%x), offload failed",
|
|
v_hba->port_cfg.port_id, rport_index,
|
|
local_ctx_id, cache_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
spin_lock_irqsave(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
prnt_qinfo->parent_sq_info.cache_id = cache_id;
|
|
prnt_qinfo->offload_state = HIFC_QUEUE_STATE_OFFLOADED;
|
|
atomic_set(&prnt_qinfo->parent_sq_info.sq_cashed, UNF_TRUE);
|
|
|
|
if (prnt_qinfo->parent_sq_info.destroy_sqe.valid == UNF_TRUE) {
|
|
destroy_sqe_info.valid =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.valid;
|
|
|
|
destroy_sqe_info.rport_index =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_index;
|
|
|
|
destroy_sqe_info.time_out =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.time_out;
|
|
|
|
destroy_sqe_info.start_jiff =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.start_jiff;
|
|
|
|
destroy_sqe_info.rport_info.nport_id =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.nport_id;
|
|
destroy_sqe_info.rport_info.rport_index =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.rport_index;
|
|
destroy_sqe_info.rport_info.port_name =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.port_name;
|
|
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.valid = UNF_FALSE;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
hifc_pop_destroy_parent_queue_sqe((void *)v_hba, &destroy_sqe_info);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
unsigned int hifc_get_gs_req_and_rsp_pld_len(unsigned short cmd_code,
|
|
unsigned int *v_gs_pld_len,
|
|
unsigned int *v_gs_rsp_pld_len)
|
|
{
|
|
UNF_CHECK_VALID(0x4917, UNF_TRUE, v_gs_pld_len,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x4917, UNF_TRUE, v_gs_rsp_pld_len,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
switch (cmd_code) {
|
|
case NS_GPN_ID:
|
|
*v_gs_pld_len = UNF_GPNID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_GPNID_RSP_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case NS_GNN_ID:
|
|
*v_gs_pld_len = UNF_GNNID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_GNNID_RSP_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case NS_GFF_ID:
|
|
*v_gs_pld_len = UNF_GFFID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_GFFID_RSP_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case NS_GID_FT:
|
|
case NS_GID_PT:
|
|
*v_gs_pld_len = UNF_GID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_GID_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case NS_RFT_ID:
|
|
*v_gs_pld_len = UNF_RFTID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_RFTID_RSP_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case NS_RFF_ID:
|
|
*v_gs_pld_len = UNF_RFFID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_RFFID_RSP_PAYLOAD_LEN;
|
|
break;
|
|
case NS_GA_NXT:
|
|
*v_gs_pld_len = UNF_GID_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_GID_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
case NS_GIEL:
|
|
*v_gs_pld_len = UNF_RFTID_RSP_PAYLOAD_LEN;
|
|
*v_gs_rsp_pld_len = UNF_GID_ACC_PAYLOAD_LEN;
|
|
break;
|
|
|
|
default:
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]Unknown GS commond type(0x%x)", cmd_code);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_send_gs_via_parent(void *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned short ox_id, rx_id;
|
|
unsigned short cmd_code = UNF_ZERO;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int gs_pld_len = UNF_ZERO;
|
|
unsigned int gs_rsp_pld_len = UNF_ZERO;
|
|
void *gs_pld_addr = NULL;
|
|
struct hifc_hba_s *hba = NULL;
|
|
struct hifc_parent_sq_info_s *sq_info;
|
|
struct hifcoe_sqe_s sqe;
|
|
unsigned long long fram_phy_addr;
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
memset(&sqe, 0, sizeof(struct hifcoe_sqe_s));
|
|
|
|
sq_info = hifc_find_parent_sq_by_pkg(hba, v_pkg);
|
|
if (!sq_info) {
|
|
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Get NULL parent SQ information");
|
|
|
|
return ret;
|
|
}
|
|
|
|
cmd_code = HIFC_GET_GS_CMND_CODE(v_pkg->cmnd);
|
|
|
|
ret = hifc_get_gs_req_and_rsp_pld_len(cmd_code, &gs_pld_len,
|
|
&gs_rsp_pld_len);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) send GS SID(0x%x) DID(0x%x), get error GS request and response payload length",
|
|
hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gs_pld_addr = (void *)(HIFC_GET_CMND_PAYLOAD_ADDR(v_pkg));
|
|
fram_phy_addr = v_pkg->unf_cmnd_pload_bl.buf_dma_addr +
|
|
sizeof(struct unf_fchead_s);
|
|
|
|
if (cmd_code == NS_GID_FT || cmd_code == NS_GID_PT)
|
|
gs_pld_addr = (void *)(UNF_GET_GID_PAYLOAD(v_pkg));
|
|
|
|
/* Assemble the SQE Control Section part */
|
|
hifc_build_service_wqe_ctrl_section(
|
|
&sqe.ctrl_sl,
|
|
HIFC_BYTES_TO_QW_NUM(HIFC_SQE_TS_SIZE),
|
|
HIFC_BYTES_TO_QW_NUM(sizeof(struct hifcoe_variable_sge_s)));
|
|
/* Assemble the SQE Task Section part */
|
|
ox_id = UNF_GET_OXID(v_pkg) + hba->exit_base;
|
|
rx_id = UNF_GET_RXID(v_pkg);
|
|
hifc_build_service_wqe_ts_common(&sqe.ts_sl,
|
|
sq_info->rport_index, ox_id,
|
|
rx_id, HIFC_LSW(gs_pld_len));
|
|
hifc_build_gs_wqe_ts_req(&sqe, UNF_GETXCHGALLOCTIME(v_pkg));
|
|
|
|
hifc_build_els_gs_wqe_sge(&sqe, gs_pld_addr, fram_phy_addr, gs_pld_len,
|
|
sq_info->context_id, v_hba);
|
|
|
|
ret = hifc_parent_sq_enqueue(sq_info, &sqe);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_send_gs_cmnd(void *v_hba, struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct hifc_hba_s *hba = NULL;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
|
|
UNF_CHECK_VALID(0x4913, UNF_TRUE, v_hba, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x4914, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x4915, UNF_TRUE, UNF_GET_SFS_ENTRY(v_pkg),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x4916, UNF_TRUE, HIFC_GET_CMND_PAYLOAD_ADDR(v_pkg),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
HIFC_CHECK_PKG_ALLOCTIME(v_pkg);
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
prnt_qinfo = hifc_find_parent_queue_info_by_pkg(hba, v_pkg);
|
|
|
|
if (!prnt_qinfo) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) send GS SID(0x%x) DID(0x%x), get a null parent queue information",
|
|
hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (HIFC_RPORT_NOT_OFFLOADED(prnt_qinfo)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) send GS SID(0x%x) DID(0x%x), send GS Request before PLOGI",
|
|
hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret = hifc_send_gs_via_parent(v_hba, v_pkg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_get_bls_pld_len(struct unf_frame_pkg_s *v_pkg,
|
|
unsigned int *v_frame_len)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int rctl = 0;
|
|
|
|
UNF_CHECK_VALID(0x4917, UNF_TRUE, v_frame_len, return UNF_RETURN_ERROR);
|
|
|
|
rctl = UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head);
|
|
if (rctl == HIFC_RCTL_BLS_ACC) {
|
|
/* BA_ACC */
|
|
*v_frame_len = sizeof(struct unf_ba_acc_s);
|
|
} else if (rctl == HIFC_RCTL_BLS_RJT) {
|
|
/* BA_RJT */
|
|
*v_frame_len = sizeof(struct unf_ba_rjt_s);
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[warn]PKG Rclt(0x%x) not BLS ACC or RJT", rctl);
|
|
|
|
*v_frame_len = 0;
|
|
ret = UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_send_bls_via_cmdq(struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int rctl = 0;
|
|
unsigned int bls_pld_len = 0;
|
|
unsigned short rx_id = INVALID_VALUE16;
|
|
unsigned short ox_id = INVALID_VALUE16;
|
|
unsigned short exch_id = INVALID_VALUE16;
|
|
unsigned char *bls_pld_addr = NULL;
|
|
union hifc_cmdqe_u cmdqe;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
|
|
sq_info = hifc_find_parent_sq_by_pkg(v_hba, v_pkg);
|
|
if (!sq_info) {
|
|
HIFC_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[warn]Port(0x%x) send BLS SID_DID(0x%x_0x%x) with null parent queue information",
|
|
v_hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* Determine whether the value is ACC or RTJ and obtain the payload
|
|
* length of the ABTS_RSP
|
|
*/
|
|
ret = hifc_get_bls_pld_len(v_pkg, &bls_pld_len);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) cmdq send BLS PKG DID(0x%x) failed",
|
|
v_hba->port_index, v_pkg->frame_head.rctl_did);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
rctl = UNF_GET_FC_HEADER_RCTL(&v_pkg->frame_head);
|
|
exch_id = (v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]) & 0xffff;
|
|
if ((exch_id == INVALID_VALUE16) && (rctl == HIFC_RCTL_BLS_ACC)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) cmdq send BA_ACC with error RXID(0xffff)",
|
|
v_hba->port_index);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/*
|
|
* FC-FS-3 15.3.3.1 Description:
|
|
* The OX_ID and RX_ID shall be set to match the Exchange in which
|
|
* the ABTS frame was transmitted.
|
|
*/
|
|
rx_id = UNF_GET_FC_HEADER_RXID(&v_pkg->frame_head);
|
|
ox_id = UNF_GET_FC_HEADER_OXID(&v_pkg->frame_head);
|
|
|
|
if (exch_id != INVALID_VALUE16) {
|
|
exch_id = exch_id + v_hba->exit_base;
|
|
} else {
|
|
/* If the number is not an immediate number and the rxid is not
|
|
* allocated to the CM, the CM may correspond to the rjt.
|
|
*/
|
|
}
|
|
|
|
memset(&cmdqe, 0, sizeof(cmdqe));
|
|
hifc_build_cmdqe_common(&cmdqe, HIFC_CMDQE_ABTS_RSP, exch_id);
|
|
cmdqe.snd_abts_rsp.wd1.ox_id = ox_id;
|
|
cmdqe.snd_abts_rsp.wd1.port_id = v_hba->port_index;
|
|
cmdqe.snd_abts_rsp.wd1.payload_len = bls_pld_len;
|
|
cmdqe.snd_abts_rsp.wd1.rsp_type = ((rctl == HIFC_RCTL_BLS_ACC) ? 0 : 1);
|
|
cmdqe.snd_abts_rsp.wd2.conn_id = sq_info->rport_index;
|
|
cmdqe.snd_abts_rsp.wd2.scqn = hifc_get_rport_maped_sts_scqn(v_hba,
|
|
sq_info->rport_index);
|
|
cmdqe.snd_abts_rsp.wd3.xid = sq_info->context_id;
|
|
cmdqe.snd_abts_rsp.wd4.cid = sq_info->cache_id;
|
|
cmdqe.snd_abts_rsp.wd5.req_rx_id = rx_id;
|
|
bls_pld_addr = HIFC_GET_RSP_PAYLOAD_ADDR(v_pkg);
|
|
memcpy(cmdqe.snd_abts_rsp.payload, bls_pld_addr, bls_pld_len);
|
|
|
|
/* Send the ABTS_RSP command via ROOT CMDQ. */
|
|
ret = hifc_root_cmdq_enqueue(v_hba, &cmdqe, sizeof(cmdqe.snd_abts_rsp));
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) RPort(0x%x) send ABTS_RSP OXID(0x%x) RXID(0x%x) EXCHID(0x%x)",
|
|
v_hba->port_cfg.port_id, sq_info->rport_index, ox_id,
|
|
rx_id, exch_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_send_bls_via_parent(struct hifc_hba_s *v_hba,
|
|
struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned short ox_id = INVALID_VALUE16;
|
|
unsigned short rx_id = INVALID_VALUE16;
|
|
struct hifcoe_sqe_s sqe;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
|
|
UNF_CHECK_VALID(0x5015, UNF_TRUE, (v_pkg->type == UNF_PKG_BLS_REQ),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
memset(&sqe, 0, sizeof(struct hifcoe_sqe_s));
|
|
|
|
prnt_qinfo = hifc_find_parent_queue_info_by_pkg(v_hba, v_pkg);
|
|
if (!prnt_qinfo) {
|
|
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) send BLS SID_DID(0x%x_0x%x) with null parent queue information",
|
|
v_hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return ret;
|
|
}
|
|
|
|
sq_info = hifc_find_parent_sq_by_pkg(v_hba, v_pkg);
|
|
if (!sq_info) {
|
|
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) send ABTS SID_DID(0x%x_0x%x) with null parent queue information",
|
|
v_hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rx_id = UNF_GET_RXID(v_pkg);
|
|
ox_id = UNF_GET_OXID(v_pkg) + v_hba->exit_base;
|
|
|
|
/* Assemble the SQE Control Section part.
|
|
* The ABTS does not have Payload. bdsl=0
|
|
*/
|
|
hifc_build_service_wqe_ctrl_section(
|
|
&sqe.ctrl_sl,
|
|
HIFC_BYTES_TO_QW_NUM(HIFC_SQE_TS_SIZE), 0);
|
|
|
|
/* Assemble the SQE Task Section BLS Common part. The value of DW2
|
|
* of BLS WQE is Rsvd, and the value of DW2 is 0
|
|
*/
|
|
hifc_build_service_wqe_ts_common(&sqe.ts_sl, sq_info->rport_index,
|
|
ox_id, rx_id, 0);
|
|
|
|
/* Assemble the special part of the ABTS */
|
|
hifc_build_bls_wqe_ts_req(&sqe, v_pkg->frame_head.parameter,
|
|
UNF_GETXCHGALLOCTIME(v_pkg));
|
|
|
|
ret = hifc_parent_sq_enqueue(sq_info, &sqe);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_send_bls_cmnd(void *v_hba, struct unf_frame_pkg_s *v_pkg)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct hifc_hba_s *hba = NULL;
|
|
unsigned long flag = 0;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
|
|
UNF_CHECK_VALID(0x4913, UNF_TRUE, v_hba, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x4914, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x4913, UNF_TRUE, UNF_PKG_BLS_REQ == v_pkg->type,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
HIFC_CHECK_PKG_ALLOCTIME(v_pkg);
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
prnt_qinfo = hifc_find_parent_queue_info_by_pkg(hba, v_pkg);
|
|
if (!prnt_qinfo) {
|
|
HIFC_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) send BLS SID_DID(0x%x_0x%x) with null parent queue information",
|
|
hba->port_cfg.port_id, v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did);
|
|
|
|
return ret;
|
|
}
|
|
|
|
spin_lock_irqsave(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
if (HIFC_RPORT_OFFLOADED(prnt_qinfo)) {
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock,
|
|
flag);
|
|
|
|
/* INI: send ABTS_REQ via parent SQ */
|
|
ret = hifc_send_bls_via_parent(hba, v_pkg);
|
|
|
|
} else {
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock,
|
|
flag);
|
|
|
|
ret = hifc_send_bls_via_cmdq(hba, v_pkg);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_flush_sq_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
/*
|
|
* RCVD sq flush sts
|
|
* --->>> continue flush or clear done
|
|
*/
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
if (v_scqe->flush_sts.wd0.port_id != v_hba->port_index) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EVENT, UNF_CRITICAL,
|
|
"[err]Port(0x%x) clear_sts_port_idx(0x%x) not match hba_port_idx(0x%x), stage(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
v_scqe->clear_sts.wd0.port_id,
|
|
v_hba->port_index,
|
|
v_hba->q_set_stage);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (v_scqe->flush_sts.wd0.last_flush) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EVENT, UNF_INFO,
|
|
"[info]Port(0x%x) flush sq(0x%x) done, stage(0x%x)",
|
|
v_hba->port_cfg.port_id, v_hba->next_clearing_sq,
|
|
v_hba->q_set_stage);
|
|
|
|
/* If the Flush STS is last one, send cmd done */
|
|
ret = hifc_clear_sq_wqe_done(v_hba);
|
|
} else {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EVENT, UNF_MAJOR,
|
|
"[info]Port(0x%x) continue flush sq(0x%x), stage(0x%x)",
|
|
v_hba->port_cfg.port_id, v_hba->next_clearing_sq,
|
|
v_hba->q_set_stage);
|
|
|
|
ret = hifc_clear_pending_sq_wqe(v_hba);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_buf_clear_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
/*
|
|
* clear: fetched sq wqe
|
|
* ---to--->>> pending sq wqe
|
|
*/
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
if (v_scqe->clear_sts.wd0.port_id != v_hba->port_index) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EVENT, UNF_CRITICAL,
|
|
"[err]Port(0x%x) clear_sts_port_idx(0x%x) not match hba_port_idx(0x%x), stage(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
v_scqe->clear_sts.wd0.port_id,
|
|
v_hba->port_index,
|
|
v_hba->q_set_stage);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EVENT, UNF_KEVENT,
|
|
"[info]Port(0x%x) cleared all fetched wqe, start clear sq pending wqe, stage (0x%x)",
|
|
v_hba->port_cfg.port_id, v_hba->q_set_stage);
|
|
|
|
v_hba->q_set_stage = HIFC_QUEUE_SET_STAGE_FLUSHING;
|
|
ret = hifc_clear_pending_sq_wqe(v_hba);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_sess_rst_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int rport_index = INVALID_VALUE32;
|
|
unsigned long flag = 0;
|
|
struct hifc_parent_queue_info_s *parent_queue_info = NULL;
|
|
struct hifcoe_scqe_sess_sts_s *sess_sts =
|
|
(struct hifcoe_scqe_sess_sts_s *)(void *)v_scqe;
|
|
unsigned int ctx_flush_done;
|
|
unsigned int *ctx_dw = NULL;
|
|
int ret;
|
|
|
|
rport_index = sess_sts->wd1.conn_id;
|
|
if (rport_index >= UNF_HIFC_MAXRPORT_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive reset session cmd sts failed, invlaid rport_index(0x%x) status_code(0x%x) remain_cnt(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
rport_index,
|
|
sess_sts->ch.wd0.err_code,
|
|
sess_sts->ch.wd0.cqe_remain_cnt);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
parent_queue_info =
|
|
&v_hba->parent_queue_mgr->parent_queues[rport_index];
|
|
|
|
/*
|
|
* If only session reset is used, the offload status of sq remains
|
|
* unchanged. If a link is deleted, the offload status is set to
|
|
* destroying and is irreversible.
|
|
*/
|
|
spin_lock_irqsave(&parent_queue_info->parent_queue_state_lock, flag);
|
|
|
|
/*
|
|
* According to the fault tolerance principle, even if the connection
|
|
* deletion times out and the sts returns to delete the connection, one
|
|
* indicates thatthe cancel timer is successful, and 0 indicates that
|
|
* the timer is being processed.
|
|
*/
|
|
if (!cancel_delayed_work(
|
|
&parent_queue_info->parent_sq_info.del_work)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) rport_index(0x%x) delete rport timer maybe timeout",
|
|
v_hba->port_cfg.port_id,
|
|
rport_index);
|
|
}
|
|
|
|
/*
|
|
* If the SessRstSts is returned too late and the Parent Queue Info
|
|
* resource is released, OK is returned.
|
|
*/
|
|
if (parent_queue_info->offload_state != HIFC_QUEUE_STATE_DESTROYING) {
|
|
spin_unlock_irqrestore(
|
|
&parent_queue_info->parent_queue_state_lock, flag);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[info]Port(0x%x) reset session cmd complete, no need to free parent qinfo, rport_index(0x%x) status_code(0x%x) remain_cnt(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
rport_index,
|
|
sess_sts->ch.wd0.err_code,
|
|
sess_sts->ch.wd0.cqe_remain_cnt);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
if (parent_queue_info->parent_ctx.cqm_parent_ctx_obj) {
|
|
ctx_dw = (unsigned int *)((void *)(parent_queue_info->parent_ctx.cqm_parent_ctx_obj->vaddr));
|
|
ctx_flush_done = ctx_dw[HIFC_CTXT_FLUSH_DONE_DW_POS] &
|
|
HIFC_CTXT_FLUSH_DONE_MASK_BE;
|
|
/* memory barr */
|
|
mb();
|
|
if (ctx_flush_done == 0) {
|
|
spin_unlock_irqrestore(
|
|
&parent_queue_info->parent_queue_state_lock,
|
|
flag);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN,
|
|
UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) rport(0x%x) flushdone is not set, delay to free parent session",
|
|
v_hba->port_cfg.port_id, rport_index);
|
|
|
|
/* If flushdone bit is not set,delay free Sq info */
|
|
ret = queue_delayed_work(
|
|
v_hba->work_queue,
|
|
&parent_queue_info->parent_sq_info.flush_done_tmo_work,
|
|
(unsigned long)
|
|
msecs_to_jiffies((unsigned int)
|
|
HIFC_SQ_WAIT_FLUSH_DONE_TIMEOUT_MS));
|
|
if (ret == (int)false) {
|
|
HIFC_HBA_STAT(
|
|
v_hba,
|
|
HIFC_STAT_PARENT_SQ_QUEUE_DELAYED_WORK);
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) rport(0x%x) queue delayed work failed iret:%d",
|
|
v_hba->port_cfg.port_id,
|
|
rport_index, ret);
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&parent_queue_info->parent_queue_state_lock,
|
|
flag);
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) begin to free parent session with rport_index(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
rport_index);
|
|
|
|
hifc_free_parent_queue_info(v_hba, parent_queue_info);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_clear_srq_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
/*
|
|
* clear ELS/Immi SRQ
|
|
* ---then--->>> Destroy SRQ
|
|
*/
|
|
|
|
struct hifc_hba_s *hba = v_hba;
|
|
struct hifc_srq_info_s *srq_info = NULL;
|
|
|
|
if (HIFC_GET_SCQE_STATUS(v_scqe) != 0) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) clear srq failed, status(0x%x)",
|
|
v_hba->port_cfg.port_id,
|
|
HIFC_GET_SCQE_STATUS(v_scqe));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
srq_info = &hba->els_srq_info;
|
|
|
|
/*
|
|
* 1: cancel timer succeed
|
|
* 0: the timer is being processed, the SQ is released when the timer
|
|
* times out
|
|
*/
|
|
if (cancel_delayed_work(&srq_info->del_work)) {
|
|
/*
|
|
* not free srq resource, it will be freed on hba remove
|
|
*/
|
|
srq_info->state = HIFC_CLEAN_DONE;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_marker_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int ox_id = INVALID_VALUE32;
|
|
unsigned int rx_id = INVALID_VALUE32;
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
struct hifcoe_scqe_itmf_marker_sts_s *marker_sts = NULL;
|
|
|
|
marker_sts = &v_scqe->itmf_marker_sts;
|
|
ox_id = (unsigned int)marker_sts->wd1.ox_id;
|
|
ox_id = ox_id - v_hba->exit_base;
|
|
rx_id = (unsigned int)marker_sts->wd1.rx_id;
|
|
pkg.frame_head.oxid_rxid = rx_id | (unsigned int)(ox_id) << 16;
|
|
|
|
pkg.frame_head.csctl_sid = marker_sts->wd3.sid;
|
|
pkg.frame_head.rctl_did = marker_sts->wd2.did;
|
|
|
|
/* 1. set pkg status */
|
|
if (unlikely(HIFC_SCQE_HAS_ERRCODE(v_scqe)))
|
|
pkg.status = UNF_IO_FAILED;
|
|
else
|
|
pkg.status = UNF_IO_SUCCESS;
|
|
|
|
/* 2 .process rcvd marker STS: set exchange state */
|
|
ret = hifc_rcv_tmf_marker_sts(v_hba, &pkg, ox_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int hifc_scq_rcv_abts_marker_sts(struct hifc_hba_s *v_hba,
|
|
union hifcoe_scqe_u *v_scqe)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int ox_id = INVALID_VALUE32;
|
|
unsigned int rx_id = INVALID_VALUE32;
|
|
struct unf_frame_pkg_s pkg = { 0 };
|
|
|
|
struct hifcoe_scqe_abts_marker_sts_s *abts_sts = NULL;
|
|
|
|
abts_sts = &v_scqe->abts_marker_sts;
|
|
if (!abts_sts) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]ABTS marker STS is NULL");
|
|
return ret;
|
|
}
|
|
|
|
ox_id = (unsigned int)abts_sts->wd1.ox_id;
|
|
ox_id = ox_id - v_hba->exit_base;
|
|
rx_id = (unsigned int)abts_sts->wd1.rx_id;
|
|
pkg.frame_head.oxid_rxid = rx_id | (unsigned int)(ox_id) << 16;
|
|
pkg.frame_head.csctl_sid = abts_sts->wd3.sid;
|
|
pkg.frame_head.rctl_did = abts_sts->wd2.did;
|
|
/* abts marker abts_maker_status as ucode stat */
|
|
pkg.abts_maker_status = (unsigned int)abts_sts->wd3.io_state;
|
|
|
|
if (unlikely(HIFC_SCQE_HAS_ERRCODE(v_scqe)))
|
|
pkg.status = UNF_IO_FAILED;
|
|
else
|
|
pkg.status = UNF_IO_SUCCESS;
|
|
|
|
ret = hifc_rcv_abts_marker_sts(v_hba, &pkg, ox_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hifc_handle_aeq_offload_err(struct hifc_hba_s *v_hba,
|
|
struct hifcoe_aqe_data_s *v_aeq_msg)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
struct hifcoe_aqe_data_s *aeq_msg;
|
|
unsigned int rport_index = 0;
|
|
unsigned int local_ctx_id = 0;
|
|
struct hifc_parent_queue_info_s *prnt_qinfo = NULL;
|
|
struct hifc_destroy_ctrl_info_s destroy_sqe_info = { 0 };
|
|
unsigned long flag = 0;
|
|
|
|
aeq_msg = v_aeq_msg;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive off_load Err Event, EvtCode(0x%x) Conn_id(0x%x) Xid(0x%x)",
|
|
v_hba->port_cfg.port_id, aeq_msg->wd0.evt_code,
|
|
aeq_msg->wd0.conn_id, aeq_msg->wd1.xid);
|
|
|
|
/* Currently, only the offload failure caused by insufficient scqe is
|
|
* processed. Other errors are not processed temporarily.
|
|
*/
|
|
if (unlikely(aeq_msg->wd0.evt_code !=
|
|
FCOE_ERROR_OFFLOAD_LACKOF_SCQE_FAIL)) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive an unsupported error code of AEQ Event, EvtCode(0x%x) Conn_id(0x%x)",
|
|
v_hba->port_cfg.port_id, aeq_msg->wd0.evt_code,
|
|
aeq_msg->wd0.conn_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
HIFC_SCQ_ERR_TYPE_STAT(v_hba, FCOE_ERROR_OFFLOAD_LACKOF_SCQE_FAIL);
|
|
|
|
rport_index = aeq_msg->wd0.conn_id;
|
|
local_ctx_id = aeq_msg->wd1.xid;
|
|
|
|
if (rport_index >= UNF_HIFC_MAXRPORT_NUM) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive an error offload status: rport index(0x%x) is invalid, Xid(0x%x)",
|
|
v_hba->port_cfg.port_id, rport_index,
|
|
aeq_msg->wd1.xid);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
prnt_qinfo = &v_hba->parent_queue_mgr->parent_queues[rport_index];
|
|
if (hifc_check_rport_is_valid(prnt_qinfo, local_ctx_id) != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) receive an error offload status: rport index(0x%x), context id(0x%x) is invalid",
|
|
v_hba->port_cfg.port_id, rport_index, local_ctx_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
spin_lock_irqsave(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
/* The offload status is restored only
|
|
* when the offload status is offloading
|
|
*/
|
|
if (prnt_qinfo->offload_state == HIFC_QUEUE_STATE_OFFLOADING)
|
|
prnt_qinfo->offload_state = HIFC_QUEUE_STATE_INITIALIZED;
|
|
|
|
spin_unlock_irqrestore(&prnt_qinfo->parent_queue_state_lock, flag);
|
|
|
|
if (prnt_qinfo->parent_sq_info.destroy_sqe.valid == UNF_TRUE) {
|
|
destroy_sqe_info.valid =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.valid;
|
|
destroy_sqe_info.rport_index =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_index;
|
|
destroy_sqe_info.time_out =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.time_out;
|
|
destroy_sqe_info.start_jiff =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.start_jiff;
|
|
|
|
destroy_sqe_info.rport_info.nport_id =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.nport_id;
|
|
|
|
destroy_sqe_info.rport_info.rport_index =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.rport_index;
|
|
|
|
destroy_sqe_info.rport_info.port_name =
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.port_name;
|
|
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.valid = UNF_FALSE;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) pop up delay destroy parent sq, sqe start time 0x%llx, timeout value 0x%x, rport index 0x%x, offload state 0x%x",
|
|
v_hba->port_cfg.port_id,
|
|
destroy_sqe_info.start_jiff,
|
|
destroy_sqe_info.time_out,
|
|
prnt_qinfo->parent_sq_info.destroy_sqe.rport_info.rport_index,
|
|
HIFC_QUEUE_STATE_INITIALIZED);
|
|
|
|
ret = hifc_free_parent_resource(v_hba,
|
|
&destroy_sqe_info.rport_info);
|
|
if (ret != RETURN_OK) {
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) pop delay destroy parent sq failed, rport index 0x%x, rport nport id 0x%x",
|
|
v_hba->port_cfg.port_id,
|
|
destroy_sqe_info.rport_info.rport_index,
|
|
destroy_sqe_info.rport_info.nport_id);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|