1339 lines
43 KiB
C
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;
|
|
}
|