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

1339 lines
43 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Huawei Hifc PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*
*/
#include "hifc_knl_adp.h"
#include "unf_log.h"
#include "unf_exchg.h"
#include "unf_rport.h"
#include "unf_io.h"
#include "unf_portman.h"
#include "unf_io_abnormal.h"
#define UNF_GET_FCP_CTL(pkg) ((((pkg)->status) >> 8) & 0xFF)
#define UNF_GET_SCSI_STATUS(pkg) (((pkg)->status) & 0xFF)
static unsigned int unf_io_success_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status);
static unsigned int unf_ini_error_default_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status);
static unsigned int unf_io_under_flow_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status);
static unsigned int unf_ini_dif_error_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status);
struct unf_ini_error_handler {
unsigned int error_code;
unsigned int (*pfn_unf_ini_error_handler)(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status);
};
struct unf_ini_error_handler ini_error_handler_table[] = {
{ UNF_IO_SUCCESS, unf_io_success_handler },
{ UNF_IO_ABORTED, unf_ini_error_default_handler },
{ UNF_IO_FAILED, unf_ini_error_default_handler },
{ UNF_IO_ABORT_ABTS, unf_ini_error_default_handler },
{ UNF_IO_ABORT_LOGIN, unf_ini_error_default_handler },
{ UNF_IO_ABORT_REET, unf_ini_error_default_handler },
{ UNF_IO_ABORT_FAILED, unf_ini_error_default_handler },
{ UNF_IO_OUTOF_ORDER, unf_ini_error_default_handler },
{ UNF_IO_FTO, unf_ini_error_default_handler },
{ UNF_IO_LINK_FAILURE, unf_ini_error_default_handler },
{ UNF_IO_OVER_FLOW, unf_ini_error_default_handler },
{ UNF_IO_RSP_OVER, unf_ini_error_default_handler },
{ UNF_IO_LOST_FRAME, unf_ini_error_default_handler },
{ UNF_IO_UNDER_FLOW, unf_io_under_flow_handler },
{ UNF_IO_HOST_PROG_ERROR, unf_ini_error_default_handler },
{ UNF_IO_SEST_PROG_ERROR, unf_ini_error_default_handler },
{ UNF_IO_INVALID_ENTRY, unf_ini_error_default_handler },
{ UNF_IO_ABORT_SEQ_NOT, unf_ini_error_default_handler },
{ UNF_IO_REJECT, unf_ini_error_default_handler },
{ UNF_IO_EDC_IN_ERROR, unf_ini_error_default_handler },
{ UNF_IO_EDC_OUT_ERROR, unf_ini_error_default_handler },
{ UNF_IO_UNINIT_KEK_ERR, unf_ini_error_default_handler },
{ UNF_IO_DEK_OUTOF_RANGE, unf_ini_error_default_handler },
{ UNF_IO_KEY_UNWRAP_ERR, unf_ini_error_default_handler },
{ UNF_IO_KEY_TAG_ERR, unf_ini_error_default_handler },
{ UNF_IO_KEY_ECC_ERR, unf_ini_error_default_handler },
{ UNF_IO_BLOCK_SIZE_ERROR, unf_ini_error_default_handler },
{ UNF_IO_ILLEGAL_CIPHER_MODE, unf_ini_error_default_handler },
{ UNF_IO_CLEAN_UP, unf_ini_error_default_handler },
{ UNF_IO_ABORTED_BY_TARGET, unf_ini_error_default_handler },
{ UNF_IO_TRANSPORT_ERROR, unf_ini_error_default_handler },
{ UNF_IO_LINK_FLASH, unf_ini_error_default_handler },
{ UNF_IO_TIMEOUT, unf_ini_error_default_handler },
{ UNF_IO_DMA_ERROR, unf_ini_error_default_handler },
{ UNF_IO_DIF_ERROR, unf_ini_dif_error_handler },
{ UNF_IO_INCOMPLETE, unf_ini_error_default_handler },
{ UNF_IO_DIF_REF_ERROR, unf_ini_dif_error_handler },
{ UNF_IO_DIF_GEN_ERROR, unf_ini_dif_error_handler }
};
void unf_done_ini_xchg(struct unf_xchg_s *v_xchg)
{
/*
* About I/O Done
* 1. normal case
* 2. Send ABTS & RCVD RSP
* 3. Send ABTS & timer timeout
*/
struct unf_scsi_cmd_s scsi_cmd = { 0 };
unsigned long flags = 0;
struct unf_scsi_cmd_info_s *scsi_cmnd_info = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
unsigned int scsi_id = 0;
UNF_CHECK_VALID(0x1301, TRUE, v_xchg, return);
/* scsi_cmnd validity check */
if (unlikely(!v_xchg->scsi_cmnd_info.scsi_cmnd))
return;
/* 1. Free RX_ID for INI SIRT: Do not care
* 2. set & check exchange state
*
* for Set UP_ABORT Tag:
* 1) L_Port destroy
* 2) AC power down
* 3) LUN reset
* 4) Target/Session reset
* 5) SCSI send Abort(ABTS)
*/
spin_lock_irqsave(&v_xchg->xchg_state_lock, flags);
v_xchg->io_state |= INI_IO_STATE_DONE;
if (unlikely(v_xchg->io_state & (INI_IO_STATE_UPABORT |
INI_IO_STATE_UPSEND_ERR |
INI_IO_STATE_TMF_ABORT))) {
/*
* a. UPABORT: scsi have send ABTS
* --->>> do not call SCSI_Done, return directly
* b. UPSEND_ERR: error happened duiring LLDD send SCSI_CMD
* --->>> do not call SCSI_Done, scsi need retry
*/
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_KEVENT,
"[event]Exchange(0x%p) Cmdsn:0x%lx upCmd:%p oxid(0x%x) with state(0x%x) has been aborted or send error",
v_xchg, (unsigned long)v_xchg->cmnd_sn,
v_xchg->scsi_cmnd_info.scsi_cmnd, v_xchg->ox_id,
v_xchg->io_state);
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
/* here, return directly */
return;
}
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
/* 3. Get scsi_cmnd info */
scsi_cmnd_info = &v_xchg->scsi_cmnd_info;
/*
* 4. Set:
* scsi_cmnd;
* cmnd_done_func;
* cmnd up_level_done;
* sense_buff_addr;
* resid_length;
* cmnd_result;
* dif_info
*
* UNF_SCSI_CMND <<-- UNF_SCSI_CMND_INFO
*/
UNF_SET_HOST_CMND((&scsi_cmd), scsi_cmnd_info->scsi_cmnd);
UNF_SET_CMND_DONE_FUNC((&scsi_cmd), scsi_cmnd_info->pfn_done);
scsi_cmd.drv_private = v_xchg->lport;
if (unlikely((UNF_SCSI_STATUS(v_xchg->scsi_cmnd_info.result)) &
FCP_SNS_LEN_VALID_MASK)) {
unf_save_sense_data(
scsi_cmd.upper_cmnd,
(char *)v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu,
SCSI_SENSE_DATA_LEN);
}
UNF_SET_RESID((&scsi_cmd), (unsigned int)v_xchg->resid_len);
UNF_SET_CMND_RESULT((&scsi_cmd), scsi_cmnd_info->result);
memcpy(&scsi_cmd.dif_info, &v_xchg->dif_info,
sizeof(struct dif_info_s));
scsi_id = scsi_cmnd_info->scsi_id;
/* 5. call scsi_cmnd_done func: unf_scsi_done */
UNF_DONE_SCSI_CMND(&scsi_cmd);
/* 6. Update IO result CNT */
if (likely(v_xchg->lport)) {
scsi_image_table = &v_xchg->lport->rport_scsi_table;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id,
(scsi_cmnd_info->result >> 16));
}
}
static inline unsigned int unf_ini_get_sgl_entry_buf(
ini_get_sgl_entry_buf pfn_unf_ini_get_sgl,
void *v_cmnd,
void *v_driver_sgl,
void **v_upper_sgl,
unsigned int *v_req_index,
unsigned int *v_index,
char **v_buf,
unsigned int *v_buf_len)
{
if (unlikely(!pfn_unf_ini_get_sgl)) {
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"Command(0x%p) Get sgl Entry func Null.", v_cmnd);
return UNF_RETURN_ERROR;
}
return pfn_unf_ini_get_sgl(v_cmnd, v_driver_sgl, v_upper_sgl,
v_req_index, v_index, v_buf, v_buf_len);
}
unsigned int unf_ini_get_sgl_entry(void *v_pkg, char **v_buf,
unsigned int *v_buf_len)
{
struct unf_frame_pkg_s *pkg = (struct unf_frame_pkg_s *)v_pkg;
struct unf_xchg_s *xchg = NULL;
unsigned int ret = RETURN_OK;
UNF_CHECK_VALID(0x1305, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1306, UNF_TRUE, v_buf, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1307, UNF_TRUE, v_buf_len, return UNF_RETURN_ERROR);
xchg = (struct unf_xchg_s *)pkg->xchg_contex;
UNF_CHECK_VALID(0x1308, UNF_TRUE, xchg, return UNF_RETURN_ERROR);
/* Get SGL Entry buffer for INI Mode */
ret = unf_ini_get_sgl_entry_buf(
xchg->scsi_cmnd_info.pfn_unf_get_sgl_entry_buf,
xchg->scsi_cmnd_info.scsi_cmnd,
NULL,
&xchg->req_sgl_info.sgl,
&xchg->scsi_cmnd_info.port_id,
&((xchg->req_sgl_info).entry_index),
v_buf, v_buf_len);
return ret;
}
unsigned int unf_ini_get_dif_sgl_entry(void *v_pkg, char **v_buf,
unsigned int *v_buf_len)
{
struct unf_frame_pkg_s *pkg = (struct unf_frame_pkg_s *)v_pkg;
struct unf_xchg_s *xchg = NULL;
unsigned int ret = RETURN_OK;
UNF_CHECK_VALID(0x1305, UNF_TRUE, v_pkg, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1306, UNF_TRUE, v_buf, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1307, UNF_TRUE, v_buf_len, return UNF_RETURN_ERROR);
xchg = (struct unf_xchg_s *)pkg->xchg_contex;
UNF_CHECK_VALID(0x1308, UNF_TRUE, xchg, return UNF_RETURN_ERROR);
/* Get SGL Entry buffer for INI Mode */
ret = unf_ini_get_sgl_entry_buf(
xchg->scsi_cmnd_info.pfn_unf_get_sgl_entry_buf,
xchg->scsi_cmnd_info.scsi_cmnd,
NULL,
&xchg->dif_sgl_info.sgl,
&xchg->scsi_cmnd_info.port_id,
&xchg->dif_sgl_info.entry_index,
v_buf, v_buf_len);
return ret;
}
unsigned int unf_get_uplevel_cmnd_errcode(
struct unf_ini_error_code_s *v_err_table,
unsigned int v_err_table_count,
unsigned int v_drv_err_code)
{
unsigned int i;
/* fail return UNF_RETURN_ERROR,adjust by up level */
if (unlikely(!v_err_table)) {
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"Error Code Table is Null, Error Code(0x%x).",
v_drv_err_code);
return (unsigned int)UNF_SCSI_HOST(DID_ERROR);
}
for (i = 0; i < v_err_table_count; i++) {
if (v_drv_err_code == v_err_table[i].drv_err_code)
return v_err_table[i].ap_err_code;
}
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Unsupported Ap Error code by Error Code(0x%x).",
v_drv_err_code);
return (unsigned int)UNF_SCSI_HOST(DID_ERROR);
}
static unsigned int unf_ini_status_handle(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg)
{
unsigned int i;
unsigned int ret;
unsigned int status;
for (i = 0;
i < sizeof(ini_error_handler_table) /
sizeof(struct unf_ini_error_handler);
i++) {
if (UNF_GET_LL_ERR(v_pkg) ==
ini_error_handler_table[i].error_code) {
status = unf_get_uplevel_cmnd_errcode(
v_xchg->scsi_cmnd_info.err_code_table,
v_xchg->scsi_cmnd_info.err_code_table_cout,
UNF_GET_LL_ERR(v_pkg));
if (ini_error_handler_table[i].pfn_unf_ini_error_handler) {
ret = ini_error_handler_table[i].pfn_unf_ini_error_handler(
v_xchg,
v_pkg,
status);
} else {
/* set exchange->result
* ---to--->>>scsi_result
*/
ret = unf_ini_error_default_handler(v_xchg,
v_pkg,
status);
}
return ret;
}
}
status = unf_get_uplevel_cmnd_errcode(
v_xchg->scsi_cmnd_info.err_code_table,
v_xchg->scsi_cmnd_info.err_code_table_cout,
UNF_IO_SOFT_ERR);
ret = unf_ini_error_default_handler(v_xchg, v_pkg, status);
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Can not find com status, SID(0x%x) exchange(0x%p) com_status(0x%x) DID(0x%x) hot_pool_tag(0x%x)",
v_xchg->sid, v_xchg, v_pkg->status,
v_xchg->did, v_xchg->hot_pool_tag);
return ret;
}
static void unf_analysis_response_info(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int *v_status)
{
unsigned char *resp_buf = NULL;
/* LL_Driver use Little End, and copy RSP_INFO to COM_Driver */
if (v_pkg->unf_rsp_pload_bl.buffer_ptr) {
if (v_pkg->unf_rsp_pload_bl.buffer_ptr[0] !=
UNF_FCP_TM_RSP_COMPLETE) {
*v_status = UNF_SCSI_HOST(DID_BUS_BUSY);
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%p) DID bus busy, scsi_status(0x%x)",
v_xchg->lport, UNF_GET_SCSI_STATUS(v_pkg));
}
} else {
resp_buf =
(unsigned char *)v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu;
if ((resp_buf)) {
/* If chip use Little End, then change it to Big End */
if ((v_pkg->byte_orders & UNF_BIT_3) == 0)
unf_cpu_to_big_end(
resp_buf,
v_pkg->unf_rsp_pload_bl.length);
/* Chip DAM data with Big End */
if (resp_buf[3] != UNF_FCP_TM_RSP_COMPLETE) {
*v_status = UNF_SCSI_HOST(DID_BUS_BUSY);
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT,
UNF_WARN,
"[warn]Port(0x%p) DID bus busy, scsi_status(0x%x)",
v_xchg->lport,
UNF_GET_SCSI_STATUS(v_pkg));
}
}
}
}
static void unf_analysis_sense_info(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg)
{
#define MIN(x, y) ((x) < (y) ? (x) : (y))
unsigned int length = 0;
/* 4 bytes Align */
length = v_pkg->unf_sense_pload_bl.length;
if (length % 4 != 0)
length = 4 * ((length / 4) + 1);
/*
* If have sense info then copy directly
* else, the chip has been dma the data to sense buffer
*/
if (v_pkg->unf_sense_pload_bl.buffer_ptr) {
/* carry from wqe by ll_driver & ucode: do not used */
unf_cpu_to_big_end(v_pkg->unf_sense_pload_bl.buffer_ptr,
length);
memcpy(v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu,
v_pkg->unf_sense_pload_bl.buffer_ptr,
(unsigned int)MIN(UNF_SCSI_SENSE_DATA_LEN,
v_pkg->unf_sense_pload_bl.length));
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[info]LPort(0x%p), Sense Length(%u), Scsi Status(0x%x).",
v_xchg->lport,
v_pkg->unf_sense_pload_bl.length,
UNF_GET_SCSI_STATUS(v_pkg));
} else if ((length != 0) &&
(v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu)) {
/* has been dma to exchange buffer */
if ((v_pkg->byte_orders & UNF_BIT_4) == 0) {
unf_cpu_to_big_end(((unsigned char *)
(v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu)) +
v_pkg->unf_rsp_pload_bl.length,
v_pkg->unf_sense_pload_bl.length);
}
memcpy(v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu,
((unsigned char *)
(v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu)) +
v_pkg->unf_rsp_pload_bl.length,
(unsigned int)MIN(UNF_SCSI_SENSE_DATA_LEN,
v_pkg->unf_sense_pload_bl.length));
}
}
static unsigned int unf_io_success_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status)
{
unsigned char scsi_status;
unsigned char control;
unsigned int status = v_status;
UNF_CHECK_VALID(0x1311, TRUE, v_xchg, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1312, TRUE, v_pkg, return UNF_RETURN_ERROR);
control = UNF_GET_FCP_CTL(v_pkg);
scsi_status = UNF_GET_SCSI_STATUS(v_pkg);
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_INFO,
"[info]Port(0x%p), Exchange(0x%p) Completed, Control(0x%x), Scsi Status(0x%x)",
v_xchg->lport, v_xchg, control, scsi_status);
if (control & FCP_SNS_LEN_VALID_MASK) {
/* has sense info */
if (scsi_status == FCP_SCSI_STATUS_GOOD)
scsi_status = SCSI_CHECK_CONDITION;
unf_analysis_sense_info(v_xchg, v_pkg);
} else {
/*
* When the FCP_RSP_LEN_VALID bit is set to one,
* the content of the SCSI STATUS CODE field is not reliable
* and shall be ignored by the application client.
*/
if (control & FCP_RSP_LEN_VALID_MASK)
unf_analysis_response_info(v_xchg, v_pkg, &status);
}
v_xchg->scsi_cmnd_info.result = status |
UNF_SCSI_STATUS(scsi_status);
return RETURN_OK;
}
static unsigned int unf_ini_error_default_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status)
{
/* set exchange->result ---to--->>> scsi_cmnd->result */
UNF_CHECK_VALID(0x1313, TRUE, v_xchg, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1314, TRUE, v_pkg, return UNF_RETURN_ERROR);
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_ABNORMAL, UNF_WARN,
"[warn]SID(0x%x) exchange(0x%p) com_status(0x%x) up_status(0x%x) DID(0x%x) hot_pool_tag(0x%x) response_len(0x%x)",
v_xchg->sid, v_xchg, v_pkg->status, v_status,
v_xchg->did, v_xchg->hot_pool_tag, v_pkg->residus_len);
v_xchg->scsi_cmnd_info.result =
v_status | UNF_SCSI_STATUS(UNF_GET_SCSI_STATUS(v_pkg));
return RETURN_OK;
}
static unsigned int unf_ini_dif_error_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status)
{
struct unf_dif_control_info_s *dif_control = NULL;
unsigned char *sense_data = NULL;
unsigned short sense_code = 0;
UNF_CHECK_VALID(0x1315, TRUE, v_xchg, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1316, TRUE, v_pkg, return UNF_RETURN_ERROR);
UNF_REFERNCE_VAR(v_status);
/*
* According to DIF scheme
* drive set check condition(0x2) when dif error occurs,
* and returns the values base on the upper-layer verification resule
* Check sequence: crc,Lba,App,
* if CRC error is found, the subsequent check is not performed
*/
v_xchg->scsi_cmnd_info.result =
UNF_SCSI_STATUS(SCSI_CHECK_CONDITION);
dif_control = &v_pkg->dif_control;
if (v_pkg->status_sub_code == 0) {
UNF_GET_DIF_ERROR_LEVEL1(v_xchg, dif_control, 0,
sense_code, DRV_DIF_CRC_ERR);
UNF_GET_DIF_ERROR_LEVEL2(v_xchg, dif_control, 0,
sense_code, DRV_DIF_LBA_ERR);
UNF_GET_DIF_ERROR_LEVEL3(v_xchg, dif_control, 0,
sense_code, DRV_DIF_APP_ERR);
if (sense_code == 0) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Unexpected DIF unwonted, operation_code(0x%x) actual DIF(0x%llx) expected DIF(0x%llx)",
v_xchg->dif_control.protect_opcode,
*(unsigned long long *)
&dif_control->actual_dif[0],
*(unsigned long long *)
&dif_control->expected_dif[0]);
}
} else {
sense_code = (unsigned short)v_pkg->status_sub_code;
}
sense_data = (unsigned char *)
v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu;
memset(sense_data, 0, SCSI_SENSE_DATA_LEN);
sense_data[0] = 0x70; /* response code */
sense_data[2] = ILLEGAL_REQUEST; /* sense key:0x05; */
sense_data[7] = 0x7; /* additional sense length */
sense_data[12] = (unsigned char)(sense_code >> 8);
sense_data[13] = (unsigned char)sense_code;
/* valid sense data length snscode[13] */
return RETURN_OK;
}
static unsigned int unf_io_under_flow_handler(struct unf_xchg_s *v_xchg,
struct unf_frame_pkg_s *v_pkg,
unsigned int v_status)
{
/* under flow: residlen > 0 */
UNF_CHECK_VALID(0x1317, TRUE, v_xchg, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1318, TRUE, v_pkg, return UNF_RETURN_ERROR);
if ((v_xchg->fcp_cmnd.cdb[0] != SCSIOPC_REPORT_LUN) &&
(v_xchg->fcp_cmnd.cdb[0] != SCSIOPC_INQUIRY)) {
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_INFO,
"[info]IO under flow: SID(0x%x) exchange(0x%p) com status(0x%x) up_status(0x%x) DID(0x%x) hot_pool_tag(0x%x) response SID(0x%x)",
v_xchg->sid, v_xchg, v_pkg->status, v_status,
v_xchg->did, v_xchg->hot_pool_tag,
v_pkg->residus_len);
}
v_xchg->resid_len = (int)v_pkg->residus_len;
(void)unf_io_success_handler(v_xchg, v_pkg, v_status);
return RETURN_OK;
}
void unf_complete_cmnd(struct unf_scsi_cmd_s *v_scsi_cmnd, unsigned int result)
{
/*
* Exception during process Que_CMND
* 1. L_Port == NULL;
* 2. L_Port == removing;
* 3. R_Port == NULL;
* 4. Xchg == NULL.
*/
UNF_CHECK_VALID(0x1319, TRUE, UNF_GET_CMND_DONE_FUNC(v_scsi_cmnd),
return);
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_INFO,
"[info]Command(0x%p), Result(0x%x).", v_scsi_cmnd, result);
UNF_SET_CMND_RESULT(v_scsi_cmnd, result);
/* struct unf_scsi_cmd_s->pfn_done -->> unf_scsi_done */
UNF_DONE_SCSI_CMND(v_scsi_cmnd);
}
static inline void unf_bind_xchg_scsi_cmd(struct unf_xchg_s *v_xchg,
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
struct unf_scsi_cmd_info_s *scsi_cmnd_info = NULL;
scsi_cmnd_info = &v_xchg->scsi_cmnd_info;
/* UNF_SCSI_CMND_INFO <<-- UNF_SCSI_CMND */
scsi_cmnd_info->err_code_table =
UNF_GET_ERR_CODE_TABLE(v_scsi_cmnd);
scsi_cmnd_info->err_code_table_cout =
UNF_GET_ERR_CODE_TABLE_COUNT(v_scsi_cmnd);
scsi_cmnd_info->pfn_done = UNF_GET_CMND_DONE_FUNC(v_scsi_cmnd);
scsi_cmnd_info->scsi_cmnd = UNF_GET_HOST_CMND(v_scsi_cmnd);
scsi_cmnd_info->sense_buf =
(char *)UNF_GET_SENSE_BUF_ADDR(v_scsi_cmnd);
/* unf_get_frame_entry_buf */
scsi_cmnd_info->pfn_unf_get_sgl_entry_buf =
UNF_GET_SGL_ENTRY_BUF_FUNC(v_scsi_cmnd);
scsi_cmnd_info->sgl = UNF_GET_CMND_SGL(v_scsi_cmnd);
scsi_cmnd_info->time_out = v_scsi_cmnd->time_out;
scsi_cmnd_info->entry_cnt = v_scsi_cmnd->entry_count;
scsi_cmnd_info->port_id = (unsigned int)v_scsi_cmnd->port_id;
scsi_cmnd_info->scsi_id = UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd);
}
unsigned int unf_ini_scsi_completed(void *v_lport,
struct unf_frame_pkg_s *v_pkg)
{
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = NULL;
struct unf_fcp_cmnd_s *fcp_cmnd = NULL;
unsigned int control;
unsigned short xchg_tag;
unsigned int ret;
unsigned long flag = 0;
UNF_CHECK_VALID(0x1323, TRUE, v_lport, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x1324, TRUE, v_pkg, return UNF_RETURN_ERROR);
lport = (struct unf_lport_s *)v_lport;
xchg_tag =
(unsigned short)v_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX];
/* 1. Find Exchange Context */
xchg = unf_cm_lookup_xchg_by_tag(v_lport, (unsigned short)xchg_tag);
if (unlikely(!xchg)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x_0x%x) can not find exchange by tag(0x%x)",
lport->port_id, lport->nport_id, xchg_tag);
/* NOTE: return directly */
return UNF_RETURN_ERROR;
}
/* 2. Consistency check */
UNF_CHECK_ALLOCTIME_VALID(lport, xchg_tag, xchg,
v_pkg->private[PKG_PRIVATE_XCHG_ALLOC_TIME],
xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME]);
/* 3. Increase ref_cnt for exchange protecting */
ret = unf_xchg_ref_inc(xchg, INI_RESPONSE_DONE); /* hold */
UNF_CHECK_VALID(0x1325, TRUE, (ret == RETURN_OK),
return UNF_RETURN_ERROR);
fcp_cmnd = &xchg->fcp_cmnd;
control = fcp_cmnd->control;
control = UNF_GET_TASK_MGMT_FLAGS(control);
/* 4. Cancel timer if necessary */
if (xchg->scsi_cmnd_info.time_out != 0)
lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(xchg);
/* 5. process scsi TMF if necessary */
if (control != 0) {
unf_process_scsi_mgmt_result(v_pkg, xchg);
unf_xchg_ref_dec(xchg, INI_RESPONSE_DONE); /* cancel hold */
/* NOTE: return directly */
return RETURN_OK;
}
/* 6. Xchg Abort state check */
spin_lock_irqsave(&xchg->xchg_state_lock, flag);
if (INI_IO_STATE_UPABORT & xchg->io_state) {
spin_unlock_irqrestore(&xchg->xchg_state_lock, flag);
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_WARN,
"[warn]Port(0x%x) find exchange(%p) state(0x%x) has been aborted",
lport->port_id, xchg, xchg->io_state);
/* NOTE: release exchange during SCSI ABORT(ABTS) */
unf_xchg_ref_dec(xchg, INI_RESPONSE_DONE); /* cancel hold */
return ret;
}
spin_unlock_irqrestore(&xchg->xchg_state_lock, flag);
/*
* 7. INI SCSI CMND Status process
* set exchange->result ---to--->>> scsi_result
*/
ret = unf_ini_status_handle(xchg, v_pkg);
/* 8. NOTE: release exchange if necessary */
unf_cm_free_xchg(lport, xchg);
/* 9. dec exch ref_cnt */
/* cancel hold: release resource now */
unf_xchg_ref_dec(xchg, INI_RESPONSE_DONE);
return ret;
}
unsigned int unf_hardware_start_io(struct unf_lport_s *v_lport,
struct unf_frame_pkg_s *v_pkg)
{
if (unlikely(!v_lport->low_level_func.service_op.pfn_unf_cmnd_send)) {
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Port(0x%x) low level send scsi function is NULL",
v_lport->port_id);
return UNF_RETURN_ERROR;
}
return v_lport->low_level_func.service_op.pfn_unf_cmnd_send(
v_lport->fc_port,
v_pkg);
}
struct unf_rport_s *unf_find_rport_by_scsi_id(
struct unf_lport_s *v_lport,
struct unf_ini_error_code_s *v_err_code_table,
unsigned int v_err_code_table_cout,
unsigned int v_scsi_id,
unsigned int *v_scsi_result)
{
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
struct unf_wwpn_rport_info_s *wwpn_rport_info = NULL;
struct unf_rport_s *rport = NULL;
unsigned long flags = 0;
/* scsi_table -> session_table -> image_table */
scsi_image_table = &v_lport->rport_scsi_table;
/* 1. Scsi_Id validity check */
if (unlikely(v_scsi_id >= scsi_image_table->max_scsi_id)) {
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Input scsi_id(0x%x) bigger than max_scsi_id(0x%x).",
v_scsi_id, scsi_image_table->max_scsi_id);
*v_scsi_result = unf_get_uplevel_cmnd_errcode(
v_err_code_table,
v_err_code_table_cout,
UNF_IO_SOFT_ERR); /* did_soft_error */
return NULL;
}
/* 2. GetR_Port_Info/R_Port: use Scsi_Id find from L_Port's
* Rport_Scsi_Table (image table)
*/
spin_lock_irqsave(&scsi_image_table->scsi_image_table_lock, flags);
wwpn_rport_info = &scsi_image_table->wwn_rport_info_table[v_scsi_id];
rport = wwpn_rport_info->rport;
spin_unlock_irqrestore(&scsi_image_table->scsi_image_table_lock, flags);
if (unlikely(!rport)) {
*v_scsi_result = unf_get_uplevel_cmnd_errcode(
v_err_code_table,
v_err_code_table_cout,
/* did_not_connect */
UNF_IO_PORT_LOGOUT);
return NULL;
}
return rport;
}
static unsigned int unf_build_xchg_fcp_cmnd(struct unf_fcp_cmnd_s *v_fcp_cmnd,
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/* SCSI_CMND -->> FCP_CMND */
if (UNF_GET_DATA_DIRECTION(v_scsi_cmnd) == DMA_TO_DEVICE) {
v_fcp_cmnd->control = UNF_FCP_WR_DATA;
} else if (UNF_GET_DATA_DIRECTION(v_scsi_cmnd) == DMA_FROM_DEVICE) {
v_fcp_cmnd->control = UNF_FCP_RD_DATA;
} else {
/* DMA Direction None */
v_fcp_cmnd->control = 0;
}
memcpy(v_fcp_cmnd->cdb, &UNF_GET_FCP_CMND(v_scsi_cmnd),
v_scsi_cmnd->cmnd_len);
if (((v_fcp_cmnd->control == UNF_FCP_WR_DATA) &&
(IS_READ_COMMAND(v_fcp_cmnd->cdb[0]))) ||
((v_fcp_cmnd->control == UNF_FCP_RD_DATA) &&
(IS_WRITE_COMMAND(v_fcp_cmnd->cdb[0])))) {
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MINOR,
"Scsi command direction inconsistent, CDB[0](0x%x), direction(0x%x).",
v_fcp_cmnd->cdb[0], v_fcp_cmnd->control);
return UNF_RETURN_ERROR;
}
memcpy(v_fcp_cmnd->lun, v_scsi_cmnd->pc_lun_id,
sizeof(v_fcp_cmnd->lun));
unf_big_end_to_cpu((void *)v_fcp_cmnd->cdb,
sizeof(v_fcp_cmnd->cdb));
v_fcp_cmnd->data_length = UNF_GET_DATA_LEN(v_scsi_cmnd);
return RETURN_OK;
}
static void unf_adjust_xchg_len(struct unf_xchg_s *v_xchg,
unsigned int v_scsi_cmnd)
{
switch (v_scsi_cmnd) {
case SCSIOPC_REQUEST_SENSE: /* requires different buffer */
v_xchg->data_len = SCSI_SENSE_DATA_LEN;
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MINOR,
"Request Sense new.");
break;
case SCSIOPC_TEST_UNIT_READY:
case SCSIOPC_RESERVE:
case SCSIOPC_RELEASE:
case SCSIOPC_START_STOP_UNIT:
v_xchg->data_len = 0;
break;
default:
break;
}
}
static void unf_copy_dif_control(struct unf_dif_control_info_s *v_dif_control,
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
v_dif_control->fcp_dl = v_scsi_cmnd->dif_control.fcp_dl;
v_dif_control->protect_opcode =
v_scsi_cmnd->dif_control.protect_opcode;
v_dif_control->start_lba = v_scsi_cmnd->dif_control.start_lba;
v_dif_control->app_tag = v_scsi_cmnd->dif_control.app_tag;
v_dif_control->flags = v_scsi_cmnd->dif_control.flags;
v_dif_control->dif_sge_count =
v_scsi_cmnd->dif_control.dif_sge_count;
v_dif_control->dif_sgl = v_scsi_cmnd->dif_control.dif_sgl;
}
static void unf_adjsut_dif_pci_transfer_len(struct unf_xchg_s *v_xchg,
unsigned int direction)
{
struct unf_dif_control_info_s *dif_control = NULL;
unsigned int sector_size = 512;
dif_control = &v_xchg->dif_control;
if (dif_control->protect_opcode == UNF_DIF_ACTION_NONE)
return;
switch (dif_control->protect_opcode & UNF_DIF_ACTION_MASK) {
case UNF_DIF_ACTION_INSERT:
if (direction == DMA_TO_DEVICE) {
/* write IO,insert,Indicates that data with DIF is
* transmitted over the link.
*/
dif_control->fcp_dl =
v_xchg->data_len +
UNF_CAL_BLOCK_CNT(v_xchg->data_len,
sector_size) *
UNF_DIF_AREA_SIZE;
} else {
/* read IO,insert,Indicates that the internal DIf is
* carried, and the link does not carry the DIf.
*/
dif_control->fcp_dl = v_xchg->data_len;
}
break;
case UNF_DIF_ACTION_VERIFY_AND_DELETE:
if (direction == DMA_TO_DEVICE) {
/* write IO,Delete,Indicates that the internal DIf is
* carried, and the link does not carry the DIf.
*/
dif_control->fcp_dl = v_xchg->data_len;
} else {
/* read IO,Delete,Indicates that data with DIF is
* carried on the link and does not contain DIF
* on internal.
*/
dif_control->fcp_dl =
v_xchg->data_len +
UNF_CAL_BLOCK_CNT(v_xchg->data_len,
sector_size) *
UNF_DIF_AREA_SIZE;
}
break;
case UNF_DIF_ACTION_VERIFY_AND_FORWARD:
dif_control->fcp_dl =
v_xchg->data_len +
UNF_CAL_BLOCK_CNT(v_xchg->data_len, sector_size) *
UNF_DIF_AREA_SIZE;
break;
default:
dif_control->fcp_dl = v_xchg->data_len;
break;
}
v_xchg->fcp_cmnd.data_length = dif_control->fcp_dl;
}
static int unf_save_scsi_cmnd_to_xchg(struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport,
struct unf_xchg_s *v_xchg,
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
struct unf_lport_s *lport = v_lport;
struct unf_rport_s *rport = v_rport;
struct unf_xchg_s *xchg = v_xchg;
unsigned int result;
v_scsi_cmnd->driver_scribble = (void *)xchg->start_jif;
xchg->rport = rport;
xchg->rport_bind_jifs = rport->rport_alloc_jifs;
if (lport->low_level_func.xchg_mgr_type ==
UNF_LOW_LEVEL_MGR_TYPE_PASSTIVE)
xchg->ox_id = xchg->hot_pool_tag;
/* Build Xchg SCSI_CMND info */
unf_bind_xchg_scsi_cmd(xchg, v_scsi_cmnd);
xchg->data_len = UNF_GET_DATA_LEN(v_scsi_cmnd);
xchg->data_direction = UNF_GET_DATA_DIRECTION(v_scsi_cmnd);
xchg->sid = lport->nport_id;
xchg->did = rport->nport_id;
xchg->private[PKG_PRIVATE_XCHG_RPORT_INDEX] = rport->rport_index;
xchg->world_id = v_scsi_cmnd->world_id;
xchg->cmnd_sn = v_scsi_cmnd->cmnd_sn;
xchg->scsi_id = v_scsi_cmnd->scsi_id;
/* Build Xchg fcp_cmnd */
result = unf_build_xchg_fcp_cmnd(&xchg->fcp_cmnd, v_scsi_cmnd);
if (unlikely(result != RETURN_OK))
return UNF_RETURN_ERROR;
unf_adjust_xchg_len(xchg, UNF_GET_FCP_CMND(v_scsi_cmnd));
/* Dif (control) info */
unf_copy_dif_control(&xchg->dif_control, v_scsi_cmnd);
memcpy(&xchg->dif_info, &v_scsi_cmnd->dif_info,
sizeof(struct dif_info_s));
unf_adjsut_dif_pci_transfer_len(xchg,
UNF_GET_DATA_DIRECTION(v_scsi_cmnd));
/* single sgl info */
if ((xchg->data_direction != DMA_NONE) &&
(UNF_GET_CMND_SGL(v_scsi_cmnd))) {
xchg->req_sgl_info.sgl = UNF_GET_CMND_SGL(v_scsi_cmnd);
/* Save the sgl header for easy location and printing. */
xchg->req_sgl_info.sgl_start = xchg->req_sgl_info.sgl;
xchg->req_sgl_info.req_index = 0;
xchg->req_sgl_info.entry_index = 0;
}
if (v_scsi_cmnd->dif_control.dif_sgl) {
xchg->dif_sgl_info.sgl = UNF_INI_GET_DIF_SGL(v_scsi_cmnd);
xchg->dif_sgl_info.entry_index = 0;
xchg->dif_sgl_info.req_index = 0;
xchg->dif_sgl_info.sgl_start = xchg->dif_sgl_info.sgl;
}
return RETURN_OK;
}
static int unf_send_fcp_cmnd(struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport,
struct unf_xchg_s *v_xchg)
{
struct unf_scsi_cmd_info_s *scsi_cmnd_info = NULL;
struct unf_lport_s *lport = v_lport;
struct unf_rport_s *rport = v_rport;
struct unf_xchg_s *xchg = v_xchg;
struct unf_frame_pkg_s pkg = { 0 };
unsigned int result;
unsigned long flags = 0;
memcpy(&pkg.dif_control, &xchg->dif_control,
sizeof(struct unf_dif_control_info_s));
pkg.dif_control.fcp_dl = xchg->dif_control.fcp_dl;
pkg.transfer_len = xchg->data_len; /* Pcie data transfer length */
pkg.xchg_contex = xchg;
pkg.qos_level = 0;
pkg.entry_count = xchg->scsi_cmnd_info.entry_cnt;
scsi_cmnd_info = &v_xchg->scsi_cmnd_info;
if ((xchg->data_direction == DMA_NONE) || (!scsi_cmnd_info->sgl))
pkg.entry_count = 0;
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME] =
xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME];
pkg.private[PKG_PRIVATE_XCHG_VP_INDEX] = lport->vp_index;
pkg.private[PKG_PRIVATE_XCHG_RPORT_INDEX] = rport->rport_index;
pkg.private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = xchg->hot_pool_tag;
pkg.fcp_cmnd = &xchg->fcp_cmnd;
pkg.frame_head.csctl_sid = lport->nport_id;
pkg.frame_head.rctl_did = rport->nport_id;
pkg.upper_cmd = xchg->scsi_cmnd_info.scsi_cmnd;
/* exch->fcp_rsp_id --->>> pkg->buffer_ptr */
pkg.unf_rsp_pload_bl.buffer_ptr =
(unsigned char *)
v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu;
pkg.unf_rsp_pload_bl.buf_dma_addr =
v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu_phy_addr;
pkg.unf_rsp_pload_bl.length = PAGE_SIZE;
pkg.frame_head.oxid_rxid =
((unsigned int)xchg->ox_id << 16 | xchg->rx_id);
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EQUIP_ATT, UNF_INFO,
"[info]LPort (0x%p), Nport ID(0x%x) RPort ID(0x%x) direction(0x%x) magic number(0x%x) send IO to OX_ID(0x%x) entry count(0x%x) tag(0x%x)",
lport, lport->nport_id, rport->nport_id,
v_xchg->data_direction,
pkg.private[PKG_PRIVATE_XCHG_ALLOC_TIME],
v_xchg->ox_id, pkg.entry_count, xchg->hot_pool_tag);
atomic_inc(&rport->pending_io_cnt);
if ((rport->tape_support_needed == UNF_TRUE) &&
(atomic_read(&rport->pending_io_cnt) <= 3)) {
spin_lock_irqsave(&xchg->xchg_state_lock, flags);
v_xchg->io_state |= INI_IO_STATE_REC_TIMEOUT_WAIT;
spin_unlock_irqrestore(&xchg->xchg_state_lock, flags);
scsi_cmnd_info->abort_timeout = scsi_cmnd_info->time_out;
scsi_cmnd_info->time_out = UNF_REC_TOV;
}
/* 3. add INI I/O timer if necessary */
if (scsi_cmnd_info->time_out != 0) {
/* I/O inner timer, do not used at this time */
lport->xchg_mgr_temp.pfn_unf_xchg_add_timer(
xchg,
scsi_cmnd_info->time_out,
UNF_TIMER_TYPE_REQ_IO);
}
/* 4. R_Port state check */
if (unlikely((rport->lport_ini_state != UNF_PORT_STATE_LINKUP) ||
(rport->rp_state > UNF_RPORT_ST_READY))) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[info]Port(0x%x) RPort(0x%p) NPortId(0x%x) inistate(0x%x): RPort state(0x%x) upper_cmd(0x%p) is not ready",
lport->port_id, rport, rport->nport_id,
rport->lport_ini_state, rport->rp_state,
pkg.upper_cmd);
result = unf_get_uplevel_cmnd_errcode(
scsi_cmnd_info->err_code_table,
scsi_cmnd_info->err_code_table_cout,
UNF_IO_INCOMPLETE);
scsi_cmnd_info->result = result;
if (scsi_cmnd_info->time_out != 0)
lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(xchg);
unf_cm_free_xchg(lport, xchg);
/* DID_IMM_RETRY */
return RETURN_OK;
} else if (rport->rp_state < UNF_RPORT_ST_READY) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[info]Port(0x%x) RPort(0x%p) NPortId(0x%x) inistate(0x%x): RPort state(0x%x) upper_cmd(0x%p) is not ready",
lport->port_id, rport, rport->nport_id,
rport->lport_ini_state, rport->rp_state,
pkg.upper_cmd);
spin_lock_irqsave(&v_xchg->xchg_state_lock, flags);
xchg->io_state |= INI_IO_STATE_UPSEND_ERR; /* need retry */
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
if (unlikely(scsi_cmnd_info->time_out != 0))
lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(
(void *)xchg);
/* Host busy & need scsi retry */
return UNF_RETURN_ERROR;
}
/* 5. send scsi_cmnd to FC_LL Driver */
if (unf_hardware_start_io(lport, &pkg) != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port (0x%x) upper_cmd(0x%p) Hardware Send IO failed.",
lport->port_id, pkg.upper_cmd);
unf_release_esgls(xchg);
result = unf_get_uplevel_cmnd_errcode(
scsi_cmnd_info->err_code_table,
scsi_cmnd_info->err_code_table_cout,
UNF_IO_INCOMPLETE);
scsi_cmnd_info->result = result;
if (scsi_cmnd_info->time_out != 0)
lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(xchg);
unf_cm_free_xchg(lport, xchg);
/* SCSI_DONE */
return RETURN_OK;
}
return RETURN_OK;
}
int unf_prefer_to_send_scsi_cmnd(struct unf_xchg_s *v_xchg)
{
/*
* About INI_IO_STATE_DRABORT:
* 1. Set ABORT tag: Clean L_Port/V_Port Link Down I/O
* with: INI_busy_list, delay_list, delay_transfer_list, wait_list
*
* 2. Set ABORT tag: for target session:
* with: INI_busy_list, delay_list, delay_transfer_list, wait_list
* a. R_Port remove
* b. Send PLOGI_ACC callback
* c. RCVD PLOGI
* d. RCVD LOGO
*
* 3. if set ABORT: prevent send scsi_cmnd to target
*/
struct unf_lport_s *lport = NULL;
struct unf_rport_s *rport = NULL;
int ret;
unsigned long flags = 0;
lport = v_xchg->lport;
rport = v_xchg->rport;
if (unlikely(!lport || !rport)) {
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Port(0x%p) or RPort(0x%p) is NULL", lport,
rport);
/* if happened (never happen): need retry */
return UNF_RETURN_ERROR;
}
/* 1. inc ref_cnt to protect exchange */
ret = (int)unf_xchg_ref_inc(v_xchg, INI_SEND_CMND);
if (unlikely(ret != RETURN_OK)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) exhg(%p) exception ref(%d) ",
lport->port_id, v_xchg,
atomic_read(&v_xchg->ref_cnt));
/* exchange exception, need retry */
spin_lock_irqsave(&v_xchg->xchg_state_lock, flags);
v_xchg->io_state |= INI_IO_STATE_UPSEND_ERR;
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
/* INI_IO_STATE_UPSEND_ERR: Host busy --->>> need retry */
return UNF_RETURN_ERROR;
}
/* 2. Xchg Abort state check: Free EXCH if necessary */
spin_lock_irqsave(&v_xchg->xchg_state_lock, flags);
if (unlikely((v_xchg->io_state & INI_IO_STATE_UPABORT) ||
(v_xchg->io_state & INI_IO_STATE_DRABORT))) {
/* Prevent to send: UP_ABORT/DRV_ABORT */
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
v_xchg->scsi_cmnd_info.result = UNF_SCSI_HOST(DID_IMM_RETRY);
unf_xchg_ref_dec(v_xchg, INI_SEND_CMND);
unf_cm_free_xchg(lport, v_xchg);
/*
* Release exchange & return directly:
* 1. FC LLDD rcvd ABTS before scsi_cmnd: do nothing
* 2. INI_IO_STATE_UPABORT/INI_IO_STATE_DRABORT:
* discard this cmnd directly
*/
return RETURN_OK;
}
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
/* 3. Send FCP_CMND to FC_LL Driver */
ret = unf_send_fcp_cmnd(lport, rport, v_xchg);
if (unlikely(ret != RETURN_OK)) {
/* exchange exception, need retry */
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) send exhg(%p) OX_ID(0x%x) RX_ID(0x%x) to Rport(%p) NPortID(0x%x) state(0x%x) scsi_id(0x%x) failed",
lport->port_id, v_xchg, v_xchg->ox_id,
v_xchg->rx_id,
rport, rport->nport_id, rport->rp_state,
rport->scsi_id);
spin_lock_irqsave(&v_xchg->xchg_state_lock, flags);
v_xchg->io_state |= INI_IO_STATE_UPSEND_ERR; /* need retry */
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flags);
/* INI_IO_STATE_UPSEND_ERR: Host busy --->>> need retry */
unf_cm_free_xchg(lport, v_xchg);
}
/* 4. dec ref_cnt */
unf_xchg_ref_dec(v_xchg, INI_SEND_CMND);
return ret;
}
struct unf_lport_s *unf_find_lport_by_scsi_cmd(
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
struct unf_lport_s *lport = NULL;
/* cmd -->> L_Port */
lport = (struct unf_lport_s *)UNF_GET_HOST_PORT_BY_CMND(v_scsi_cmnd);
if (unlikely(!lport)) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Find Port by scsi_cmnd(0x%p) failed",
v_scsi_cmnd);
/* cmnd -->> scsi_host_id -->> L_Port */
lport = unf_find_lport_by_scsi_host_id(
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
}
return lport;
}
int unf_cm_queue_command(struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/* SCSI Command --->>> FC FCP Command */
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = NULL;
struct unf_rport_s *rport = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
unsigned int result = 0;
int ret;
unsigned long flags = 0;
unsigned int scsi_id;
unsigned int exhg_mgr_type = UNF_XCHG_MGR_TYPE_RANDOM;
/* 1. Get L_Port */
lport = unf_find_lport_by_scsi_cmd(v_scsi_cmnd);
/*
* corresponds to the insertion or removal scenario or
* the remove card scenario.
* This method is used to search for LPort information
* based on SCSI_HOST_ID.
* The Slave alloc is not invoked when LUNs are not scanned.
* Therefore, the Lport cannot be obtained.
* You need to obtain the Lport from the Lport linked list.
*
* FC After Link Up, the first SCSI command is inquiry.
* Before inquiry, SCSI delivers slave_alloc.
*/
if (!lport) {
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Find Port by scsi cmd(0x%p) failed",
v_scsi_cmnd);
/* find from ini_error_code_table1 */
result = unf_get_uplevel_cmnd_errcode(
v_scsi_cmnd->err_code_table,
v_scsi_cmnd->err_code_table_cout,
UNF_IO_NO_LPORT); /* did_not_connect */
/* DID_NOT_CONNECT & SCSI_DONE & RETURN_OK(0) & I/O error */
unf_complete_cmnd(v_scsi_cmnd, result);
return RETURN_OK;
}
/* Get Local SCSI_Image_table & SCSI_ID */
scsi_image_table = &lport->rport_scsi_table;
scsi_id = v_scsi_cmnd->scsi_id;
/* 2. L_Port State check */
if (unlikely((lport->b_port_removing == UNF_TRUE) ||
(lport->b_pcie_linkdown == UNF_TRUE))) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) is removing(%d) or pcielinkdown(%d) and return with scsi_id(0x%x)",
lport->port_id, lport->b_port_removing,
lport->b_pcie_linkdown,
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
result = unf_get_uplevel_cmnd_errcode(
v_scsi_cmnd->err_code_table,
v_scsi_cmnd->err_code_table_cout,
UNF_IO_NO_LPORT); /* did_not_connect */
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, (result >> 16));
/* DID_NOT_CONNECT & SCSI_DONE & RETURN_OK(0) & I/O error */
unf_complete_cmnd(v_scsi_cmnd, result);
return RETURN_OK;
}
/* 3. Get R_Port */
rport = unf_find_rport_by_scsi_id(lport,
v_scsi_cmnd->err_code_table,
v_scsi_cmnd->err_code_table_cout,
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd),
&result);
if (unlikely(!rport)) {
/* never happen: do not care */
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(0x%x) find RPort by scsi_id(0x%x) failed",
lport->port_id,
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, (result >> 16));
/* DID_NOT_CONNECT/DID_SOFT_ERROR & SCSI_DONE &
* RETURN_OK(0) & I/O error
*/
unf_complete_cmnd(v_scsi_cmnd, result);
return RETURN_OK;
}
/* 4. Can't get exchange & retrun host busy, retry by uplevel */
xchg = (struct unf_xchg_s *)unf_cm_get_free_xchg(
lport,
exhg_mgr_type << 16 | UNF_XCHG_TYPE_INI);
if (unlikely(!xchg)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[err]Port(0x%x) get free exchange for INI IO(0x%x) failed",
lport->port_id,
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
/* NOTE: need scsi retry */
return UNF_RETURN_ERROR;
}
xchg->scsi_cmnd_info.result = UNF_SCSI_HOST(DID_ERROR);
/* 5. Save the SCSI CMND information in advance. */
ret = unf_save_scsi_cmnd_to_xchg(lport, rport, xchg, v_scsi_cmnd);
if (unlikely(ret != RETURN_OK)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[err]Port(0x%x) save scsi_cmnd info(0x%x) to exchange failed",
lport->port_id,
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
spin_lock_irqsave(&xchg->xchg_state_lock, flags);
xchg->io_state |= INI_IO_STATE_UPSEND_ERR;
spin_unlock_irqrestore(&xchg->xchg_state_lock, flags);
/* INI_IO_STATE_UPSEND_ERR: Don't Do SCSI_DONE,
* need retry I/O
*/
unf_cm_free_xchg(lport, xchg);
/* NOTE: need scsi retry */
return UNF_RETURN_ERROR;
}
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_INFO,
"[info]Get exchange(0x%p) OX_ID(0x%x) RX_ID(0x%x) hot_pool_tag(0x%x) for Pcmd:%p,Cmdsn:0x%lx,WorldId:%u",
xchg, xchg->ox_id, xchg->rx_id,
xchg->hot_pool_tag, v_scsi_cmnd->upper_cmnd,
(unsigned long)v_scsi_cmnd->cmnd_sn,
v_scsi_cmnd->world_id);
/* 6. Send SCSI CMND */
ret = unf_prefer_to_send_scsi_cmnd(xchg);
return ret;
}