hifc/hifc/unf_io_abnormal.c

934 lines
28 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_exchg.h"
#include "unf_rport.h"
#include "unf_io.h"
#include "unf_portman.h"
#include "unf_service.h"
#include "unf_io_abnormal.h"
int unf_send_scsi_mgmt_cmnd(struct unf_xchg_s *v_xchg,
struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport,
struct unf_scsi_cmd_s *v_scsi_cmnd,
enum unf_task_mgmt_cmnd_e v_task_mgnt_cmd_type);
static int unf_send_abts_success(struct unf_lport_s *v_lport,
struct unf_xchg_s *v_xchg,
struct unf_scsi_cmd_s *v_scsi_cmnd,
unsigned int time_out_value)
{
int wait_marker = UNF_TRUE;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
unsigned int scsi_id;
unsigned int ret;
unsigned long flag = 0;
spin_lock_irqsave(&v_xchg->xchg_state_lock, flag);
wait_marker = (v_xchg->abts_state & MARKER_STS_RECEIVED) ?
UNF_FALSE : UNF_TRUE;
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag);
if (wait_marker) {
if (down_timeout(
&v_xchg->task_sema,
(long long)msecs_to_jiffies(time_out_value))) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT,
UNF_WARN,
"[warn]Port(0x%x) recv abts marker timeout,Exch(0x%p) OX_ID(0x%x 0x%x) RX_ID(0x%x)",
v_lport->port_id, v_xchg,
v_xchg->ox_id, v_xchg->hot_pool_tag,
v_xchg->rx_id);
/* Cancel abts rsp timer when sema timeout */
v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(
(void *)v_xchg);
/* Cnacel the flag of INI_IO_STATE_UPABORT and
* process the io in TMF
*/
spin_lock_irqsave(&v_xchg->xchg_state_lock, flag);
v_xchg->io_state &= ~INI_IO_STATE_UPABORT;
v_xchg->io_state |= INI_IO_STATE_TMF_ABORT;
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag);
return UNF_SCSI_ABORT_FAIL;
}
} else {
v_xchg->ucode_abts_state = UNF_IO_SUCCESS;
}
scsi_image_table = &v_lport->rport_scsi_table;
scsi_id = v_scsi_cmnd->scsi_id;
spin_lock_irqsave(&v_xchg->xchg_state_lock, flag);
if ((v_xchg->ucode_abts_state == UNF_IO_SUCCESS) ||
(v_xchg->scsi_cmnd_info.result == UNF_IO_ABORT_PORT_REMOVING)) {
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag);
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[info]Port(0x%x) Send ABTS succeed and recv marker Exch(0x%p) OX_ID(0x%x) RX_ID(0x%x) marker status(0x%x)",
v_lport->port_id, v_xchg,
v_xchg->ox_id, v_xchg->rx_id,
v_xchg->ucode_abts_state);
ret = DID_RESET;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret);
unf_complete_cmnd(v_scsi_cmnd, DID_RESET << 16);
return UNF_SCSI_ABORT_SUCCESS;
}
v_xchg->io_state &= ~INI_IO_STATE_UPABORT;
v_xchg->io_state |= INI_IO_STATE_TMF_ABORT;
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag);
/* Cancel abts rsp timer when sema timeout */
v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer((void *)v_xchg);
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
"[warn]Port(0x%x) send ABTS failed. Exch(0x%p) oxid(0x%x) hot_tag(0x%x) ret(0x%x) v_xchg->io_state (0x%x)",
v_lport->port_id, v_xchg, v_xchg->ox_id,
v_xchg->hot_pool_tag,
v_xchg->scsi_cmnd_info.result, v_xchg->io_state);
/* return fail and then enter TMF */
return UNF_SCSI_ABORT_FAIL;
}
static int unf_ini_abort_cmnd(struct unf_lport_s *v_lport,
struct unf_xchg_s *v_xchg,
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/*
* About INI_IO_STATE_UPABORT:
*
* 1. Check: AC power down
* 2. Check: L_Port destroy
* 3. Check: I/O XCHG timeout
* 4. Set ABORT: send ABTS
* 5. Set ABORT: LUN reset
* 6. Set ABORT: Target reset
* 7. Check: Prevent to send I/O to target (UNF_PreferToSendScsiCmnd)
* 8. Check: Done INI XCHG --->>> do not call scsi_done, return directly
* 9. Check: INI SCSI Complete --->>>
* do not call scsi_done, return directly
*/
#define UNF_RPORT_NOTREADY_WAIT_SEM_TIMEOUT (2000) /* 2s */
struct unf_lport_s *lport = NULL;
struct unf_rport_s *rport = NULL;
unsigned long flag = 0;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
unsigned int scsi_id;
unsigned int ret;
unsigned int time_out_value = (unsigned int)UNF_WAIT_SEM_TIMEOUT;
UNF_CHECK_VALID(0x1335, TRUE, v_lport, return UNF_SCSI_ABORT_FAIL);
lport = v_lport;
/* 1. Xchg State Set: INI_IO_STATE_UPABORT */
spin_lock_irqsave(&v_xchg->xchg_state_lock, flag);
v_xchg->io_state |= INI_IO_STATE_UPABORT;
rport = v_xchg->rport;
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag);
/* 2. R_Port check */
if (unlikely(!rport)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) send ABTS but no RPort, OX_ID(0x%x) RX_ID(0x%x)",
lport->port_id, v_xchg->ox_id, v_xchg->rx_id);
return UNF_SCSI_ABORT_SUCCESS;
}
spin_lock_irqsave(&rport->rport_state_lock, flag);
if (unlikely(rport->rp_state != UNF_RPORT_ST_READY)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT,
UNF_WARN,
"[warn]Port(0x%x) find RPort's state(0x%x) is not ready but send ABTS also, exchange(0x%p) tag(0x%x)",
lport->port_id, rport->rp_state,
v_xchg, v_xchg->hot_pool_tag);
/*
* Important: Send ABTS also & update timer
* Purpose: only used for release chip (uCode) resource,
* continue
*/
time_out_value = UNF_RPORT_NOTREADY_WAIT_SEM_TIMEOUT;
}
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
/* 3. L_Port State check */
if (unlikely(lport->b_port_removing == UNF_TRUE)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) is removing", lport->port_id);
v_xchg->io_state &= ~INI_IO_STATE_UPABORT;
return UNF_SCSI_ABORT_FAIL;
}
scsi_image_table = &lport->rport_scsi_table;
scsi_id = v_scsi_cmnd->scsi_id;
/* If pcie linkdown, complete this io and flush all io */
if (unlikely(lport->b_pcie_linkdown == UNF_TRUE)) {
ret = DID_RESET;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret);
unf_complete_cmnd(v_scsi_cmnd, DID_RESET << 16);
unf_free_lport_all_xchg(v_lport);
return UNF_SCSI_ABORT_SUCCESS;
}
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_KEVENT,
"[abort]Port(0x%x) Exchg(0x%p) delay(%llu) SID(0x%x) DID(0x%x) wwpn(0x%llx) OxID(0x%x 0x%x) scsi_id(0x%x) lun_id(0x%x) cmdsn(0x%llx)",
lport->port_id, v_xchg,
(unsigned long long)jiffies_to_msecs(jiffies) -
(unsigned long long)jiffies_to_msecs(v_xchg->alloc_jif),
v_xchg->sid, v_xchg->did, rport->port_name,
v_xchg->ox_id, v_xchg->hot_pool_tag, v_scsi_cmnd->scsi_id,
(unsigned int)v_scsi_cmnd->lun_id, v_scsi_cmnd->cmnd_sn);
/* Init abts marker semaphore */
sema_init(&v_xchg->task_sema, 0);
if (v_xchg->scsi_cmnd_info.time_out != 0)
lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(v_xchg);
/* Add timer for sending ABTS */
v_lport->xchg_mgr_temp.pfn_unf_xchg_add_timer(
(void *)v_xchg,
(unsigned long)UNF_WAIT_ABTS_RSP_TIMEOUT,
UNF_TIMER_TYPE_INI_ABTS);
/* 4. Send INI ABTS CMND */
if (unf_send_abts(lport, v_xchg) != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) Send ABTS failed. Exch(0x%p) OX_ID(0x%x 0x%x) RX_ID(0x%x)",
lport->port_id, v_xchg,
v_xchg->ox_id, v_xchg->hot_pool_tag,
v_xchg->rx_id);
/* Cancel timer when sending ABTS failed */
v_lport->xchg_mgr_temp.pfn_unf_xchg_cancel_timer(
(void *)v_xchg);
/* Cnacel the flag of INI_IO_STATE_UPABORT
* and process the io in TMF
*/
spin_lock_irqsave(&v_xchg->xchg_state_lock, flag);
v_xchg->io_state &= ~INI_IO_STATE_UPABORT;
v_xchg->io_state |= INI_IO_STATE_TMF_ABORT;
spin_unlock_irqrestore(&v_xchg->xchg_state_lock, flag);
return UNF_SCSI_ABORT_FAIL;
}
return unf_send_abts_success(lport, v_xchg, v_scsi_cmnd,
time_out_value);
}
static void unf_flush_ini_resp_que(struct unf_lport_s *v_lport)
{
UNF_CHECK_VALID(0x1335, TRUE, v_lport, return);
if (v_lport->low_level_func.service_op.pfn_unf_flush_ini_resp_que)
(void)v_lport->low_level_func.service_op.pfn_unf_flush_ini_resp_que(v_lport->fc_port);
}
int unf_cm_eh_abort_handler(struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/*
* SCSI ABORT Command --->>> FC ABTS Command
* If return ABORT_FAIL then enter TMF process
*/
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = NULL;
struct unf_rport_s *rport = NULL;
struct unf_lport_s *xchg_lport = NULL;
int ret;
unsigned long flag = 0;
/* 1. Get L_Port: Point to Scsi_host */
lport = unf_find_lport_by_scsi_cmd(v_scsi_cmnd);
if (!lport) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Can't find port by scsi host id(0x%x)",
UNF_GET_SCSI_HOST_ID_BY_CMND(v_scsi_cmnd));
return UNF_SCSI_ABORT_FAIL;
}
/* 2. find target Xchg for INI Abort CMND */
xchg = unf_cm_lookup_xchg_by_cmnd_sn(lport, v_scsi_cmnd->cmnd_sn,
v_scsi_cmnd->world_id);
if (unlikely(!xchg)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_ABNORMAL,
UNF_WARN,
"[warn]Port(0x%x) can't find exchange by Cmdsn(0x%lx)",
lport->port_id,
(unsigned long)v_scsi_cmnd->cmnd_sn);
unf_flush_ini_resp_que(lport);
return UNF_SCSI_ABORT_SUCCESS;
}
/* 3. increase ref_cnt to protect exchange */
ret = (int)unf_xchg_ref_inc(xchg, INI_EH_ABORT);
if (unlikely(ret != RETURN_OK)) {
unf_flush_ini_resp_que(lport);
return UNF_SCSI_ABORT_SUCCESS;
}
v_scsi_cmnd->upper_cmnd = xchg->scsi_cmnd_info.scsi_cmnd;
xchg->debug_hook = UNF_TRUE;
/* 4. Exchang L_Port/R_Port Get & check */
spin_lock_irqsave(&xchg->xchg_state_lock, flag);
xchg_lport = xchg->lport;
rport = xchg->rport;
spin_unlock_irqrestore(&xchg->xchg_state_lock, flag);
if (unlikely(!xchg_lport || !rport)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Exchange(0x%p)'s L_Port or R_Port is NULL, state(0x%x)",
xchg, xchg->io_state);
unf_xchg_ref_dec(xchg, INI_EH_ABORT);
if (!xchg_lport)
return UNF_SCSI_ABORT_FAIL; /* for L_Port */
return UNF_SCSI_ABORT_SUCCESS; /* for R_Port */
}
/* 5. Send INI Abort Cmnd */
ret = unf_ini_abort_cmnd(xchg_lport, xchg, v_scsi_cmnd);
/* 6. decrease exchange ref_cnt */
unf_xchg_ref_dec(xchg, INI_EH_ABORT);
return ret;
}
static unsigned int unf_tmf_timeout_recovery_default(void *v_rport,
void *v_xchg)
{
struct unf_lport_s *lport = NULL;
unsigned long flag = 0;
struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg;
struct unf_rport_s *rport = (struct unf_rport_s *)v_rport;
lport = xchg->lport;
UNF_CHECK_VALID(0x4614, UNF_TRUE, lport, return UNF_RETURN_ERROR);
spin_lock_irqsave(&rport->rport_state_lock, flag);
unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO);
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
unf_rport_enter_logo(lport, rport);
return RETURN_OK;
}
void unf_abts_timeout_recovery_default(void *v_rport, void *v_xchg)
{
struct unf_lport_s *lport = NULL;
unsigned long flag = 0;
struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg;
struct unf_rport_s *rport = (struct unf_rport_s *)v_rport;
lport = xchg->lport;
UNF_CHECK_VALID(0x4614, UNF_TRUE, lport, return);
spin_lock_irqsave(&xchg->xchg_state_lock, flag);
if (INI_IO_STATE_DONE & xchg->io_state) {
spin_unlock_irqrestore(&xchg->xchg_state_lock, flag);
return;
}
spin_unlock_irqrestore(&xchg->xchg_state_lock, flag);
if (xchg->rport_bind_jifs != rport->rport_alloc_jifs)
return;
spin_lock_irqsave(&rport->rport_state_lock, flag);
unf_rport_state_ma(rport, UNF_EVENT_RPORT_LOGO);
spin_unlock_irqrestore(&rport->rport_state_lock, flag);
unf_rport_enter_logo(lport, rport);
}
unsigned int unf_tmf_timeout_recovery_special(void *v_rport, void *v_xchg)
{
/* Do port reset or R_Port LOGO */
int ret = UNF_RETURN_ERROR;
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = (struct unf_xchg_s *)v_xchg;
struct unf_rport_s *rport = (struct unf_rport_s *)v_rport;
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_rport,
return UNF_RETURN_ERROR);
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_xchg,
return UNF_RETURN_ERROR);
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, xchg->lport,
return UNF_RETURN_ERROR);
lport = xchg->lport->root_lport;
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, lport,
return UNF_RETURN_ERROR);
/* 1. TMF response timeout & Marker STS timeout */
if (!(xchg->tmf_state &
(MARKER_STS_RECEIVED | TMF_RESPONSE_RECEIVED))) {
/* TMF timeout & marker timeout */
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) receive marker status timeout and do recovery",
lport->port_id);
/* Do port reset */
ret = unf_cm_reset_port(lport->port_id);
if (ret != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT,
UNF_WARN,
"[warn]Port(0x%x) do reset failed",
lport->port_id);
return UNF_RETURN_ERROR;
}
return RETURN_OK;
}
/* 2. default case: Do LOGO process */
unf_tmf_timeout_recovery_default(rport, xchg);
return RETURN_OK;
}
void unf_tmf_abnormal_recovery(struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport,
struct unf_xchg_s *v_xchg)
{
/*
* for device(lun)/target(session) reset:
* Do port reset or R_Port LOGO
*/
if (v_lport->pfn_unf_tmf_abnormal_recovery)
v_lport->pfn_unf_tmf_abnormal_recovery((void *)v_rport,
(void *)v_xchg);
}
static void unf_build_task_mgmt_fcp_cmnd(struct unf_fcp_cmnd_s *v_fcp_cmnd,
struct unf_scsi_cmd_s *v_scsi_cmnd,
enum unf_task_mgmt_cmnd_e v_task_mgmt)
{
UNF_CHECK_VALID(0x1339, UNF_TRUE, v_fcp_cmnd, return);
UNF_CHECK_VALID(0x1340, UNF_TRUE, v_scsi_cmnd, return);
unf_big_end_to_cpu((void *)v_scsi_cmnd->pc_lun_id, UNF_FCP_LUNID_LEN_8);
(*(unsigned long long *)(v_scsi_cmnd->pc_lun_id)) >>= 8;
memcpy(v_fcp_cmnd->lun, v_scsi_cmnd->pc_lun_id,
sizeof(v_fcp_cmnd->lun));
/*
* If the TASK MANAGEMENT FLAGS field is set to a nonzero value,
* the FCP_CDB field, the FCP_DL field, the TASK ATTRIBUTE field,
* the RDDATA bit, and the WRDATA bit shall be ignored and the
* FCP_BIDIRECTIONAL_READ_DL field shall not be
* included in the FCP_CMND IU payload
*/
v_fcp_cmnd->control = UNF_SET_TASK_MGMT_FLAGS(v_task_mgmt);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
"SCSI cmnd(0x%x) is task mgmt cmnd. ntrl Flag(LITTLE END) is 0x%x.",
v_task_mgmt, v_fcp_cmnd->control);
}
int unf_send_scsi_mgmt_cmnd(struct unf_xchg_s *v_xchg,
struct unf_lport_s *v_lport,
struct unf_rport_s *v_rport,
struct unf_scsi_cmd_s *v_scsi_cmnd,
enum unf_task_mgmt_cmnd_e v_task_mgnt_cmd_type)
{
/*
* 1. Device/LUN reset
* 2. Target/Session reset
*/
struct unf_xchg_s *xchg = NULL;
int ret = SUCCESS;
struct unf_frame_pkg_s pkg = { 0 };
unsigned long flag = 0;
UNF_CHECK_VALID(0x1341, UNF_TRUE, v_xchg, return FAILED);
UNF_CHECK_VALID(0x1342, UNF_TRUE, v_lport, return FAILED);
UNF_CHECK_VALID(0x1343, UNF_TRUE, v_rport, return FAILED);
UNF_CHECK_VALID(0x1344, UNF_TRUE, v_scsi_cmnd, return FAILED);
UNF_CHECK_VALID(0x1345, UNF_TRUE,
((v_task_mgnt_cmd_type <= UNF_FCP_TM_TERMINATE_TASK) &&
(v_task_mgnt_cmd_type >= UNF_FCP_TM_QUERY_TASK_SET)),
return FAILED);
xchg = v_xchg;
xchg->lport = v_lport;
xchg->rport = v_rport;
/* 1. State: Up_Task */
spin_lock_irqsave(&xchg->xchg_state_lock, flag);
xchg->io_state |= INI_IO_STATE_UPTASK;
spin_unlock_irqrestore(&xchg->xchg_state_lock, flag);
if (v_lport->low_level_func.xchg_mgr_type ==
UNF_LOW_LEVEL_MGR_TYPE_PASSTIVE) {
xchg->ox_id = xchg->hot_pool_tag;
pkg.frame_head.oxid_rxid =
((unsigned int)xchg->ox_id << 16) | xchg->rx_id;
}
/* 2. Set TASK MANAGEMENT FLAGS of FCP_CMND to
* the corresponding task management command
*/
unf_build_task_mgmt_fcp_cmnd(&xchg->fcp_cmnd, v_scsi_cmnd,
v_task_mgnt_cmd_type);
pkg.xchg_contex = xchg;
pkg.private[PKG_PRIVATE_XCHG_RPORT_INDEX] = v_rport->rport_index;
pkg.fcp_cmnd = &xchg->fcp_cmnd;
pkg.private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX] = xchg->hot_pool_tag;
pkg.frame_head.csctl_sid = v_lport->nport_id;
pkg.frame_head.rctl_did = v_rport->nport_id;
pkg.unf_rsp_pload_bl.buffer_ptr =
(unsigned char *)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.private[PKG_PRIVATE_XCHG_ALLOC_TIME] =
v_xchg->private[PKG_PRIVATE_XCHG_ALLOC_TIME];
if (unlikely(v_lport->b_pcie_linkdown == UNF_TRUE)) {
unf_free_lport_all_xchg(v_lport);
return SUCCESS;
}
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
"[event]Port(0x%x) send task_cmnd(0x%x) to RPort(0x%x) Hottag(0x%x) lunid(0x%llx)",
v_lport->port_id, v_task_mgnt_cmd_type,
v_rport->nport_id, xchg->hot_pool_tag,
*((unsigned long long *)v_scsi_cmnd->pc_lun_id));
/* 3. Init exchange task semaphore */
sema_init(&xchg->task_sema, 0);
/* 4. Send Mgmt Task to low-level */
if (unf_hardware_start_io(v_lport, &pkg) != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) send task_cmnd(0x%x) to RPort(0x%x) failed",
v_lport->port_id, v_task_mgnt_cmd_type,
v_rport->nport_id);
return FAILED;
}
/*
* semaphore timeout
*
* Code review: The second input parameter needs to
* be converted to jiffies.
* set semaphore after the message is sent successfully.
* The semaphore is returned when the semaphore times out
* or is woken up.
*
* 5. The semaphore is cleared and counted when the Mgmt
* Task message is sent,
* and is Wake Up when the RSP message is received.
* If the semaphore is not Wake Up, the semaphore is
* triggered after timeout.
* That is, no RSP message is received within the timeout period.
*/
if (down_timeout(&xchg->task_sema,
(long long)msecs_to_jiffies((unsigned int)UNF_WAIT_SEM_TIMEOUT))) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) send task_cmnd(0x%x) to RPort(0x%x) timeout scsi id(0x%x) lun id(0x%x)",
v_lport->nport_id, v_task_mgnt_cmd_type,
v_rport->nport_id,
v_scsi_cmnd->scsi_id,
(unsigned int)v_scsi_cmnd->lun_id);
/* semaphore timeout */
ret = FAILED;
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
if (v_lport->en_states == UNF_LPORT_ST_RESET)
ret = SUCCESS;
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
return ret;
}
/*
* 6. NOTE: no timeout (has been waken up)
* Do Scsi_Cmnd(Mgmt Task) result checking
*
* FAILED: with error code or RSP is error
* SUCCESS: others
*/
if (xchg->scsi_cmnd_info.result == UNF_IO_SUCCESS) {
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[info]Port(0x%x) send task_cmnd(0x%x) to RPort(0x%x) and receive rsp succeed",
v_lport->nport_id, v_task_mgnt_cmd_type,
v_rport->nport_id);
ret = SUCCESS;
} else {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) send task_cmnd(0x%x) to RPort(0x%x) and receive rsp failed scsi id(0x%x) lun id(0x%x)",
v_lport->nport_id, v_task_mgnt_cmd_type,
v_rport->nport_id,
v_scsi_cmnd->scsi_id,
(unsigned int)v_scsi_cmnd->lun_id);
ret = FAILED;
}
return ret;
}
int unf_cm_eh_device_reset_handler(struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/* SCSI Device/LUN Reset Command --->>> FC LUN/Device Reset Command */
struct unf_lport_s *lport = NULL;
struct unf_rport_s *rport = NULL;
struct unf_xchg_s *xchg = NULL;
unsigned int cmnd_result = 0;
int ret = SUCCESS;
UNF_CHECK_VALID(0x1349, UNF_TRUE, v_scsi_cmnd, return FAILED);
UNF_CHECK_VALID(0x1350, UNF_TRUE, v_scsi_cmnd->pc_lun_id,
return FAILED);
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[event]Enter device/LUN reset handler");
/* 1. Get L_Port */
lport = unf_find_lport_by_scsi_cmd(v_scsi_cmnd);
if (!lport) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Can't find port by scsi_host_id(0x%x)",
UNF_GET_SCSI_HOST_ID_BY_CMND(v_scsi_cmnd));
return FAILED;
}
/* 2. L_Port State checking */
if (unlikely(lport->b_port_removing == UNF_TRUE)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%p) is removing", lport);
return FAILED;
}
/*
* 3. Get R_Port: no rport is found or rport is not ready,return ok
* from: L_Port -->> rport_scsi_table (image table)
* -->> rport_info_table
*/
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),
&cmnd_result);
if (unlikely(!rport)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) Can't find rport by scsi_id(0x%x)",
lport->port_id,
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
return SUCCESS;
}
/*
* 4. Set the I/O of the corresponding LUN to abort.
*
* LUN Reset: set UP_ABORT tag, with:
* INI_Busy_list, IO_Wait_list,
* IO_Delay_list, IO_Delay_transfer_list
*/
unf_cm_xchg_abort_by_lun(
lport, rport,
*((unsigned long long *)v_scsi_cmnd->pc_lun_id),
NULL, UNF_FALSE);
/* 5. R_Port state check */
if (unlikely(rport->rp_state != UNF_RPORT_ST_READY)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) RPort(0x%x) state(0x%x) SCSI Command(0x%p), rport is not ready",
lport->port_id, rport->nport_id,
rport->rp_state, v_scsi_cmnd);
return SUCCESS;
}
/* 6. Get & inc ref_cnt free Xchg for Device reset */
xchg = (struct unf_xchg_s *)unf_cm_get_free_xchg(lport,
UNF_XCHG_TYPE_INI);
if (unlikely(!xchg)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%p) can't get free exchange", lport);
return FAILED;
}
/* increase ref_cnt for protecting exchange */
ret = (int)unf_xchg_ref_inc(xchg, INI_EH_DEVICE_RESET);
UNF_CHECK_VALID(0x1351, UNF_TRUE, (ret == RETURN_OK), return FAILED);
/* 7. Send Device/LUN Reset to Low level */
ret = unf_send_scsi_mgmt_cmnd(xchg, lport, rport,
v_scsi_cmnd,
UNF_FCP_TM_LOGICAL_UNIT_RESET);
if (unlikely(ret == FAILED)) {
/*
* Do port reset or R_Port LOGO:
* 1. FAILED: send failed
* 2. FAILED: semaphore timeout
* 3. SUCCESS: rcvd rsp & semaphore has been waken up
*/
unf_tmf_abnormal_recovery(lport, rport, xchg);
}
/*
* 8. Release resource immediately if necessary
* NOTE: here, semaphore timeout or rcvd rsp
* (semaphore has been waken up)
*/
if (likely((lport->b_port_removing != UNF_TRUE) ||
(lport->root_lport != lport)))
unf_cm_free_xchg(xchg->lport, xchg);
/* decrease ref_cnt */
unf_xchg_ref_dec(xchg, INI_EH_DEVICE_RESET);
return SUCCESS;
}
int unf_cm_target_reset_handler(struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/* SCSI Target Reset Command --->>> FC Session Reset/Delete Command */
struct unf_lport_s *lport = NULL;
struct unf_rport_s *rport = NULL;
struct unf_xchg_s *xchg = NULL;
unsigned int cmnd_result = 0;
int ret;
UNF_CHECK_VALID(0x1355, UNF_TRUE, v_scsi_cmnd, return FAILED);
UNF_CHECK_VALID(0x1356, UNF_TRUE, v_scsi_cmnd->pc_lun_id,
return FAILED);
/* 1. Get L_Port */
lport = unf_find_lport_by_scsi_cmd(v_scsi_cmnd);
if (!lport) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Can't find port by scsi_host_id(0x%x)",
UNF_GET_SCSI_HOST_ID_BY_CMND(v_scsi_cmnd));
return FAILED;
}
/* 2. L_Port State check */
if (unlikely(lport->b_port_removing == UNF_TRUE)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%p) is removing", lport);
return FAILED;
}
/*
* 3. Get R_Port: no rport is found or rport is not ready,return ok
* from: L_Port -->> rport_scsi_table (image table) -->>
* rport_info_table
*/
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),
&cmnd_result);
if (unlikely(!rport)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Can't find rport by scsi_id(0x%x)",
UNF_GET_SCSI_ID_BY_CMND(v_scsi_cmnd));
return SUCCESS;
}
/*
* 4. set UP_ABORT on Target IO and Session IO
*
* LUN Reset: set UP_ABORT tag, with:
* INI_Busy_list, IO_Wait_list,
* IO_Delay_list, IO_Delay_transfer_list
*/
unf_cm_xchg_abort_by_session(lport, rport);
/* 5. R_Port state check */
if (unlikely(rport->rp_state != UNF_RPORT_ST_READY)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%x) RPort(0x%x) state(0x%x) is not ready, SCSI Command(0x%p)",
lport->port_id, rport->nport_id,
rport->rp_state, v_scsi_cmnd);
return SUCCESS;
}
/* 6. Get free Xchg for Target Reset CMND */
xchg = (struct unf_xchg_s *)unf_cm_get_free_xchg(lport,
UNF_XCHG_TYPE_INI);
if (unlikely(!xchg)) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Port(0x%p) can't get free exchange", lport);
return FAILED;
}
/* increase ref_cnt to protect exchange */
ret = (int)unf_xchg_ref_inc(xchg, INI_EH_DEVICE_RESET);
UNF_CHECK_VALID(0x1357, UNF_TRUE, (ret == RETURN_OK), return FAILED);
/* 7. Send Target Reset Cmnd to low-level */
ret = unf_send_scsi_mgmt_cmnd(xchg, lport, rport, v_scsi_cmnd,
UNF_FCP_TM_TARGET_RESET);
if (unlikely(ret == FAILED)) {
/*
* Do port reset or R_Port LOGO:
* 1. FAILED: send failed
* 2. FAILED: semaphore timeout
* 3. SUCCESS: rcvd rsp & semaphore has been waken up
*/
unf_tmf_abnormal_recovery(lport, rport, xchg);
}
/*
* 8. Release resource immediately if necessary
* NOTE: here, semaphore timeout or rcvd rsp
* (semaphore has been waken up)
*/
if (likely((lport->b_port_removing != UNF_TRUE) ||
(lport->root_lport != lport)))
unf_cm_free_xchg(xchg->lport, xchg);
/* decrease exchange ref_cnt */
unf_xchg_ref_dec(xchg, INI_EH_DEVICE_RESET);
return SUCCESS;
}
int unf_cm_bus_reset_handler(struct unf_scsi_cmd_s *v_scsi_cmnd)
{
/* SCSI BUS Reset Command --->>> FC Port Reset Command */
struct unf_lport_s *lport = NULL;
int cmnd_result = 0;
/* 1. Get L_Port */
lport = unf_find_lport_by_scsi_cmd(v_scsi_cmnd);
if (!lport) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Can't find port by scsi_host_id(0x%x)",
UNF_GET_SCSI_HOST_ID_BY_CMND(v_scsi_cmnd));
return FAILED;
}
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_KEVENT,
"[event]Do port reset with scsi_bus_reset");
cmnd_result = unf_cm_reset_port(lport->port_id);
if (unlikely(cmnd_result == UNF_RETURN_ERROR))
return FAILED;
else
return SUCCESS;
}
void unf_process_scsi_mgmt_result(struct unf_frame_pkg_s *v_pkg,
struct unf_xchg_s *v_xchg)
{
unsigned char *rsp_info = NULL;
unsigned char rsp_code = 0;
unsigned int code_index = 0;
/*
* LLT found that:RSP_CODE is the third byte of FCP_RSP_INFO,
* on Little endian should be byte 0, For detail FCP_4 Table 26
* FCP_RSP_INFO field format
*
* 1. state setting
* 2. wake up semaphore
*/
UNF_CHECK_VALID(0x1321, TRUE, v_pkg, return);
UNF_CHECK_VALID(0x1322, TRUE, v_xchg, return);
v_xchg->tmf_state |= TMF_RESPONSE_RECEIVED;
if (UNF_GET_LL_ERR(v_pkg) != UNF_IO_SUCCESS) {
UNF_TRACE(UNF_EVTLOG_IO_WARN, UNF_LOG_IO_ATT, UNF_WARN,
"[warn]Send scsi manage command failed with error code(0x%x)",
UNF_GET_LL_ERR(v_pkg));
v_xchg->scsi_cmnd_info.result = UNF_IO_FAILED;
/* wakeup semaphore & return */
up(&v_xchg->task_sema);
return;
}
rsp_info = v_pkg->unf_rsp_pload_bl.buffer_ptr;
if (!rsp_info && (v_pkg->unf_rsp_pload_bl.length != 0)) {
rsp_info =
(unsigned char *)
v_xchg->fcp_sfs_union.fcp_rsp_entry.fcp_rsp_iu;
/* change to little end if necessary */
if (rsp_info && (v_pkg->byte_orders & UNF_BIT_3))
unf_big_end_to_cpu(
rsp_info,
v_pkg->unf_rsp_pload_bl.length);
}
if (!rsp_info) {
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[info]FCP response data pointer is NULL with Xchg TAG(0x%x)",
v_xchg->hot_pool_tag);
v_xchg->scsi_cmnd_info.result = UNF_IO_SUCCESS;
/* wakeup semaphore & return */
up(&v_xchg->task_sema);
return;
}
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[info]FCP response data length(0x%x), RSP_CODE(0x%x:%x:%x:%x:%x:%x:%x:%x)",
v_pkg->unf_rsp_pload_bl.length,
rsp_info[0],
rsp_info[1],
rsp_info[2],
rsp_info[3],
rsp_info[4],
rsp_info[5],
rsp_info[6],
rsp_info[7]);
rsp_code = rsp_info[code_index];
if ((rsp_code == UNF_FCP_TM_RSP_COMPLETE) ||
(rsp_code == UNF_FCP_TM_RSP_SUCCEED))
v_xchg->scsi_cmnd_info.result = UNF_IO_SUCCESS;
else
v_xchg->scsi_cmnd_info.result = UNF_IO_FAILED;
/* wakeup semaphore & return */
up(&v_xchg->task_sema);
}