// 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); }