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

1579 lines
46 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_scsi_common.h"
#include "unf_lport.h"
#include "unf_rport.h"
#include "unf_portman.h"
#include "unf_npiv.h"
#include "unf_exchg.h"
#include "unf_io.h"
static int unf_scsi_queue_cmd(struct Scsi_Host *shost,
struct scsi_cmnd *v_cmnd);
static int unf_scsi_abort_scsi_cmnd(struct scsi_cmnd *v_cmnd);
static int unf_scsi_device_reset_handler(struct scsi_cmnd *v_cmnd);
static int unf_scsi_bus_reset_handler(struct scsi_cmnd *v_cmnd);
static int unf_scsi_target_reset_handler(struct scsi_cmnd *v_cmnd);
static int unf_scsi_slave_alloc(struct scsi_device *sdev);
static void unf_scsi_destroy_slave(struct scsi_device *sdev);
static int unf_scsi_slave_configure(struct scsi_device *sdev);
static int unf_scsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
static void unf_scsi_scan_start(struct Scsi_Host *shost);
static struct scsi_transport_template *scsi_transport_template;
static struct scsi_transport_template *scsi_transport_template_v;
struct unf_ini_error_code_s ini_error_code_table1[] = {
{ UNF_IO_SUCCESS, UNF_SCSI_HOST(DID_OK) },
{ UNF_IO_ABORTED, UNF_SCSI_HOST(DID_ABORT) },
{ UNF_IO_FAILED, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_ABORT_ABTS, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_ABORT_LOGIN, UNF_SCSI_HOST(DID_NO_CONNECT) },
{ UNF_IO_ABORT_REET, UNF_SCSI_HOST(DID_RESET) },
{ UNF_IO_ABORT_FAILED, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_OUTOF_ORDER, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_FTO, UNF_SCSI_HOST(DID_TIME_OUT) },
{ UNF_IO_LINK_FAILURE, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_OVER_FLOW, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_RSP_OVER, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_LOST_FRAME, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_UNDER_FLOW, UNF_SCSI_HOST(DID_OK) },
{ UNF_IO_HOST_PROG_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_SEST_PROG_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_INVALID_ENTRY, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_ABORT_SEQ_NOT, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_REJECT, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_EDC_IN_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_EDC_OUT_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_UNINIT_KEK_ERR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_DEK_OUTOF_RANGE, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_KEY_UNWRAP_ERR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_KEY_TAG_ERR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_KEY_ECC_ERR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_BLOCK_SIZE_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_ILLEGAL_CIPHER_MODE, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_CLEAN_UP, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_ABORTED_BY_TARGET, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_TRANSPORT_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_LINK_FLASH, UNF_SCSI_HOST(DID_NO_CONNECT) },
{ UNF_IO_TIMEOUT, UNF_SCSI_HOST(DID_TIME_OUT) },
{ UNF_IO_DMA_ERROR, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_NO_LPORT, UNF_SCSI_HOST(DID_NO_CONNECT) },
{ UNF_IO_NO_XCHG, UNF_SCSI_HOST(DID_SOFT_ERROR) },
{ UNF_IO_SOFT_ERR, UNF_SCSI_HOST(DID_SOFT_ERROR) },
{ UNF_IO_PORT_LOGOUT, UNF_SCSI_HOST(DID_NO_CONNECT) },
{ UNF_IO_ERREND, UNF_SCSI_HOST(DID_ERROR) },
{ UNF_IO_DIF_ERROR, (UNF_SCSI_HOST(DID_OK) | UNF_SCSI_STATUS(SCSI_CHECK_CONDITION)) },
{ UNF_IO_INCOMPLETE, UNF_SCSI_HOST(DID_IMM_RETRY) },
{ UNF_IO_DIF_REF_ERROR, (UNF_SCSI_HOST(DID_OK) | UNF_SCSI_STATUS(SCSI_CHECK_CONDITION)) },
{ UNF_IO_DIF_GEN_ERROR, (UNF_SCSI_HOST(DID_OK) | UNF_SCSI_STATUS(SCSI_CHECK_CONDITION)) }
};
unsigned int ini_err_code_table_cnt1 =
sizeof(ini_error_code_table1) / sizeof(struct unf_ini_error_code_s);
static void unf_set_rport_loss_tmo(struct fc_rport *rport,
unsigned int timeout)
{
if (timeout)
rport->dev_loss_tmo = timeout;
else
rport->dev_loss_tmo = 1;
}
static void unf_get_host_port_id(struct Scsi_Host *shost)
{
struct unf_lport_s *lport = NULL;
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return;
}
fc_host_port_id(shost) = lport->port_id;
}
static void unf_get_host_speed(struct Scsi_Host *shost)
{
struct unf_lport_s *lport = NULL;
unsigned int speed = FC_PORTSPEED_UNKNOWN;
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return;
}
switch (lport->speed) {
case UNF_PORT_SPEED_2_G:
speed = FC_PORTSPEED_2GBIT;
break;
case UNF_PORT_SPEED_4_G:
speed = FC_PORTSPEED_4GBIT;
break;
case UNF_PORT_SPEED_8_G:
speed = FC_PORTSPEED_8GBIT;
break;
case UNF_PORT_SPEED_16_G:
speed = FC_PORTSPEED_16GBIT;
break;
case UNF_PORT_SPEED_32_G:
speed = FC_PORTSPEED_32GBIT;
break;
default:
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[info]Port(0x%x) with unknown speed(0x%x) for FC mode",
lport->port_id, lport->speed);
break;
}
fc_host_speed(shost) = speed;
}
static void unf_get_host_port_type(struct Scsi_Host *shost)
{
struct unf_lport_s *lport = NULL;
unsigned int port_type = FC_PORTTYPE_UNKNOWN;
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return;
}
switch (lport->en_act_topo) {
case UNF_ACT_TOP_PRIVATE_LOOP:
port_type = FC_PORTTYPE_LPORT;
break;
case UNF_ACT_TOP_PUBLIC_LOOP:
port_type = FC_PORTTYPE_NLPORT;
break;
case UNF_ACT_TOP_P2P_DIRECT:
port_type = FC_PORTTYPE_PTP;
break;
case UNF_ACT_TOP_P2P_FABRIC:
port_type = FC_PORTTYPE_NPORT;
break;
default:
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[info]Port(0x%x) with unknown topo type(0x%x) for FC mode",
lport->port_id, lport->en_act_topo);
break;
}
fc_host_port_type(shost) = port_type;
}
static void unf_get_symbolic_name(struct Scsi_Host *shost)
{
unsigned char *name = NULL;
struct unf_lport_s *lport = NULL;
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Check l_port failed");
return;
}
name = fc_host_symbolic_name(shost);
if (name) {
snprintf(name, FC_SYMBOLIC_NAME_SIZE,
"HIFC_FW_RELEASE:%s HIFC_DRV_RELEASE:%s",
lport->fw_version, UNF_FC_VERSION);
}
}
static void unf_get_host_fabric_name(struct Scsi_Host *shost)
{
struct unf_lport_s *lport = NULL;
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return;
}
fc_host_fabric_name(shost) = lport->fabric_node_name;
}
static void unf_get_host_port_state(struct Scsi_Host *shost)
{
struct unf_lport_s *lport = NULL;
enum fc_port_state port_state;
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return;
}
switch (lport->link_up) {
case UNF_PORT_LINK_DOWN:
port_state = FC_PORTSTATE_OFFLINE;
break;
case UNF_PORT_LINK_UP:
port_state = FC_PORTSTATE_ONLINE;
break;
default:
port_state = FC_PORTSTATE_UNKNOWN;
break;
}
fc_host_port_state(shost) = port_state;
}
static void unf_dev_loss_timeout_callbk(struct fc_rport *rport)
{
/*
* NOTE: about rport->dd_data
* --->>> local SCSI_ID
* 1. Assignment during scsi rport link up
* 2. Released when scsi rport link down & timeout(30s)
* 3. Used during scsi do callback with slave_alloc function
*/
struct Scsi_Host *host = NULL;
struct unf_lport_s *lport = NULL;
unsigned int scsi_id = 0;
if (unlikely(!rport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]SCSI rport is null");
return;
}
host = rport_to_shost(rport);
if (unlikely(!host)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Host is null");
return;
}
/* according to Local SCSI_ID */
scsi_id = *(unsigned int *)(rport->dd_data);
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]scsi_id(0x%x) is max than(0x%x)",
scsi_id, UNF_MAX_SCSI_ID);
return;
}
lport = (struct unf_lport_s *)host->hostdata[0];
if (unf_is_lport_valid(lport) == RETURN_OK) {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_INFO,
"[event]Port(0x%x_0x%x) RPort scsi_id(0x%x) target_id(0x%x) loss timeout",
lport->port_id, lport->nport_id,
scsi_id, rport->scsi_target_id);
atomic_inc(&lport->session_loss_tmo);
/* Free SCSI ID & set table state with DEAD */
(void)unf_free_scsi_id(lport, scsi_id);
} else {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(%p) is invalid", lport);
}
/* reset scsi rport dd_data(local SCSI_ID) */
*((unsigned int *)rport->dd_data) = INVALID_VALUE32;
}
int unf_scsi_create_vport(struct fc_vport *fc_port, bool disabled)
{
struct unf_lport_s *vport = NULL;
struct unf_lport_s *lport = NULL;
struct Scsi_Host *shost = NULL;
struct vport_config_s vport_config = { 0 };
shost = vport_to_shost(fc_port);
lport = (struct unf_lport_s *)shost->hostdata[0];
if (unf_is_lport_valid(lport) != RETURN_OK) {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(%p) is invalid", lport);
return RETURN_ERROR;
}
vport_config.port_name = fc_port->port_name;
vport_config.port_mode = fc_port->roles;
vport = unf_create_vport(lport, &vport_config);
if (!vport) {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(0x%x) Create Vport failed on lldrive",
lport->port_id);
return RETURN_ERROR;
}
fc_port->dd_data = vport;
vport->vport = fc_port;
return RETURN_OK;
}
int unf_scsi_delete_vport(struct fc_vport *fc_port)
{
int ret = RETURN_ERROR;
struct unf_lport_s *vport = NULL;
vport = (struct unf_lport_s *)fc_port->dd_data;
if (unf_is_lport_valid(vport) != RETURN_OK) {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]VPort(%p) is invalid or is removing",
vport);
fc_port->dd_data = NULL;
return ret;
}
ret = (int)unf_destroy_one_vport(vport);
if (ret != RETURN_OK) {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]VPort(0x%x) destroy failed on drive",
vport->port_id);
return ret;
}
fc_port->dd_data = NULL;
return ret;
}
struct fc_function_template function_template = {
.show_host_node_name = 1,
.show_host_port_name = 1,
.show_host_supported_classes = 1,
.show_host_supported_speeds = 1,
.get_host_port_id = unf_get_host_port_id,
.show_host_port_id = 1,
.get_host_speed = unf_get_host_speed,
.show_host_speed = 1,
.get_host_port_type = unf_get_host_port_type,
.show_host_port_type = 1,
.get_host_symbolic_name = unf_get_symbolic_name,
.show_host_symbolic_name = 1,
.set_host_system_hostname = NULL,
.show_host_system_hostname = 1,
.get_host_fabric_name = unf_get_host_fabric_name,
.show_host_fabric_name = 1,
.get_host_port_state = unf_get_host_port_state,
.show_host_port_state = 1,
.dd_fcrport_size = sizeof(void *),
.show_rport_supported_classes = 1,
.get_starget_node_name = NULL,
.show_starget_node_name = 1,
.get_starget_port_name = NULL,
.show_starget_port_name = 1,
.get_starget_port_id = NULL,
.show_starget_port_id = 1,
.set_rport_dev_loss_tmo = unf_set_rport_loss_tmo,
.show_rport_dev_loss_tmo = 0,
.issue_fc_host_lip = NULL,
.dev_loss_tmo_callbk = unf_dev_loss_timeout_callbk,
.terminate_rport_io = NULL,
.get_fc_host_stats = NULL,
.vport_create = unf_scsi_create_vport,
.vport_disable = NULL,
.vport_delete = unf_scsi_delete_vport,
.bsg_request = NULL,
.bsg_timeout = NULL,
};
struct fc_function_template function_template_v = {
.show_host_node_name = 1,
.show_host_port_name = 1,
.show_host_supported_classes = 1,
.show_host_supported_speeds = 1,
.get_host_port_id = unf_get_host_port_id,
.show_host_port_id = 1,
.get_host_speed = unf_get_host_speed,
.show_host_speed = 1,
.get_host_port_type = unf_get_host_port_type,
.show_host_port_type = 1,
.get_host_symbolic_name = unf_get_symbolic_name,
.show_host_symbolic_name = 1,
.set_host_system_hostname = NULL,
.show_host_system_hostname = 1,
.get_host_fabric_name = unf_get_host_fabric_name,
.show_host_fabric_name = 1,
.get_host_port_state = unf_get_host_port_state,
.show_host_port_state = 1,
.dd_fcrport_size = sizeof(void *),
.show_rport_supported_classes = 1,
.get_starget_node_name = NULL,
.show_starget_node_name = 1,
.get_starget_port_name = NULL,
.show_starget_port_name = 1,
.get_starget_port_id = NULL,
.show_starget_port_id = 1,
.set_rport_dev_loss_tmo = unf_set_rport_loss_tmo,
.show_rport_dev_loss_tmo = 1,
.issue_fc_host_lip = NULL,
.dev_loss_tmo_callbk = unf_dev_loss_timeout_callbk,
.terminate_rport_io = NULL,
.get_fc_host_stats = NULL,
.vport_create = NULL,
.vport_disable = NULL,
.vport_delete = NULL,
.bsg_request = NULL,
.bsg_timeout = NULL,
};
struct scsi_host_template scsi_host_template = {
.module = THIS_MODULE,
.name = "HIFC",
.queuecommand = unf_scsi_queue_cmd,
.eh_abort_handler = unf_scsi_abort_scsi_cmnd,
.eh_device_reset_handler = unf_scsi_device_reset_handler,
.eh_target_reset_handler = unf_scsi_target_reset_handler,
.eh_bus_reset_handler = unf_scsi_bus_reset_handler,
.eh_host_reset_handler = NULL,
.slave_configure = unf_scsi_slave_configure,
.slave_alloc = unf_scsi_slave_alloc,
.slave_destroy = unf_scsi_destroy_slave,
.scan_finished = unf_scsi_scan_finished,
.scan_start = unf_scsi_scan_start,
.this_id = -1,
.cmd_per_lun = 3,
.shost_attrs = NULL,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
.supported_mode = MODE_INITIATOR,
};
static void unf_unmap_prot_sgl(struct scsi_cmnd *v_cmnd)
{
struct device *dev;
if ((scsi_get_prot_op(v_cmnd) != SCSI_PROT_NORMAL) &&
hifc_dif_enable && (scsi_prot_sg_count(v_cmnd))) {
dev = v_cmnd->device->host->dma_dev;
dma_unmap_sg(dev, scsi_prot_sglist(v_cmnd),
(int)scsi_prot_sg_count(v_cmnd),
v_cmnd->sc_data_direction);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
"scsi done cmd:%p op:%d,difsglcount:%d",
v_cmnd, scsi_get_prot_op(v_cmnd),
scsi_prot_sg_count(v_cmnd));
}
}
void unf_scsi_done(struct unf_scsi_cmd_s *v_scsi_cmnd)
{
struct scsi_cmnd *cmnd = NULL;
UNF_CHECK_VALID(0x509, UNF_TRUE, v_scsi_cmnd, return);
cmnd = (struct scsi_cmnd *)v_scsi_cmnd->upper_cmnd;
UNF_CHECK_VALID(0x510, UNF_TRUE, cmnd, return);
UNF_CHECK_VALID(0x511, UNF_TRUE, cmnd->scsi_done, return);
scsi_set_resid(cmnd, (int)v_scsi_cmnd->resid);
cmnd->result = v_scsi_cmnd->result;
scsi_dma_unmap(cmnd);
unf_unmap_prot_sgl(cmnd);
return cmnd->scsi_done(cmnd);
}
void unf_host_init_attr_setting(unf_scsi_host_s *scsi_host)
{
struct unf_lport_s *lport = NULL;
unsigned int speed = FC_PORTSPEED_UNKNOWN;
lport = (struct unf_lport_s *)scsi_host->hostdata[0];
fc_host_supported_classes(scsi_host) = FC_COS_CLASS3; /* class_3 */
fc_host_dev_loss_tmo(scsi_host) =
(unsigned int)unf_get_link_lose_tmo(lport); /* 30s */
fc_host_node_name(scsi_host) = lport->node_name;
fc_host_port_name(scsi_host) = lport->port_name;
fc_host_max_npiv_vports(scsi_host) =
(unsigned short)((lport == lport->root_lport) ?
lport->low_level_func.support_max_npiv_num : 0);
fc_host_npiv_vports_inuse(scsi_host) = 0;
fc_host_next_vport_number(scsi_host) = 0;
/* About speed mode */
if ((lport->low_level_func.fc_ser_max_speed == UNF_PORT_SPEED_32_G) &&
(lport->card_type == UNF_FC_SERVER_BOARD_32_G)) {
speed = FC_PORTSPEED_32GBIT | FC_PORTSPEED_16GBIT |
FC_PORTSPEED_8GBIT;
} else if ((lport->low_level_func.fc_ser_max_speed ==
UNF_PORT_SPEED_16_G) &&
(lport->card_type == UNF_FC_SERVER_BOARD_16_G)) {
speed = FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT |
FC_PORTSPEED_4GBIT;
} else if ((lport->low_level_func.fc_ser_max_speed ==
UNF_PORT_SPEED_8_G) &&
(lport->card_type == UNF_FC_SERVER_BOARD_8_G)) {
speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
FC_PORTSPEED_2GBIT;
}
fc_host_supported_speeds(scsi_host) = speed;
}
int unf_alloc_scsi_host(unf_scsi_host_s **v_scsi_host,
struct unf_host_param_s *v_host_param)
{
int ret = RETURN_ERROR;
struct Scsi_Host *scsi_host = NULL;
struct unf_lport_s *lport = NULL;
UNF_CHECK_VALID(0x512, UNF_TRUE, v_scsi_host, return RETURN_ERROR);
UNF_CHECK_VALID(0x513, UNF_TRUE, v_host_param, return RETURN_ERROR);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Alloc scsi host...");
/* Check L_Port validity */
lport = (struct unf_lport_s *)(v_host_param->lport);
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is NULL and return directly");
return RETURN_ERROR;
}
scsi_host_template.can_queue = v_host_param->can_queue;
scsi_host_template.cmd_per_lun = v_host_param->cmnd_per_lun;
scsi_host_template.sg_tablesize = v_host_param->sg_table_size;
scsi_host_template.max_sectors = v_host_param->max_sectors;
/* Alloc scsi host */
scsi_host = scsi_host_alloc(&scsi_host_template,
sizeof(unsigned long long));
if (unlikely(!scsi_host)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Register scsi host failed");
return RETURN_ERROR;
}
scsi_host->max_channel = v_host_param->max_channel;
scsi_host->max_lun = v_host_param->max_lun;
scsi_host->max_cmd_len = v_host_param->max_cmnd_len;
scsi_host->unchecked_isa_dma = 0;
scsi_host->hostdata[0] = (unsigned long)lport; /* save L_Port to scsi */
scsi_host->unique_id = scsi_host->host_no;
scsi_host->max_id = v_host_param->max_id;
scsi_host->transportt = (lport == lport->root_lport) ?
scsi_transport_template : scsi_transport_template_v;
/* register DIF/DIX protection */
if (hifc_dif_enable) {
/* Enable DIF and DIX function */
scsi_host_set_prot(scsi_host, hifc_dif_type);
hifc_guard = SHOST_DIX_GUARD_CRC;
/* Enable IP checksum algorithm in DIX */
if (dix_flag)
hifc_guard |= SHOST_DIX_GUARD_IP;
scsi_host_set_guard(scsi_host, hifc_guard);
}
/* Add scsi host */
ret = scsi_add_host(scsi_host, v_host_param->pdev);
if (unlikely(ret)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Add scsi host failed with return value %d",
ret);
scsi_host_put(scsi_host);
return RETURN_ERROR;
}
/* Set scsi host attribute */
unf_host_init_attr_setting(scsi_host);
*v_scsi_host = scsi_host;
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Alloc and add scsi host(0x%llx) succeed",
(unsigned long long)scsi_host);
return RETURN_OK;
}
void unf_free_scsi_host(unf_scsi_host_s *v_scsi_host)
{
struct Scsi_Host *scsi_host = NULL;
scsi_host = v_scsi_host;
fc_remove_host(scsi_host);
scsi_remove_host(scsi_host);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Remove scsi host(%d) succeed", scsi_host->host_no);
scsi_host_put(scsi_host);
}
static int unf_get_protect_mode(struct unf_lport_s *lport,
struct scsi_cmnd *v_cmnd,
struct unf_scsi_cmd_s *v_scsi_cmnd)
{
struct scsi_cmnd *cmd = NULL;
int difsegcnt = 0;
struct unf_dif_control_info_s *dif_control_info = NULL;
cmd = v_cmnd;
dif_control_info = &v_scsi_cmnd->dif_control;
switch (scsi_get_prot_op(cmd)) {
/* OS-HBA: Unprotected, HBA-Target: Protected */
case SCSI_PROT_READ_STRIP:
dif_control_info->protect_opcode |=
UNF_DIF_ACTION_VERIFY_AND_DELETE;
break;
case SCSI_PROT_WRITE_INSERT:
dif_control_info->protect_opcode |=
UNF_DIF_ACTION_INSERT;
break;
/* OS-HBA: Protected, HBA-Target: Unprotected */
case SCSI_PROT_READ_INSERT:
dif_control_info->protect_opcode |=
UNF_DIF_ACTION_INSERT;
break;
case SCSI_PROT_WRITE_STRIP:
dif_control_info->protect_opcode |=
UNF_DIF_ACTION_VERIFY_AND_DELETE;
break;
/* OS-HBA: Protected, HBA-Target: Protected */
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
dif_control_info->protect_opcode |=
UNF_DIF_ACTION_VERIFY_AND_FORWARD;
break;
default:
dif_control_info->protect_opcode |=
UNF_DIF_ACTION_VERIFY_AND_FORWARD;
break;
}
if (dif_sgl_mode)
dif_control_info->flags |= UNF_DIF_DOUBLE_SGL;
dif_control_info->protect_opcode |=
UNF_VERIFY_CRC_MASK | UNF_VERIFY_LBA_MASK;
dif_control_info->dif_sge_count = scsi_prot_sg_count(cmd);
dif_control_info->dif_sgl = scsi_prot_sglist(cmd);
dif_control_info->start_lba =
cpu_to_le32(((uint32_t)(0xffffffff & scsi_get_lba(cmd))));
if (scsi_prot_sg_count(cmd)) {
difsegcnt = dma_map_sg(&lport->low_level_func.dev->dev,
scsi_prot_sglist(cmd),
(int)scsi_prot_sg_count(cmd),
cmd->sc_data_direction);
if (unlikely(!difsegcnt)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) cmd:%p map dif sgl err",
lport->port_id, cmd);
return UNF_RETURN_ERROR;
}
}
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
"build scsi cmd:%p op:%d,difsglcount:%d,difsegcnt:%d",
cmd, scsi_get_prot_op(cmd), scsi_prot_sg_count(cmd),
difsegcnt);
return RETURN_OK;
}
unsigned int unf_get_frame_entry_buf(void *v_up_cmnd,
void *v_driver_sgl,
void **v_upper_sgl,
unsigned int *v_port_id,
unsigned int *v_index,
char **v_buf,
unsigned int *v_buf_len)
{
#define HIFC_1822_MAX_DMA_LENGTH (0x20000 - 1)
struct scatterlist *scsi_sgl = *v_upper_sgl;
UNF_REFERNCE_VAR(v_up_cmnd);
UNF_REFERNCE_VAR(v_driver_sgl);
UNF_REFERNCE_VAR(v_port_id);
if (unlikely(!scsi_sgl)) {
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Command(0x%p) can not get SGL.", v_up_cmnd);
return RETURN_ERROR;
}
*v_buf = (char *)sg_dma_address(scsi_sgl);
*v_buf_len = sg_dma_len(scsi_sgl);
*v_upper_sgl = (void *)sg_next(scsi_sgl);
if (unlikely((*v_buf_len > HIFC_1822_MAX_DMA_LENGTH) ||
(*v_buf_len == 0))) {
UNF_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
"[err]Command(0x%p) dmalen:0x%x is not support.",
v_up_cmnd, *v_buf_len);
return RETURN_ERROR;
}
return RETURN_OK;
}
static int unf_scsi_queue_cmd(struct Scsi_Host *shost,
struct scsi_cmnd *v_cmnd)
{
struct Scsi_Host *host = NULL;
struct scsi_cmnd *cmd = NULL;
struct unf_scsi_cmd_s scsi_cmnd = { 0 };
unsigned int scsi_id = 0;
unsigned int en_scsi_state = 0;
int ret = SCSI_MLQUEUE_HOST_BUSY;
// unsigned int uiError = 0;
struct unf_lport_s *lport = NULL;
struct fc_rport *p_rport = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
unsigned int ret_value = 0;
struct unf_rport_s *rport = NULL;
unsigned int cmnd_result = 0;
unsigned int rport_state_err = 0;
unsigned int scan_device_cmd = 0;
unsigned long long raw_lun_id = 0;
int data_seg_cnt = 0;
static atomic64_t ull_count;
host = shost;
cmd = v_cmnd;
UNF_CHECK_VALID(0x515, UNF_TRUE, host, return RETURN_ERROR);
UNF_CHECK_VALID(0x514, UNF_TRUE, cmd, return RETURN_ERROR);
/* Get L_Port from scsi_cmnd */
lport = (struct unf_lport_s *)host->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Check l_port failed, cmd(%p)", cmd);
/* scsi_done & return 0 & I/O error */
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
return 0;
}
/* Check device/session local state by device_id */
/* local SCSI_ID from device */
scsi_id = (unsigned int)((unsigned long long)cmd->device->hostdata);
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(0x%x) scsi_id(0x%x) is max than %d",
lport->port_id, scsi_id, UNF_MAX_SCSI_ID);
/* scsi_done & return 0 & I/O error */
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
return 0;
}
scsi_image_table = &lport->rport_scsi_table;
UNF_SCSI_CMD_CNT(scsi_image_table, scsi_id, cmd->cmnd[0]);
/* Get scsi r_port */
/*lint -e666 -esym(666,*)*/
p_rport = starget_to_rport(scsi_target(cmd->device));
if (unlikely(!p_rport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(0x%x) cmd(%p) to get scsi rport failed",
lport->port_id, cmd);
/* scsi_done & return 0 & I/O error */
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
ret_value = DID_NO_CONNECT;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret_value);
return 0;
}
if (unlikely(!scsi_image_table->wwn_rport_info_table)) {
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_ABNORMAL, UNF_WARN,
"[warn]Port(0x%x) WwnRportInfoTable NULL", lport->port_id);
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
ret_value = DID_NO_CONNECT;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret_value);
return 0;
}
if (unlikely(lport->b_port_removing == UNF_TRUE)) {
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_ABNORMAL, UNF_WARN,
"[warn]Port(0x%x) scsi_id(0x%x) rport(0x%p) target_id(0x%x) cmd(0x%p) is removing",
lport->port_id, scsi_id, p_rport, p_rport->scsi_target_id, cmd);
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
ret_value = DID_NO_CONNECT;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret_value);
return 0;
}
en_scsi_state = atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].en_scsi_state);
if (unlikely(en_scsi_state != UNF_SCSI_ST_ONLINE)) {
if (en_scsi_state == UNF_SCSI_ST_OFFLINE) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) scsi_state(0x%x) scsi_id(0x%x) rport(0x%p) target_id(0x%x) cmd(0x%p), target is busy",
lport->port_id, en_scsi_state, scsi_id,
p_rport, p_rport->scsi_target_id, cmd);
scan_device_cmd = (cmd->cmnd[0] == INQUIRY) ||
(cmd->cmnd[0] == REPORT_LUNS);
/* report lun or inquiry cmd, if send failed,
* do not retry, prevent the scan_mutex in
* scsi host locked up by eachother
*/
if (scan_device_cmd) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) host(0x%x) scsi_id(0x%x) lun(0x%llx) cmd(0x%x) DID_NO_CONNECT",
lport->port_id, host->host_no,
scsi_id,
(unsigned long long)cmd->device->lun,
cmd->cmnd[0]);
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
ret_value = DID_NO_CONNECT;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id,
ret_value);
return 0;
}
if (likely(scsi_image_table->wwn_rport_info_table)) {
if (likely(scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter))
atomic64_inc(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->target_busy);
}
/* Target busy: need scsi retry */
return SCSI_MLQUEUE_TARGET_BUSY;
}
/* timeout(DEAD): scsi_done & return 0 & I/O error */
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) scsi_id(0x%x) rport(0x%p) target_id(0x%x) cmd(0x%p), target is loss timeout",
lport->port_id, scsi_id, p_rport,
p_rport->scsi_target_id, cmd);
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
ret_value = DID_NO_CONNECT;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret_value);
return 0;
}
raw_lun_id = ((unsigned long long)cmd->device->lun << 16) &
0x00000000ffff0000;
if (scsi_sg_count(cmd)) {
data_seg_cnt = dma_map_sg(&lport->low_level_func.dev->dev,
scsi_sglist(cmd),
(int)scsi_sg_count(cmd),
cmd->sc_data_direction);
if (unlikely(!data_seg_cnt)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) scsi_id(0x%x) rport(0x%p) target_id(0x%x) cmd(0x%p), dma map sg err",
lport->port_id, scsi_id,
p_rport, p_rport->scsi_target_id, cmd);
cmd->result = DID_BUS_BUSY << 16;
cmd->scsi_done(cmd);
ret_value = DID_BUS_BUSY;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret_value);
return SCSI_MLQUEUE_HOST_BUSY;
}
}
/* Construct local SCSI CMND info */
/* save host_no to scsi_cmnd->scsi_host_id */
scsi_cmnd.scsi_host_id = host->host_no;
scsi_cmnd.scsi_id = scsi_id;
scsi_cmnd.lun_id = raw_lun_id;
scsi_cmnd.data_direction = cmd->sc_data_direction;
scsi_cmnd.underflow = cmd->underflow;
scsi_cmnd.cmnd_len = cmd->cmd_len;
scsi_cmnd.pcmnd = cmd->cmnd;
scsi_cmnd.transfer_len = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
scsi_cmnd.sense_buf_len = SCSI_SENSE_DATA_LEN;
scsi_cmnd.sense_buf = cmd->sense_buffer;
scsi_cmnd.time_out = 0;
scsi_cmnd.upper_cmnd = cmd;
scsi_cmnd.drv_private =
(void *)(*(unsigned long long *)shost_priv(host));
scsi_cmnd.entry_count = data_seg_cnt;
scsi_cmnd.sgl = scsi_sglist(cmd);
scsi_cmnd.pfn_unf_ini_get_sgl_entry = unf_get_frame_entry_buf;
scsi_cmnd.pfn_done = unf_scsi_done;
scsi_cmnd.pc_lun_id = (unsigned char *)&scsi_cmnd.lun_id;
scsi_cmnd.err_code_table_cout = ini_err_code_table_cnt1;
scsi_cmnd.err_code_table = ini_error_code_table1;
scsi_cmnd.world_id = 0xfffffffc;
scsi_cmnd.cmnd_sn = atomic64_inc_return(&ull_count);
if (unlikely(scsi_cmnd.cmnd_sn == 0))
scsi_cmnd.cmnd_sn = atomic64_inc_return(&ull_count);
if ((scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) &&
hifc_dif_enable) {
ret = unf_get_protect_mode(lport, cmd, &scsi_cmnd);
if (ret != RETURN_OK) {
cmd->result = DID_BUS_BUSY << 16;
cmd->scsi_done(cmd);
ret_value = DID_BUS_BUSY;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id,
ret_value);
scsi_dma_unmap(cmd);
return SCSI_MLQUEUE_HOST_BUSY;
}
}
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_INFO,
"[info]Port(0x%x) host(0x%x) scsi_id(0x%x) lun(0x%llx) transfer length(0x%x) cmd_len(0x%x) direction(0x%x) cmd(0x%x) under_flow(0x%x)",
lport->port_id, host->host_no, scsi_id,
(unsigned long long)cmd->device->lun,
scsi_cmnd.transfer_len,
scsi_cmnd.cmnd_len, cmd->sc_data_direction,
scsi_cmnd.pcmnd[0], scsi_cmnd.underflow);
/* Bind the Exchange address corresponding to scsi_cmnd to
* scsi_cmnd->host_scribble
*/
cmd->host_scribble = (unsigned char *)scsi_cmnd.cmnd_sn;
ret = unf_cm_queue_command(&scsi_cmnd);
if (ret != RETURN_OK) {
rport = unf_find_rport_by_scsi_id(lport,
ini_error_code_table1,
ini_err_code_table_cnt1,
scsi_id,
&cmnd_result);
rport_state_err = (!rport) ||
(rport->lport_ini_state !=
UNF_PORT_STATE_LINKUP) ||
(rport->rp_state == UNF_RPORT_ST_CLOSING);
scan_device_cmd = (cmd->cmnd[0] == INQUIRY) ||
(cmd->cmnd[0] == REPORT_LUNS);
/* report lun or inquiry cmd if send failed, do not retry,
* prevent the scan_mutex in scsi host locked up by eachother
*/
if (rport_state_err && scan_device_cmd) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) host(0x%x) scsi_id(0x%x) lun(0x%llx) cmd(0x%x) cmResult(0x%x) DID_NO_CONNECT",
lport->port_id, host->host_no, scsi_id,
(unsigned long long)cmd->device->lun,
cmd->cmnd[0], cmnd_result);
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
ret_value = DID_NO_CONNECT;
UNF_IO_RESULT_CNT(scsi_image_table, scsi_id, ret_value);
scsi_dma_unmap(cmd);
unf_unmap_prot_sgl(cmd);
return 0;
}
/* Host busy: scsi need to retry */
ret = SCSI_MLQUEUE_HOST_BUSY;
if (likely(scsi_image_table->wwn_rport_info_table)) {
if (likely(scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter))
atomic64_inc(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->host_busy);
}
scsi_dma_unmap(cmd);
unf_unmap_prot_sgl(cmd);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x) return(0x%x) to process INI IO falid",
lport->port_id, ret);
}
return ret;
}
static int unf_scsi_abort_scsi_cmnd(struct scsi_cmnd *v_cmnd)
{
/* SCSI ABORT Command --->>> FC ABTS */
struct unf_scsi_cmd_s scsi_cmnd = { 0 };
struct Scsi_Host *scsi_host = NULL;
int ret = FAILED;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
struct unf_lport_s *lport = NULL;
unsigned int scsi_id = 0;
unsigned int err_handle = 0;
UNF_CHECK_VALID(0x516, UNF_TRUE, v_cmnd, return FAILED);
lport = (struct unf_lport_s *)v_cmnd->device->host->hostdata[0];
scsi_id = (unsigned int)((unsigned long long)v_cmnd->device->hostdata);
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_ABORT_IO_TYPE;
UNF_SCSI_ERROR_HANDLE_CNT(scsi_image_table,
scsi_id, err_handle);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[abort]Port(0x%x) scsi_id(0x%x) lun_id(0x%x) cmnd_type(0x%x)",
lport->port_id, scsi_id,
(unsigned int)v_cmnd->device->lun,
v_cmnd->cmnd[0]);
} else {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Lport(%p) is moving or null", lport);
return UNF_SCSI_ABORT_FAIL;
}
/* Check local SCSI_ID validity */
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]scsi_id(0x%x) is max than(0x%x)",
scsi_id, UNF_MAX_SCSI_ID);
return UNF_SCSI_ABORT_FAIL;
}
/* Block scsi (check rport state -> whether offline or not) */
ret = fc_block_scsi_eh(v_cmnd);
if (unlikely(ret != 0)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Block scsi eh failed(0x%x)", ret);
return ret;
}
scsi_host = v_cmnd->device->host;
scsi_cmnd.scsi_host_id = scsi_host->host_no; // L_Port ID
scsi_cmnd.scsi_id = scsi_id; // R_Port ID (Target ID)
scsi_cmnd.lun_id = (unsigned long long)v_cmnd->device->lun; // LUN ID
scsi_cmnd.upper_cmnd = v_cmnd; // scsi_cmnd
// L_Port
scsi_cmnd.drv_private =
(void *)(*(unsigned long long *)shost_priv(scsi_host));
scsi_cmnd.cmnd_sn = (unsigned long long)(v_cmnd->host_scribble);
scsi_cmnd.pc_lun_id = (unsigned char *)&scsi_cmnd.lun_id;
scsi_cmnd.pfn_done = unf_scsi_done;
scsi_cmnd.world_id = 0xfffffffc;
/* Process scsi Abort cmnd */
ret = unf_cm_eh_abort_handler(&scsi_cmnd);
if (ret == UNF_SCSI_ABORT_SUCCESS) {
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_ABORT_IO_TYPE;
UNF_SCSI_ERROR_HANDLE_RESULT_CNT(scsi_image_table,
scsi_id, err_handle);
}
}
return ret;
}
static int unf_scsi_device_reset_handler(struct scsi_cmnd *v_cmnd)
{
/* LUN reset */
struct unf_scsi_cmd_s scsi_cmnd = { 0 };
struct Scsi_Host *scsi_host = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
int ret = FAILED;
struct unf_lport_s *lport = NULL;
unsigned int scsi_id = 0;
unsigned int err_handle = 0;
UNF_CHECK_VALID(0x517, UNF_TRUE, v_cmnd, return FAILED);
lport = (struct unf_lport_s *)v_cmnd->device->host->hostdata[0];
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_DEVICE_RESET_TYPE;
UNF_SCSI_ERROR_HANDLE_CNT(scsi_image_table,
scsi_id, err_handle);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_KEVENT,
"[device_reset]Port(0x%x) scsi_id(0x%x) lun_id(0x%x) cmnd_type(0x%x)",
lport->port_id, scsi_id,
(unsigned int)v_cmnd->device->lun,
v_cmnd->cmnd[0]);
} else {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is invalid");
return FAILED;
}
/* Check local SCSI_ID validity */
scsi_id = (unsigned int)((unsigned long long)v_cmnd->device->hostdata);
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]scsi_id(0x%x) is max than(0x%x)",
scsi_id, UNF_MAX_SCSI_ID);
return FAILED;
}
/* Block scsi (check rport state -> whether offline or not) */
ret = fc_block_scsi_eh(v_cmnd);
if (unlikely(ret != 0)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Block scsi eh failed(0x%x)", ret);
return ret;
}
scsi_host = v_cmnd->device->host;
scsi_cmnd.scsi_host_id = scsi_host->host_no; /* l_port id */
scsi_cmnd.scsi_id = scsi_id; /* r_port id */
scsi_cmnd.lun_id = (unsigned long long)v_cmnd->device->lun; /* lun id */
scsi_cmnd.upper_cmnd = v_cmnd; /* scsi_cmnd */
/* l_port */
scsi_cmnd.drv_private =
(void *)(*(unsigned long long *)shost_priv(scsi_host));
scsi_cmnd.pc_lun_id = (unsigned char *)&scsi_cmnd.lun_id; /* lun id */
/* Process scsi device/LUN reset cmnd */
ret = unf_cm_eh_device_reset_handler(&scsi_cmnd);
if (ret == UNF_SCSI_ABORT_SUCCESS) {
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_DEVICE_RESET_TYPE;
UNF_SCSI_ERROR_HANDLE_RESULT_CNT(scsi_image_table,
scsi_id,
err_handle);
}
}
return ret;
}
static int unf_scsi_bus_reset_handler(struct scsi_cmnd *v_cmnd)
{
/* BUS Reset */
struct unf_scsi_cmd_s scsi_cmnd = { 0 };
struct unf_lport_s *lport = NULL;
struct Scsi_Host *scsi_host = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
int ret = FAILED;
unsigned int scsi_id = 0;
unsigned int err_handle = 0;
UNF_CHECK_VALID(0x517, UNF_TRUE, v_cmnd, return FAILED);
lport = (struct unf_lport_s *)v_cmnd->device->host->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return FAILED;
}
/* Check local SCSI_ID validity */
scsi_id = (unsigned int)((unsigned long long)v_cmnd->device->hostdata);
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]scsi_id(0x%x) is max than(0x%x)",
scsi_id, UNF_MAX_SCSI_ID);
return FAILED;
}
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_BUS_RESET_TYPE;
UNF_SCSI_ERROR_HANDLE_CNT(scsi_image_table,
scsi_id, err_handle);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[info][bus_reset]Port(0x%x) scsi_id(0x%x) lun_id(0x%x) cmnd_type(0x%x)",
lport->port_id, scsi_id,
(unsigned int)v_cmnd->device->lun,
v_cmnd->cmnd[0]);
}
/* Block scsi (check rport state -> whether offline or not) */
ret = fc_block_scsi_eh(v_cmnd);
if (unlikely(ret != 0)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Block scsi eh failed(0x%x)", ret);
return ret;
}
scsi_host = v_cmnd->device->host;
scsi_cmnd.scsi_host_id = scsi_host->host_no; /* l_port id */
scsi_cmnd.scsi_id = scsi_id; /* r_port id */
scsi_cmnd.lun_id = (unsigned long long)v_cmnd->device->lun; /* lun id */
scsi_cmnd.upper_cmnd = v_cmnd; /* scsi_cmnd */
/* l_port */
scsi_cmnd.drv_private =
(void *)(*(unsigned long long *)shost_priv(scsi_host));
scsi_cmnd.pc_lun_id = (unsigned char *)&scsi_cmnd.lun_id; /* lun id */
/* Process scsi BUS Reset cmnd */
ret = unf_cm_bus_reset_handler(&scsi_cmnd);
if (ret == UNF_SCSI_ABORT_SUCCESS) {
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_BUS_RESET_TYPE;
UNF_SCSI_ERROR_HANDLE_RESULT_CNT(scsi_image_table,
scsi_id,
err_handle);
}
}
return ret;
}
static int unf_scsi_target_reset_handler(struct scsi_cmnd *v_cmnd)
{
/* Session reset/delete */
struct unf_scsi_cmd_s scsi_cmnd = { 0 };
struct Scsi_Host *scsi_host = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
int ret = FAILED;
struct unf_lport_s *lport = NULL;
unsigned int scsi_id = 0;
unsigned int err_handle = 0;
UNF_CHECK_VALID(0x517, UNF_TRUE, v_cmnd, return RETURN_ERROR);
lport = (struct unf_lport_s *)v_cmnd->device->host->hostdata[0];
if (unlikely(!lport)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is null");
return FAILED;
}
/* Check local SCSI_ID validity */
scsi_id = (unsigned int)((unsigned long long)v_cmnd->device->hostdata);
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]scsi_id(0x%x) is max than(0x%x)",
scsi_id, UNF_MAX_SCSI_ID);
return FAILED;
}
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_TARGET_RESET_TYPE;
UNF_SCSI_ERROR_HANDLE_CNT(scsi_image_table, scsi_id,
err_handle);
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_KEVENT,
"[target_reset]Port(0x%x) scsi_id(0x%x) lun_id(0x%x) cmnd_type(0x%x)",
lport->port_id, scsi_id,
(unsigned int)v_cmnd->device->lun,
v_cmnd->cmnd[0]);
}
/* Block scsi (check rport state -> whether offline or not) */
ret = fc_block_scsi_eh(v_cmnd);
if (unlikely(ret != 0)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Block scsi eh failed(0x%x)", ret);
return ret;
}
scsi_host = v_cmnd->device->host;
scsi_cmnd.scsi_host_id = scsi_host->host_no; /* l_port id */
scsi_cmnd.scsi_id = scsi_id; /* r_port id */
scsi_cmnd.lun_id = (unsigned long long)v_cmnd->device->lun; /* lun id */
scsi_cmnd.upper_cmnd = v_cmnd; /* scsi_cmnd */
/* l_port */
scsi_cmnd.drv_private =
(void *)(*(unsigned long long *)shost_priv(scsi_host));
scsi_cmnd.pc_lun_id = (unsigned char *)&scsi_cmnd.lun_id; /* lun id */
/* Process scsi Target/Session reset/delete cmnd */
ret = unf_cm_target_reset_handler(&scsi_cmnd);
if (ret == UNF_SCSI_ABORT_SUCCESS) {
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
err_handle = UNF_SCSI_TARGET_RESET_TYPE;
UNF_SCSI_ERROR_HANDLE_RESULT_CNT(scsi_image_table,
scsi_id, err_handle);
}
}
return ret;
}
static int unf_scsi_slave_alloc(struct scsi_device *sdev)
{
/*lint -e666 -esym(666,*)*/
struct fc_rport *rport = NULL;
unsigned int scsi_id = 0;
struct unf_lport_s *lport = NULL;
struct Scsi_Host *host = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
/* About device */
if (unlikely(!sdev)) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]SDev is null");
return -ENXIO;
}
/* About scsi rport */
rport = starget_to_rport(scsi_target(sdev));
if (unlikely(!rport || fc_remote_port_chkready(rport))) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]SCSI rport is null");
if (rport) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]SCSI rport is not ready(0x%x)",
fc_remote_port_chkready(rport));
}
return -ENXIO;
}
/* About host */
host = rport_to_shost(rport);
if (unlikely(!host)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Host is null");
return -ENXIO;
}
/* About Local Port */
lport = (struct unf_lport_s *)host->hostdata[0];
if (unf_is_lport_valid(lport) != RETURN_OK) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port is invalid");
return -ENXIO;
}
/* About Local SCSI_ID */
/* use local SCSI_ID to alloc slave device */
scsi_id = *(unsigned int *)rport->dd_data;
if (unlikely(scsi_id >= UNF_MAX_SCSI_ID)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]scsi_id(0x%x) is max than(0x%x)",
scsi_id, UNF_MAX_SCSI_ID);
return -ENXIO;
}
scsi_image_table = &lport->rport_scsi_table;
if (scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
atomic_inc(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->device_alloc);
atomic_inc(&lport->device_alloc);
/* save local SCSI_ID */
sdev->hostdata = (void *)(unsigned long long)scsi_id;
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_KEVENT,
"[event]Port(0x%x) use scsi_id(%d) to alloc device[%u:%u:%u:%u]",
lport->port_id, scsi_id, host->host_no,
sdev->channel, sdev->id, (unsigned int)sdev->lun);
return 0;
}
static void unf_scsi_destroy_slave(struct scsi_device *sdev)
{
/*
* NOTE: about sdev->hostdata
* --->>> pointing to local SCSI_ID
* 1. Assignment during slave allocation
* 2. Released when callback for slave destroy
* 3. Used during: Queue_CMND, Abort CMND, Device Reset,
* Target Reset & Bus Reset
*/
/*lint -e666 -esym(666,*)*/
struct fc_rport *rport = NULL;
unsigned int scsi_id = 0;
struct unf_lport_s *lport = NULL;
struct Scsi_Host *host = NULL;
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
/* About scsi device */
if (unlikely(!sdev)) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]SDev is null");
return;
}
/* About scsi rport */
rport = starget_to_rport(scsi_target(sdev));
if (unlikely(!rport)) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]SCSI rport is null or remote port is not ready");
return;
}
/* About host */
host = rport_to_shost(rport);
if (unlikely(!host)) {
UNF_TRACE(0x3808, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Host is null");
return;
}
/* About L_Port */
lport = (struct unf_lport_s *)host->hostdata[0];
if (unf_is_lport_valid(lport) == RETURN_OK) {
scsi_image_table = &lport->rport_scsi_table;
atomic_inc(&lport->device_destroy);
scsi_id = (unsigned int)((unsigned long long)sdev->hostdata);
if ((scsi_id < UNF_MAX_SCSI_ID) &&
(scsi_image_table->wwn_rport_info_table)) {
if (scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
atomic_inc(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->device_destroy);
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_KEVENT,
"[event]Port(0x%x) with scsi_id(%d) to destroy slave device[%u:%u:%u:%u]",
lport->port_id, scsi_id, host->host_no,
sdev->channel, sdev->id,
(unsigned int)sdev->lun);
} else {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[err]Port(0x%x) scsi_id(%d) is invalid and destroy device[%u:%u:%u:%u]",
lport->port_id, scsi_id, host->host_no,
sdev->channel, sdev->id,
(unsigned int)sdev->lun);
}
} else {
UNF_TRACE(0x3097, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(%p) is invalid", lport);
}
sdev->hostdata = NULL; /* reset local SCSI_ID */
}
static int unf_scsi_slave_configure(struct scsi_device *sdev)
{
#define UNF_SCSI_DEV_DEPTH 32
blk_queue_update_dma_alignment(sdev->request_queue, 0x7);
scsi_change_queue_depth(sdev, UNF_SCSI_DEV_DEPTH);
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_INFO,
"[event]Enter slave configure, set depth is %d, sdev->tagged_supported is (%d)",
UNF_SCSI_DEV_DEPTH, sdev->tagged_supported);
return 0;
}
static int unf_scsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Scan finished");
return 1;
}
static void unf_scsi_scan_start(struct Scsi_Host *shost)
{
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Start scsi scan...");
}
unsigned int unf_register_ini_transport(void)
{
/* Register INI Transport */
scsi_transport_template = fc_attach_transport(&function_template);
if (!scsi_transport_template) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Register FC transport to scsi failed");
return RETURN_ERROR;
}
scsi_transport_template_v = fc_attach_transport(&function_template_v);
if (!scsi_transport_template_v) {
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Register FC vport transport to scsi failed");
fc_release_transport(scsi_transport_template);
return RETURN_ERROR;
}
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Register FC transport to scsi succeed");
return RETURN_OK;
}
void unf_unregister_ini_transport(void)
{
fc_release_transport(scsi_transport_template);
fc_release_transport(scsi_transport_template_v);
UNF_TRACE(0x4101, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
"[event]Unregister FC transport succeed");
}
void unf_report_io_dm_event(void *v_lport, unsigned int type,
unsigned int value)
{
}
void unf_save_sense_data(void *scsicmd, const char *sense, int senslen)
{
struct scsi_cmnd *cmd;
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, scsicmd, return);
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, sense, return);
cmd = (struct scsi_cmnd *)scsicmd;
memcpy(cmd->sense_buffer, sense, senslen);
}