162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2008 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1362306a36Sopenharmony_ci#include <linux/dmapool.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/irqdomain.h> 1762306a36Sopenharmony_ci#include <linux/kthread.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/pm.h> 2162306a36Sopenharmony_ci#include <linux/stringify.h> 2262306a36Sopenharmony_ci#include <linux/bsg-lib.h> 2362306a36Sopenharmony_ci#include <asm/firmware.h> 2462306a36Sopenharmony_ci#include <asm/irq.h> 2562306a36Sopenharmony_ci#include <asm/vio.h> 2662306a36Sopenharmony_ci#include <scsi/scsi.h> 2762306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2862306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2962306a36Sopenharmony_ci#include <scsi/scsi_device.h> 3062306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 3162306a36Sopenharmony_ci#include <scsi/scsi_transport_fc.h> 3262306a36Sopenharmony_ci#include <scsi/scsi_bsg_fc.h> 3362306a36Sopenharmony_ci#include "ibmvfc.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic unsigned int init_timeout = IBMVFC_INIT_TIMEOUT; 3662306a36Sopenharmony_cistatic unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT; 3762306a36Sopenharmony_cistatic u64 max_lun = IBMVFC_MAX_LUN; 3862306a36Sopenharmony_cistatic unsigned int max_targets = IBMVFC_MAX_TARGETS; 3962306a36Sopenharmony_cistatic unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT; 4062306a36Sopenharmony_cistatic unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS; 4162306a36Sopenharmony_cistatic unsigned int ibmvfc_debug = IBMVFC_DEBUG; 4262306a36Sopenharmony_cistatic unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL; 4362306a36Sopenharmony_cistatic unsigned int cls3_error = IBMVFC_CLS3_ERROR; 4462306a36Sopenharmony_cistatic unsigned int mq_enabled = IBMVFC_MQ; 4562306a36Sopenharmony_cistatic unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES; 4662306a36Sopenharmony_cistatic unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS; 4762306a36Sopenharmony_cistatic unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ; 4862306a36Sopenharmony_cistatic unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic LIST_HEAD(ibmvfc_head); 5162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ibmvfc_driver_lock); 5262306a36Sopenharmony_cistatic struct scsi_transport_template *ibmvfc_transport_template; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciMODULE_DESCRIPTION("IBM Virtual Fibre Channel Driver"); 5562306a36Sopenharmony_ciMODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>"); 5662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5762306a36Sopenharmony_ciMODULE_VERSION(IBMVFC_DRIVER_VERSION); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cimodule_param_named(mq, mq_enabled, uint, S_IRUGO); 6062306a36Sopenharmony_ciMODULE_PARM_DESC(mq, "Enable multiqueue support. " 6162306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MQ) "]"); 6262306a36Sopenharmony_cimodule_param_named(scsi_host_queues, nr_scsi_hw_queues, uint, S_IRUGO); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(scsi_host_queues, "Number of SCSI Host submission queues. " 6462306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_SCSI_HW_QUEUES) "]"); 6562306a36Sopenharmony_cimodule_param_named(scsi_hw_channels, nr_scsi_channels, uint, S_IRUGO); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(scsi_hw_channels, "Number of hw scsi channels to request. " 6762306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_SCSI_CHANNELS) "]"); 6862306a36Sopenharmony_cimodule_param_named(mig_channels_only, mig_channels_only, uint, S_IRUGO); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(mig_channels_only, "Prevent migration to non-channelized system. " 7062306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MIG_NO_SUB_TO_CRQ) "]"); 7162306a36Sopenharmony_cimodule_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO); 7262306a36Sopenharmony_ciMODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. " 7362306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cimodule_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR); 7662306a36Sopenharmony_ciMODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. " 7762306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]"); 7862306a36Sopenharmony_cimodule_param_named(default_timeout, default_timeout, uint, S_IRUGO | S_IWUSR); 7962306a36Sopenharmony_ciMODULE_PARM_DESC(default_timeout, 8062306a36Sopenharmony_ci "Default timeout in seconds for initialization and EH commands. " 8162306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_DEFAULT_TIMEOUT) "]"); 8262306a36Sopenharmony_cimodule_param_named(max_requests, max_requests, uint, S_IRUGO); 8362306a36Sopenharmony_ciMODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. " 8462306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]"); 8562306a36Sopenharmony_cimodule_param_named(max_lun, max_lun, ullong, S_IRUGO); 8662306a36Sopenharmony_ciMODULE_PARM_DESC(max_lun, "Maximum allowed LUN. " 8762306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MAX_LUN) "]"); 8862306a36Sopenharmony_cimodule_param_named(max_targets, max_targets, uint, S_IRUGO); 8962306a36Sopenharmony_ciMODULE_PARM_DESC(max_targets, "Maximum allowed targets. " 9062306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]"); 9162306a36Sopenharmony_cimodule_param_named(disc_threads, disc_threads, uint, S_IRUGO); 9262306a36Sopenharmony_ciMODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. " 9362306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]"); 9462306a36Sopenharmony_cimodule_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR); 9562306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Enable driver debug information. " 9662306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_DEBUG) "]"); 9762306a36Sopenharmony_cimodule_param_named(log_level, log_level, uint, 0); 9862306a36Sopenharmony_ciMODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. " 9962306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]"); 10062306a36Sopenharmony_cimodule_param_named(cls3_error, cls3_error, uint, 0); 10162306a36Sopenharmony_ciMODULE_PARM_DESC(cls3_error, "Enable FC Class 3 Error Recovery. " 10262306a36Sopenharmony_ci "[Default=" __stringify(IBMVFC_CLS3_ERROR) "]"); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic const struct { 10562306a36Sopenharmony_ci u16 status; 10662306a36Sopenharmony_ci u16 error; 10762306a36Sopenharmony_ci u8 result; 10862306a36Sopenharmony_ci u8 retry; 10962306a36Sopenharmony_ci int log; 11062306a36Sopenharmony_ci char *name; 11162306a36Sopenharmony_ci} cmd_status [] = { 11262306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_ESTABLISH, DID_ERROR, 1, 1, "unable to establish" }, 11362306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_FAULT, DID_OK, 1, 0, "transport fault" }, 11462306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_CMD_TIMEOUT, DID_TIME_OUT, 1, 1, "command timeout" }, 11562306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_TRANSPORT_DISRUPTED, 1, 1, "network down" }, 11662306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_HW_FAILURE, DID_ERROR, 1, 1, "hardware failure" }, 11762306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DOWN_ERR, DID_REQUEUE, 0, 0, "link down" }, 11862306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DEAD_ERR, DID_ERROR, 0, 0, "link dead" }, 11962306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_REGISTER, DID_ERROR, 1, 1, "unable to register" }, 12062306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_BUSY, DID_BUS_BUSY, 1, 0, "transport busy" }, 12162306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" }, 12262306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" }, 12362306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" }, 12462306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 1, 0, "link halted" }, 12562306a36Sopenharmony_ci { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" }, 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" }, 12862306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_SW_FAILURE, DID_ERROR, 0, 1, "software failure" }, 12962306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ERROR, 0, 1, "invalid parameter" }, 13062306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ERROR, 0, 1, "missing parameter" }, 13162306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_HOST_IO_BUS, DID_ERROR, 1, 1, "host I/O bus failure" }, 13262306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ERROR, 0, 1, "transaction cancelled" }, 13362306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ERROR, 0, 1, "transaction cancelled implicit" }, 13462306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" }, 13562306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" }, 13662306a36Sopenharmony_ci { IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" }, 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" }, 13962306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_INVALID_VERSION, DID_ERROR, 0, 1, "invalid version level" }, 14062306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_ERROR, DID_ERROR, 1, 1, "logical error" }, 14162306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_INVALID_CT_IU_SIZE, DID_ERROR, 0, 1, "invalid CT_IU size" }, 14262306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_BUSY, DID_REQUEUE, 1, 0, "logical busy" }, 14362306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_PROTOCOL_ERROR, DID_ERROR, 1, 1, "protocol error" }, 14462306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_UNABLE_TO_PERFORM_REQ, DID_ERROR, 1, 1, "unable to perform request" }, 14562306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_CMD_NOT_SUPPORTED, DID_ERROR, 0, 0, "command not supported" }, 14662306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_SERVER_NOT_AVAIL, DID_ERROR, 0, 1, "server not available" }, 14762306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_CMD_IN_PROGRESS, DID_ERROR, 0, 1, "command already in progress" }, 14862306a36Sopenharmony_ci { IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" }, 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci { IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" }, 15162306a36Sopenharmony_ci { IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." }, 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void ibmvfc_npiv_login(struct ibmvfc_host *); 15562306a36Sopenharmony_cistatic void ibmvfc_tgt_send_prli(struct ibmvfc_target *); 15662306a36Sopenharmony_cistatic void ibmvfc_tgt_send_plogi(struct ibmvfc_target *); 15762306a36Sopenharmony_cistatic void ibmvfc_tgt_query_target(struct ibmvfc_target *); 15862306a36Sopenharmony_cistatic void ibmvfc_npiv_logout(struct ibmvfc_host *); 15962306a36Sopenharmony_cistatic void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *); 16062306a36Sopenharmony_cistatic void ibmvfc_tgt_move_login(struct ibmvfc_target *); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *); 16362306a36Sopenharmony_cistatic void ibmvfc_reg_sub_crqs(struct ibmvfc_host *); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic const char *unknown_error = "unknown error"; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba, 16862306a36Sopenharmony_ci unsigned long length, unsigned long *cookie, 16962306a36Sopenharmony_ci unsigned long *irq) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; 17262306a36Sopenharmony_ci long rc; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, ioba, length); 17562306a36Sopenharmony_ci *cookie = retbuf[0]; 17662306a36Sopenharmony_ci *irq = retbuf[1]; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return rc; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return (host_caps & cap_flags) ? 1 : 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost, 18962306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) 19262306a36Sopenharmony_ci return &vfc_cmd->v2.iu; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci return &vfc_cmd->v1.iu; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost, 19862306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) 20162306a36Sopenharmony_ci return &vfc_cmd->v2.rsp; 20262306a36Sopenharmony_ci else 20362306a36Sopenharmony_ci return &vfc_cmd->v1.rsp; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci#ifdef CONFIG_SCSI_IBMVFC_TRACE 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * ibmvfc_trc_start - Log a start trace entry 20962306a36Sopenharmony_ci * @evt: ibmvfc event struct 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci **/ 21262306a36Sopenharmony_cistatic void ibmvfc_trc_start(struct ibmvfc_event *evt) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 21562306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd; 21662306a36Sopenharmony_ci struct ibmvfc_mad_common *mad = &evt->iu.mad_common; 21762306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd); 21862306a36Sopenharmony_ci struct ibmvfc_trace_entry *entry; 21962306a36Sopenharmony_ci int index = atomic_inc_return(&vhost->trace_index) & IBMVFC_TRACE_INDEX_MASK; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci entry = &vhost->trace[index]; 22262306a36Sopenharmony_ci entry->evt = evt; 22362306a36Sopenharmony_ci entry->time = jiffies; 22462306a36Sopenharmony_ci entry->fmt = evt->crq.format; 22562306a36Sopenharmony_ci entry->type = IBMVFC_TRC_START; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci switch (entry->fmt) { 22862306a36Sopenharmony_ci case IBMVFC_CMD_FORMAT: 22962306a36Sopenharmony_ci entry->op_code = iu->cdb[0]; 23062306a36Sopenharmony_ci entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id); 23162306a36Sopenharmony_ci entry->lun = scsilun_to_int(&iu->lun); 23262306a36Sopenharmony_ci entry->tmf_flags = iu->tmf_flags; 23362306a36Sopenharmony_ci entry->u.start.xfer_len = be32_to_cpu(iu->xfer_len); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case IBMVFC_MAD_FORMAT: 23662306a36Sopenharmony_ci entry->op_code = be32_to_cpu(mad->opcode); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci default: 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * ibmvfc_trc_end - Log an end trace entry 24562306a36Sopenharmony_ci * @evt: ibmvfc event struct 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci **/ 24862306a36Sopenharmony_cistatic void ibmvfc_trc_end(struct ibmvfc_event *evt) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 25162306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd; 25262306a36Sopenharmony_ci struct ibmvfc_mad_common *mad = &evt->xfer_iu->mad_common; 25362306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd); 25462306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd); 25562306a36Sopenharmony_ci struct ibmvfc_trace_entry *entry; 25662306a36Sopenharmony_ci int index = atomic_inc_return(&vhost->trace_index) & IBMVFC_TRACE_INDEX_MASK; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci entry = &vhost->trace[index]; 25962306a36Sopenharmony_ci entry->evt = evt; 26062306a36Sopenharmony_ci entry->time = jiffies; 26162306a36Sopenharmony_ci entry->fmt = evt->crq.format; 26262306a36Sopenharmony_ci entry->type = IBMVFC_TRC_END; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci switch (entry->fmt) { 26562306a36Sopenharmony_ci case IBMVFC_CMD_FORMAT: 26662306a36Sopenharmony_ci entry->op_code = iu->cdb[0]; 26762306a36Sopenharmony_ci entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id); 26862306a36Sopenharmony_ci entry->lun = scsilun_to_int(&iu->lun); 26962306a36Sopenharmony_ci entry->tmf_flags = iu->tmf_flags; 27062306a36Sopenharmony_ci entry->u.end.status = be16_to_cpu(vfc_cmd->status); 27162306a36Sopenharmony_ci entry->u.end.error = be16_to_cpu(vfc_cmd->error); 27262306a36Sopenharmony_ci entry->u.end.fcp_rsp_flags = rsp->flags; 27362306a36Sopenharmony_ci entry->u.end.rsp_code = rsp->data.info.rsp_code; 27462306a36Sopenharmony_ci entry->u.end.scsi_status = rsp->scsi_status; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case IBMVFC_MAD_FORMAT: 27762306a36Sopenharmony_ci entry->op_code = be32_to_cpu(mad->opcode); 27862306a36Sopenharmony_ci entry->u.end.status = be16_to_cpu(mad->status); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci default: 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci#else 28762306a36Sopenharmony_ci#define ibmvfc_trc_start(evt) do { } while (0) 28862306a36Sopenharmony_ci#define ibmvfc_trc_end(evt) do { } while (0) 28962306a36Sopenharmony_ci#endif 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/** 29262306a36Sopenharmony_ci * ibmvfc_get_err_index - Find the index into cmd_status for the fcp response 29362306a36Sopenharmony_ci * @status: status / error class 29462306a36Sopenharmony_ci * @error: error 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * Return value: 29762306a36Sopenharmony_ci * index into cmd_status / -EINVAL on failure 29862306a36Sopenharmony_ci **/ 29962306a36Sopenharmony_cistatic int ibmvfc_get_err_index(u16 status, u16 error) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci int i; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cmd_status); i++) 30462306a36Sopenharmony_ci if ((cmd_status[i].status & status) == cmd_status[i].status && 30562306a36Sopenharmony_ci cmd_status[i].error == error) 30662306a36Sopenharmony_ci return i; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/** 31262306a36Sopenharmony_ci * ibmvfc_get_cmd_error - Find the error description for the fcp response 31362306a36Sopenharmony_ci * @status: status / error class 31462306a36Sopenharmony_ci * @error: error 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci * Return value: 31762306a36Sopenharmony_ci * error description string 31862306a36Sopenharmony_ci **/ 31962306a36Sopenharmony_cistatic const char *ibmvfc_get_cmd_error(u16 status, u16 error) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int rc = ibmvfc_get_err_index(status, error); 32262306a36Sopenharmony_ci if (rc >= 0) 32362306a36Sopenharmony_ci return cmd_status[rc].name; 32462306a36Sopenharmony_ci return unknown_error; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/** 32862306a36Sopenharmony_ci * ibmvfc_get_err_result - Find the scsi status to return for the fcp response 32962306a36Sopenharmony_ci * @vhost: ibmvfc host struct 33062306a36Sopenharmony_ci * @vfc_cmd: ibmvfc command struct 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * Return value: 33362306a36Sopenharmony_ci * SCSI result value to return for completed command 33462306a36Sopenharmony_ci **/ 33562306a36Sopenharmony_cistatic int ibmvfc_get_err_result(struct ibmvfc_host *vhost, struct ibmvfc_cmd *vfc_cmd) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int err; 33862306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd); 33962306a36Sopenharmony_ci int fc_rsp_len = be32_to_cpu(rsp->fcp_rsp_len); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if ((rsp->flags & FCP_RSP_LEN_VALID) && 34262306a36Sopenharmony_ci ((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) || 34362306a36Sopenharmony_ci rsp->data.info.rsp_code)) 34462306a36Sopenharmony_ci return DID_ERROR << 16; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci err = ibmvfc_get_err_index(be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error)); 34762306a36Sopenharmony_ci if (err >= 0) 34862306a36Sopenharmony_ci return rsp->scsi_status | (cmd_status[err].result << 16); 34962306a36Sopenharmony_ci return rsp->scsi_status | (DID_ERROR << 16); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/** 35362306a36Sopenharmony_ci * ibmvfc_retry_cmd - Determine if error status is retryable 35462306a36Sopenharmony_ci * @status: status / error class 35562306a36Sopenharmony_ci * @error: error 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * Return value: 35862306a36Sopenharmony_ci * 1 if error should be retried / 0 if it should not 35962306a36Sopenharmony_ci **/ 36062306a36Sopenharmony_cistatic int ibmvfc_retry_cmd(u16 status, u16 error) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int rc = ibmvfc_get_err_index(status, error); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (rc >= 0) 36562306a36Sopenharmony_ci return cmd_status[rc].retry; 36662306a36Sopenharmony_ci return 1; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic const char *unknown_fc_explain = "unknown fc explain"; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic const struct { 37262306a36Sopenharmony_ci u16 fc_explain; 37362306a36Sopenharmony_ci char *name; 37462306a36Sopenharmony_ci} ls_explain [] = { 37562306a36Sopenharmony_ci { 0x00, "no additional explanation" }, 37662306a36Sopenharmony_ci { 0x01, "service parameter error - options" }, 37762306a36Sopenharmony_ci { 0x03, "service parameter error - initiator control" }, 37862306a36Sopenharmony_ci { 0x05, "service parameter error - recipient control" }, 37962306a36Sopenharmony_ci { 0x07, "service parameter error - received data field size" }, 38062306a36Sopenharmony_ci { 0x09, "service parameter error - concurrent seq" }, 38162306a36Sopenharmony_ci { 0x0B, "service parameter error - credit" }, 38262306a36Sopenharmony_ci { 0x0D, "invalid N_Port/F_Port_Name" }, 38362306a36Sopenharmony_ci { 0x0E, "invalid node/Fabric Name" }, 38462306a36Sopenharmony_ci { 0x0F, "invalid common service parameters" }, 38562306a36Sopenharmony_ci { 0x11, "invalid association header" }, 38662306a36Sopenharmony_ci { 0x13, "association header required" }, 38762306a36Sopenharmony_ci { 0x15, "invalid originator S_ID" }, 38862306a36Sopenharmony_ci { 0x17, "invalid OX_ID-RX-ID combination" }, 38962306a36Sopenharmony_ci { 0x19, "command (request) already in progress" }, 39062306a36Sopenharmony_ci { 0x1E, "N_Port Login requested" }, 39162306a36Sopenharmony_ci { 0x1F, "Invalid N_Port_ID" }, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct { 39562306a36Sopenharmony_ci u16 fc_explain; 39662306a36Sopenharmony_ci char *name; 39762306a36Sopenharmony_ci} gs_explain [] = { 39862306a36Sopenharmony_ci { 0x00, "no additional explanation" }, 39962306a36Sopenharmony_ci { 0x01, "port identifier not registered" }, 40062306a36Sopenharmony_ci { 0x02, "port name not registered" }, 40162306a36Sopenharmony_ci { 0x03, "node name not registered" }, 40262306a36Sopenharmony_ci { 0x04, "class of service not registered" }, 40362306a36Sopenharmony_ci { 0x06, "initial process associator not registered" }, 40462306a36Sopenharmony_ci { 0x07, "FC-4 TYPEs not registered" }, 40562306a36Sopenharmony_ci { 0x08, "symbolic port name not registered" }, 40662306a36Sopenharmony_ci { 0x09, "symbolic node name not registered" }, 40762306a36Sopenharmony_ci { 0x0A, "port type not registered" }, 40862306a36Sopenharmony_ci { 0xF0, "authorization exception" }, 40962306a36Sopenharmony_ci { 0xF1, "authentication exception" }, 41062306a36Sopenharmony_ci { 0xF2, "data base full" }, 41162306a36Sopenharmony_ci { 0xF3, "data base empty" }, 41262306a36Sopenharmony_ci { 0xF4, "processing request" }, 41362306a36Sopenharmony_ci { 0xF5, "unable to verify connection" }, 41462306a36Sopenharmony_ci { 0xF6, "devices not in a common zone" }, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/** 41862306a36Sopenharmony_ci * ibmvfc_get_ls_explain - Return the FC Explain description text 41962306a36Sopenharmony_ci * @status: FC Explain status 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * Returns: 42262306a36Sopenharmony_ci * error string 42362306a36Sopenharmony_ci **/ 42462306a36Sopenharmony_cistatic const char *ibmvfc_get_ls_explain(u16 status) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int i; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ls_explain); i++) 42962306a36Sopenharmony_ci if (ls_explain[i].fc_explain == status) 43062306a36Sopenharmony_ci return ls_explain[i].name; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return unknown_fc_explain; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * ibmvfc_get_gs_explain - Return the FC Explain description text 43762306a36Sopenharmony_ci * @status: FC Explain status 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * Returns: 44062306a36Sopenharmony_ci * error string 44162306a36Sopenharmony_ci **/ 44262306a36Sopenharmony_cistatic const char *ibmvfc_get_gs_explain(u16 status) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci int i; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gs_explain); i++) 44762306a36Sopenharmony_ci if (gs_explain[i].fc_explain == status) 44862306a36Sopenharmony_ci return gs_explain[i].name; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return unknown_fc_explain; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic const struct { 45462306a36Sopenharmony_ci enum ibmvfc_fc_type fc_type; 45562306a36Sopenharmony_ci char *name; 45662306a36Sopenharmony_ci} fc_type [] = { 45762306a36Sopenharmony_ci { IBMVFC_FABRIC_REJECT, "fabric reject" }, 45862306a36Sopenharmony_ci { IBMVFC_PORT_REJECT, "port reject" }, 45962306a36Sopenharmony_ci { IBMVFC_LS_REJECT, "ELS reject" }, 46062306a36Sopenharmony_ci { IBMVFC_FABRIC_BUSY, "fabric busy" }, 46162306a36Sopenharmony_ci { IBMVFC_PORT_BUSY, "port busy" }, 46262306a36Sopenharmony_ci { IBMVFC_BASIC_REJECT, "basic reject" }, 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic const char *unknown_fc_type = "unknown fc type"; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/** 46862306a36Sopenharmony_ci * ibmvfc_get_fc_type - Return the FC Type description text 46962306a36Sopenharmony_ci * @status: FC Type error status 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * Returns: 47262306a36Sopenharmony_ci * error string 47362306a36Sopenharmony_ci **/ 47462306a36Sopenharmony_cistatic const char *ibmvfc_get_fc_type(u16 status) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci int i; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fc_type); i++) 47962306a36Sopenharmony_ci if (fc_type[i].fc_type == status) 48062306a36Sopenharmony_ci return fc_type[i].name; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return unknown_fc_type; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/** 48662306a36Sopenharmony_ci * ibmvfc_set_tgt_action - Set the next init action for the target 48762306a36Sopenharmony_ci * @tgt: ibmvfc target struct 48862306a36Sopenharmony_ci * @action: action to perform 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Returns: 49162306a36Sopenharmony_ci * 0 if action changed / non-zero if not changed 49262306a36Sopenharmony_ci **/ 49362306a36Sopenharmony_cistatic int ibmvfc_set_tgt_action(struct ibmvfc_target *tgt, 49462306a36Sopenharmony_ci enum ibmvfc_target_action action) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci int rc = -EINVAL; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci switch (tgt->action) { 49962306a36Sopenharmony_ci case IBMVFC_TGT_ACTION_LOGOUT_RPORT: 50062306a36Sopenharmony_ci if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT || 50162306a36Sopenharmony_ci action == IBMVFC_TGT_ACTION_DEL_RPORT) { 50262306a36Sopenharmony_ci tgt->action = action; 50362306a36Sopenharmony_ci rc = 0; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT: 50762306a36Sopenharmony_ci if (action == IBMVFC_TGT_ACTION_DEL_RPORT || 50862306a36Sopenharmony_ci action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { 50962306a36Sopenharmony_ci tgt->action = action; 51062306a36Sopenharmony_ci rc = 0; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci case IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT: 51462306a36Sopenharmony_ci if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) { 51562306a36Sopenharmony_ci tgt->action = action; 51662306a36Sopenharmony_ci rc = 0; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci case IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT: 52062306a36Sopenharmony_ci if (action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) { 52162306a36Sopenharmony_ci tgt->action = action; 52262306a36Sopenharmony_ci rc = 0; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci case IBMVFC_TGT_ACTION_DEL_RPORT: 52662306a36Sopenharmony_ci if (action == IBMVFC_TGT_ACTION_DELETED_RPORT) { 52762306a36Sopenharmony_ci tgt->action = action; 52862306a36Sopenharmony_ci rc = 0; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci case IBMVFC_TGT_ACTION_DELETED_RPORT: 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci default: 53462306a36Sopenharmony_ci tgt->action = action; 53562306a36Sopenharmony_ci rc = 0; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (action >= IBMVFC_TGT_ACTION_LOGOUT_RPORT) 54062306a36Sopenharmony_ci tgt->add_rport = 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return rc; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/** 54662306a36Sopenharmony_ci * ibmvfc_set_host_state - Set the state for the host 54762306a36Sopenharmony_ci * @vhost: ibmvfc host struct 54862306a36Sopenharmony_ci * @state: state to set host to 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * Returns: 55162306a36Sopenharmony_ci * 0 if state changed / non-zero if not changed 55262306a36Sopenharmony_ci **/ 55362306a36Sopenharmony_cistatic int ibmvfc_set_host_state(struct ibmvfc_host *vhost, 55462306a36Sopenharmony_ci enum ibmvfc_host_state state) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci int rc = 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci switch (vhost->state) { 55962306a36Sopenharmony_ci case IBMVFC_HOST_OFFLINE: 56062306a36Sopenharmony_ci rc = -EINVAL; 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci default: 56362306a36Sopenharmony_ci vhost->state = state; 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return rc; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/** 57162306a36Sopenharmony_ci * ibmvfc_set_host_action - Set the next init action for the host 57262306a36Sopenharmony_ci * @vhost: ibmvfc host struct 57362306a36Sopenharmony_ci * @action: action to perform 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci **/ 57662306a36Sopenharmony_cistatic void ibmvfc_set_host_action(struct ibmvfc_host *vhost, 57762306a36Sopenharmony_ci enum ibmvfc_host_action action) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci switch (action) { 58062306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_ALLOC_TGTS: 58162306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) 58262306a36Sopenharmony_ci vhost->action = action; 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_LOGO_WAIT: 58562306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_LOGO) 58662306a36Sopenharmony_ci vhost->action = action; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT_WAIT: 58962306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_INIT) 59062306a36Sopenharmony_ci vhost->action = action; 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_QUERY: 59362306a36Sopenharmony_ci switch (vhost->action) { 59462306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT_WAIT: 59562306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_NONE: 59662306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: 59762306a36Sopenharmony_ci vhost->action = action; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci default: 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_INIT: 60462306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS) 60562306a36Sopenharmony_ci vhost->action = action; 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_REENABLE: 60862306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_RESET: 60962306a36Sopenharmony_ci vhost->action = action; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT: 61262306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL: 61362306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_LOGO: 61462306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_QUERY_TGTS: 61562306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: 61662306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_NONE: 61762306a36Sopenharmony_ci default: 61862306a36Sopenharmony_ci switch (vhost->action) { 61962306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_RESET: 62062306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_REENABLE: 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci default: 62362306a36Sopenharmony_ci vhost->action = action; 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci/** 63162306a36Sopenharmony_ci * ibmvfc_reinit_host - Re-start host initialization (no NPIV Login) 63262306a36Sopenharmony_ci * @vhost: ibmvfc host struct 63362306a36Sopenharmony_ci * 63462306a36Sopenharmony_ci * Return value: 63562306a36Sopenharmony_ci * nothing 63662306a36Sopenharmony_ci **/ 63762306a36Sopenharmony_cistatic void ibmvfc_reinit_host(struct ibmvfc_host *vhost) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_NONE && 64062306a36Sopenharmony_ci vhost->state == IBMVFC_ACTIVE) { 64162306a36Sopenharmony_ci if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { 64262306a36Sopenharmony_ci scsi_block_requests(vhost->host); 64362306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } else 64662306a36Sopenharmony_ci vhost->reinit = 1; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/** 65262306a36Sopenharmony_ci * ibmvfc_del_tgt - Schedule cleanup and removal of the target 65362306a36Sopenharmony_ci * @tgt: ibmvfc target struct 65462306a36Sopenharmony_ci **/ 65562306a36Sopenharmony_cistatic void ibmvfc_del_tgt(struct ibmvfc_target *tgt) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT)) { 65862306a36Sopenharmony_ci tgt->job_step = ibmvfc_tgt_implicit_logout_and_del; 65962306a36Sopenharmony_ci tgt->init_retries = 0; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci wake_up(&tgt->vhost->work_wait_q); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/** 66562306a36Sopenharmony_ci * ibmvfc_link_down - Handle a link down event from the adapter 66662306a36Sopenharmony_ci * @vhost: ibmvfc host struct 66762306a36Sopenharmony_ci * @state: ibmvfc host state to enter 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci **/ 67062306a36Sopenharmony_cistatic void ibmvfc_link_down(struct ibmvfc_host *vhost, 67162306a36Sopenharmony_ci enum ibmvfc_host_state state) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct ibmvfc_target *tgt; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ENTER; 67662306a36Sopenharmony_ci scsi_block_requests(vhost->host); 67762306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 67862306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 67962306a36Sopenharmony_ci ibmvfc_set_host_state(vhost, state); 68062306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL); 68162306a36Sopenharmony_ci vhost->events_to_log |= IBMVFC_AE_LINKDOWN; 68262306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 68362306a36Sopenharmony_ci LEAVE; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/** 68762306a36Sopenharmony_ci * ibmvfc_init_host - Start host initialization 68862306a36Sopenharmony_ci * @vhost: ibmvfc host struct 68962306a36Sopenharmony_ci * 69062306a36Sopenharmony_ci * Return value: 69162306a36Sopenharmony_ci * nothing 69262306a36Sopenharmony_ci **/ 69362306a36Sopenharmony_cistatic void ibmvfc_init_host(struct ibmvfc_host *vhost) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct ibmvfc_target *tgt; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) { 69862306a36Sopenharmony_ci if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) { 69962306a36Sopenharmony_ci dev_err(vhost->dev, 70062306a36Sopenharmony_ci "Host initialization retries exceeded. Taking adapter offline\n"); 70162306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); 70262306a36Sopenharmony_ci return; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { 70762306a36Sopenharmony_ci memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE); 70862306a36Sopenharmony_ci vhost->async_crq.cur = 0; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 71162306a36Sopenharmony_ci if (vhost->client_migrated) 71262306a36Sopenharmony_ci tgt->need_login = 1; 71362306a36Sopenharmony_ci else 71462306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci scsi_block_requests(vhost->host); 71862306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); 71962306a36Sopenharmony_ci vhost->job_step = ibmvfc_npiv_login; 72062306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/** 72562306a36Sopenharmony_ci * ibmvfc_send_crq - Send a CRQ 72662306a36Sopenharmony_ci * @vhost: ibmvfc host struct 72762306a36Sopenharmony_ci * @word1: the first 64 bits of the data 72862306a36Sopenharmony_ci * @word2: the second 64 bits of the data 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * Return value: 73162306a36Sopenharmony_ci * 0 on success / other on failure 73262306a36Sopenharmony_ci **/ 73362306a36Sopenharmony_cistatic int ibmvfc_send_crq(struct ibmvfc_host *vhost, u64 word1, u64 word2) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(vhost->dev); 73662306a36Sopenharmony_ci return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int ibmvfc_send_sub_crq(struct ibmvfc_host *vhost, u64 cookie, u64 word1, 74062306a36Sopenharmony_ci u64 word2, u64 word3, u64 word4) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(vhost->dev); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return plpar_hcall_norets(H_SEND_SUB_CRQ, vdev->unit_address, cookie, 74562306a36Sopenharmony_ci word1, word2, word3, word4); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * ibmvfc_send_crq_init - Send a CRQ init message 75062306a36Sopenharmony_ci * @vhost: ibmvfc host struct 75162306a36Sopenharmony_ci * 75262306a36Sopenharmony_ci * Return value: 75362306a36Sopenharmony_ci * 0 on success / other on failure 75462306a36Sopenharmony_ci **/ 75562306a36Sopenharmony_cistatic int ibmvfc_send_crq_init(struct ibmvfc_host *vhost) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Sending CRQ init\n"); 75862306a36Sopenharmony_ci return ibmvfc_send_crq(vhost, 0xC001000000000000LL, 0); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci/** 76262306a36Sopenharmony_ci * ibmvfc_send_crq_init_complete - Send a CRQ init complete message 76362306a36Sopenharmony_ci * @vhost: ibmvfc host struct 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Return value: 76662306a36Sopenharmony_ci * 0 on success / other on failure 76762306a36Sopenharmony_ci **/ 76862306a36Sopenharmony_cistatic int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Sending CRQ init complete\n"); 77162306a36Sopenharmony_ci return ibmvfc_send_crq(vhost, 0xC002000000000000LL, 0); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/** 77562306a36Sopenharmony_ci * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host 77662306a36Sopenharmony_ci * @vhost: ibmvfc host who owns the event pool 77762306a36Sopenharmony_ci * @queue: ibmvfc queue struct 77862306a36Sopenharmony_ci * @size: pool size 77962306a36Sopenharmony_ci * 78062306a36Sopenharmony_ci * Returns zero on success. 78162306a36Sopenharmony_ci **/ 78262306a36Sopenharmony_cistatic int ibmvfc_init_event_pool(struct ibmvfc_host *vhost, 78362306a36Sopenharmony_ci struct ibmvfc_queue *queue, 78462306a36Sopenharmony_ci unsigned int size) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci int i; 78762306a36Sopenharmony_ci struct ibmvfc_event_pool *pool = &queue->evt_pool; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ENTER; 79062306a36Sopenharmony_ci if (!size) 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci pool->size = size; 79462306a36Sopenharmony_ci pool->events = kcalloc(size, sizeof(*pool->events), GFP_KERNEL); 79562306a36Sopenharmony_ci if (!pool->events) 79662306a36Sopenharmony_ci return -ENOMEM; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci pool->iu_storage = dma_alloc_coherent(vhost->dev, 79962306a36Sopenharmony_ci size * sizeof(*pool->iu_storage), 80062306a36Sopenharmony_ci &pool->iu_token, 0); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (!pool->iu_storage) { 80362306a36Sopenharmony_ci kfree(pool->events); 80462306a36Sopenharmony_ci return -ENOMEM; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->sent); 80862306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->free); 80962306a36Sopenharmony_ci spin_lock_init(&queue->l_lock); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci for (i = 0; i < size; ++i) { 81262306a36Sopenharmony_ci struct ibmvfc_event *evt = &pool->events[i]; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* 81562306a36Sopenharmony_ci * evt->active states 81662306a36Sopenharmony_ci * 1 = in flight 81762306a36Sopenharmony_ci * 0 = being completed 81862306a36Sopenharmony_ci * -1 = free/freed 81962306a36Sopenharmony_ci */ 82062306a36Sopenharmony_ci atomic_set(&evt->active, -1); 82162306a36Sopenharmony_ci atomic_set(&evt->free, 1); 82262306a36Sopenharmony_ci evt->crq.valid = 0x80; 82362306a36Sopenharmony_ci evt->crq.ioba = cpu_to_be64(pool->iu_token + (sizeof(*evt->xfer_iu) * i)); 82462306a36Sopenharmony_ci evt->xfer_iu = pool->iu_storage + i; 82562306a36Sopenharmony_ci evt->vhost = vhost; 82662306a36Sopenharmony_ci evt->queue = queue; 82762306a36Sopenharmony_ci evt->ext_list = NULL; 82862306a36Sopenharmony_ci list_add_tail(&evt->queue_list, &queue->free); 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci LEAVE; 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci/** 83662306a36Sopenharmony_ci * ibmvfc_free_event_pool - Frees memory of the event pool of a host 83762306a36Sopenharmony_ci * @vhost: ibmvfc host who owns the event pool 83862306a36Sopenharmony_ci * @queue: ibmvfc queue struct 83962306a36Sopenharmony_ci * 84062306a36Sopenharmony_ci **/ 84162306a36Sopenharmony_cistatic void ibmvfc_free_event_pool(struct ibmvfc_host *vhost, 84262306a36Sopenharmony_ci struct ibmvfc_queue *queue) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci int i; 84562306a36Sopenharmony_ci struct ibmvfc_event_pool *pool = &queue->evt_pool; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci ENTER; 84862306a36Sopenharmony_ci for (i = 0; i < pool->size; ++i) { 84962306a36Sopenharmony_ci list_del(&pool->events[i].queue_list); 85062306a36Sopenharmony_ci BUG_ON(atomic_read(&pool->events[i].free) != 1); 85162306a36Sopenharmony_ci if (pool->events[i].ext_list) 85262306a36Sopenharmony_ci dma_pool_free(vhost->sg_pool, 85362306a36Sopenharmony_ci pool->events[i].ext_list, 85462306a36Sopenharmony_ci pool->events[i].ext_list_token); 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci kfree(pool->events); 85862306a36Sopenharmony_ci dma_free_coherent(vhost->dev, 85962306a36Sopenharmony_ci pool->size * sizeof(*pool->iu_storage), 86062306a36Sopenharmony_ci pool->iu_storage, pool->iu_token); 86162306a36Sopenharmony_ci LEAVE; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci/** 86562306a36Sopenharmony_ci * ibmvfc_free_queue - Deallocate queue 86662306a36Sopenharmony_ci * @vhost: ibmvfc host struct 86762306a36Sopenharmony_ci * @queue: ibmvfc queue struct 86862306a36Sopenharmony_ci * 86962306a36Sopenharmony_ci * Unmaps dma and deallocates page for messages 87062306a36Sopenharmony_ci **/ 87162306a36Sopenharmony_cistatic void ibmvfc_free_queue(struct ibmvfc_host *vhost, 87262306a36Sopenharmony_ci struct ibmvfc_queue *queue) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct device *dev = vhost->dev; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci dma_unmap_single(dev, queue->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); 87762306a36Sopenharmony_ci free_page((unsigned long)queue->msgs.handle); 87862306a36Sopenharmony_ci queue->msgs.handle = NULL; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci ibmvfc_free_event_pool(vhost, queue); 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/** 88462306a36Sopenharmony_ci * ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ 88562306a36Sopenharmony_ci * @vhost: ibmvfc host struct 88662306a36Sopenharmony_ci * 88762306a36Sopenharmony_ci * Frees irq, deallocates a page for messages, unmaps dma, and unregisters 88862306a36Sopenharmony_ci * the crq with the hypervisor. 88962306a36Sopenharmony_ci **/ 89062306a36Sopenharmony_cistatic void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci long rc = 0; 89362306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(vhost->dev); 89462306a36Sopenharmony_ci struct ibmvfc_queue *crq = &vhost->crq; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Releasing CRQ\n"); 89762306a36Sopenharmony_ci free_irq(vdev->irq, vhost); 89862306a36Sopenharmony_ci tasklet_kill(&vhost->tasklet); 89962306a36Sopenharmony_ci do { 90062306a36Sopenharmony_ci if (rc) 90162306a36Sopenharmony_ci msleep(100); 90262306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); 90362306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci vhost->state = IBMVFC_NO_CRQ; 90662306a36Sopenharmony_ci vhost->logged_in = 0; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci ibmvfc_free_queue(vhost, crq); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci/** 91262306a36Sopenharmony_ci * ibmvfc_reenable_crq_queue - reenables the CRQ 91362306a36Sopenharmony_ci * @vhost: ibmvfc host struct 91462306a36Sopenharmony_ci * 91562306a36Sopenharmony_ci * Return value: 91662306a36Sopenharmony_ci * 0 on success / other on failure 91762306a36Sopenharmony_ci **/ 91862306a36Sopenharmony_cistatic int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci int rc = 0; 92162306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(vhost->dev); 92262306a36Sopenharmony_ci unsigned long flags; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci ibmvfc_dereg_sub_crqs(vhost); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* Re-enable the CRQ */ 92762306a36Sopenharmony_ci do { 92862306a36Sopenharmony_ci if (rc) 92962306a36Sopenharmony_ci msleep(100); 93062306a36Sopenharmony_ci rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); 93162306a36Sopenharmony_ci } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (rc) 93462306a36Sopenharmony_ci dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 93762306a36Sopenharmony_ci spin_lock(vhost->crq.q_lock); 93862306a36Sopenharmony_ci vhost->do_enquiry = 1; 93962306a36Sopenharmony_ci vhost->using_channels = 0; 94062306a36Sopenharmony_ci spin_unlock(vhost->crq.q_lock); 94162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci ibmvfc_reg_sub_crqs(vhost); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return rc; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/** 94962306a36Sopenharmony_ci * ibmvfc_reset_crq - resets a crq after a failure 95062306a36Sopenharmony_ci * @vhost: ibmvfc host struct 95162306a36Sopenharmony_ci * 95262306a36Sopenharmony_ci * Return value: 95362306a36Sopenharmony_ci * 0 on success / other on failure 95462306a36Sopenharmony_ci **/ 95562306a36Sopenharmony_cistatic int ibmvfc_reset_crq(struct ibmvfc_host *vhost) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci int rc = 0; 95862306a36Sopenharmony_ci unsigned long flags; 95962306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(vhost->dev); 96062306a36Sopenharmony_ci struct ibmvfc_queue *crq = &vhost->crq; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ibmvfc_dereg_sub_crqs(vhost); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Close the CRQ */ 96562306a36Sopenharmony_ci do { 96662306a36Sopenharmony_ci if (rc) 96762306a36Sopenharmony_ci msleep(100); 96862306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); 96962306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 97262306a36Sopenharmony_ci spin_lock(vhost->crq.q_lock); 97362306a36Sopenharmony_ci vhost->state = IBMVFC_NO_CRQ; 97462306a36Sopenharmony_ci vhost->logged_in = 0; 97562306a36Sopenharmony_ci vhost->do_enquiry = 1; 97662306a36Sopenharmony_ci vhost->using_channels = 0; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* Clean out the queue */ 97962306a36Sopenharmony_ci memset(crq->msgs.crq, 0, PAGE_SIZE); 98062306a36Sopenharmony_ci crq->cur = 0; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* And re-open it again */ 98362306a36Sopenharmony_ci rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, 98462306a36Sopenharmony_ci crq->msg_token, PAGE_SIZE); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (rc == H_CLOSED) 98762306a36Sopenharmony_ci /* Adapter is good, but other end is not ready */ 98862306a36Sopenharmony_ci dev_warn(vhost->dev, "Partner adapter not ready\n"); 98962306a36Sopenharmony_ci else if (rc != 0) 99062306a36Sopenharmony_ci dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci spin_unlock(vhost->crq.q_lock); 99362306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci ibmvfc_reg_sub_crqs(vhost); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return rc; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci/** 100162306a36Sopenharmony_ci * ibmvfc_valid_event - Determines if event is valid. 100262306a36Sopenharmony_ci * @pool: event_pool that contains the event 100362306a36Sopenharmony_ci * @evt: ibmvfc event to be checked for validity 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * Return value: 100662306a36Sopenharmony_ci * 1 if event is valid / 0 if event is not valid 100762306a36Sopenharmony_ci **/ 100862306a36Sopenharmony_cistatic int ibmvfc_valid_event(struct ibmvfc_event_pool *pool, 100962306a36Sopenharmony_ci struct ibmvfc_event *evt) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci int index = evt - pool->events; 101262306a36Sopenharmony_ci if (index < 0 || index >= pool->size) /* outside of bounds */ 101362306a36Sopenharmony_ci return 0; 101462306a36Sopenharmony_ci if (evt != pool->events + index) /* unaligned */ 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci return 1; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/** 102062306a36Sopenharmony_ci * ibmvfc_free_event - Free the specified event 102162306a36Sopenharmony_ci * @evt: ibmvfc_event to be freed 102262306a36Sopenharmony_ci * 102362306a36Sopenharmony_ci **/ 102462306a36Sopenharmony_cistatic void ibmvfc_free_event(struct ibmvfc_event *evt) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct ibmvfc_event_pool *pool = &evt->queue->evt_pool; 102762306a36Sopenharmony_ci unsigned long flags; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci BUG_ON(!ibmvfc_valid_event(pool, evt)); 103062306a36Sopenharmony_ci BUG_ON(atomic_inc_return(&evt->free) != 1); 103162306a36Sopenharmony_ci BUG_ON(atomic_dec_and_test(&evt->active)); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci spin_lock_irqsave(&evt->queue->l_lock, flags); 103462306a36Sopenharmony_ci list_add_tail(&evt->queue_list, &evt->queue->free); 103562306a36Sopenharmony_ci if (evt->eh_comp) 103662306a36Sopenharmony_ci complete(evt->eh_comp); 103762306a36Sopenharmony_ci spin_unlock_irqrestore(&evt->queue->l_lock, flags); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci/** 104162306a36Sopenharmony_ci * ibmvfc_scsi_eh_done - EH done function for queuecommand commands 104262306a36Sopenharmony_ci * @evt: ibmvfc event struct 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * This function does not setup any error status, that must be done 104562306a36Sopenharmony_ci * before this function gets called. 104662306a36Sopenharmony_ci **/ 104762306a36Sopenharmony_cistatic void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct scsi_cmnd *cmnd = evt->cmnd; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (cmnd) { 105262306a36Sopenharmony_ci scsi_dma_unmap(cmnd); 105362306a36Sopenharmony_ci scsi_done(cmnd); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci ibmvfc_free_event(evt); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci/** 106062306a36Sopenharmony_ci * ibmvfc_complete_purge - Complete failed command list 106162306a36Sopenharmony_ci * @purge_list: list head of failed commands 106262306a36Sopenharmony_ci * 106362306a36Sopenharmony_ci * This function runs completions on commands to fail as a result of a 106462306a36Sopenharmony_ci * host reset or platform migration. 106562306a36Sopenharmony_ci **/ 106662306a36Sopenharmony_cistatic void ibmvfc_complete_purge(struct list_head *purge_list) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct ibmvfc_event *evt, *pos; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci list_for_each_entry_safe(evt, pos, purge_list, queue_list) { 107162306a36Sopenharmony_ci list_del(&evt->queue_list); 107262306a36Sopenharmony_ci ibmvfc_trc_end(evt); 107362306a36Sopenharmony_ci evt->done(evt); 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/** 107862306a36Sopenharmony_ci * ibmvfc_fail_request - Fail request with specified error code 107962306a36Sopenharmony_ci * @evt: ibmvfc event struct 108062306a36Sopenharmony_ci * @error_code: error code to fail request with 108162306a36Sopenharmony_ci * 108262306a36Sopenharmony_ci * Return value: 108362306a36Sopenharmony_ci * none 108462306a36Sopenharmony_ci **/ 108562306a36Sopenharmony_cistatic void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci /* 108862306a36Sopenharmony_ci * Anything we are failing should still be active. Otherwise, it 108962306a36Sopenharmony_ci * implies we already got a response for the command and are doing 109062306a36Sopenharmony_ci * something bad like double completing it. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci BUG_ON(!atomic_dec_and_test(&evt->active)); 109362306a36Sopenharmony_ci if (evt->cmnd) { 109462306a36Sopenharmony_ci evt->cmnd->result = (error_code << 16); 109562306a36Sopenharmony_ci evt->done = ibmvfc_scsi_eh_done; 109662306a36Sopenharmony_ci } else 109762306a36Sopenharmony_ci evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci del_timer(&evt->timer); 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci/** 110362306a36Sopenharmony_ci * ibmvfc_purge_requests - Our virtual adapter just shut down. Purge any sent requests 110462306a36Sopenharmony_ci * @vhost: ibmvfc host struct 110562306a36Sopenharmony_ci * @error_code: error code to fail requests with 110662306a36Sopenharmony_ci * 110762306a36Sopenharmony_ci * Return value: 110862306a36Sopenharmony_ci * none 110962306a36Sopenharmony_ci **/ 111062306a36Sopenharmony_cistatic void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci struct ibmvfc_event *evt, *pos; 111362306a36Sopenharmony_ci struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs; 111462306a36Sopenharmony_ci unsigned long flags; 111562306a36Sopenharmony_ci int hwqs = 0; 111662306a36Sopenharmony_ci int i; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (vhost->using_channels) 111962306a36Sopenharmony_ci hwqs = vhost->scsi_scrqs.active_queues; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Purging all requests\n"); 112262306a36Sopenharmony_ci spin_lock_irqsave(&vhost->crq.l_lock, flags); 112362306a36Sopenharmony_ci list_for_each_entry_safe(evt, pos, &vhost->crq.sent, queue_list) 112462306a36Sopenharmony_ci ibmvfc_fail_request(evt, error_code); 112562306a36Sopenharmony_ci list_splice_init(&vhost->crq.sent, &vhost->purge); 112662306a36Sopenharmony_ci spin_unlock_irqrestore(&vhost->crq.l_lock, flags); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci for (i = 0; i < hwqs; i++) { 112962306a36Sopenharmony_ci spin_lock_irqsave(queues[i].q_lock, flags); 113062306a36Sopenharmony_ci spin_lock(&queues[i].l_lock); 113162306a36Sopenharmony_ci list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list) 113262306a36Sopenharmony_ci ibmvfc_fail_request(evt, error_code); 113362306a36Sopenharmony_ci list_splice_init(&queues[i].sent, &vhost->purge); 113462306a36Sopenharmony_ci spin_unlock(&queues[i].l_lock); 113562306a36Sopenharmony_ci spin_unlock_irqrestore(queues[i].q_lock, flags); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci/** 114062306a36Sopenharmony_ci * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ 114162306a36Sopenharmony_ci * @vhost: struct ibmvfc host to reset 114262306a36Sopenharmony_ci **/ 114362306a36Sopenharmony_cistatic void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci ibmvfc_purge_requests(vhost, DID_ERROR); 114662306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); 114762306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/** 115162306a36Sopenharmony_ci * __ibmvfc_reset_host - Reset the connection to the server (no locking) 115262306a36Sopenharmony_ci * @vhost: struct ibmvfc host to reset 115362306a36Sopenharmony_ci **/ 115462306a36Sopenharmony_cistatic void __ibmvfc_reset_host(struct ibmvfc_host *vhost) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT && 115762306a36Sopenharmony_ci !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { 115862306a36Sopenharmony_ci scsi_block_requests(vhost->host); 115962306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO); 116062306a36Sopenharmony_ci vhost->job_step = ibmvfc_npiv_logout; 116162306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 116262306a36Sopenharmony_ci } else 116362306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci/** 116762306a36Sopenharmony_ci * ibmvfc_reset_host - Reset the connection to the server 116862306a36Sopenharmony_ci * @vhost: ibmvfc host struct 116962306a36Sopenharmony_ci **/ 117062306a36Sopenharmony_cistatic void ibmvfc_reset_host(struct ibmvfc_host *vhost) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci unsigned long flags; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 117562306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 117662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci/** 118062306a36Sopenharmony_ci * ibmvfc_retry_host_init - Retry host initialization if allowed 118162306a36Sopenharmony_ci * @vhost: ibmvfc host struct 118262306a36Sopenharmony_ci * 118362306a36Sopenharmony_ci * Returns: 1 if init will be retried / 0 if not 118462306a36Sopenharmony_ci * 118562306a36Sopenharmony_ci **/ 118662306a36Sopenharmony_cistatic int ibmvfc_retry_host_init(struct ibmvfc_host *vhost) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci int retry = 0; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) { 119162306a36Sopenharmony_ci vhost->delay_init = 1; 119262306a36Sopenharmony_ci if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) { 119362306a36Sopenharmony_ci dev_err(vhost->dev, 119462306a36Sopenharmony_ci "Host initialization retries exceeded. Taking adapter offline\n"); 119562306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); 119662306a36Sopenharmony_ci } else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES) 119762306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 119862306a36Sopenharmony_ci else { 119962306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); 120062306a36Sopenharmony_ci retry = 1; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 120562306a36Sopenharmony_ci return retry; 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci/** 120962306a36Sopenharmony_ci * __ibmvfc_get_target - Find the specified scsi_target (no locking) 121062306a36Sopenharmony_ci * @starget: scsi target struct 121162306a36Sopenharmony_ci * 121262306a36Sopenharmony_ci * Return value: 121362306a36Sopenharmony_ci * ibmvfc_target struct / NULL if not found 121462306a36Sopenharmony_ci **/ 121562306a36Sopenharmony_cistatic struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 121862306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 121962306a36Sopenharmony_ci struct ibmvfc_target *tgt; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 122262306a36Sopenharmony_ci if (tgt->target_id == starget->id) { 122362306a36Sopenharmony_ci kref_get(&tgt->kref); 122462306a36Sopenharmony_ci return tgt; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci return NULL; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci/** 123062306a36Sopenharmony_ci * ibmvfc_get_target - Find the specified scsi_target 123162306a36Sopenharmony_ci * @starget: scsi target struct 123262306a36Sopenharmony_ci * 123362306a36Sopenharmony_ci * Return value: 123462306a36Sopenharmony_ci * ibmvfc_target struct / NULL if not found 123562306a36Sopenharmony_ci **/ 123662306a36Sopenharmony_cistatic struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 123962306a36Sopenharmony_ci struct ibmvfc_target *tgt; 124062306a36Sopenharmony_ci unsigned long flags; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 124362306a36Sopenharmony_ci tgt = __ibmvfc_get_target(starget); 124462306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 124562306a36Sopenharmony_ci return tgt; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci/** 124962306a36Sopenharmony_ci * ibmvfc_get_host_speed - Get host port speed 125062306a36Sopenharmony_ci * @shost: scsi host struct 125162306a36Sopenharmony_ci * 125262306a36Sopenharmony_ci * Return value: 125362306a36Sopenharmony_ci * none 125462306a36Sopenharmony_ci **/ 125562306a36Sopenharmony_cistatic void ibmvfc_get_host_speed(struct Scsi_Host *shost) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 125862306a36Sopenharmony_ci unsigned long flags; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 126162306a36Sopenharmony_ci if (vhost->state == IBMVFC_ACTIVE) { 126262306a36Sopenharmony_ci switch (be64_to_cpu(vhost->login_buf->resp.link_speed) / 100) { 126362306a36Sopenharmony_ci case 1: 126462306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_1GBIT; 126562306a36Sopenharmony_ci break; 126662306a36Sopenharmony_ci case 2: 126762306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_2GBIT; 126862306a36Sopenharmony_ci break; 126962306a36Sopenharmony_ci case 4: 127062306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_4GBIT; 127162306a36Sopenharmony_ci break; 127262306a36Sopenharmony_ci case 8: 127362306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_8GBIT; 127462306a36Sopenharmony_ci break; 127562306a36Sopenharmony_ci case 10: 127662306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_10GBIT; 127762306a36Sopenharmony_ci break; 127862306a36Sopenharmony_ci case 16: 127962306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_16GBIT; 128062306a36Sopenharmony_ci break; 128162306a36Sopenharmony_ci default: 128262306a36Sopenharmony_ci ibmvfc_log(vhost, 3, "Unknown port speed: %lld Gbit\n", 128362306a36Sopenharmony_ci be64_to_cpu(vhost->login_buf->resp.link_speed) / 100); 128462306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci } else 128862306a36Sopenharmony_ci fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; 128962306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/** 129362306a36Sopenharmony_ci * ibmvfc_get_host_port_state - Get host port state 129462306a36Sopenharmony_ci * @shost: scsi host struct 129562306a36Sopenharmony_ci * 129662306a36Sopenharmony_ci * Return value: 129762306a36Sopenharmony_ci * none 129862306a36Sopenharmony_ci **/ 129962306a36Sopenharmony_cistatic void ibmvfc_get_host_port_state(struct Scsi_Host *shost) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 130262306a36Sopenharmony_ci unsigned long flags; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 130562306a36Sopenharmony_ci switch (vhost->state) { 130662306a36Sopenharmony_ci case IBMVFC_INITIALIZING: 130762306a36Sopenharmony_ci case IBMVFC_ACTIVE: 130862306a36Sopenharmony_ci fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; 130962306a36Sopenharmony_ci break; 131062306a36Sopenharmony_ci case IBMVFC_LINK_DOWN: 131162306a36Sopenharmony_ci fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; 131262306a36Sopenharmony_ci break; 131362306a36Sopenharmony_ci case IBMVFC_LINK_DEAD: 131462306a36Sopenharmony_ci case IBMVFC_HOST_OFFLINE: 131562306a36Sopenharmony_ci fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; 131662306a36Sopenharmony_ci break; 131762306a36Sopenharmony_ci case IBMVFC_HALTED: 131862306a36Sopenharmony_ci fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED; 131962306a36Sopenharmony_ci break; 132062306a36Sopenharmony_ci case IBMVFC_NO_CRQ: 132162306a36Sopenharmony_ci fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; 132262306a36Sopenharmony_ci break; 132362306a36Sopenharmony_ci default: 132462306a36Sopenharmony_ci ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state); 132562306a36Sopenharmony_ci fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci/** 133262306a36Sopenharmony_ci * ibmvfc_set_rport_dev_loss_tmo - Set rport's device loss timeout 133362306a36Sopenharmony_ci * @rport: rport struct 133462306a36Sopenharmony_ci * @timeout: timeout value 133562306a36Sopenharmony_ci * 133662306a36Sopenharmony_ci * Return value: 133762306a36Sopenharmony_ci * none 133862306a36Sopenharmony_ci **/ 133962306a36Sopenharmony_cistatic void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci if (timeout) 134262306a36Sopenharmony_ci rport->dev_loss_tmo = timeout; 134362306a36Sopenharmony_ci else 134462306a36Sopenharmony_ci rport->dev_loss_tmo = 1; 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci/** 134862306a36Sopenharmony_ci * ibmvfc_release_tgt - Free memory allocated for a target 134962306a36Sopenharmony_ci * @kref: kref struct 135062306a36Sopenharmony_ci * 135162306a36Sopenharmony_ci **/ 135262306a36Sopenharmony_cistatic void ibmvfc_release_tgt(struct kref *kref) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref); 135562306a36Sopenharmony_ci kfree(tgt); 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/** 135962306a36Sopenharmony_ci * ibmvfc_get_starget_node_name - Get SCSI target's node name 136062306a36Sopenharmony_ci * @starget: scsi target struct 136162306a36Sopenharmony_ci * 136262306a36Sopenharmony_ci * Return value: 136362306a36Sopenharmony_ci * none 136462306a36Sopenharmony_ci **/ 136562306a36Sopenharmony_cistatic void ibmvfc_get_starget_node_name(struct scsi_target *starget) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci struct ibmvfc_target *tgt = ibmvfc_get_target(starget); 136862306a36Sopenharmony_ci fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0; 136962306a36Sopenharmony_ci if (tgt) 137062306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci/** 137462306a36Sopenharmony_ci * ibmvfc_get_starget_port_name - Get SCSI target's port name 137562306a36Sopenharmony_ci * @starget: scsi target struct 137662306a36Sopenharmony_ci * 137762306a36Sopenharmony_ci * Return value: 137862306a36Sopenharmony_ci * none 137962306a36Sopenharmony_ci **/ 138062306a36Sopenharmony_cistatic void ibmvfc_get_starget_port_name(struct scsi_target *starget) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci struct ibmvfc_target *tgt = ibmvfc_get_target(starget); 138362306a36Sopenharmony_ci fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0; 138462306a36Sopenharmony_ci if (tgt) 138562306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci/** 138962306a36Sopenharmony_ci * ibmvfc_get_starget_port_id - Get SCSI target's port ID 139062306a36Sopenharmony_ci * @starget: scsi target struct 139162306a36Sopenharmony_ci * 139262306a36Sopenharmony_ci * Return value: 139362306a36Sopenharmony_ci * none 139462306a36Sopenharmony_ci **/ 139562306a36Sopenharmony_cistatic void ibmvfc_get_starget_port_id(struct scsi_target *starget) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci struct ibmvfc_target *tgt = ibmvfc_get_target(starget); 139862306a36Sopenharmony_ci fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1; 139962306a36Sopenharmony_ci if (tgt) 140062306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci/** 140462306a36Sopenharmony_ci * ibmvfc_wait_while_resetting - Wait while the host resets 140562306a36Sopenharmony_ci * @vhost: ibmvfc host struct 140662306a36Sopenharmony_ci * 140762306a36Sopenharmony_ci * Return value: 140862306a36Sopenharmony_ci * 0 on success / other on failure 140962306a36Sopenharmony_ci **/ 141062306a36Sopenharmony_cistatic int ibmvfc_wait_while_resetting(struct ibmvfc_host *vhost) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci long timeout = wait_event_timeout(vhost->init_wait_q, 141362306a36Sopenharmony_ci ((vhost->state == IBMVFC_ACTIVE || 141462306a36Sopenharmony_ci vhost->state == IBMVFC_HOST_OFFLINE || 141562306a36Sopenharmony_ci vhost->state == IBMVFC_LINK_DEAD) && 141662306a36Sopenharmony_ci vhost->action == IBMVFC_HOST_ACTION_NONE), 141762306a36Sopenharmony_ci (init_timeout * HZ)); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci return timeout ? 0 : -EIO; 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci/** 142362306a36Sopenharmony_ci * ibmvfc_issue_fc_host_lip - Re-initiate link initialization 142462306a36Sopenharmony_ci * @shost: scsi host struct 142562306a36Sopenharmony_ci * 142662306a36Sopenharmony_ci * Return value: 142762306a36Sopenharmony_ci * 0 on success / other on failure 142862306a36Sopenharmony_ci **/ 142962306a36Sopenharmony_cistatic int ibmvfc_issue_fc_host_lip(struct Scsi_Host *shost) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci dev_err(vhost->dev, "Initiating host LIP. Resetting connection\n"); 143462306a36Sopenharmony_ci ibmvfc_reset_host(vhost); 143562306a36Sopenharmony_ci return ibmvfc_wait_while_resetting(vhost); 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci/** 143962306a36Sopenharmony_ci * ibmvfc_gather_partition_info - Gather info about the LPAR 144062306a36Sopenharmony_ci * @vhost: ibmvfc host struct 144162306a36Sopenharmony_ci * 144262306a36Sopenharmony_ci * Return value: 144362306a36Sopenharmony_ci * none 144462306a36Sopenharmony_ci **/ 144562306a36Sopenharmony_cistatic void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct device_node *rootdn; 144862306a36Sopenharmony_ci const char *name; 144962306a36Sopenharmony_ci const unsigned int *num; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci rootdn = of_find_node_by_path("/"); 145262306a36Sopenharmony_ci if (!rootdn) 145362306a36Sopenharmony_ci return; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci name = of_get_property(rootdn, "ibm,partition-name", NULL); 145662306a36Sopenharmony_ci if (name) 145762306a36Sopenharmony_ci strncpy(vhost->partition_name, name, sizeof(vhost->partition_name)); 145862306a36Sopenharmony_ci num = of_get_property(rootdn, "ibm,partition-no", NULL); 145962306a36Sopenharmony_ci if (num) 146062306a36Sopenharmony_ci vhost->partition_number = *num; 146162306a36Sopenharmony_ci of_node_put(rootdn); 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci/** 146562306a36Sopenharmony_ci * ibmvfc_set_login_info - Setup info for NPIV login 146662306a36Sopenharmony_ci * @vhost: ibmvfc host struct 146762306a36Sopenharmony_ci * 146862306a36Sopenharmony_ci * Return value: 146962306a36Sopenharmony_ci * none 147062306a36Sopenharmony_ci **/ 147162306a36Sopenharmony_cistatic void ibmvfc_set_login_info(struct ibmvfc_host *vhost) 147262306a36Sopenharmony_ci{ 147362306a36Sopenharmony_ci struct ibmvfc_npiv_login *login_info = &vhost->login_info; 147462306a36Sopenharmony_ci struct ibmvfc_queue *async_crq = &vhost->async_crq; 147562306a36Sopenharmony_ci struct device_node *of_node = vhost->dev->of_node; 147662306a36Sopenharmony_ci const char *location; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci memset(login_info, 0, sizeof(*login_info)); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX); 148162306a36Sopenharmony_ci login_info->max_dma_len = cpu_to_be64(IBMVFC_MAX_SECTORS << 9); 148262306a36Sopenharmony_ci login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu)); 148362306a36Sopenharmony_ci login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp)); 148462306a36Sopenharmony_ci login_info->partition_num = cpu_to_be32(vhost->partition_number); 148562306a36Sopenharmony_ci login_info->vfc_frame_version = cpu_to_be32(1); 148662306a36Sopenharmony_ci login_info->fcp_version = cpu_to_be16(3); 148762306a36Sopenharmony_ci login_info->flags = cpu_to_be16(IBMVFC_FLUSH_ON_HALT); 148862306a36Sopenharmony_ci if (vhost->client_migrated) 148962306a36Sopenharmony_ci login_info->flags |= cpu_to_be16(IBMVFC_CLIENT_MIGRATED); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci login_info->max_cmds = cpu_to_be32(max_requests + IBMVFC_NUM_INTERNAL_REQ); 149262306a36Sopenharmony_ci login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (vhost->mq_enabled || vhost->using_channels) 149562306a36Sopenharmony_ci login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token); 149862306a36Sopenharmony_ci login_info->async.len = cpu_to_be32(async_crq->size * 149962306a36Sopenharmony_ci sizeof(*async_crq->msgs.async)); 150062306a36Sopenharmony_ci strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME); 150162306a36Sopenharmony_ci strncpy(login_info->device_name, 150262306a36Sopenharmony_ci dev_name(&vhost->host->shost_gendev), IBMVFC_MAX_NAME); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci location = of_get_property(of_node, "ibm,loc-code", NULL); 150562306a36Sopenharmony_ci location = location ? location : dev_name(vhost->dev); 150662306a36Sopenharmony_ci strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci/** 151062306a36Sopenharmony_ci * ibmvfc_get_event - Gets the next free event in pool 151162306a36Sopenharmony_ci * @queue: ibmvfc queue struct 151262306a36Sopenharmony_ci * 151362306a36Sopenharmony_ci * Returns a free event from the pool. 151462306a36Sopenharmony_ci **/ 151562306a36Sopenharmony_cistatic struct ibmvfc_event *ibmvfc_get_event(struct ibmvfc_queue *queue) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct ibmvfc_event *evt; 151862306a36Sopenharmony_ci unsigned long flags; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci spin_lock_irqsave(&queue->l_lock, flags); 152162306a36Sopenharmony_ci if (list_empty(&queue->free)) { 152262306a36Sopenharmony_ci ibmvfc_log(queue->vhost, 4, "empty event pool on queue:%ld\n", queue->hwq_id); 152362306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->l_lock, flags); 152462306a36Sopenharmony_ci return NULL; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci evt = list_entry(queue->free.next, struct ibmvfc_event, queue_list); 152762306a36Sopenharmony_ci atomic_set(&evt->free, 0); 152862306a36Sopenharmony_ci list_del(&evt->queue_list); 152962306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->l_lock, flags); 153062306a36Sopenharmony_ci return evt; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci/** 153462306a36Sopenharmony_ci * ibmvfc_locked_done - Calls evt completion with host_lock held 153562306a36Sopenharmony_ci * @evt: ibmvfc evt to complete 153662306a36Sopenharmony_ci * 153762306a36Sopenharmony_ci * All non-scsi command completion callbacks have the expectation that the 153862306a36Sopenharmony_ci * host_lock is held. This callback is used by ibmvfc_init_event to wrap a 153962306a36Sopenharmony_ci * MAD evt with the host_lock. 154062306a36Sopenharmony_ci **/ 154162306a36Sopenharmony_cistatic void ibmvfc_locked_done(struct ibmvfc_event *evt) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci unsigned long flags; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci spin_lock_irqsave(evt->vhost->host->host_lock, flags); 154662306a36Sopenharmony_ci evt->_done(evt); 154762306a36Sopenharmony_ci spin_unlock_irqrestore(evt->vhost->host->host_lock, flags); 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci/** 155162306a36Sopenharmony_ci * ibmvfc_init_event - Initialize fields in an event struct that are always 155262306a36Sopenharmony_ci * required. 155362306a36Sopenharmony_ci * @evt: The event 155462306a36Sopenharmony_ci * @done: Routine to call when the event is responded to 155562306a36Sopenharmony_ci * @format: SRP or MAD format 155662306a36Sopenharmony_ci **/ 155762306a36Sopenharmony_cistatic void ibmvfc_init_event(struct ibmvfc_event *evt, 155862306a36Sopenharmony_ci void (*done) (struct ibmvfc_event *), u8 format) 155962306a36Sopenharmony_ci{ 156062306a36Sopenharmony_ci evt->cmnd = NULL; 156162306a36Sopenharmony_ci evt->sync_iu = NULL; 156262306a36Sopenharmony_ci evt->eh_comp = NULL; 156362306a36Sopenharmony_ci evt->crq.format = format; 156462306a36Sopenharmony_ci if (format == IBMVFC_CMD_FORMAT) 156562306a36Sopenharmony_ci evt->done = done; 156662306a36Sopenharmony_ci else { 156762306a36Sopenharmony_ci evt->_done = done; 156862306a36Sopenharmony_ci evt->done = ibmvfc_locked_done; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci evt->hwq = 0; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci/** 157462306a36Sopenharmony_ci * ibmvfc_map_sg_list - Initialize scatterlist 157562306a36Sopenharmony_ci * @scmd: scsi command struct 157662306a36Sopenharmony_ci * @nseg: number of scatterlist segments 157762306a36Sopenharmony_ci * @md: memory descriptor list to initialize 157862306a36Sopenharmony_ci **/ 157962306a36Sopenharmony_cistatic void ibmvfc_map_sg_list(struct scsi_cmnd *scmd, int nseg, 158062306a36Sopenharmony_ci struct srp_direct_buf *md) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci int i; 158362306a36Sopenharmony_ci struct scatterlist *sg; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci scsi_for_each_sg(scmd, sg, nseg, i) { 158662306a36Sopenharmony_ci md[i].va = cpu_to_be64(sg_dma_address(sg)); 158762306a36Sopenharmony_ci md[i].len = cpu_to_be32(sg_dma_len(sg)); 158862306a36Sopenharmony_ci md[i].key = 0; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci/** 159362306a36Sopenharmony_ci * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes descriptor fields 159462306a36Sopenharmony_ci * @scmd: struct scsi_cmnd with the scatterlist 159562306a36Sopenharmony_ci * @evt: ibmvfc event struct 159662306a36Sopenharmony_ci * @vfc_cmd: vfc_cmd that contains the memory descriptor 159762306a36Sopenharmony_ci * @dev: device for which to map dma memory 159862306a36Sopenharmony_ci * 159962306a36Sopenharmony_ci * Returns: 160062306a36Sopenharmony_ci * 0 on success / non-zero on failure 160162306a36Sopenharmony_ci **/ 160262306a36Sopenharmony_cistatic int ibmvfc_map_sg_data(struct scsi_cmnd *scmd, 160362306a36Sopenharmony_ci struct ibmvfc_event *evt, 160462306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd, struct device *dev) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci int sg_mapped; 160862306a36Sopenharmony_ci struct srp_direct_buf *data = &vfc_cmd->ioba; 160962306a36Sopenharmony_ci struct ibmvfc_host *vhost = dev_get_drvdata(dev); 161062306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(evt->vhost, vfc_cmd); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (cls3_error) 161362306a36Sopenharmony_ci vfc_cmd->flags |= cpu_to_be16(IBMVFC_CLASS_3_ERR); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci sg_mapped = scsi_dma_map(scmd); 161662306a36Sopenharmony_ci if (!sg_mapped) { 161762306a36Sopenharmony_ci vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC); 161862306a36Sopenharmony_ci return 0; 161962306a36Sopenharmony_ci } else if (unlikely(sg_mapped < 0)) { 162062306a36Sopenharmony_ci if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) 162162306a36Sopenharmony_ci scmd_printk(KERN_ERR, scmd, "Failed to map DMA buffer for command\n"); 162262306a36Sopenharmony_ci return sg_mapped; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (scmd->sc_data_direction == DMA_TO_DEVICE) { 162662306a36Sopenharmony_ci vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE); 162762306a36Sopenharmony_ci iu->add_cdb_len |= IBMVFC_WRDATA; 162862306a36Sopenharmony_ci } else { 162962306a36Sopenharmony_ci vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ); 163062306a36Sopenharmony_ci iu->add_cdb_len |= IBMVFC_RDDATA; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (sg_mapped == 1) { 163462306a36Sopenharmony_ci ibmvfc_map_sg_list(scmd, sg_mapped, data); 163562306a36Sopenharmony_ci return 0; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci if (!evt->ext_list) { 164162306a36Sopenharmony_ci evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC, 164262306a36Sopenharmony_ci &evt->ext_list_token); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (!evt->ext_list) { 164562306a36Sopenharmony_ci scsi_dma_unmap(scmd); 164662306a36Sopenharmony_ci if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) 164762306a36Sopenharmony_ci scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n"); 164862306a36Sopenharmony_ci return -ENOMEM; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci ibmvfc_map_sg_list(scmd, sg_mapped, evt->ext_list); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci data->va = cpu_to_be64(evt->ext_list_token); 165562306a36Sopenharmony_ci data->len = cpu_to_be32(sg_mapped * sizeof(struct srp_direct_buf)); 165662306a36Sopenharmony_ci data->key = 0; 165762306a36Sopenharmony_ci return 0; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci/** 166162306a36Sopenharmony_ci * ibmvfc_timeout - Internal command timeout handler 166262306a36Sopenharmony_ci * @t: struct ibmvfc_event that timed out 166362306a36Sopenharmony_ci * 166462306a36Sopenharmony_ci * Called when an internally generated command times out 166562306a36Sopenharmony_ci **/ 166662306a36Sopenharmony_cistatic void ibmvfc_timeout(struct timer_list *t) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct ibmvfc_event *evt = from_timer(evt, t, timer); 166962306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 167062306a36Sopenharmony_ci dev_err(vhost->dev, "Command timed out (%p). Resetting connection\n", evt); 167162306a36Sopenharmony_ci ibmvfc_reset_host(vhost); 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci/** 167562306a36Sopenharmony_ci * ibmvfc_send_event - Transforms event to u64 array and calls send_crq() 167662306a36Sopenharmony_ci * @evt: event to be sent 167762306a36Sopenharmony_ci * @vhost: ibmvfc host struct 167862306a36Sopenharmony_ci * @timeout: timeout in seconds - 0 means do not time command 167962306a36Sopenharmony_ci * 168062306a36Sopenharmony_ci * Returns the value returned from ibmvfc_send_crq(). (Zero for success) 168162306a36Sopenharmony_ci **/ 168262306a36Sopenharmony_cistatic int ibmvfc_send_event(struct ibmvfc_event *evt, 168362306a36Sopenharmony_ci struct ibmvfc_host *vhost, unsigned long timeout) 168462306a36Sopenharmony_ci{ 168562306a36Sopenharmony_ci __be64 *crq_as_u64 = (__be64 *) &evt->crq; 168662306a36Sopenharmony_ci unsigned long flags; 168762306a36Sopenharmony_ci int rc; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci /* Copy the IU into the transfer area */ 169062306a36Sopenharmony_ci *evt->xfer_iu = evt->iu; 169162306a36Sopenharmony_ci if (evt->crq.format == IBMVFC_CMD_FORMAT) 169262306a36Sopenharmony_ci evt->xfer_iu->cmd.tag = cpu_to_be64((u64)evt); 169362306a36Sopenharmony_ci else if (evt->crq.format == IBMVFC_MAD_FORMAT) 169462306a36Sopenharmony_ci evt->xfer_iu->mad_common.tag = cpu_to_be64((u64)evt); 169562306a36Sopenharmony_ci else 169662306a36Sopenharmony_ci BUG(); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci timer_setup(&evt->timer, ibmvfc_timeout, 0); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci if (timeout) { 170162306a36Sopenharmony_ci evt->timer.expires = jiffies + (timeout * HZ); 170262306a36Sopenharmony_ci add_timer(&evt->timer); 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci spin_lock_irqsave(&evt->queue->l_lock, flags); 170662306a36Sopenharmony_ci list_add_tail(&evt->queue_list, &evt->queue->sent); 170762306a36Sopenharmony_ci atomic_set(&evt->active, 1); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci mb(); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (evt->queue->fmt == IBMVFC_SUB_CRQ_FMT) 171262306a36Sopenharmony_ci rc = ibmvfc_send_sub_crq(vhost, 171362306a36Sopenharmony_ci evt->queue->vios_cookie, 171462306a36Sopenharmony_ci be64_to_cpu(crq_as_u64[0]), 171562306a36Sopenharmony_ci be64_to_cpu(crq_as_u64[1]), 171662306a36Sopenharmony_ci 0, 0); 171762306a36Sopenharmony_ci else 171862306a36Sopenharmony_ci rc = ibmvfc_send_crq(vhost, be64_to_cpu(crq_as_u64[0]), 171962306a36Sopenharmony_ci be64_to_cpu(crq_as_u64[1])); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (rc) { 172262306a36Sopenharmony_ci atomic_set(&evt->active, 0); 172362306a36Sopenharmony_ci list_del(&evt->queue_list); 172462306a36Sopenharmony_ci spin_unlock_irqrestore(&evt->queue->l_lock, flags); 172562306a36Sopenharmony_ci del_timer(&evt->timer); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY. 172862306a36Sopenharmony_ci * Firmware will send a CRQ with a transport event (0xFF) to 172962306a36Sopenharmony_ci * tell this client what has happened to the transport. This 173062306a36Sopenharmony_ci * will be handled in ibmvfc_handle_crq() 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_ci if (rc == H_CLOSED) { 173362306a36Sopenharmony_ci if (printk_ratelimit()) 173462306a36Sopenharmony_ci dev_warn(vhost->dev, "Send warning. Receive queue closed, will retry.\n"); 173562306a36Sopenharmony_ci if (evt->cmnd) 173662306a36Sopenharmony_ci scsi_dma_unmap(evt->cmnd); 173762306a36Sopenharmony_ci ibmvfc_free_event(evt); 173862306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 173962306a36Sopenharmony_ci } 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci dev_err(vhost->dev, "Send error (rc=%d)\n", rc); 174262306a36Sopenharmony_ci if (evt->cmnd) { 174362306a36Sopenharmony_ci evt->cmnd->result = DID_ERROR << 16; 174462306a36Sopenharmony_ci evt->done = ibmvfc_scsi_eh_done; 174562306a36Sopenharmony_ci } else 174662306a36Sopenharmony_ci evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci evt->done(evt); 174962306a36Sopenharmony_ci } else { 175062306a36Sopenharmony_ci spin_unlock_irqrestore(&evt->queue->l_lock, flags); 175162306a36Sopenharmony_ci ibmvfc_trc_start(evt); 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci return 0; 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci/** 175862306a36Sopenharmony_ci * ibmvfc_log_error - Log an error for the failed command if appropriate 175962306a36Sopenharmony_ci * @evt: ibmvfc event to log 176062306a36Sopenharmony_ci * 176162306a36Sopenharmony_ci **/ 176262306a36Sopenharmony_cistatic void ibmvfc_log_error(struct ibmvfc_event *evt) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd; 176562306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 176662306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd); 176762306a36Sopenharmony_ci struct scsi_cmnd *cmnd = evt->cmnd; 176862306a36Sopenharmony_ci const char *err = unknown_error; 176962306a36Sopenharmony_ci int index = ibmvfc_get_err_index(be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error)); 177062306a36Sopenharmony_ci int logerr = 0; 177162306a36Sopenharmony_ci int rsp_code = 0; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (index >= 0) { 177462306a36Sopenharmony_ci logerr = cmd_status[index].log; 177562306a36Sopenharmony_ci err = cmd_status[index].name; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1))) 177962306a36Sopenharmony_ci return; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (rsp->flags & FCP_RSP_LEN_VALID) 178262306a36Sopenharmony_ci rsp_code = rsp->data.info.rsp_code; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci scmd_printk(KERN_ERR, cmnd, "Command (%02X) : %s (%x:%x) " 178562306a36Sopenharmony_ci "flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n", 178662306a36Sopenharmony_ci cmnd->cmnd[0], err, be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error), 178762306a36Sopenharmony_ci rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status); 178862306a36Sopenharmony_ci} 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci/** 179162306a36Sopenharmony_ci * ibmvfc_relogin - Log back into the specified device 179262306a36Sopenharmony_ci * @sdev: scsi device struct 179362306a36Sopenharmony_ci * 179462306a36Sopenharmony_ci **/ 179562306a36Sopenharmony_cistatic void ibmvfc_relogin(struct scsi_device *sdev) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 179862306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 179962306a36Sopenharmony_ci struct ibmvfc_target *tgt; 180062306a36Sopenharmony_ci unsigned long flags; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 180362306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 180462306a36Sopenharmony_ci if (rport == tgt->rport) { 180562306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 180662306a36Sopenharmony_ci break; 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci ibmvfc_reinit_host(vhost); 181162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci/** 181562306a36Sopenharmony_ci * ibmvfc_scsi_done - Handle responses from commands 181662306a36Sopenharmony_ci * @evt: ibmvfc event to be handled 181762306a36Sopenharmony_ci * 181862306a36Sopenharmony_ci * Used as a callback when sending scsi cmds. 181962306a36Sopenharmony_ci **/ 182062306a36Sopenharmony_cistatic void ibmvfc_scsi_done(struct ibmvfc_event *evt) 182162306a36Sopenharmony_ci{ 182262306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd; 182362306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(evt->vhost, vfc_cmd); 182462306a36Sopenharmony_ci struct scsi_cmnd *cmnd = evt->cmnd; 182562306a36Sopenharmony_ci u32 rsp_len = 0; 182662306a36Sopenharmony_ci u32 sense_len = be32_to_cpu(rsp->fcp_sense_len); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci if (cmnd) { 182962306a36Sopenharmony_ci if (be16_to_cpu(vfc_cmd->response_flags) & IBMVFC_ADAPTER_RESID_VALID) 183062306a36Sopenharmony_ci scsi_set_resid(cmnd, be32_to_cpu(vfc_cmd->adapter_resid)); 183162306a36Sopenharmony_ci else if (rsp->flags & FCP_RESID_UNDER) 183262306a36Sopenharmony_ci scsi_set_resid(cmnd, be32_to_cpu(rsp->fcp_resid)); 183362306a36Sopenharmony_ci else 183462306a36Sopenharmony_ci scsi_set_resid(cmnd, 0); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (vfc_cmd->status) { 183762306a36Sopenharmony_ci cmnd->result = ibmvfc_get_err_result(evt->vhost, vfc_cmd); 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci if (rsp->flags & FCP_RSP_LEN_VALID) 184062306a36Sopenharmony_ci rsp_len = be32_to_cpu(rsp->fcp_rsp_len); 184162306a36Sopenharmony_ci if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE) 184262306a36Sopenharmony_ci sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len; 184362306a36Sopenharmony_ci if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8) 184462306a36Sopenharmony_ci memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len); 184562306a36Sopenharmony_ci if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) && 184662306a36Sopenharmony_ci (be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED)) 184762306a36Sopenharmony_ci ibmvfc_relogin(cmnd->device); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER))) 185062306a36Sopenharmony_ci cmnd->result = (DID_ERROR << 16); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci ibmvfc_log_error(evt); 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (!cmnd->result && 185662306a36Sopenharmony_ci (scsi_bufflen(cmnd) - scsi_get_resid(cmnd) < cmnd->underflow)) 185762306a36Sopenharmony_ci cmnd->result = (DID_ERROR << 16); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci scsi_dma_unmap(cmnd); 186062306a36Sopenharmony_ci scsi_done(cmnd); 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci ibmvfc_free_event(evt); 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci/** 186762306a36Sopenharmony_ci * ibmvfc_host_chkready - Check if the host can accept commands 186862306a36Sopenharmony_ci * @vhost: struct ibmvfc host 186962306a36Sopenharmony_ci * 187062306a36Sopenharmony_ci * Returns: 187162306a36Sopenharmony_ci * 1 if host can accept command / 0 if not 187262306a36Sopenharmony_ci **/ 187362306a36Sopenharmony_cistatic inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost) 187462306a36Sopenharmony_ci{ 187562306a36Sopenharmony_ci int result = 0; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci switch (vhost->state) { 187862306a36Sopenharmony_ci case IBMVFC_LINK_DEAD: 187962306a36Sopenharmony_ci case IBMVFC_HOST_OFFLINE: 188062306a36Sopenharmony_ci result = DID_NO_CONNECT << 16; 188162306a36Sopenharmony_ci break; 188262306a36Sopenharmony_ci case IBMVFC_NO_CRQ: 188362306a36Sopenharmony_ci case IBMVFC_INITIALIZING: 188462306a36Sopenharmony_ci case IBMVFC_HALTED: 188562306a36Sopenharmony_ci case IBMVFC_LINK_DOWN: 188662306a36Sopenharmony_ci result = DID_REQUEUE << 16; 188762306a36Sopenharmony_ci break; 188862306a36Sopenharmony_ci case IBMVFC_ACTIVE: 188962306a36Sopenharmony_ci result = 0; 189062306a36Sopenharmony_ci break; 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci return result; 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct scsi_device *sdev) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 189962306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 190062306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd; 190162306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd); 190262306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd); 190362306a36Sopenharmony_ci size_t offset; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci memset(vfc_cmd, 0, sizeof(*vfc_cmd)); 190662306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) { 190762306a36Sopenharmony_ci offset = offsetof(struct ibmvfc_cmd, v2.rsp); 190862306a36Sopenharmony_ci vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name); 190962306a36Sopenharmony_ci } else 191062306a36Sopenharmony_ci offset = offsetof(struct ibmvfc_cmd, v1.rsp); 191162306a36Sopenharmony_ci vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset); 191262306a36Sopenharmony_ci vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp)); 191362306a36Sopenharmony_ci vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE); 191462306a36Sopenharmony_ci vfc_cmd->payload_len = cpu_to_be32(sizeof(*iu)); 191562306a36Sopenharmony_ci vfc_cmd->resp_len = cpu_to_be32(sizeof(*rsp)); 191662306a36Sopenharmony_ci vfc_cmd->cancel_key = cpu_to_be32((unsigned long)sdev->hostdata); 191762306a36Sopenharmony_ci vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id); 191862306a36Sopenharmony_ci int_to_scsilun(sdev->lun, &iu->lun); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci return vfc_cmd; 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci/** 192462306a36Sopenharmony_ci * ibmvfc_queuecommand - The queuecommand function of the scsi template 192562306a36Sopenharmony_ci * @shost: scsi host struct 192662306a36Sopenharmony_ci * @cmnd: struct scsi_cmnd to be executed 192762306a36Sopenharmony_ci * 192862306a36Sopenharmony_ci * Returns: 192962306a36Sopenharmony_ci * 0 on success / other on failure 193062306a36Sopenharmony_ci **/ 193162306a36Sopenharmony_cistatic int ibmvfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 193462306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); 193562306a36Sopenharmony_ci struct ibmvfc_cmd *vfc_cmd; 193662306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu; 193762306a36Sopenharmony_ci struct ibmvfc_event *evt; 193862306a36Sopenharmony_ci u32 tag_and_hwq = blk_mq_unique_tag(scsi_cmd_to_rq(cmnd)); 193962306a36Sopenharmony_ci u16 hwq = blk_mq_unique_tag_to_hwq(tag_and_hwq); 194062306a36Sopenharmony_ci u16 scsi_channel; 194162306a36Sopenharmony_ci int rc; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci if (unlikely((rc = fc_remote_port_chkready(rport))) || 194462306a36Sopenharmony_ci unlikely((rc = ibmvfc_host_chkready(vhost)))) { 194562306a36Sopenharmony_ci cmnd->result = rc; 194662306a36Sopenharmony_ci scsi_done(cmnd); 194762306a36Sopenharmony_ci return 0; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci cmnd->result = (DID_OK << 16); 195162306a36Sopenharmony_ci if (vhost->using_channels) { 195262306a36Sopenharmony_ci scsi_channel = hwq % vhost->scsi_scrqs.active_queues; 195362306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->scsi_scrqs.scrqs[scsi_channel]); 195462306a36Sopenharmony_ci if (!evt) 195562306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci evt->hwq = hwq % vhost->scsi_scrqs.active_queues; 195862306a36Sopenharmony_ci } else { 195962306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 196062306a36Sopenharmony_ci if (!evt) 196162306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_scsi_done, IBMVFC_CMD_FORMAT); 196562306a36Sopenharmony_ci evt->cmnd = cmnd; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci vfc_cmd = ibmvfc_init_vfc_cmd(evt, cmnd->device); 196862306a36Sopenharmony_ci iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci iu->xfer_len = cpu_to_be32(scsi_bufflen(cmnd)); 197162306a36Sopenharmony_ci memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci if (cmnd->flags & SCMD_TAGGED) { 197462306a36Sopenharmony_ci vfc_cmd->task_tag = cpu_to_be64(scsi_cmd_to_rq(cmnd)->tag); 197562306a36Sopenharmony_ci iu->pri_task_attr = IBMVFC_SIMPLE_TASK; 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci vfc_cmd->correlation = cpu_to_be64((u64)evt); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev)))) 198162306a36Sopenharmony_ci return ibmvfc_send_event(evt, vhost, 0); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci ibmvfc_free_event(evt); 198462306a36Sopenharmony_ci if (rc == -ENOMEM) 198562306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) 198862306a36Sopenharmony_ci scmd_printk(KERN_ERR, cmnd, 198962306a36Sopenharmony_ci "Failed to map DMA buffer for command. rc=%d\n", rc); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci cmnd->result = DID_ERROR << 16; 199262306a36Sopenharmony_ci scsi_done(cmnd); 199362306a36Sopenharmony_ci return 0; 199462306a36Sopenharmony_ci} 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci/** 199762306a36Sopenharmony_ci * ibmvfc_sync_completion - Signal that a synchronous command has completed 199862306a36Sopenharmony_ci * @evt: ibmvfc event struct 199962306a36Sopenharmony_ci * 200062306a36Sopenharmony_ci **/ 200162306a36Sopenharmony_cistatic void ibmvfc_sync_completion(struct ibmvfc_event *evt) 200262306a36Sopenharmony_ci{ 200362306a36Sopenharmony_ci /* copy the response back */ 200462306a36Sopenharmony_ci if (evt->sync_iu) 200562306a36Sopenharmony_ci *evt->sync_iu = *evt->xfer_iu; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci complete(&evt->comp); 200862306a36Sopenharmony_ci} 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci/** 201162306a36Sopenharmony_ci * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands 201262306a36Sopenharmony_ci * @evt: struct ibmvfc_event 201362306a36Sopenharmony_ci * 201462306a36Sopenharmony_ci **/ 201562306a36Sopenharmony_cistatic void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt) 201662306a36Sopenharmony_ci{ 201762306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci ibmvfc_free_event(evt); 202062306a36Sopenharmony_ci vhost->aborting_passthru = 0; 202162306a36Sopenharmony_ci dev_info(vhost->dev, "Passthru command cancelled\n"); 202262306a36Sopenharmony_ci} 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci/** 202562306a36Sopenharmony_ci * ibmvfc_bsg_timeout - Handle a BSG timeout 202662306a36Sopenharmony_ci * @job: struct bsg_job that timed out 202762306a36Sopenharmony_ci * 202862306a36Sopenharmony_ci * Returns: 202962306a36Sopenharmony_ci * 0 on success / other on failure 203062306a36Sopenharmony_ci **/ 203162306a36Sopenharmony_cistatic int ibmvfc_bsg_timeout(struct bsg_job *job) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(fc_bsg_to_shost(job)); 203462306a36Sopenharmony_ci unsigned long port_id = (unsigned long)job->dd_data; 203562306a36Sopenharmony_ci struct ibmvfc_event *evt; 203662306a36Sopenharmony_ci struct ibmvfc_tmf *tmf; 203762306a36Sopenharmony_ci unsigned long flags; 203862306a36Sopenharmony_ci int rc; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci ENTER; 204162306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 204262306a36Sopenharmony_ci if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) { 204362306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 204462306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 204562306a36Sopenharmony_ci return 0; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci vhost->aborting_passthru = 1; 204962306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 205062306a36Sopenharmony_ci if (!evt) { 205162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 205262306a36Sopenharmony_ci return -ENOMEM; 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci tmf = &evt->iu.tmf; 205862306a36Sopenharmony_ci memset(tmf, 0, sizeof(*tmf)); 205962306a36Sopenharmony_ci tmf->common.version = cpu_to_be32(1); 206062306a36Sopenharmony_ci tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD); 206162306a36Sopenharmony_ci tmf->common.length = cpu_to_be16(sizeof(*tmf)); 206262306a36Sopenharmony_ci tmf->scsi_id = cpu_to_be64(port_id); 206362306a36Sopenharmony_ci tmf->cancel_key = cpu_to_be32(IBMVFC_PASSTHRU_CANCEL_KEY); 206462306a36Sopenharmony_ci tmf->my_cancel_key = cpu_to_be32(IBMVFC_INTERNAL_CANCEL_KEY); 206562306a36Sopenharmony_ci rc = ibmvfc_send_event(evt, vhost, default_timeout); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (rc != 0) { 206862306a36Sopenharmony_ci vhost->aborting_passthru = 0; 206962306a36Sopenharmony_ci dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc); 207062306a36Sopenharmony_ci rc = -EIO; 207162306a36Sopenharmony_ci } else 207262306a36Sopenharmony_ci dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n", 207362306a36Sopenharmony_ci port_id); 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci LEAVE; 207862306a36Sopenharmony_ci return rc; 207962306a36Sopenharmony_ci} 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci/** 208262306a36Sopenharmony_ci * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command 208362306a36Sopenharmony_ci * @vhost: struct ibmvfc_host to send command 208462306a36Sopenharmony_ci * @port_id: port ID to send command 208562306a36Sopenharmony_ci * 208662306a36Sopenharmony_ci * Returns: 208762306a36Sopenharmony_ci * 0 on success / other on failure 208862306a36Sopenharmony_ci **/ 208962306a36Sopenharmony_cistatic int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id) 209062306a36Sopenharmony_ci{ 209162306a36Sopenharmony_ci struct ibmvfc_port_login *plogi; 209262306a36Sopenharmony_ci struct ibmvfc_target *tgt; 209362306a36Sopenharmony_ci struct ibmvfc_event *evt; 209462306a36Sopenharmony_ci union ibmvfc_iu rsp_iu; 209562306a36Sopenharmony_ci unsigned long flags; 209662306a36Sopenharmony_ci int rc = 0, issue_login = 1; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci ENTER; 209962306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 210062306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 210162306a36Sopenharmony_ci if (tgt->scsi_id == port_id) { 210262306a36Sopenharmony_ci issue_login = 0; 210362306a36Sopenharmony_ci break; 210462306a36Sopenharmony_ci } 210562306a36Sopenharmony_ci } 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci if (!issue_login) 210862306a36Sopenharmony_ci goto unlock_out; 210962306a36Sopenharmony_ci if (unlikely((rc = ibmvfc_host_chkready(vhost)))) 211062306a36Sopenharmony_ci goto unlock_out; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 211362306a36Sopenharmony_ci if (!evt) { 211462306a36Sopenharmony_ci rc = -ENOMEM; 211562306a36Sopenharmony_ci goto unlock_out; 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); 211862306a36Sopenharmony_ci plogi = &evt->iu.plogi; 211962306a36Sopenharmony_ci memset(plogi, 0, sizeof(*plogi)); 212062306a36Sopenharmony_ci plogi->common.version = cpu_to_be32(1); 212162306a36Sopenharmony_ci plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN); 212262306a36Sopenharmony_ci plogi->common.length = cpu_to_be16(sizeof(*plogi)); 212362306a36Sopenharmony_ci plogi->scsi_id = cpu_to_be64(port_id); 212462306a36Sopenharmony_ci evt->sync_iu = &rsp_iu; 212562306a36Sopenharmony_ci init_completion(&evt->comp); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci rc = ibmvfc_send_event(evt, vhost, default_timeout); 212862306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci if (rc) 213162306a36Sopenharmony_ci return -EIO; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci wait_for_completion(&evt->comp); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci if (rsp_iu.plogi.common.status) 213662306a36Sopenharmony_ci rc = -EIO; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 213962306a36Sopenharmony_ci ibmvfc_free_event(evt); 214062306a36Sopenharmony_ciunlock_out: 214162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 214262306a36Sopenharmony_ci LEAVE; 214362306a36Sopenharmony_ci return rc; 214462306a36Sopenharmony_ci} 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci/** 214762306a36Sopenharmony_ci * ibmvfc_bsg_request - Handle a BSG request 214862306a36Sopenharmony_ci * @job: struct bsg_job to be executed 214962306a36Sopenharmony_ci * 215062306a36Sopenharmony_ci * Returns: 215162306a36Sopenharmony_ci * 0 on success / other on failure 215262306a36Sopenharmony_ci **/ 215362306a36Sopenharmony_cistatic int ibmvfc_bsg_request(struct bsg_job *job) 215462306a36Sopenharmony_ci{ 215562306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(fc_bsg_to_shost(job)); 215662306a36Sopenharmony_ci struct fc_rport *rport = fc_bsg_to_rport(job); 215762306a36Sopenharmony_ci struct ibmvfc_passthru_mad *mad; 215862306a36Sopenharmony_ci struct ibmvfc_event *evt; 215962306a36Sopenharmony_ci union ibmvfc_iu rsp_iu; 216062306a36Sopenharmony_ci unsigned long flags, port_id = -1; 216162306a36Sopenharmony_ci struct fc_bsg_request *bsg_request = job->request; 216262306a36Sopenharmony_ci struct fc_bsg_reply *bsg_reply = job->reply; 216362306a36Sopenharmony_ci unsigned int code = bsg_request->msgcode; 216462306a36Sopenharmony_ci int rc = 0, req_seg, rsp_seg, issue_login = 0; 216562306a36Sopenharmony_ci u32 fc_flags, rsp_len; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci ENTER; 216862306a36Sopenharmony_ci bsg_reply->reply_payload_rcv_len = 0; 216962306a36Sopenharmony_ci if (rport) 217062306a36Sopenharmony_ci port_id = rport->port_id; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci switch (code) { 217362306a36Sopenharmony_ci case FC_BSG_HST_ELS_NOLOGIN: 217462306a36Sopenharmony_ci port_id = (bsg_request->rqst_data.h_els.port_id[0] << 16) | 217562306a36Sopenharmony_ci (bsg_request->rqst_data.h_els.port_id[1] << 8) | 217662306a36Sopenharmony_ci bsg_request->rqst_data.h_els.port_id[2]; 217762306a36Sopenharmony_ci fallthrough; 217862306a36Sopenharmony_ci case FC_BSG_RPT_ELS: 217962306a36Sopenharmony_ci fc_flags = IBMVFC_FC_ELS; 218062306a36Sopenharmony_ci break; 218162306a36Sopenharmony_ci case FC_BSG_HST_CT: 218262306a36Sopenharmony_ci issue_login = 1; 218362306a36Sopenharmony_ci port_id = (bsg_request->rqst_data.h_ct.port_id[0] << 16) | 218462306a36Sopenharmony_ci (bsg_request->rqst_data.h_ct.port_id[1] << 8) | 218562306a36Sopenharmony_ci bsg_request->rqst_data.h_ct.port_id[2]; 218662306a36Sopenharmony_ci fallthrough; 218762306a36Sopenharmony_ci case FC_BSG_RPT_CT: 218862306a36Sopenharmony_ci fc_flags = IBMVFC_FC_CT_IU; 218962306a36Sopenharmony_ci break; 219062306a36Sopenharmony_ci default: 219162306a36Sopenharmony_ci return -ENOTSUPP; 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci if (port_id == -1) 219562306a36Sopenharmony_ci return -EINVAL; 219662306a36Sopenharmony_ci if (!mutex_trylock(&vhost->passthru_mutex)) 219762306a36Sopenharmony_ci return -EBUSY; 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci job->dd_data = (void *)port_id; 220062306a36Sopenharmony_ci req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list, 220162306a36Sopenharmony_ci job->request_payload.sg_cnt, DMA_TO_DEVICE); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci if (!req_seg) { 220462306a36Sopenharmony_ci mutex_unlock(&vhost->passthru_mutex); 220562306a36Sopenharmony_ci return -ENOMEM; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list, 220962306a36Sopenharmony_ci job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci if (!rsp_seg) { 221262306a36Sopenharmony_ci dma_unmap_sg(vhost->dev, job->request_payload.sg_list, 221362306a36Sopenharmony_ci job->request_payload.sg_cnt, DMA_TO_DEVICE); 221462306a36Sopenharmony_ci mutex_unlock(&vhost->passthru_mutex); 221562306a36Sopenharmony_ci return -ENOMEM; 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci if (req_seg > 1 || rsp_seg > 1) { 221962306a36Sopenharmony_ci rc = -EINVAL; 222062306a36Sopenharmony_ci goto out; 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci if (issue_login) 222462306a36Sopenharmony_ci rc = ibmvfc_bsg_plogi(vhost, port_id); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) || 222962306a36Sopenharmony_ci unlikely((rc = ibmvfc_host_chkready(vhost)))) { 223062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 223162306a36Sopenharmony_ci goto out; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 223562306a36Sopenharmony_ci if (!evt) { 223662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 223762306a36Sopenharmony_ci rc = -ENOMEM; 223862306a36Sopenharmony_ci goto out; 223962306a36Sopenharmony_ci } 224062306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); 224162306a36Sopenharmony_ci mad = &evt->iu.passthru; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 224462306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 224562306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_PASSTHRU); 224662306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu)); 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci mad->cmd_ioba.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + 224962306a36Sopenharmony_ci offsetof(struct ibmvfc_passthru_mad, iu)); 225062306a36Sopenharmony_ci mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu)); 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci mad->iu.cmd_len = cpu_to_be32(job->request_payload.payload_len); 225362306a36Sopenharmony_ci mad->iu.rsp_len = cpu_to_be32(job->reply_payload.payload_len); 225462306a36Sopenharmony_ci mad->iu.flags = cpu_to_be32(fc_flags); 225562306a36Sopenharmony_ci mad->iu.cancel_key = cpu_to_be32(IBMVFC_PASSTHRU_CANCEL_KEY); 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci mad->iu.cmd.va = cpu_to_be64(sg_dma_address(job->request_payload.sg_list)); 225862306a36Sopenharmony_ci mad->iu.cmd.len = cpu_to_be32(sg_dma_len(job->request_payload.sg_list)); 225962306a36Sopenharmony_ci mad->iu.rsp.va = cpu_to_be64(sg_dma_address(job->reply_payload.sg_list)); 226062306a36Sopenharmony_ci mad->iu.rsp.len = cpu_to_be32(sg_dma_len(job->reply_payload.sg_list)); 226162306a36Sopenharmony_ci mad->iu.scsi_id = cpu_to_be64(port_id); 226262306a36Sopenharmony_ci mad->iu.tag = cpu_to_be64((u64)evt); 226362306a36Sopenharmony_ci rsp_len = be32_to_cpu(mad->iu.rsp.len); 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci evt->sync_iu = &rsp_iu; 226662306a36Sopenharmony_ci init_completion(&evt->comp); 226762306a36Sopenharmony_ci rc = ibmvfc_send_event(evt, vhost, 0); 226862306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci if (rc) { 227162306a36Sopenharmony_ci rc = -EIO; 227262306a36Sopenharmony_ci goto out; 227362306a36Sopenharmony_ci } 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci wait_for_completion(&evt->comp); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if (rsp_iu.passthru.common.status) 227862306a36Sopenharmony_ci rc = -EIO; 227962306a36Sopenharmony_ci else 228062306a36Sopenharmony_ci bsg_reply->reply_payload_rcv_len = rsp_len; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 228362306a36Sopenharmony_ci ibmvfc_free_event(evt); 228462306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 228562306a36Sopenharmony_ci bsg_reply->result = rc; 228662306a36Sopenharmony_ci bsg_job_done(job, bsg_reply->result, 228762306a36Sopenharmony_ci bsg_reply->reply_payload_rcv_len); 228862306a36Sopenharmony_ci rc = 0; 228962306a36Sopenharmony_ciout: 229062306a36Sopenharmony_ci dma_unmap_sg(vhost->dev, job->request_payload.sg_list, 229162306a36Sopenharmony_ci job->request_payload.sg_cnt, DMA_TO_DEVICE); 229262306a36Sopenharmony_ci dma_unmap_sg(vhost->dev, job->reply_payload.sg_list, 229362306a36Sopenharmony_ci job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 229462306a36Sopenharmony_ci mutex_unlock(&vhost->passthru_mutex); 229562306a36Sopenharmony_ci LEAVE; 229662306a36Sopenharmony_ci return rc; 229762306a36Sopenharmony_ci} 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci/** 230062306a36Sopenharmony_ci * ibmvfc_reset_device - Reset the device with the specified reset type 230162306a36Sopenharmony_ci * @sdev: scsi device to reset 230262306a36Sopenharmony_ci * @type: reset type 230362306a36Sopenharmony_ci * @desc: reset type description for log messages 230462306a36Sopenharmony_ci * 230562306a36Sopenharmony_ci * Returns: 230662306a36Sopenharmony_ci * 0 on success / other on failure 230762306a36Sopenharmony_ci **/ 230862306a36Sopenharmony_cistatic int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc) 230962306a36Sopenharmony_ci{ 231062306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 231162306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 231262306a36Sopenharmony_ci struct ibmvfc_cmd *tmf; 231362306a36Sopenharmony_ci struct ibmvfc_event *evt = NULL; 231462306a36Sopenharmony_ci union ibmvfc_iu rsp_iu; 231562306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu; 231662306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *fc_rsp = ibmvfc_get_fcp_rsp(vhost, &rsp_iu.cmd); 231762306a36Sopenharmony_ci int rsp_rc = -EBUSY; 231862306a36Sopenharmony_ci unsigned long flags; 231962306a36Sopenharmony_ci int rsp_code = 0; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 232262306a36Sopenharmony_ci if (vhost->state == IBMVFC_ACTIVE) { 232362306a36Sopenharmony_ci if (vhost->using_channels) 232462306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->scsi_scrqs.scrqs[0]); 232562306a36Sopenharmony_ci else 232662306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci if (!evt) { 232962306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 233062306a36Sopenharmony_ci return -ENOMEM; 233162306a36Sopenharmony_ci } 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT); 233462306a36Sopenharmony_ci tmf = ibmvfc_init_vfc_cmd(evt, sdev); 233562306a36Sopenharmony_ci iu = ibmvfc_get_fcp_iu(vhost, tmf); 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF)); 233862306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) 233962306a36Sopenharmony_ci tmf->target_wwpn = cpu_to_be64(rport->port_name); 234062306a36Sopenharmony_ci iu->tmf_flags = type; 234162306a36Sopenharmony_ci evt->sync_iu = &rsp_iu; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci init_completion(&evt->comp); 234462306a36Sopenharmony_ci rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout); 234562306a36Sopenharmony_ci } 234662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci if (rsp_rc != 0) { 234962306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Failed to send %s reset event. rc=%d\n", 235062306a36Sopenharmony_ci desc, rsp_rc); 235162306a36Sopenharmony_ci return -EIO; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc); 235562306a36Sopenharmony_ci wait_for_completion(&evt->comp); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci if (rsp_iu.cmd.status) 235862306a36Sopenharmony_ci rsp_code = ibmvfc_get_err_result(vhost, &rsp_iu.cmd); 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci if (rsp_code) { 236162306a36Sopenharmony_ci if (fc_rsp->flags & FCP_RSP_LEN_VALID) 236262306a36Sopenharmony_ci rsp_code = fc_rsp->data.info.rsp_code; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) " 236562306a36Sopenharmony_ci "flags: %x fcp_rsp: %x, scsi_status: %x\n", desc, 236662306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)), 236762306a36Sopenharmony_ci be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code, 236862306a36Sopenharmony_ci fc_rsp->scsi_status); 236962306a36Sopenharmony_ci rsp_rc = -EIO; 237062306a36Sopenharmony_ci } else 237162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s reset successful\n", desc); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 237462306a36Sopenharmony_ci ibmvfc_free_event(evt); 237562306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 237662306a36Sopenharmony_ci return rsp_rc; 237762306a36Sopenharmony_ci} 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci/** 238062306a36Sopenharmony_ci * ibmvfc_match_rport - Match function for specified remote port 238162306a36Sopenharmony_ci * @evt: ibmvfc event struct 238262306a36Sopenharmony_ci * @rport: device to match 238362306a36Sopenharmony_ci * 238462306a36Sopenharmony_ci * Returns: 238562306a36Sopenharmony_ci * 1 if event matches rport / 0 if event does not match rport 238662306a36Sopenharmony_ci **/ 238762306a36Sopenharmony_cistatic int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ci struct fc_rport *cmd_rport; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci if (evt->cmnd) { 239262306a36Sopenharmony_ci cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device)); 239362306a36Sopenharmony_ci if (cmd_rport == rport) 239462306a36Sopenharmony_ci return 1; 239562306a36Sopenharmony_ci } 239662306a36Sopenharmony_ci return 0; 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci/** 240062306a36Sopenharmony_ci * ibmvfc_match_target - Match function for specified target 240162306a36Sopenharmony_ci * @evt: ibmvfc event struct 240262306a36Sopenharmony_ci * @device: device to match (starget) 240362306a36Sopenharmony_ci * 240462306a36Sopenharmony_ci * Returns: 240562306a36Sopenharmony_ci * 1 if event matches starget / 0 if event does not match starget 240662306a36Sopenharmony_ci **/ 240762306a36Sopenharmony_cistatic int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) 240862306a36Sopenharmony_ci{ 240962306a36Sopenharmony_ci if (evt->cmnd && scsi_target(evt->cmnd->device) == device) 241062306a36Sopenharmony_ci return 1; 241162306a36Sopenharmony_ci return 0; 241262306a36Sopenharmony_ci} 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci/** 241562306a36Sopenharmony_ci * ibmvfc_match_lun - Match function for specified LUN 241662306a36Sopenharmony_ci * @evt: ibmvfc event struct 241762306a36Sopenharmony_ci * @device: device to match (sdev) 241862306a36Sopenharmony_ci * 241962306a36Sopenharmony_ci * Returns: 242062306a36Sopenharmony_ci * 1 if event matches sdev / 0 if event does not match sdev 242162306a36Sopenharmony_ci **/ 242262306a36Sopenharmony_cistatic int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device) 242362306a36Sopenharmony_ci{ 242462306a36Sopenharmony_ci if (evt->cmnd && evt->cmnd->device == device) 242562306a36Sopenharmony_ci return 1; 242662306a36Sopenharmony_ci return 0; 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci/** 243062306a36Sopenharmony_ci * ibmvfc_event_is_free - Check if event is free or not 243162306a36Sopenharmony_ci * @evt: ibmvfc event struct 243262306a36Sopenharmony_ci * 243362306a36Sopenharmony_ci * Returns: 243462306a36Sopenharmony_ci * true / false 243562306a36Sopenharmony_ci **/ 243662306a36Sopenharmony_cistatic bool ibmvfc_event_is_free(struct ibmvfc_event *evt) 243762306a36Sopenharmony_ci{ 243862306a36Sopenharmony_ci struct ibmvfc_event *loop_evt; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci list_for_each_entry(loop_evt, &evt->queue->free, queue_list) 244162306a36Sopenharmony_ci if (loop_evt == evt) 244262306a36Sopenharmony_ci return true; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci return false; 244562306a36Sopenharmony_ci} 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci/** 244862306a36Sopenharmony_ci * ibmvfc_wait_for_ops - Wait for ops to complete 244962306a36Sopenharmony_ci * @vhost: ibmvfc host struct 245062306a36Sopenharmony_ci * @device: device to match (starget or sdev) 245162306a36Sopenharmony_ci * @match: match function 245262306a36Sopenharmony_ci * 245362306a36Sopenharmony_ci * Returns: 245462306a36Sopenharmony_ci * SUCCESS / FAILED 245562306a36Sopenharmony_ci **/ 245662306a36Sopenharmony_cistatic int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, 245762306a36Sopenharmony_ci int (*match) (struct ibmvfc_event *, void *)) 245862306a36Sopenharmony_ci{ 245962306a36Sopenharmony_ci struct ibmvfc_event *evt; 246062306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(comp); 246162306a36Sopenharmony_ci int wait, i, q_index, q_size; 246262306a36Sopenharmony_ci unsigned long flags; 246362306a36Sopenharmony_ci signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ; 246462306a36Sopenharmony_ci struct ibmvfc_queue *queues; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci ENTER; 246762306a36Sopenharmony_ci if (vhost->mq_enabled && vhost->using_channels) { 246862306a36Sopenharmony_ci queues = vhost->scsi_scrqs.scrqs; 246962306a36Sopenharmony_ci q_size = vhost->scsi_scrqs.active_queues; 247062306a36Sopenharmony_ci } else { 247162306a36Sopenharmony_ci queues = &vhost->crq; 247262306a36Sopenharmony_ci q_size = 1; 247362306a36Sopenharmony_ci } 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci do { 247662306a36Sopenharmony_ci wait = 0; 247762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 247862306a36Sopenharmony_ci for (q_index = 0; q_index < q_size; q_index++) { 247962306a36Sopenharmony_ci spin_lock(&queues[q_index].l_lock); 248062306a36Sopenharmony_ci for (i = 0; i < queues[q_index].evt_pool.size; i++) { 248162306a36Sopenharmony_ci evt = &queues[q_index].evt_pool.events[i]; 248262306a36Sopenharmony_ci if (!ibmvfc_event_is_free(evt)) { 248362306a36Sopenharmony_ci if (match(evt, device)) { 248462306a36Sopenharmony_ci evt->eh_comp = ∁ 248562306a36Sopenharmony_ci wait++; 248662306a36Sopenharmony_ci } 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci } 248962306a36Sopenharmony_ci spin_unlock(&queues[q_index].l_lock); 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci if (wait) { 249462306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&comp, timeout); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci if (!timeout) { 249762306a36Sopenharmony_ci wait = 0; 249862306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 249962306a36Sopenharmony_ci for (q_index = 0; q_index < q_size; q_index++) { 250062306a36Sopenharmony_ci spin_lock(&queues[q_index].l_lock); 250162306a36Sopenharmony_ci for (i = 0; i < queues[q_index].evt_pool.size; i++) { 250262306a36Sopenharmony_ci evt = &queues[q_index].evt_pool.events[i]; 250362306a36Sopenharmony_ci if (!ibmvfc_event_is_free(evt)) { 250462306a36Sopenharmony_ci if (match(evt, device)) { 250562306a36Sopenharmony_ci evt->eh_comp = NULL; 250662306a36Sopenharmony_ci wait++; 250762306a36Sopenharmony_ci } 250862306a36Sopenharmony_ci } 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci spin_unlock(&queues[q_index].l_lock); 251162306a36Sopenharmony_ci } 251262306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 251362306a36Sopenharmony_ci if (wait) 251462306a36Sopenharmony_ci dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); 251562306a36Sopenharmony_ci LEAVE; 251662306a36Sopenharmony_ci return wait ? FAILED : SUCCESS; 251762306a36Sopenharmony_ci } 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci } while (wait); 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci LEAVE; 252262306a36Sopenharmony_ci return SUCCESS; 252362306a36Sopenharmony_ci} 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_cistatic struct ibmvfc_event *ibmvfc_init_tmf(struct ibmvfc_queue *queue, 252662306a36Sopenharmony_ci struct scsi_device *sdev, 252762306a36Sopenharmony_ci int type) 252862306a36Sopenharmony_ci{ 252962306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 253062306a36Sopenharmony_ci struct scsi_target *starget = scsi_target(sdev); 253162306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(starget); 253262306a36Sopenharmony_ci struct ibmvfc_event *evt; 253362306a36Sopenharmony_ci struct ibmvfc_tmf *tmf; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci evt = ibmvfc_get_event(queue); 253662306a36Sopenharmony_ci if (!evt) 253762306a36Sopenharmony_ci return NULL; 253862306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_ci tmf = &evt->iu.tmf; 254162306a36Sopenharmony_ci memset(tmf, 0, sizeof(*tmf)); 254262306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) { 254362306a36Sopenharmony_ci tmf->common.version = cpu_to_be32(2); 254462306a36Sopenharmony_ci tmf->target_wwpn = cpu_to_be64(rport->port_name); 254562306a36Sopenharmony_ci } else { 254662306a36Sopenharmony_ci tmf->common.version = cpu_to_be32(1); 254762306a36Sopenharmony_ci } 254862306a36Sopenharmony_ci tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD); 254962306a36Sopenharmony_ci tmf->common.length = cpu_to_be16(sizeof(*tmf)); 255062306a36Sopenharmony_ci tmf->scsi_id = cpu_to_be64(rport->port_id); 255162306a36Sopenharmony_ci int_to_scsilun(sdev->lun, &tmf->lun); 255262306a36Sopenharmony_ci if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS)) 255362306a36Sopenharmony_ci type &= ~IBMVFC_TMF_SUPPRESS_ABTS; 255462306a36Sopenharmony_ci if (vhost->state == IBMVFC_ACTIVE) 255562306a36Sopenharmony_ci tmf->flags = cpu_to_be32((type | IBMVFC_TMF_LUA_VALID)); 255662306a36Sopenharmony_ci else 255762306a36Sopenharmony_ci tmf->flags = cpu_to_be32(((type & IBMVFC_TMF_SUPPRESS_ABTS) | IBMVFC_TMF_LUA_VALID)); 255862306a36Sopenharmony_ci tmf->cancel_key = cpu_to_be32((unsigned long)sdev->hostdata); 255962306a36Sopenharmony_ci tmf->my_cancel_key = cpu_to_be32((unsigned long)starget->hostdata); 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci init_completion(&evt->comp); 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci return evt; 256462306a36Sopenharmony_ci} 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_cistatic int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type) 256762306a36Sopenharmony_ci{ 256862306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 256962306a36Sopenharmony_ci struct ibmvfc_event *evt, *found_evt, *temp; 257062306a36Sopenharmony_ci struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs; 257162306a36Sopenharmony_ci unsigned long flags; 257262306a36Sopenharmony_ci int num_hwq, i; 257362306a36Sopenharmony_ci int fail = 0; 257462306a36Sopenharmony_ci LIST_HEAD(cancelq); 257562306a36Sopenharmony_ci u16 status; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci ENTER; 257862306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 257962306a36Sopenharmony_ci num_hwq = vhost->scsi_scrqs.active_queues; 258062306a36Sopenharmony_ci for (i = 0; i < num_hwq; i++) { 258162306a36Sopenharmony_ci spin_lock(queues[i].q_lock); 258262306a36Sopenharmony_ci spin_lock(&queues[i].l_lock); 258362306a36Sopenharmony_ci found_evt = NULL; 258462306a36Sopenharmony_ci list_for_each_entry(evt, &queues[i].sent, queue_list) { 258562306a36Sopenharmony_ci if (evt->cmnd && evt->cmnd->device == sdev) { 258662306a36Sopenharmony_ci found_evt = evt; 258762306a36Sopenharmony_ci break; 258862306a36Sopenharmony_ci } 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci spin_unlock(&queues[i].l_lock); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci if (found_evt && vhost->logged_in) { 259362306a36Sopenharmony_ci evt = ibmvfc_init_tmf(&queues[i], sdev, type); 259462306a36Sopenharmony_ci if (!evt) { 259562306a36Sopenharmony_ci spin_unlock(queues[i].q_lock); 259662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 259762306a36Sopenharmony_ci return -ENOMEM; 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci evt->sync_iu = &queues[i].cancel_rsp; 260062306a36Sopenharmony_ci ibmvfc_send_event(evt, vhost, default_timeout); 260162306a36Sopenharmony_ci list_add_tail(&evt->cancel, &cancelq); 260262306a36Sopenharmony_ci } 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci spin_unlock(queues[i].q_lock); 260562306a36Sopenharmony_ci } 260662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci if (list_empty(&cancelq)) { 260962306a36Sopenharmony_ci if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) 261062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "No events found to cancel\n"); 261162306a36Sopenharmony_ci return 0; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n"); 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci list_for_each_entry_safe(evt, temp, &cancelq, cancel) { 261762306a36Sopenharmony_ci wait_for_completion(&evt->comp); 261862306a36Sopenharmony_ci status = be16_to_cpu(evt->queue->cancel_rsp.mad_common.status); 261962306a36Sopenharmony_ci list_del(&evt->cancel); 262062306a36Sopenharmony_ci ibmvfc_free_event(evt); 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci if (status != IBMVFC_MAD_SUCCESS) { 262362306a36Sopenharmony_ci sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status); 262462306a36Sopenharmony_ci switch (status) { 262562306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 262662306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 262762306a36Sopenharmony_ci /* Host adapter most likely going through reset, return success to 262862306a36Sopenharmony_ci * the caller will wait for the command being cancelled to get returned 262962306a36Sopenharmony_ci */ 263062306a36Sopenharmony_ci break; 263162306a36Sopenharmony_ci default: 263262306a36Sopenharmony_ci fail = 1; 263362306a36Sopenharmony_ci break; 263462306a36Sopenharmony_ci } 263562306a36Sopenharmony_ci } 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci if (fail) 263962306a36Sopenharmony_ci return -EIO; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n"); 264262306a36Sopenharmony_ci LEAVE; 264362306a36Sopenharmony_ci return 0; 264462306a36Sopenharmony_ci} 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_cistatic int ibmvfc_cancel_all_sq(struct scsi_device *sdev, int type) 264762306a36Sopenharmony_ci{ 264862306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 264962306a36Sopenharmony_ci struct ibmvfc_event *evt, *found_evt; 265062306a36Sopenharmony_ci union ibmvfc_iu rsp; 265162306a36Sopenharmony_ci int rsp_rc = -EBUSY; 265262306a36Sopenharmony_ci unsigned long flags; 265362306a36Sopenharmony_ci u16 status; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci ENTER; 265662306a36Sopenharmony_ci found_evt = NULL; 265762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 265862306a36Sopenharmony_ci spin_lock(&vhost->crq.l_lock); 265962306a36Sopenharmony_ci list_for_each_entry(evt, &vhost->crq.sent, queue_list) { 266062306a36Sopenharmony_ci if (evt->cmnd && evt->cmnd->device == sdev) { 266162306a36Sopenharmony_ci found_evt = evt; 266262306a36Sopenharmony_ci break; 266362306a36Sopenharmony_ci } 266462306a36Sopenharmony_ci } 266562306a36Sopenharmony_ci spin_unlock(&vhost->crq.l_lock); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci if (!found_evt) { 266862306a36Sopenharmony_ci if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) 266962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "No events found to cancel\n"); 267062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 267162306a36Sopenharmony_ci return 0; 267262306a36Sopenharmony_ci } 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci if (vhost->logged_in) { 267562306a36Sopenharmony_ci evt = ibmvfc_init_tmf(&vhost->crq, sdev, type); 267662306a36Sopenharmony_ci evt->sync_iu = &rsp; 267762306a36Sopenharmony_ci rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout); 267862306a36Sopenharmony_ci } 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci if (rsp_rc != 0) { 268362306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc); 268462306a36Sopenharmony_ci /* If failure is received, the host adapter is most likely going 268562306a36Sopenharmony_ci through reset, return success so the caller will wait for the command 268662306a36Sopenharmony_ci being cancelled to get returned */ 268762306a36Sopenharmony_ci return 0; 268862306a36Sopenharmony_ci } 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n"); 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci wait_for_completion(&evt->comp); 269362306a36Sopenharmony_ci status = be16_to_cpu(rsp.mad_common.status); 269462306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 269562306a36Sopenharmony_ci ibmvfc_free_event(evt); 269662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci if (status != IBMVFC_MAD_SUCCESS) { 269962306a36Sopenharmony_ci sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status); 270062306a36Sopenharmony_ci switch (status) { 270162306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 270262306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 270362306a36Sopenharmony_ci /* Host adapter most likely going through reset, return success to 270462306a36Sopenharmony_ci the caller will wait for the command being cancelled to get returned */ 270562306a36Sopenharmony_ci return 0; 270662306a36Sopenharmony_ci default: 270762306a36Sopenharmony_ci return -EIO; 270862306a36Sopenharmony_ci }; 270962306a36Sopenharmony_ci } 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n"); 271262306a36Sopenharmony_ci return 0; 271362306a36Sopenharmony_ci} 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci/** 271662306a36Sopenharmony_ci * ibmvfc_cancel_all - Cancel all outstanding commands to the device 271762306a36Sopenharmony_ci * @sdev: scsi device to cancel commands 271862306a36Sopenharmony_ci * @type: type of error recovery being performed 271962306a36Sopenharmony_ci * 272062306a36Sopenharmony_ci * This sends a cancel to the VIOS for the specified device. This does 272162306a36Sopenharmony_ci * NOT send any abort to the actual device. That must be done separately. 272262306a36Sopenharmony_ci * 272362306a36Sopenharmony_ci * Returns: 272462306a36Sopenharmony_ci * 0 on success / other on failure 272562306a36Sopenharmony_ci **/ 272662306a36Sopenharmony_cistatic int ibmvfc_cancel_all(struct scsi_device *sdev, int type) 272762306a36Sopenharmony_ci{ 272862306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci if (vhost->mq_enabled && vhost->using_channels) 273162306a36Sopenharmony_ci return ibmvfc_cancel_all_mq(sdev, type); 273262306a36Sopenharmony_ci else 273362306a36Sopenharmony_ci return ibmvfc_cancel_all_sq(sdev, type); 273462306a36Sopenharmony_ci} 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci/** 273762306a36Sopenharmony_ci * ibmvfc_match_key - Match function for specified cancel key 273862306a36Sopenharmony_ci * @evt: ibmvfc event struct 273962306a36Sopenharmony_ci * @key: cancel key to match 274062306a36Sopenharmony_ci * 274162306a36Sopenharmony_ci * Returns: 274262306a36Sopenharmony_ci * 1 if event matches key / 0 if event does not match key 274362306a36Sopenharmony_ci **/ 274462306a36Sopenharmony_cistatic int ibmvfc_match_key(struct ibmvfc_event *evt, void *key) 274562306a36Sopenharmony_ci{ 274662306a36Sopenharmony_ci unsigned long cancel_key = (unsigned long)key; 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci if (evt->crq.format == IBMVFC_CMD_FORMAT && 274962306a36Sopenharmony_ci be32_to_cpu(evt->iu.cmd.cancel_key) == cancel_key) 275062306a36Sopenharmony_ci return 1; 275162306a36Sopenharmony_ci return 0; 275262306a36Sopenharmony_ci} 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci/** 275562306a36Sopenharmony_ci * ibmvfc_match_evt - Match function for specified event 275662306a36Sopenharmony_ci * @evt: ibmvfc event struct 275762306a36Sopenharmony_ci * @match: event to match 275862306a36Sopenharmony_ci * 275962306a36Sopenharmony_ci * Returns: 276062306a36Sopenharmony_ci * 1 if event matches key / 0 if event does not match key 276162306a36Sopenharmony_ci **/ 276262306a36Sopenharmony_cistatic int ibmvfc_match_evt(struct ibmvfc_event *evt, void *match) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci if (evt == match) 276562306a36Sopenharmony_ci return 1; 276662306a36Sopenharmony_ci return 0; 276762306a36Sopenharmony_ci} 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci/** 277062306a36Sopenharmony_ci * ibmvfc_abort_task_set - Abort outstanding commands to the device 277162306a36Sopenharmony_ci * @sdev: scsi device to abort commands 277262306a36Sopenharmony_ci * 277362306a36Sopenharmony_ci * This sends an Abort Task Set to the VIOS for the specified device. This does 277462306a36Sopenharmony_ci * NOT send any cancel to the VIOS. That must be done separately. 277562306a36Sopenharmony_ci * 277662306a36Sopenharmony_ci * Returns: 277762306a36Sopenharmony_ci * 0 on success / other on failure 277862306a36Sopenharmony_ci **/ 277962306a36Sopenharmony_cistatic int ibmvfc_abort_task_set(struct scsi_device *sdev) 278062306a36Sopenharmony_ci{ 278162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 278262306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 278362306a36Sopenharmony_ci struct ibmvfc_cmd *tmf; 278462306a36Sopenharmony_ci struct ibmvfc_event *evt, *found_evt; 278562306a36Sopenharmony_ci union ibmvfc_iu rsp_iu; 278662306a36Sopenharmony_ci struct ibmvfc_fcp_cmd_iu *iu; 278762306a36Sopenharmony_ci struct ibmvfc_fcp_rsp *fc_rsp = ibmvfc_get_fcp_rsp(vhost, &rsp_iu.cmd); 278862306a36Sopenharmony_ci int rc, rsp_rc = -EBUSY; 278962306a36Sopenharmony_ci unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT; 279062306a36Sopenharmony_ci int rsp_code = 0; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci found_evt = NULL; 279362306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 279462306a36Sopenharmony_ci spin_lock(&vhost->crq.l_lock); 279562306a36Sopenharmony_ci list_for_each_entry(evt, &vhost->crq.sent, queue_list) { 279662306a36Sopenharmony_ci if (evt->cmnd && evt->cmnd->device == sdev) { 279762306a36Sopenharmony_ci found_evt = evt; 279862306a36Sopenharmony_ci break; 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci } 280162306a36Sopenharmony_ci spin_unlock(&vhost->crq.l_lock); 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci if (!found_evt) { 280462306a36Sopenharmony_ci if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) 280562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "No events found to abort\n"); 280662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 280762306a36Sopenharmony_ci return 0; 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci if (vhost->state == IBMVFC_ACTIVE) { 281162306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 281262306a36Sopenharmony_ci if (!evt) { 281362306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 281462306a36Sopenharmony_ci return -ENOMEM; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT); 281762306a36Sopenharmony_ci tmf = ibmvfc_init_vfc_cmd(evt, sdev); 281862306a36Sopenharmony_ci iu = ibmvfc_get_fcp_iu(vhost, tmf); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) 282162306a36Sopenharmony_ci tmf->target_wwpn = cpu_to_be64(rport->port_name); 282262306a36Sopenharmony_ci iu->tmf_flags = IBMVFC_ABORT_TASK_SET; 282362306a36Sopenharmony_ci tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF)); 282462306a36Sopenharmony_ci evt->sync_iu = &rsp_iu; 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci tmf->correlation = cpu_to_be64((u64)evt); 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci init_completion(&evt->comp); 282962306a36Sopenharmony_ci rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout); 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci if (rsp_rc != 0) { 283562306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc); 283662306a36Sopenharmony_ci return -EIO; 283762306a36Sopenharmony_ci } 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); 284062306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&evt->comp, timeout); 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci if (!timeout) { 284362306a36Sopenharmony_ci rc = ibmvfc_cancel_all(sdev, 0); 284462306a36Sopenharmony_ci if (!rc) { 284562306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key); 284662306a36Sopenharmony_ci if (rc == SUCCESS) 284762306a36Sopenharmony_ci rc = 0; 284862306a36Sopenharmony_ci } 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci if (rc) { 285162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n"); 285262306a36Sopenharmony_ci ibmvfc_reset_host(vhost); 285362306a36Sopenharmony_ci rsp_rc = -EIO; 285462306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key); 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci if (rc == SUCCESS) 285762306a36Sopenharmony_ci rsp_rc = 0; 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, evt, ibmvfc_match_evt); 286062306a36Sopenharmony_ci if (rc != SUCCESS) { 286162306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 286262306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 286362306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 286462306a36Sopenharmony_ci rsp_rc = 0; 286562306a36Sopenharmony_ci } 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci goto out; 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci if (rsp_iu.cmd.status) 287262306a36Sopenharmony_ci rsp_code = ibmvfc_get_err_result(vhost, &rsp_iu.cmd); 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_ci if (rsp_code) { 287562306a36Sopenharmony_ci if (fc_rsp->flags & FCP_RSP_LEN_VALID) 287662306a36Sopenharmony_ci rsp_code = fc_rsp->data.info.rsp_code; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) " 287962306a36Sopenharmony_ci "flags: %x fcp_rsp: %x, scsi_status: %x\n", 288062306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)), 288162306a36Sopenharmony_ci be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code, 288262306a36Sopenharmony_ci fc_rsp->scsi_status); 288362306a36Sopenharmony_ci rsp_rc = -EIO; 288462306a36Sopenharmony_ci } else 288562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Abort successful\n"); 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ciout: 288862306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 288962306a36Sopenharmony_ci ibmvfc_free_event(evt); 289062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 289162306a36Sopenharmony_ci return rsp_rc; 289262306a36Sopenharmony_ci} 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci/** 289562306a36Sopenharmony_ci * ibmvfc_eh_abort_handler - Abort a command 289662306a36Sopenharmony_ci * @cmd: scsi command to abort 289762306a36Sopenharmony_ci * 289862306a36Sopenharmony_ci * Returns: 289962306a36Sopenharmony_ci * SUCCESS / FAST_IO_FAIL / FAILED 290062306a36Sopenharmony_ci **/ 290162306a36Sopenharmony_cistatic int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd) 290262306a36Sopenharmony_ci{ 290362306a36Sopenharmony_ci struct scsi_device *sdev = cmd->device; 290462306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 290562306a36Sopenharmony_ci int cancel_rc, block_rc; 290662306a36Sopenharmony_ci int rc = FAILED; 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci ENTER; 290962306a36Sopenharmony_ci block_rc = fc_block_scsi_eh(cmd); 291062306a36Sopenharmony_ci ibmvfc_wait_while_resetting(vhost); 291162306a36Sopenharmony_ci if (block_rc != FAST_IO_FAIL) { 291262306a36Sopenharmony_ci cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET); 291362306a36Sopenharmony_ci ibmvfc_abort_task_set(sdev); 291462306a36Sopenharmony_ci } else 291562306a36Sopenharmony_ci cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci if (!cancel_rc) 291862306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun); 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci if (block_rc == FAST_IO_FAIL && rc != FAILED) 292162306a36Sopenharmony_ci rc = FAST_IO_FAIL; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci LEAVE; 292462306a36Sopenharmony_ci return rc; 292562306a36Sopenharmony_ci} 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci/** 292862306a36Sopenharmony_ci * ibmvfc_eh_device_reset_handler - Reset a single LUN 292962306a36Sopenharmony_ci * @cmd: scsi command struct 293062306a36Sopenharmony_ci * 293162306a36Sopenharmony_ci * Returns: 293262306a36Sopenharmony_ci * SUCCESS / FAST_IO_FAIL / FAILED 293362306a36Sopenharmony_ci **/ 293462306a36Sopenharmony_cistatic int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd) 293562306a36Sopenharmony_ci{ 293662306a36Sopenharmony_ci struct scsi_device *sdev = cmd->device; 293762306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 293862306a36Sopenharmony_ci int cancel_rc, block_rc, reset_rc = 0; 293962306a36Sopenharmony_ci int rc = FAILED; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci ENTER; 294262306a36Sopenharmony_ci block_rc = fc_block_scsi_eh(cmd); 294362306a36Sopenharmony_ci ibmvfc_wait_while_resetting(vhost); 294462306a36Sopenharmony_ci if (block_rc != FAST_IO_FAIL) { 294562306a36Sopenharmony_ci cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET); 294662306a36Sopenharmony_ci reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN"); 294762306a36Sopenharmony_ci } else 294862306a36Sopenharmony_ci cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci if (!cancel_rc && !reset_rc) 295162306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun); 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci if (block_rc == FAST_IO_FAIL && rc != FAILED) 295462306a36Sopenharmony_ci rc = FAST_IO_FAIL; 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci LEAVE; 295762306a36Sopenharmony_ci return rc; 295862306a36Sopenharmony_ci} 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci/** 296162306a36Sopenharmony_ci * ibmvfc_dev_cancel_all_noreset - Device iterated cancel all function 296262306a36Sopenharmony_ci * @sdev: scsi device struct 296362306a36Sopenharmony_ci * @data: return code 296462306a36Sopenharmony_ci * 296562306a36Sopenharmony_ci **/ 296662306a36Sopenharmony_cistatic void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data) 296762306a36Sopenharmony_ci{ 296862306a36Sopenharmony_ci unsigned long *rc = data; 296962306a36Sopenharmony_ci *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); 297062306a36Sopenharmony_ci} 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci/** 297362306a36Sopenharmony_ci * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function 297462306a36Sopenharmony_ci * @sdev: scsi device struct 297562306a36Sopenharmony_ci * @data: return code 297662306a36Sopenharmony_ci * 297762306a36Sopenharmony_ci **/ 297862306a36Sopenharmony_cistatic void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data) 297962306a36Sopenharmony_ci{ 298062306a36Sopenharmony_ci unsigned long *rc = data; 298162306a36Sopenharmony_ci *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET); 298262306a36Sopenharmony_ci} 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci/** 298562306a36Sopenharmony_ci * ibmvfc_eh_target_reset_handler - Reset the target 298662306a36Sopenharmony_ci * @cmd: scsi command struct 298762306a36Sopenharmony_ci * 298862306a36Sopenharmony_ci * Returns: 298962306a36Sopenharmony_ci * SUCCESS / FAST_IO_FAIL / FAILED 299062306a36Sopenharmony_ci **/ 299162306a36Sopenharmony_cistatic int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd) 299262306a36Sopenharmony_ci{ 299362306a36Sopenharmony_ci struct scsi_device *sdev = cmd->device; 299462306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(sdev->host); 299562306a36Sopenharmony_ci struct scsi_target *starget = scsi_target(sdev); 299662306a36Sopenharmony_ci int block_rc; 299762306a36Sopenharmony_ci int reset_rc = 0; 299862306a36Sopenharmony_ci int rc = FAILED; 299962306a36Sopenharmony_ci unsigned long cancel_rc = 0; 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_ci ENTER; 300262306a36Sopenharmony_ci block_rc = fc_block_scsi_eh(cmd); 300362306a36Sopenharmony_ci ibmvfc_wait_while_resetting(vhost); 300462306a36Sopenharmony_ci if (block_rc != FAST_IO_FAIL) { 300562306a36Sopenharmony_ci starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset); 300662306a36Sopenharmony_ci reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target"); 300762306a36Sopenharmony_ci } else 300862306a36Sopenharmony_ci starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_noreset); 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci if (!cancel_rc && !reset_rc) 301162306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target); 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci if (block_rc == FAST_IO_FAIL && rc != FAILED) 301462306a36Sopenharmony_ci rc = FAST_IO_FAIL; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci LEAVE; 301762306a36Sopenharmony_ci return rc; 301862306a36Sopenharmony_ci} 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci/** 302162306a36Sopenharmony_ci * ibmvfc_eh_host_reset_handler - Reset the connection to the server 302262306a36Sopenharmony_ci * @cmd: struct scsi_cmnd having problems 302362306a36Sopenharmony_ci * 302462306a36Sopenharmony_ci **/ 302562306a36Sopenharmony_cistatic int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd) 302662306a36Sopenharmony_ci{ 302762306a36Sopenharmony_ci int rc; 302862306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(cmd->device->host); 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci dev_err(vhost->dev, "Resetting connection due to error recovery\n"); 303162306a36Sopenharmony_ci rc = ibmvfc_issue_fc_host_lip(vhost->host); 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci return rc ? FAILED : SUCCESS; 303462306a36Sopenharmony_ci} 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci/** 303762306a36Sopenharmony_ci * ibmvfc_terminate_rport_io - Terminate all pending I/O to the rport. 303862306a36Sopenharmony_ci * @rport: rport struct 303962306a36Sopenharmony_ci * 304062306a36Sopenharmony_ci * Return value: 304162306a36Sopenharmony_ci * none 304262306a36Sopenharmony_ci **/ 304362306a36Sopenharmony_cistatic void ibmvfc_terminate_rport_io(struct fc_rport *rport) 304462306a36Sopenharmony_ci{ 304562306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 304662306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 304762306a36Sopenharmony_ci struct fc_rport *dev_rport; 304862306a36Sopenharmony_ci struct scsi_device *sdev; 304962306a36Sopenharmony_ci struct ibmvfc_target *tgt; 305062306a36Sopenharmony_ci unsigned long rc, flags; 305162306a36Sopenharmony_ci unsigned int found; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci ENTER; 305462306a36Sopenharmony_ci shost_for_each_device(sdev, shost) { 305562306a36Sopenharmony_ci dev_rport = starget_to_rport(scsi_target(sdev)); 305662306a36Sopenharmony_ci if (dev_rport != rport) 305762306a36Sopenharmony_ci continue; 305862306a36Sopenharmony_ci ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); 305962306a36Sopenharmony_ci } 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci if (rc == FAILED) 306462306a36Sopenharmony_ci ibmvfc_issue_fc_host_lip(shost); 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 306762306a36Sopenharmony_ci found = 0; 306862306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 306962306a36Sopenharmony_ci if (tgt->scsi_id == rport->port_id) { 307062306a36Sopenharmony_ci found++; 307162306a36Sopenharmony_ci break; 307262306a36Sopenharmony_ci } 307362306a36Sopenharmony_ci } 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci if (found && tgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) { 307662306a36Sopenharmony_ci /* 307762306a36Sopenharmony_ci * If we get here, that means we previously attempted to send 307862306a36Sopenharmony_ci * an implicit logout to the target but it failed, most likely 307962306a36Sopenharmony_ci * due to I/O being pending, so we need to send it again 308062306a36Sopenharmony_ci */ 308162306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 308262306a36Sopenharmony_ci ibmvfc_reinit_host(vhost); 308362306a36Sopenharmony_ci } 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 308662306a36Sopenharmony_ci LEAVE; 308762306a36Sopenharmony_ci} 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_cistatic const struct ibmvfc_async_desc ae_desc [] = { 309062306a36Sopenharmony_ci { "PLOGI", IBMVFC_AE_ELS_PLOGI, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, 309162306a36Sopenharmony_ci { "LOGO", IBMVFC_AE_ELS_LOGO, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, 309262306a36Sopenharmony_ci { "PRLO", IBMVFC_AE_ELS_PRLO, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, 309362306a36Sopenharmony_ci { "N-Port SCN", IBMVFC_AE_SCN_NPORT, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, 309462306a36Sopenharmony_ci { "Group SCN", IBMVFC_AE_SCN_GROUP, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, 309562306a36Sopenharmony_ci { "Domain SCN", IBMVFC_AE_SCN_DOMAIN, IBMVFC_DEFAULT_LOG_LEVEL }, 309662306a36Sopenharmony_ci { "Fabric SCN", IBMVFC_AE_SCN_FABRIC, IBMVFC_DEFAULT_LOG_LEVEL }, 309762306a36Sopenharmony_ci { "Link Up", IBMVFC_AE_LINK_UP, IBMVFC_DEFAULT_LOG_LEVEL }, 309862306a36Sopenharmony_ci { "Link Down", IBMVFC_AE_LINK_DOWN, IBMVFC_DEFAULT_LOG_LEVEL }, 309962306a36Sopenharmony_ci { "Link Dead", IBMVFC_AE_LINK_DEAD, IBMVFC_DEFAULT_LOG_LEVEL }, 310062306a36Sopenharmony_ci { "Halt", IBMVFC_AE_HALT, IBMVFC_DEFAULT_LOG_LEVEL }, 310162306a36Sopenharmony_ci { "Resume", IBMVFC_AE_RESUME, IBMVFC_DEFAULT_LOG_LEVEL }, 310262306a36Sopenharmony_ci { "Adapter Failed", IBMVFC_AE_ADAPTER_FAILED, IBMVFC_DEFAULT_LOG_LEVEL }, 310362306a36Sopenharmony_ci}; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_cistatic const struct ibmvfc_async_desc unknown_ae = { 310662306a36Sopenharmony_ci "Unknown async", 0, IBMVFC_DEFAULT_LOG_LEVEL 310762306a36Sopenharmony_ci}; 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci/** 311062306a36Sopenharmony_ci * ibmvfc_get_ae_desc - Get text description for async event 311162306a36Sopenharmony_ci * @ae: async event 311262306a36Sopenharmony_ci * 311362306a36Sopenharmony_ci **/ 311462306a36Sopenharmony_cistatic const struct ibmvfc_async_desc *ibmvfc_get_ae_desc(u64 ae) 311562306a36Sopenharmony_ci{ 311662306a36Sopenharmony_ci int i; 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ae_desc); i++) 311962306a36Sopenharmony_ci if (ae_desc[i].ae == ae) 312062306a36Sopenharmony_ci return &ae_desc[i]; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci return &unknown_ae; 312362306a36Sopenharmony_ci} 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_cistatic const struct { 312662306a36Sopenharmony_ci enum ibmvfc_ae_link_state state; 312762306a36Sopenharmony_ci const char *desc; 312862306a36Sopenharmony_ci} link_desc [] = { 312962306a36Sopenharmony_ci { IBMVFC_AE_LS_LINK_UP, " link up" }, 313062306a36Sopenharmony_ci { IBMVFC_AE_LS_LINK_BOUNCED, " link bounced" }, 313162306a36Sopenharmony_ci { IBMVFC_AE_LS_LINK_DOWN, " link down" }, 313262306a36Sopenharmony_ci { IBMVFC_AE_LS_LINK_DEAD, " link dead" }, 313362306a36Sopenharmony_ci}; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci/** 313662306a36Sopenharmony_ci * ibmvfc_get_link_state - Get text description for link state 313762306a36Sopenharmony_ci * @state: link state 313862306a36Sopenharmony_ci * 313962306a36Sopenharmony_ci **/ 314062306a36Sopenharmony_cistatic const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state) 314162306a36Sopenharmony_ci{ 314262306a36Sopenharmony_ci int i; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(link_desc); i++) 314562306a36Sopenharmony_ci if (link_desc[i].state == state) 314662306a36Sopenharmony_ci return link_desc[i].desc; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci return ""; 314962306a36Sopenharmony_ci} 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci/** 315262306a36Sopenharmony_ci * ibmvfc_handle_async - Handle an async event from the adapter 315362306a36Sopenharmony_ci * @crq: crq to process 315462306a36Sopenharmony_ci * @vhost: ibmvfc host struct 315562306a36Sopenharmony_ci * 315662306a36Sopenharmony_ci **/ 315762306a36Sopenharmony_cistatic void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, 315862306a36Sopenharmony_ci struct ibmvfc_host *vhost) 315962306a36Sopenharmony_ci{ 316062306a36Sopenharmony_ci const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(be64_to_cpu(crq->event)); 316162306a36Sopenharmony_ci struct ibmvfc_target *tgt; 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx," 316462306a36Sopenharmony_ci " node_name: %llx%s\n", desc->desc, be64_to_cpu(crq->scsi_id), 316562306a36Sopenharmony_ci be64_to_cpu(crq->wwpn), be64_to_cpu(crq->node_name), 316662306a36Sopenharmony_ci ibmvfc_get_link_state(crq->link_state)); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci switch (be64_to_cpu(crq->event)) { 316962306a36Sopenharmony_ci case IBMVFC_AE_RESUME: 317062306a36Sopenharmony_ci switch (crq->link_state) { 317162306a36Sopenharmony_ci case IBMVFC_AE_LS_LINK_DOWN: 317262306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); 317362306a36Sopenharmony_ci break; 317462306a36Sopenharmony_ci case IBMVFC_AE_LS_LINK_DEAD: 317562306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 317662306a36Sopenharmony_ci break; 317762306a36Sopenharmony_ci case IBMVFC_AE_LS_LINK_UP: 317862306a36Sopenharmony_ci case IBMVFC_AE_LS_LINK_BOUNCED: 317962306a36Sopenharmony_ci default: 318062306a36Sopenharmony_ci vhost->events_to_log |= IBMVFC_AE_LINKUP; 318162306a36Sopenharmony_ci vhost->delay_init = 1; 318262306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 318362306a36Sopenharmony_ci break; 318462306a36Sopenharmony_ci } 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci break; 318762306a36Sopenharmony_ci case IBMVFC_AE_LINK_UP: 318862306a36Sopenharmony_ci vhost->events_to_log |= IBMVFC_AE_LINKUP; 318962306a36Sopenharmony_ci vhost->delay_init = 1; 319062306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 319162306a36Sopenharmony_ci break; 319262306a36Sopenharmony_ci case IBMVFC_AE_SCN_FABRIC: 319362306a36Sopenharmony_ci case IBMVFC_AE_SCN_DOMAIN: 319462306a36Sopenharmony_ci vhost->events_to_log |= IBMVFC_AE_RSCN; 319562306a36Sopenharmony_ci if (vhost->state < IBMVFC_HALTED) { 319662306a36Sopenharmony_ci vhost->delay_init = 1; 319762306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 319862306a36Sopenharmony_ci } 319962306a36Sopenharmony_ci break; 320062306a36Sopenharmony_ci case IBMVFC_AE_SCN_NPORT: 320162306a36Sopenharmony_ci case IBMVFC_AE_SCN_GROUP: 320262306a36Sopenharmony_ci vhost->events_to_log |= IBMVFC_AE_RSCN; 320362306a36Sopenharmony_ci ibmvfc_reinit_host(vhost); 320462306a36Sopenharmony_ci break; 320562306a36Sopenharmony_ci case IBMVFC_AE_ELS_LOGO: 320662306a36Sopenharmony_ci case IBMVFC_AE_ELS_PRLO: 320762306a36Sopenharmony_ci case IBMVFC_AE_ELS_PLOGI: 320862306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 320962306a36Sopenharmony_ci if (!crq->scsi_id && !crq->wwpn && !crq->node_name) 321062306a36Sopenharmony_ci break; 321162306a36Sopenharmony_ci if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id) 321262306a36Sopenharmony_ci continue; 321362306a36Sopenharmony_ci if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn) 321462306a36Sopenharmony_ci continue; 321562306a36Sopenharmony_ci if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name) 321662306a36Sopenharmony_ci continue; 321762306a36Sopenharmony_ci if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO) 321862306a36Sopenharmony_ci tgt->logo_rcvd = 1; 321962306a36Sopenharmony_ci if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) { 322062306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 322162306a36Sopenharmony_ci ibmvfc_reinit_host(vhost); 322262306a36Sopenharmony_ci } 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci break; 322562306a36Sopenharmony_ci case IBMVFC_AE_LINK_DOWN: 322662306a36Sopenharmony_ci case IBMVFC_AE_ADAPTER_FAILED: 322762306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); 322862306a36Sopenharmony_ci break; 322962306a36Sopenharmony_ci case IBMVFC_AE_LINK_DEAD: 323062306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 323162306a36Sopenharmony_ci break; 323262306a36Sopenharmony_ci case IBMVFC_AE_HALT: 323362306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_HALTED); 323462306a36Sopenharmony_ci break; 323562306a36Sopenharmony_ci default: 323662306a36Sopenharmony_ci dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event); 323762306a36Sopenharmony_ci break; 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci} 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci/** 324262306a36Sopenharmony_ci * ibmvfc_handle_crq - Handles and frees received events in the CRQ 324362306a36Sopenharmony_ci * @crq: Command/Response queue 324462306a36Sopenharmony_ci * @vhost: ibmvfc host struct 324562306a36Sopenharmony_ci * @evt_doneq: Event done queue 324662306a36Sopenharmony_ci * 324762306a36Sopenharmony_ci**/ 324862306a36Sopenharmony_cistatic void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost, 324962306a36Sopenharmony_ci struct list_head *evt_doneq) 325062306a36Sopenharmony_ci{ 325162306a36Sopenharmony_ci long rc; 325262306a36Sopenharmony_ci struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu(crq->ioba); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci switch (crq->valid) { 325562306a36Sopenharmony_ci case IBMVFC_CRQ_INIT_RSP: 325662306a36Sopenharmony_ci switch (crq->format) { 325762306a36Sopenharmony_ci case IBMVFC_CRQ_INIT: 325862306a36Sopenharmony_ci dev_info(vhost->dev, "Partner initialized\n"); 325962306a36Sopenharmony_ci /* Send back a response */ 326062306a36Sopenharmony_ci rc = ibmvfc_send_crq_init_complete(vhost); 326162306a36Sopenharmony_ci if (rc == 0) 326262306a36Sopenharmony_ci ibmvfc_init_host(vhost); 326362306a36Sopenharmony_ci else 326462306a36Sopenharmony_ci dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc); 326562306a36Sopenharmony_ci break; 326662306a36Sopenharmony_ci case IBMVFC_CRQ_INIT_COMPLETE: 326762306a36Sopenharmony_ci dev_info(vhost->dev, "Partner initialization complete\n"); 326862306a36Sopenharmony_ci ibmvfc_init_host(vhost); 326962306a36Sopenharmony_ci break; 327062306a36Sopenharmony_ci default: 327162306a36Sopenharmony_ci dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format); 327262306a36Sopenharmony_ci } 327362306a36Sopenharmony_ci return; 327462306a36Sopenharmony_ci case IBMVFC_CRQ_XPORT_EVENT: 327562306a36Sopenharmony_ci vhost->state = IBMVFC_NO_CRQ; 327662306a36Sopenharmony_ci vhost->logged_in = 0; 327762306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); 327862306a36Sopenharmony_ci if (crq->format == IBMVFC_PARTITION_MIGRATED) { 327962306a36Sopenharmony_ci /* We need to re-setup the interpartition connection */ 328062306a36Sopenharmony_ci dev_info(vhost->dev, "Partition migrated, Re-enabling adapter\n"); 328162306a36Sopenharmony_ci vhost->client_migrated = 1; 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci scsi_block_requests(vhost->host); 328462306a36Sopenharmony_ci ibmvfc_purge_requests(vhost, DID_REQUEUE); 328562306a36Sopenharmony_ci ibmvfc_set_host_state(vhost, IBMVFC_LINK_DOWN); 328662306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE); 328762306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 328862306a36Sopenharmony_ci } else if (crq->format == IBMVFC_PARTNER_FAILED || crq->format == IBMVFC_PARTNER_DEREGISTER) { 328962306a36Sopenharmony_ci dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format); 329062306a36Sopenharmony_ci ibmvfc_purge_requests(vhost, DID_ERROR); 329162306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); 329262306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET); 329362306a36Sopenharmony_ci } else { 329462306a36Sopenharmony_ci dev_err(vhost->dev, "Received unknown transport event from partner (rc=%d)\n", crq->format); 329562306a36Sopenharmony_ci } 329662306a36Sopenharmony_ci return; 329762306a36Sopenharmony_ci case IBMVFC_CRQ_CMD_RSP: 329862306a36Sopenharmony_ci break; 329962306a36Sopenharmony_ci default: 330062306a36Sopenharmony_ci dev_err(vhost->dev, "Got an invalid message type 0x%02x\n", crq->valid); 330162306a36Sopenharmony_ci return; 330262306a36Sopenharmony_ci } 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci if (crq->format == IBMVFC_ASYNC_EVENT) 330562306a36Sopenharmony_ci return; 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ci /* The only kind of payload CRQs we should get are responses to 330862306a36Sopenharmony_ci * things we send. Make sure this response is to something we 330962306a36Sopenharmony_ci * actually sent 331062306a36Sopenharmony_ci */ 331162306a36Sopenharmony_ci if (unlikely(!ibmvfc_valid_event(&vhost->crq.evt_pool, evt))) { 331262306a36Sopenharmony_ci dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n", 331362306a36Sopenharmony_ci crq->ioba); 331462306a36Sopenharmony_ci return; 331562306a36Sopenharmony_ci } 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci if (unlikely(atomic_dec_if_positive(&evt->active))) { 331862306a36Sopenharmony_ci dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n", 331962306a36Sopenharmony_ci crq->ioba); 332062306a36Sopenharmony_ci return; 332162306a36Sopenharmony_ci } 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci spin_lock(&evt->queue->l_lock); 332462306a36Sopenharmony_ci list_move_tail(&evt->queue_list, evt_doneq); 332562306a36Sopenharmony_ci spin_unlock(&evt->queue->l_lock); 332662306a36Sopenharmony_ci} 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci/** 332962306a36Sopenharmony_ci * ibmvfc_scan_finished - Check if the device scan is done. 333062306a36Sopenharmony_ci * @shost: scsi host struct 333162306a36Sopenharmony_ci * @time: current elapsed time 333262306a36Sopenharmony_ci * 333362306a36Sopenharmony_ci * Returns: 333462306a36Sopenharmony_ci * 0 if scan is not done / 1 if scan is done 333562306a36Sopenharmony_ci **/ 333662306a36Sopenharmony_cistatic int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time) 333762306a36Sopenharmony_ci{ 333862306a36Sopenharmony_ci unsigned long flags; 333962306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 334062306a36Sopenharmony_ci int done = 0; 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 334362306a36Sopenharmony_ci if (!vhost->scan_timeout) 334462306a36Sopenharmony_ci done = 1; 334562306a36Sopenharmony_ci else if (time >= (vhost->scan_timeout * HZ)) { 334662306a36Sopenharmony_ci dev_info(vhost->dev, "Scan taking longer than %d seconds, " 334762306a36Sopenharmony_ci "continuing initialization\n", vhost->scan_timeout); 334862306a36Sopenharmony_ci done = 1; 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci if (vhost->scan_complete) { 335262306a36Sopenharmony_ci vhost->scan_timeout = init_timeout; 335362306a36Sopenharmony_ci done = 1; 335462306a36Sopenharmony_ci } 335562306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 335662306a36Sopenharmony_ci return done; 335762306a36Sopenharmony_ci} 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci/** 336062306a36Sopenharmony_ci * ibmvfc_slave_alloc - Setup the device's task set value 336162306a36Sopenharmony_ci * @sdev: struct scsi_device device to configure 336262306a36Sopenharmony_ci * 336362306a36Sopenharmony_ci * Set the device's task set value so that error handling works as 336462306a36Sopenharmony_ci * expected. 336562306a36Sopenharmony_ci * 336662306a36Sopenharmony_ci * Returns: 336762306a36Sopenharmony_ci * 0 on success / -ENXIO if device does not exist 336862306a36Sopenharmony_ci **/ 336962306a36Sopenharmony_cistatic int ibmvfc_slave_alloc(struct scsi_device *sdev) 337062306a36Sopenharmony_ci{ 337162306a36Sopenharmony_ci struct Scsi_Host *shost = sdev->host; 337262306a36Sopenharmony_ci struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); 337362306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 337462306a36Sopenharmony_ci unsigned long flags = 0; 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci if (!rport || fc_remote_port_chkready(rport)) 337762306a36Sopenharmony_ci return -ENXIO; 337862306a36Sopenharmony_ci 337962306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 338062306a36Sopenharmony_ci sdev->hostdata = (void *)(unsigned long)vhost->task_set++; 338162306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 338262306a36Sopenharmony_ci return 0; 338362306a36Sopenharmony_ci} 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci/** 338662306a36Sopenharmony_ci * ibmvfc_target_alloc - Setup the target's task set value 338762306a36Sopenharmony_ci * @starget: struct scsi_target 338862306a36Sopenharmony_ci * 338962306a36Sopenharmony_ci * Set the target's task set value so that error handling works as 339062306a36Sopenharmony_ci * expected. 339162306a36Sopenharmony_ci * 339262306a36Sopenharmony_ci * Returns: 339362306a36Sopenharmony_ci * 0 on success / -ENXIO if device does not exist 339462306a36Sopenharmony_ci **/ 339562306a36Sopenharmony_cistatic int ibmvfc_target_alloc(struct scsi_target *starget) 339662306a36Sopenharmony_ci{ 339762306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 339862306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 339962306a36Sopenharmony_ci unsigned long flags = 0; 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 340262306a36Sopenharmony_ci starget->hostdata = (void *)(unsigned long)vhost->task_set++; 340362306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 340462306a36Sopenharmony_ci return 0; 340562306a36Sopenharmony_ci} 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci/** 340862306a36Sopenharmony_ci * ibmvfc_slave_configure - Configure the device 340962306a36Sopenharmony_ci * @sdev: struct scsi_device device to configure 341062306a36Sopenharmony_ci * 341162306a36Sopenharmony_ci * Enable allow_restart for a device if it is a disk. Adjust the 341262306a36Sopenharmony_ci * queue_depth here also. 341362306a36Sopenharmony_ci * 341462306a36Sopenharmony_ci * Returns: 341562306a36Sopenharmony_ci * 0 341662306a36Sopenharmony_ci **/ 341762306a36Sopenharmony_cistatic int ibmvfc_slave_configure(struct scsi_device *sdev) 341862306a36Sopenharmony_ci{ 341962306a36Sopenharmony_ci struct Scsi_Host *shost = sdev->host; 342062306a36Sopenharmony_ci unsigned long flags = 0; 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 342362306a36Sopenharmony_ci if (sdev->type == TYPE_DISK) { 342462306a36Sopenharmony_ci sdev->allow_restart = 1; 342562306a36Sopenharmony_ci blk_queue_rq_timeout(sdev->request_queue, 120 * HZ); 342662306a36Sopenharmony_ci } 342762306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 342862306a36Sopenharmony_ci return 0; 342962306a36Sopenharmony_ci} 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci/** 343262306a36Sopenharmony_ci * ibmvfc_change_queue_depth - Change the device's queue depth 343362306a36Sopenharmony_ci * @sdev: scsi device struct 343462306a36Sopenharmony_ci * @qdepth: depth to set 343562306a36Sopenharmony_ci * 343662306a36Sopenharmony_ci * Return value: 343762306a36Sopenharmony_ci * actual depth set 343862306a36Sopenharmony_ci **/ 343962306a36Sopenharmony_cistatic int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth) 344062306a36Sopenharmony_ci{ 344162306a36Sopenharmony_ci if (qdepth > IBMVFC_MAX_CMDS_PER_LUN) 344262306a36Sopenharmony_ci qdepth = IBMVFC_MAX_CMDS_PER_LUN; 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci return scsi_change_queue_depth(sdev, qdepth); 344562306a36Sopenharmony_ci} 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_cistatic ssize_t ibmvfc_show_host_partition_name(struct device *dev, 344862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 344962306a36Sopenharmony_ci{ 345062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 345162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 345462306a36Sopenharmony_ci vhost->login_buf->resp.partition_name); 345562306a36Sopenharmony_ci} 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_cistatic ssize_t ibmvfc_show_host_device_name(struct device *dev, 345862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 345962306a36Sopenharmony_ci{ 346062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 346162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 346462306a36Sopenharmony_ci vhost->login_buf->resp.device_name); 346562306a36Sopenharmony_ci} 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_cistatic ssize_t ibmvfc_show_host_loc_code(struct device *dev, 346862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 346962306a36Sopenharmony_ci{ 347062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 347162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 347462306a36Sopenharmony_ci vhost->login_buf->resp.port_loc_code); 347562306a36Sopenharmony_ci} 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_cistatic ssize_t ibmvfc_show_host_drc_name(struct device *dev, 347862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 347962306a36Sopenharmony_ci{ 348062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 348162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 348462306a36Sopenharmony_ci vhost->login_buf->resp.drc_name); 348562306a36Sopenharmony_ci} 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_cistatic ssize_t ibmvfc_show_host_npiv_version(struct device *dev, 348862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 348962306a36Sopenharmony_ci{ 349062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 349162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 349262306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", be32_to_cpu(vhost->login_buf->resp.version)); 349362306a36Sopenharmony_ci} 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_cistatic ssize_t ibmvfc_show_host_capabilities(struct device *dev, 349662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 349762306a36Sopenharmony_ci{ 349862306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 349962306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 350062306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%llx\n", be64_to_cpu(vhost->login_buf->resp.capabilities)); 350162306a36Sopenharmony_ci} 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci/** 350462306a36Sopenharmony_ci * ibmvfc_show_log_level - Show the adapter's error logging level 350562306a36Sopenharmony_ci * @dev: class device struct 350662306a36Sopenharmony_ci * @attr: unused 350762306a36Sopenharmony_ci * @buf: buffer 350862306a36Sopenharmony_ci * 350962306a36Sopenharmony_ci * Return value: 351062306a36Sopenharmony_ci * number of bytes printed to buffer 351162306a36Sopenharmony_ci **/ 351262306a36Sopenharmony_cistatic ssize_t ibmvfc_show_log_level(struct device *dev, 351362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 351462306a36Sopenharmony_ci{ 351562306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 351662306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 351762306a36Sopenharmony_ci unsigned long flags = 0; 351862306a36Sopenharmony_ci int len; 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 352162306a36Sopenharmony_ci len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->log_level); 352262306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 352362306a36Sopenharmony_ci return len; 352462306a36Sopenharmony_ci} 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci/** 352762306a36Sopenharmony_ci * ibmvfc_store_log_level - Change the adapter's error logging level 352862306a36Sopenharmony_ci * @dev: class device struct 352962306a36Sopenharmony_ci * @attr: unused 353062306a36Sopenharmony_ci * @buf: buffer 353162306a36Sopenharmony_ci * @count: buffer size 353262306a36Sopenharmony_ci * 353362306a36Sopenharmony_ci * Return value: 353462306a36Sopenharmony_ci * number of bytes printed to buffer 353562306a36Sopenharmony_ci **/ 353662306a36Sopenharmony_cistatic ssize_t ibmvfc_store_log_level(struct device *dev, 353762306a36Sopenharmony_ci struct device_attribute *attr, 353862306a36Sopenharmony_ci const char *buf, size_t count) 353962306a36Sopenharmony_ci{ 354062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 354162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 354262306a36Sopenharmony_ci unsigned long flags = 0; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 354562306a36Sopenharmony_ci vhost->log_level = simple_strtoul(buf, NULL, 10); 354662306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 354762306a36Sopenharmony_ci return strlen(buf); 354862306a36Sopenharmony_ci} 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_cistatic ssize_t ibmvfc_show_scsi_channels(struct device *dev, 355162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 355262306a36Sopenharmony_ci{ 355362306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 355462306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 355562306a36Sopenharmony_ci unsigned long flags = 0; 355662306a36Sopenharmony_ci int len; 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 355962306a36Sopenharmony_ci len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->client_scsi_channels); 356062306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 356162306a36Sopenharmony_ci return len; 356262306a36Sopenharmony_ci} 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_cistatic ssize_t ibmvfc_store_scsi_channels(struct device *dev, 356562306a36Sopenharmony_ci struct device_attribute *attr, 356662306a36Sopenharmony_ci const char *buf, size_t count) 356762306a36Sopenharmony_ci{ 356862306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 356962306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 357062306a36Sopenharmony_ci unsigned long flags = 0; 357162306a36Sopenharmony_ci unsigned int channels; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 357462306a36Sopenharmony_ci channels = simple_strtoul(buf, NULL, 10); 357562306a36Sopenharmony_ci vhost->client_scsi_channels = min(channels, nr_scsi_hw_queues); 357662306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 357762306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 357862306a36Sopenharmony_ci return strlen(buf); 357962306a36Sopenharmony_ci} 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_cistatic DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL); 358262306a36Sopenharmony_cistatic DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL); 358362306a36Sopenharmony_cistatic DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL); 358462306a36Sopenharmony_cistatic DEVICE_ATTR(drc_name, S_IRUGO, ibmvfc_show_host_drc_name, NULL); 358562306a36Sopenharmony_cistatic DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL); 358662306a36Sopenharmony_cistatic DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL); 358762306a36Sopenharmony_cistatic DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR, 358862306a36Sopenharmony_ci ibmvfc_show_log_level, ibmvfc_store_log_level); 358962306a36Sopenharmony_cistatic DEVICE_ATTR(nr_scsi_channels, S_IRUGO | S_IWUSR, 359062306a36Sopenharmony_ci ibmvfc_show_scsi_channels, ibmvfc_store_scsi_channels); 359162306a36Sopenharmony_ci 359262306a36Sopenharmony_ci#ifdef CONFIG_SCSI_IBMVFC_TRACE 359362306a36Sopenharmony_ci/** 359462306a36Sopenharmony_ci * ibmvfc_read_trace - Dump the adapter trace 359562306a36Sopenharmony_ci * @filp: open sysfs file 359662306a36Sopenharmony_ci * @kobj: kobject struct 359762306a36Sopenharmony_ci * @bin_attr: bin_attribute struct 359862306a36Sopenharmony_ci * @buf: buffer 359962306a36Sopenharmony_ci * @off: offset 360062306a36Sopenharmony_ci * @count: buffer size 360162306a36Sopenharmony_ci * 360262306a36Sopenharmony_ci * Return value: 360362306a36Sopenharmony_ci * number of bytes printed to buffer 360462306a36Sopenharmony_ci **/ 360562306a36Sopenharmony_cistatic ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj, 360662306a36Sopenharmony_ci struct bin_attribute *bin_attr, 360762306a36Sopenharmony_ci char *buf, loff_t off, size_t count) 360862306a36Sopenharmony_ci{ 360962306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 361062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 361162306a36Sopenharmony_ci struct ibmvfc_host *vhost = shost_priv(shost); 361262306a36Sopenharmony_ci unsigned long flags = 0; 361362306a36Sopenharmony_ci int size = IBMVFC_TRACE_SIZE; 361462306a36Sopenharmony_ci char *src = (char *)vhost->trace; 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci if (off > size) 361762306a36Sopenharmony_ci return 0; 361862306a36Sopenharmony_ci if (off + count > size) { 361962306a36Sopenharmony_ci size -= off; 362062306a36Sopenharmony_ci count = size; 362162306a36Sopenharmony_ci } 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 362462306a36Sopenharmony_ci memcpy(buf, &src[off], count); 362562306a36Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 362662306a36Sopenharmony_ci return count; 362762306a36Sopenharmony_ci} 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_cistatic struct bin_attribute ibmvfc_trace_attr = { 363062306a36Sopenharmony_ci .attr = { 363162306a36Sopenharmony_ci .name = "trace", 363262306a36Sopenharmony_ci .mode = S_IRUGO, 363362306a36Sopenharmony_ci }, 363462306a36Sopenharmony_ci .size = 0, 363562306a36Sopenharmony_ci .read = ibmvfc_read_trace, 363662306a36Sopenharmony_ci}; 363762306a36Sopenharmony_ci#endif 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_cistatic struct attribute *ibmvfc_host_attrs[] = { 364062306a36Sopenharmony_ci &dev_attr_partition_name.attr, 364162306a36Sopenharmony_ci &dev_attr_device_name.attr, 364262306a36Sopenharmony_ci &dev_attr_port_loc_code.attr, 364362306a36Sopenharmony_ci &dev_attr_drc_name.attr, 364462306a36Sopenharmony_ci &dev_attr_npiv_version.attr, 364562306a36Sopenharmony_ci &dev_attr_capabilities.attr, 364662306a36Sopenharmony_ci &dev_attr_log_level.attr, 364762306a36Sopenharmony_ci &dev_attr_nr_scsi_channels.attr, 364862306a36Sopenharmony_ci NULL 364962306a36Sopenharmony_ci}; 365062306a36Sopenharmony_ci 365162306a36Sopenharmony_ciATTRIBUTE_GROUPS(ibmvfc_host); 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_cistatic const struct scsi_host_template driver_template = { 365462306a36Sopenharmony_ci .module = THIS_MODULE, 365562306a36Sopenharmony_ci .name = "IBM POWER Virtual FC Adapter", 365662306a36Sopenharmony_ci .proc_name = IBMVFC_NAME, 365762306a36Sopenharmony_ci .queuecommand = ibmvfc_queuecommand, 365862306a36Sopenharmony_ci .eh_timed_out = fc_eh_timed_out, 365962306a36Sopenharmony_ci .eh_abort_handler = ibmvfc_eh_abort_handler, 366062306a36Sopenharmony_ci .eh_device_reset_handler = ibmvfc_eh_device_reset_handler, 366162306a36Sopenharmony_ci .eh_target_reset_handler = ibmvfc_eh_target_reset_handler, 366262306a36Sopenharmony_ci .eh_host_reset_handler = ibmvfc_eh_host_reset_handler, 366362306a36Sopenharmony_ci .slave_alloc = ibmvfc_slave_alloc, 366462306a36Sopenharmony_ci .slave_configure = ibmvfc_slave_configure, 366562306a36Sopenharmony_ci .target_alloc = ibmvfc_target_alloc, 366662306a36Sopenharmony_ci .scan_finished = ibmvfc_scan_finished, 366762306a36Sopenharmony_ci .change_queue_depth = ibmvfc_change_queue_depth, 366862306a36Sopenharmony_ci .cmd_per_lun = 16, 366962306a36Sopenharmony_ci .can_queue = IBMVFC_MAX_REQUESTS_DEFAULT, 367062306a36Sopenharmony_ci .this_id = -1, 367162306a36Sopenharmony_ci .sg_tablesize = SG_ALL, 367262306a36Sopenharmony_ci .max_sectors = IBMVFC_MAX_SECTORS, 367362306a36Sopenharmony_ci .shost_groups = ibmvfc_host_groups, 367462306a36Sopenharmony_ci .track_queue_depth = 1, 367562306a36Sopenharmony_ci .host_tagset = 1, 367662306a36Sopenharmony_ci}; 367762306a36Sopenharmony_ci 367862306a36Sopenharmony_ci/** 367962306a36Sopenharmony_ci * ibmvfc_next_async_crq - Returns the next entry in async queue 368062306a36Sopenharmony_ci * @vhost: ibmvfc host struct 368162306a36Sopenharmony_ci * 368262306a36Sopenharmony_ci * Returns: 368362306a36Sopenharmony_ci * Pointer to next entry in queue / NULL if empty 368462306a36Sopenharmony_ci **/ 368562306a36Sopenharmony_cistatic struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost) 368662306a36Sopenharmony_ci{ 368762306a36Sopenharmony_ci struct ibmvfc_queue *async_crq = &vhost->async_crq; 368862306a36Sopenharmony_ci struct ibmvfc_async_crq *crq; 368962306a36Sopenharmony_ci 369062306a36Sopenharmony_ci crq = &async_crq->msgs.async[async_crq->cur]; 369162306a36Sopenharmony_ci if (crq->valid & 0x80) { 369262306a36Sopenharmony_ci if (++async_crq->cur == async_crq->size) 369362306a36Sopenharmony_ci async_crq->cur = 0; 369462306a36Sopenharmony_ci rmb(); 369562306a36Sopenharmony_ci } else 369662306a36Sopenharmony_ci crq = NULL; 369762306a36Sopenharmony_ci 369862306a36Sopenharmony_ci return crq; 369962306a36Sopenharmony_ci} 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci/** 370262306a36Sopenharmony_ci * ibmvfc_next_crq - Returns the next entry in message queue 370362306a36Sopenharmony_ci * @vhost: ibmvfc host struct 370462306a36Sopenharmony_ci * 370562306a36Sopenharmony_ci * Returns: 370662306a36Sopenharmony_ci * Pointer to next entry in queue / NULL if empty 370762306a36Sopenharmony_ci **/ 370862306a36Sopenharmony_cistatic struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost) 370962306a36Sopenharmony_ci{ 371062306a36Sopenharmony_ci struct ibmvfc_queue *queue = &vhost->crq; 371162306a36Sopenharmony_ci struct ibmvfc_crq *crq; 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci crq = &queue->msgs.crq[queue->cur]; 371462306a36Sopenharmony_ci if (crq->valid & 0x80) { 371562306a36Sopenharmony_ci if (++queue->cur == queue->size) 371662306a36Sopenharmony_ci queue->cur = 0; 371762306a36Sopenharmony_ci rmb(); 371862306a36Sopenharmony_ci } else 371962306a36Sopenharmony_ci crq = NULL; 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_ci return crq; 372262306a36Sopenharmony_ci} 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_ci/** 372562306a36Sopenharmony_ci * ibmvfc_interrupt - Interrupt handler 372662306a36Sopenharmony_ci * @irq: number of irq to handle, not used 372762306a36Sopenharmony_ci * @dev_instance: ibmvfc_host that received interrupt 372862306a36Sopenharmony_ci * 372962306a36Sopenharmony_ci * Returns: 373062306a36Sopenharmony_ci * IRQ_HANDLED 373162306a36Sopenharmony_ci **/ 373262306a36Sopenharmony_cistatic irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) 373362306a36Sopenharmony_ci{ 373462306a36Sopenharmony_ci struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance; 373562306a36Sopenharmony_ci unsigned long flags; 373662306a36Sopenharmony_ci 373762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 373862306a36Sopenharmony_ci vio_disable_interrupts(to_vio_dev(vhost->dev)); 373962306a36Sopenharmony_ci tasklet_schedule(&vhost->tasklet); 374062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 374162306a36Sopenharmony_ci return IRQ_HANDLED; 374262306a36Sopenharmony_ci} 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_ci/** 374562306a36Sopenharmony_ci * ibmvfc_tasklet - Interrupt handler tasklet 374662306a36Sopenharmony_ci * @data: ibmvfc host struct 374762306a36Sopenharmony_ci * 374862306a36Sopenharmony_ci * Returns: 374962306a36Sopenharmony_ci * Nothing 375062306a36Sopenharmony_ci **/ 375162306a36Sopenharmony_cistatic void ibmvfc_tasklet(void *data) 375262306a36Sopenharmony_ci{ 375362306a36Sopenharmony_ci struct ibmvfc_host *vhost = data; 375462306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(vhost->dev); 375562306a36Sopenharmony_ci struct ibmvfc_crq *crq; 375662306a36Sopenharmony_ci struct ibmvfc_async_crq *async; 375762306a36Sopenharmony_ci struct ibmvfc_event *evt, *temp; 375862306a36Sopenharmony_ci unsigned long flags; 375962306a36Sopenharmony_ci int done = 0; 376062306a36Sopenharmony_ci LIST_HEAD(evt_doneq); 376162306a36Sopenharmony_ci 376262306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 376362306a36Sopenharmony_ci spin_lock(vhost->crq.q_lock); 376462306a36Sopenharmony_ci while (!done) { 376562306a36Sopenharmony_ci /* Pull all the valid messages off the async CRQ */ 376662306a36Sopenharmony_ci while ((async = ibmvfc_next_async_crq(vhost)) != NULL) { 376762306a36Sopenharmony_ci ibmvfc_handle_async(async, vhost); 376862306a36Sopenharmony_ci async->valid = 0; 376962306a36Sopenharmony_ci wmb(); 377062306a36Sopenharmony_ci } 377162306a36Sopenharmony_ci 377262306a36Sopenharmony_ci /* Pull all the valid messages off the CRQ */ 377362306a36Sopenharmony_ci while ((crq = ibmvfc_next_crq(vhost)) != NULL) { 377462306a36Sopenharmony_ci ibmvfc_handle_crq(crq, vhost, &evt_doneq); 377562306a36Sopenharmony_ci crq->valid = 0; 377662306a36Sopenharmony_ci wmb(); 377762306a36Sopenharmony_ci } 377862306a36Sopenharmony_ci 377962306a36Sopenharmony_ci vio_enable_interrupts(vdev); 378062306a36Sopenharmony_ci if ((async = ibmvfc_next_async_crq(vhost)) != NULL) { 378162306a36Sopenharmony_ci vio_disable_interrupts(vdev); 378262306a36Sopenharmony_ci ibmvfc_handle_async(async, vhost); 378362306a36Sopenharmony_ci async->valid = 0; 378462306a36Sopenharmony_ci wmb(); 378562306a36Sopenharmony_ci } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) { 378662306a36Sopenharmony_ci vio_disable_interrupts(vdev); 378762306a36Sopenharmony_ci ibmvfc_handle_crq(crq, vhost, &evt_doneq); 378862306a36Sopenharmony_ci crq->valid = 0; 378962306a36Sopenharmony_ci wmb(); 379062306a36Sopenharmony_ci } else 379162306a36Sopenharmony_ci done = 1; 379262306a36Sopenharmony_ci } 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_ci spin_unlock(vhost->crq.q_lock); 379562306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ci list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) { 379862306a36Sopenharmony_ci del_timer(&evt->timer); 379962306a36Sopenharmony_ci list_del(&evt->queue_list); 380062306a36Sopenharmony_ci ibmvfc_trc_end(evt); 380162306a36Sopenharmony_ci evt->done(evt); 380262306a36Sopenharmony_ci } 380362306a36Sopenharmony_ci} 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_cistatic int ibmvfc_toggle_scrq_irq(struct ibmvfc_queue *scrq, int enable) 380662306a36Sopenharmony_ci{ 380762306a36Sopenharmony_ci struct device *dev = scrq->vhost->dev; 380862306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(dev); 380962306a36Sopenharmony_ci unsigned long rc; 381062306a36Sopenharmony_ci int irq_action = H_ENABLE_VIO_INTERRUPT; 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_ci if (!enable) 381362306a36Sopenharmony_ci irq_action = H_DISABLE_VIO_INTERRUPT; 381462306a36Sopenharmony_ci 381562306a36Sopenharmony_ci rc = plpar_hcall_norets(H_VIOCTL, vdev->unit_address, irq_action, 381662306a36Sopenharmony_ci scrq->hw_irq, 0, 0); 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci if (rc) 381962306a36Sopenharmony_ci dev_err(dev, "Couldn't %s sub-crq[%lu] irq. rc=%ld\n", 382062306a36Sopenharmony_ci enable ? "enable" : "disable", scrq->hwq_id, rc); 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci return rc; 382362306a36Sopenharmony_ci} 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_cistatic void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost, 382662306a36Sopenharmony_ci struct list_head *evt_doneq) 382762306a36Sopenharmony_ci{ 382862306a36Sopenharmony_ci struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu(crq->ioba); 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci switch (crq->valid) { 383162306a36Sopenharmony_ci case IBMVFC_CRQ_CMD_RSP: 383262306a36Sopenharmony_ci break; 383362306a36Sopenharmony_ci case IBMVFC_CRQ_XPORT_EVENT: 383462306a36Sopenharmony_ci return; 383562306a36Sopenharmony_ci default: 383662306a36Sopenharmony_ci dev_err(vhost->dev, "Got and invalid message type 0x%02x\n", crq->valid); 383762306a36Sopenharmony_ci return; 383862306a36Sopenharmony_ci } 383962306a36Sopenharmony_ci 384062306a36Sopenharmony_ci /* The only kind of payload CRQs we should get are responses to 384162306a36Sopenharmony_ci * things we send. Make sure this response is to something we 384262306a36Sopenharmony_ci * actually sent 384362306a36Sopenharmony_ci */ 384462306a36Sopenharmony_ci if (unlikely(!ibmvfc_valid_event(&evt->queue->evt_pool, evt))) { 384562306a36Sopenharmony_ci dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n", 384662306a36Sopenharmony_ci crq->ioba); 384762306a36Sopenharmony_ci return; 384862306a36Sopenharmony_ci } 384962306a36Sopenharmony_ci 385062306a36Sopenharmony_ci if (unlikely(atomic_dec_if_positive(&evt->active))) { 385162306a36Sopenharmony_ci dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n", 385262306a36Sopenharmony_ci crq->ioba); 385362306a36Sopenharmony_ci return; 385462306a36Sopenharmony_ci } 385562306a36Sopenharmony_ci 385662306a36Sopenharmony_ci spin_lock(&evt->queue->l_lock); 385762306a36Sopenharmony_ci list_move_tail(&evt->queue_list, evt_doneq); 385862306a36Sopenharmony_ci spin_unlock(&evt->queue->l_lock); 385962306a36Sopenharmony_ci} 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_cistatic struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq) 386262306a36Sopenharmony_ci{ 386362306a36Sopenharmony_ci struct ibmvfc_crq *crq; 386462306a36Sopenharmony_ci 386562306a36Sopenharmony_ci crq = &scrq->msgs.scrq[scrq->cur].crq; 386662306a36Sopenharmony_ci if (crq->valid & 0x80) { 386762306a36Sopenharmony_ci if (++scrq->cur == scrq->size) 386862306a36Sopenharmony_ci scrq->cur = 0; 386962306a36Sopenharmony_ci rmb(); 387062306a36Sopenharmony_ci } else 387162306a36Sopenharmony_ci crq = NULL; 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci return crq; 387462306a36Sopenharmony_ci} 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_cistatic void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq) 387762306a36Sopenharmony_ci{ 387862306a36Sopenharmony_ci struct ibmvfc_crq *crq; 387962306a36Sopenharmony_ci struct ibmvfc_event *evt, *temp; 388062306a36Sopenharmony_ci unsigned long flags; 388162306a36Sopenharmony_ci int done = 0; 388262306a36Sopenharmony_ci LIST_HEAD(evt_doneq); 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci spin_lock_irqsave(scrq->q_lock, flags); 388562306a36Sopenharmony_ci while (!done) { 388662306a36Sopenharmony_ci while ((crq = ibmvfc_next_scrq(scrq)) != NULL) { 388762306a36Sopenharmony_ci ibmvfc_handle_scrq(crq, scrq->vhost, &evt_doneq); 388862306a36Sopenharmony_ci crq->valid = 0; 388962306a36Sopenharmony_ci wmb(); 389062306a36Sopenharmony_ci } 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci ibmvfc_toggle_scrq_irq(scrq, 1); 389362306a36Sopenharmony_ci if ((crq = ibmvfc_next_scrq(scrq)) != NULL) { 389462306a36Sopenharmony_ci ibmvfc_toggle_scrq_irq(scrq, 0); 389562306a36Sopenharmony_ci ibmvfc_handle_scrq(crq, scrq->vhost, &evt_doneq); 389662306a36Sopenharmony_ci crq->valid = 0; 389762306a36Sopenharmony_ci wmb(); 389862306a36Sopenharmony_ci } else 389962306a36Sopenharmony_ci done = 1; 390062306a36Sopenharmony_ci } 390162306a36Sopenharmony_ci spin_unlock_irqrestore(scrq->q_lock, flags); 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) { 390462306a36Sopenharmony_ci del_timer(&evt->timer); 390562306a36Sopenharmony_ci list_del(&evt->queue_list); 390662306a36Sopenharmony_ci ibmvfc_trc_end(evt); 390762306a36Sopenharmony_ci evt->done(evt); 390862306a36Sopenharmony_ci } 390962306a36Sopenharmony_ci} 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_cistatic irqreturn_t ibmvfc_interrupt_scsi(int irq, void *scrq_instance) 391262306a36Sopenharmony_ci{ 391362306a36Sopenharmony_ci struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance; 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci ibmvfc_toggle_scrq_irq(scrq, 0); 391662306a36Sopenharmony_ci ibmvfc_drain_sub_crq(scrq); 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci return IRQ_HANDLED; 391962306a36Sopenharmony_ci} 392062306a36Sopenharmony_ci 392162306a36Sopenharmony_ci/** 392262306a36Sopenharmony_ci * ibmvfc_init_tgt - Set the next init job step for the target 392362306a36Sopenharmony_ci * @tgt: ibmvfc target struct 392462306a36Sopenharmony_ci * @job_step: job step to perform 392562306a36Sopenharmony_ci * 392662306a36Sopenharmony_ci **/ 392762306a36Sopenharmony_cistatic void ibmvfc_init_tgt(struct ibmvfc_target *tgt, 392862306a36Sopenharmony_ci void (*job_step) (struct ibmvfc_target *)) 392962306a36Sopenharmony_ci{ 393062306a36Sopenharmony_ci if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT)) 393162306a36Sopenharmony_ci tgt->job_step = job_step; 393262306a36Sopenharmony_ci wake_up(&tgt->vhost->work_wait_q); 393362306a36Sopenharmony_ci} 393462306a36Sopenharmony_ci 393562306a36Sopenharmony_ci/** 393662306a36Sopenharmony_ci * ibmvfc_retry_tgt_init - Attempt to retry a step in target initialization 393762306a36Sopenharmony_ci * @tgt: ibmvfc target struct 393862306a36Sopenharmony_ci * @job_step: initialization job step 393962306a36Sopenharmony_ci * 394062306a36Sopenharmony_ci * Returns: 1 if step will be retried / 0 if not 394162306a36Sopenharmony_ci * 394262306a36Sopenharmony_ci **/ 394362306a36Sopenharmony_cistatic int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt, 394462306a36Sopenharmony_ci void (*job_step) (struct ibmvfc_target *)) 394562306a36Sopenharmony_ci{ 394662306a36Sopenharmony_ci if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) { 394762306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 394862306a36Sopenharmony_ci wake_up(&tgt->vhost->work_wait_q); 394962306a36Sopenharmony_ci return 0; 395062306a36Sopenharmony_ci } else 395162306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, job_step); 395262306a36Sopenharmony_ci return 1; 395362306a36Sopenharmony_ci} 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_ci/* Defined in FC-LS */ 395662306a36Sopenharmony_cistatic const struct { 395762306a36Sopenharmony_ci int code; 395862306a36Sopenharmony_ci int retry; 395962306a36Sopenharmony_ci int logged_in; 396062306a36Sopenharmony_ci} prli_rsp [] = { 396162306a36Sopenharmony_ci { 0, 1, 0 }, 396262306a36Sopenharmony_ci { 1, 0, 1 }, 396362306a36Sopenharmony_ci { 2, 1, 0 }, 396462306a36Sopenharmony_ci { 3, 1, 0 }, 396562306a36Sopenharmony_ci { 4, 0, 0 }, 396662306a36Sopenharmony_ci { 5, 0, 0 }, 396762306a36Sopenharmony_ci { 6, 0, 1 }, 396862306a36Sopenharmony_ci { 7, 0, 0 }, 396962306a36Sopenharmony_ci { 8, 1, 0 }, 397062306a36Sopenharmony_ci}; 397162306a36Sopenharmony_ci 397262306a36Sopenharmony_ci/** 397362306a36Sopenharmony_ci * ibmvfc_get_prli_rsp - Find PRLI response index 397462306a36Sopenharmony_ci * @flags: PRLI response flags 397562306a36Sopenharmony_ci * 397662306a36Sopenharmony_ci **/ 397762306a36Sopenharmony_cistatic int ibmvfc_get_prli_rsp(u16 flags) 397862306a36Sopenharmony_ci{ 397962306a36Sopenharmony_ci int i; 398062306a36Sopenharmony_ci int code = (flags & 0x0f00) >> 8; 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(prli_rsp); i++) 398362306a36Sopenharmony_ci if (prli_rsp[i].code == code) 398462306a36Sopenharmony_ci return i; 398562306a36Sopenharmony_ci 398662306a36Sopenharmony_ci return 0; 398762306a36Sopenharmony_ci} 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_ci/** 399062306a36Sopenharmony_ci * ibmvfc_tgt_prli_done - Completion handler for Process Login 399162306a36Sopenharmony_ci * @evt: ibmvfc event struct 399262306a36Sopenharmony_ci * 399362306a36Sopenharmony_ci **/ 399462306a36Sopenharmony_cistatic void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) 399562306a36Sopenharmony_ci{ 399662306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 399762306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 399862306a36Sopenharmony_ci struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli; 399962306a36Sopenharmony_ci struct ibmvfc_prli_svc_parms *parms = &rsp->parms; 400062306a36Sopenharmony_ci u32 status = be16_to_cpu(rsp->common.status); 400162306a36Sopenharmony_ci int index, level = IBMVFC_DEFAULT_LOG_LEVEL; 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_ci vhost->discovery_threads--; 400462306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 400562306a36Sopenharmony_ci switch (status) { 400662306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 400762306a36Sopenharmony_ci tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n", 400862306a36Sopenharmony_ci parms->type, parms->flags, parms->service_parms); 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci if (parms->type == IBMVFC_SCSI_FCP_TYPE) { 401162306a36Sopenharmony_ci index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags)); 401262306a36Sopenharmony_ci if (prli_rsp[index].logged_in) { 401362306a36Sopenharmony_ci if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) { 401462306a36Sopenharmony_ci tgt->need_login = 0; 401562306a36Sopenharmony_ci tgt->ids.roles = 0; 401662306a36Sopenharmony_ci if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_TARGET_FUNC) 401762306a36Sopenharmony_ci tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET; 401862306a36Sopenharmony_ci if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_INITIATOR_FUNC) 401962306a36Sopenharmony_ci tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR; 402062306a36Sopenharmony_ci tgt->add_rport = 1; 402162306a36Sopenharmony_ci } else 402262306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 402362306a36Sopenharmony_ci } else if (prli_rsp[index].retry) 402462306a36Sopenharmony_ci ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); 402562306a36Sopenharmony_ci else 402662306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 402762306a36Sopenharmony_ci } else 402862306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 402962306a36Sopenharmony_ci break; 403062306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 403162306a36Sopenharmony_ci break; 403262306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 403362306a36Sopenharmony_ci ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); 403462306a36Sopenharmony_ci break; 403562306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 403662306a36Sopenharmony_ci default: 403762306a36Sopenharmony_ci if ((be16_to_cpu(rsp->status) & IBMVFC_VIOS_FAILURE) && 403862306a36Sopenharmony_ci be16_to_cpu(rsp->error) == IBMVFC_PLOGI_REQUIRED) 403962306a36Sopenharmony_ci level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); 404062306a36Sopenharmony_ci else if (tgt->logo_rcvd) 404162306a36Sopenharmony_ci level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); 404262306a36Sopenharmony_ci else if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error))) 404362306a36Sopenharmony_ci level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); 404462306a36Sopenharmony_ci else 404562306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 404662306a36Sopenharmony_ci 404762306a36Sopenharmony_ci tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n", 404862306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), 404962306a36Sopenharmony_ci be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status); 405062306a36Sopenharmony_ci break; 405162306a36Sopenharmony_ci } 405262306a36Sopenharmony_ci 405362306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 405462306a36Sopenharmony_ci ibmvfc_free_event(evt); 405562306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 405662306a36Sopenharmony_ci} 405762306a36Sopenharmony_ci 405862306a36Sopenharmony_ci/** 405962306a36Sopenharmony_ci * ibmvfc_tgt_send_prli - Send a process login 406062306a36Sopenharmony_ci * @tgt: ibmvfc target struct 406162306a36Sopenharmony_ci * 406262306a36Sopenharmony_ci **/ 406362306a36Sopenharmony_cistatic void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt) 406462306a36Sopenharmony_ci{ 406562306a36Sopenharmony_ci struct ibmvfc_process_login *prli; 406662306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 406762306a36Sopenharmony_ci struct ibmvfc_event *evt; 406862306a36Sopenharmony_ci 406962306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 407062306a36Sopenharmony_ci return; 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci kref_get(&tgt->kref); 407362306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 407462306a36Sopenharmony_ci if (!evt) { 407562306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 407662306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 407762306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 407862306a36Sopenharmony_ci return; 407962306a36Sopenharmony_ci } 408062306a36Sopenharmony_ci vhost->discovery_threads++; 408162306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_tgt_prli_done, IBMVFC_MAD_FORMAT); 408262306a36Sopenharmony_ci evt->tgt = tgt; 408362306a36Sopenharmony_ci prli = &evt->iu.prli; 408462306a36Sopenharmony_ci memset(prli, 0, sizeof(*prli)); 408562306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) { 408662306a36Sopenharmony_ci prli->common.version = cpu_to_be32(2); 408762306a36Sopenharmony_ci prli->target_wwpn = cpu_to_be64(tgt->wwpn); 408862306a36Sopenharmony_ci } else { 408962306a36Sopenharmony_ci prli->common.version = cpu_to_be32(1); 409062306a36Sopenharmony_ci } 409162306a36Sopenharmony_ci prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN); 409262306a36Sopenharmony_ci prli->common.length = cpu_to_be16(sizeof(*prli)); 409362306a36Sopenharmony_ci prli->scsi_id = cpu_to_be64(tgt->scsi_id); 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_ci prli->parms.type = IBMVFC_SCSI_FCP_TYPE; 409662306a36Sopenharmony_ci prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR); 409762306a36Sopenharmony_ci prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC); 409862306a36Sopenharmony_ci prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED); 409962306a36Sopenharmony_ci 410062306a36Sopenharmony_ci if (cls3_error) 410162306a36Sopenharmony_ci prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY); 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 410462306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, default_timeout)) { 410562306a36Sopenharmony_ci vhost->discovery_threads--; 410662306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 410762306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 410862306a36Sopenharmony_ci } else 410962306a36Sopenharmony_ci tgt_dbg(tgt, "Sent process login\n"); 411062306a36Sopenharmony_ci} 411162306a36Sopenharmony_ci 411262306a36Sopenharmony_ci/** 411362306a36Sopenharmony_ci * ibmvfc_tgt_plogi_done - Completion handler for Port Login 411462306a36Sopenharmony_ci * @evt: ibmvfc event struct 411562306a36Sopenharmony_ci * 411662306a36Sopenharmony_ci **/ 411762306a36Sopenharmony_cistatic void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt) 411862306a36Sopenharmony_ci{ 411962306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 412062306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 412162306a36Sopenharmony_ci struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi; 412262306a36Sopenharmony_ci u32 status = be16_to_cpu(rsp->common.status); 412362306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 412462306a36Sopenharmony_ci 412562306a36Sopenharmony_ci vhost->discovery_threads--; 412662306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 412762306a36Sopenharmony_ci switch (status) { 412862306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 412962306a36Sopenharmony_ci tgt_dbg(tgt, "Port Login succeeded\n"); 413062306a36Sopenharmony_ci if (tgt->ids.port_name && 413162306a36Sopenharmony_ci tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) { 413262306a36Sopenharmony_ci vhost->reinit = 1; 413362306a36Sopenharmony_ci tgt_dbg(tgt, "Port re-init required\n"); 413462306a36Sopenharmony_ci break; 413562306a36Sopenharmony_ci } 413662306a36Sopenharmony_ci tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name); 413762306a36Sopenharmony_ci tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name); 413862306a36Sopenharmony_ci tgt->ids.port_id = tgt->scsi_id; 413962306a36Sopenharmony_ci memcpy(&tgt->service_parms, &rsp->service_parms, 414062306a36Sopenharmony_ci sizeof(tgt->service_parms)); 414162306a36Sopenharmony_ci memcpy(&tgt->service_parms_change, &rsp->service_parms_change, 414262306a36Sopenharmony_ci sizeof(tgt->service_parms_change)); 414362306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli); 414462306a36Sopenharmony_ci break; 414562306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 414662306a36Sopenharmony_ci break; 414762306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 414862306a36Sopenharmony_ci ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); 414962306a36Sopenharmony_ci break; 415062306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 415162306a36Sopenharmony_ci default: 415262306a36Sopenharmony_ci if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error))) 415362306a36Sopenharmony_ci level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); 415462306a36Sopenharmony_ci else 415562306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", 415862306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), 415962306a36Sopenharmony_ci be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), 416062306a36Sopenharmony_ci ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type), 416162306a36Sopenharmony_ci ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status); 416262306a36Sopenharmony_ci break; 416362306a36Sopenharmony_ci } 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 416662306a36Sopenharmony_ci ibmvfc_free_event(evt); 416762306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 416862306a36Sopenharmony_ci} 416962306a36Sopenharmony_ci 417062306a36Sopenharmony_ci/** 417162306a36Sopenharmony_ci * ibmvfc_tgt_send_plogi - Send PLOGI to the specified target 417262306a36Sopenharmony_ci * @tgt: ibmvfc target struct 417362306a36Sopenharmony_ci * 417462306a36Sopenharmony_ci **/ 417562306a36Sopenharmony_cistatic void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt) 417662306a36Sopenharmony_ci{ 417762306a36Sopenharmony_ci struct ibmvfc_port_login *plogi; 417862306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 417962306a36Sopenharmony_ci struct ibmvfc_event *evt; 418062306a36Sopenharmony_ci 418162306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 418262306a36Sopenharmony_ci return; 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci kref_get(&tgt->kref); 418562306a36Sopenharmony_ci tgt->logo_rcvd = 0; 418662306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 418762306a36Sopenharmony_ci if (!evt) { 418862306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 418962306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 419062306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 419162306a36Sopenharmony_ci return; 419262306a36Sopenharmony_ci } 419362306a36Sopenharmony_ci vhost->discovery_threads++; 419462306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 419562306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_tgt_plogi_done, IBMVFC_MAD_FORMAT); 419662306a36Sopenharmony_ci evt->tgt = tgt; 419762306a36Sopenharmony_ci plogi = &evt->iu.plogi; 419862306a36Sopenharmony_ci memset(plogi, 0, sizeof(*plogi)); 419962306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) { 420062306a36Sopenharmony_ci plogi->common.version = cpu_to_be32(2); 420162306a36Sopenharmony_ci plogi->target_wwpn = cpu_to_be64(tgt->wwpn); 420262306a36Sopenharmony_ci } else { 420362306a36Sopenharmony_ci plogi->common.version = cpu_to_be32(1); 420462306a36Sopenharmony_ci } 420562306a36Sopenharmony_ci plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN); 420662306a36Sopenharmony_ci plogi->common.length = cpu_to_be16(sizeof(*plogi)); 420762306a36Sopenharmony_ci plogi->scsi_id = cpu_to_be64(tgt->scsi_id); 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, default_timeout)) { 421062306a36Sopenharmony_ci vhost->discovery_threads--; 421162306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 421262306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 421362306a36Sopenharmony_ci } else 421462306a36Sopenharmony_ci tgt_dbg(tgt, "Sent port login\n"); 421562306a36Sopenharmony_ci} 421662306a36Sopenharmony_ci 421762306a36Sopenharmony_ci/** 421862306a36Sopenharmony_ci * ibmvfc_tgt_implicit_logout_done - Completion handler for Implicit Logout MAD 421962306a36Sopenharmony_ci * @evt: ibmvfc event struct 422062306a36Sopenharmony_ci * 422162306a36Sopenharmony_ci **/ 422262306a36Sopenharmony_cistatic void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt) 422362306a36Sopenharmony_ci{ 422462306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 422562306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 422662306a36Sopenharmony_ci struct ibmvfc_implicit_logout *rsp = &evt->xfer_iu->implicit_logout; 422762306a36Sopenharmony_ci u32 status = be16_to_cpu(rsp->common.status); 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci vhost->discovery_threads--; 423062306a36Sopenharmony_ci ibmvfc_free_event(evt); 423162306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci switch (status) { 423462306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 423562306a36Sopenharmony_ci tgt_dbg(tgt, "Implicit Logout succeeded\n"); 423662306a36Sopenharmony_ci break; 423762306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 423862306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 423962306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 424062306a36Sopenharmony_ci return; 424162306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 424262306a36Sopenharmony_ci default: 424362306a36Sopenharmony_ci tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status); 424462306a36Sopenharmony_ci break; 424562306a36Sopenharmony_ci } 424662306a36Sopenharmony_ci 424762306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_plogi); 424862306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 424962306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 425062306a36Sopenharmony_ci} 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci/** 425362306a36Sopenharmony_ci * __ibmvfc_tgt_get_implicit_logout_evt - Allocate and init an event for implicit logout 425462306a36Sopenharmony_ci * @tgt: ibmvfc target struct 425562306a36Sopenharmony_ci * @done: Routine to call when the event is responded to 425662306a36Sopenharmony_ci * 425762306a36Sopenharmony_ci * Returns: 425862306a36Sopenharmony_ci * Allocated and initialized ibmvfc_event struct 425962306a36Sopenharmony_ci **/ 426062306a36Sopenharmony_cistatic struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_target *tgt, 426162306a36Sopenharmony_ci void (*done) (struct ibmvfc_event *)) 426262306a36Sopenharmony_ci{ 426362306a36Sopenharmony_ci struct ibmvfc_implicit_logout *mad; 426462306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 426562306a36Sopenharmony_ci struct ibmvfc_event *evt; 426662306a36Sopenharmony_ci 426762306a36Sopenharmony_ci kref_get(&tgt->kref); 426862306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 426962306a36Sopenharmony_ci if (!evt) 427062306a36Sopenharmony_ci return NULL; 427162306a36Sopenharmony_ci ibmvfc_init_event(evt, done, IBMVFC_MAD_FORMAT); 427262306a36Sopenharmony_ci evt->tgt = tgt; 427362306a36Sopenharmony_ci mad = &evt->iu.implicit_logout; 427462306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 427562306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 427662306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT); 427762306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(*mad)); 427862306a36Sopenharmony_ci mad->old_scsi_id = cpu_to_be64(tgt->scsi_id); 427962306a36Sopenharmony_ci return evt; 428062306a36Sopenharmony_ci} 428162306a36Sopenharmony_ci 428262306a36Sopenharmony_ci/** 428362306a36Sopenharmony_ci * ibmvfc_tgt_implicit_logout - Initiate an Implicit Logout for specified target 428462306a36Sopenharmony_ci * @tgt: ibmvfc target struct 428562306a36Sopenharmony_ci * 428662306a36Sopenharmony_ci **/ 428762306a36Sopenharmony_cistatic void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt) 428862306a36Sopenharmony_ci{ 428962306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 429062306a36Sopenharmony_ci struct ibmvfc_event *evt; 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 429362306a36Sopenharmony_ci return; 429462306a36Sopenharmony_ci 429562306a36Sopenharmony_ci vhost->discovery_threads++; 429662306a36Sopenharmony_ci evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt, 429762306a36Sopenharmony_ci ibmvfc_tgt_implicit_logout_done); 429862306a36Sopenharmony_ci if (!evt) { 429962306a36Sopenharmony_ci vhost->discovery_threads--; 430062306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 430162306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 430262306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 430362306a36Sopenharmony_ci return; 430462306a36Sopenharmony_ci } 430562306a36Sopenharmony_ci 430662306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 430762306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, default_timeout)) { 430862306a36Sopenharmony_ci vhost->discovery_threads--; 430962306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 431062306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 431162306a36Sopenharmony_ci } else 431262306a36Sopenharmony_ci tgt_dbg(tgt, "Sent Implicit Logout\n"); 431362306a36Sopenharmony_ci} 431462306a36Sopenharmony_ci 431562306a36Sopenharmony_ci/** 431662306a36Sopenharmony_ci * ibmvfc_tgt_implicit_logout_and_del_done - Completion handler for Implicit Logout MAD 431762306a36Sopenharmony_ci * @evt: ibmvfc event struct 431862306a36Sopenharmony_ci * 431962306a36Sopenharmony_ci **/ 432062306a36Sopenharmony_cistatic void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt) 432162306a36Sopenharmony_ci{ 432262306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 432362306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 432462306a36Sopenharmony_ci struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru; 432562306a36Sopenharmony_ci u32 status = be16_to_cpu(mad->common.status); 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_ci vhost->discovery_threads--; 432862306a36Sopenharmony_ci ibmvfc_free_event(evt); 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci /* 433162306a36Sopenharmony_ci * If our state is IBMVFC_HOST_OFFLINE, we could be unloading the 433262306a36Sopenharmony_ci * driver in which case we need to free up all the targets. If we are 433362306a36Sopenharmony_ci * not unloading, we will still go through a hard reset to get out of 433462306a36Sopenharmony_ci * offline state, so there is no need to track the old targets in that 433562306a36Sopenharmony_ci * case. 433662306a36Sopenharmony_ci */ 433762306a36Sopenharmony_ci if (status == IBMVFC_MAD_SUCCESS || vhost->state == IBMVFC_HOST_OFFLINE) 433862306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); 433962306a36Sopenharmony_ci else 434062306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT); 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed"); 434362306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 434462306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 434562306a36Sopenharmony_ci} 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci/** 434862306a36Sopenharmony_ci * ibmvfc_tgt_implicit_logout_and_del - Initiate an Implicit Logout for specified target 434962306a36Sopenharmony_ci * @tgt: ibmvfc target struct 435062306a36Sopenharmony_ci * 435162306a36Sopenharmony_ci **/ 435262306a36Sopenharmony_cistatic void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt) 435362306a36Sopenharmony_ci{ 435462306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 435562306a36Sopenharmony_ci struct ibmvfc_event *evt; 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci if (!vhost->logged_in) { 435862306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); 435962306a36Sopenharmony_ci return; 436062306a36Sopenharmony_ci } 436162306a36Sopenharmony_ci 436262306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 436362306a36Sopenharmony_ci return; 436462306a36Sopenharmony_ci 436562306a36Sopenharmony_ci vhost->discovery_threads++; 436662306a36Sopenharmony_ci evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt, 436762306a36Sopenharmony_ci ibmvfc_tgt_implicit_logout_and_del_done); 436862306a36Sopenharmony_ci 436962306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT); 437062306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, default_timeout)) { 437162306a36Sopenharmony_ci vhost->discovery_threads--; 437262306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); 437362306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 437462306a36Sopenharmony_ci } else 437562306a36Sopenharmony_ci tgt_dbg(tgt, "Sent Implicit Logout\n"); 437662306a36Sopenharmony_ci} 437762306a36Sopenharmony_ci 437862306a36Sopenharmony_ci/** 437962306a36Sopenharmony_ci * ibmvfc_tgt_move_login_done - Completion handler for Move Login 438062306a36Sopenharmony_ci * @evt: ibmvfc event struct 438162306a36Sopenharmony_ci * 438262306a36Sopenharmony_ci **/ 438362306a36Sopenharmony_cistatic void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt) 438462306a36Sopenharmony_ci{ 438562306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 438662306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 438762306a36Sopenharmony_ci struct ibmvfc_move_login *rsp = &evt->xfer_iu->move_login; 438862306a36Sopenharmony_ci u32 status = be16_to_cpu(rsp->common.status); 438962306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_ci vhost->discovery_threads--; 439262306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 439362306a36Sopenharmony_ci switch (status) { 439462306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 439562306a36Sopenharmony_ci tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id); 439662306a36Sopenharmony_ci tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name); 439762306a36Sopenharmony_ci tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name); 439862306a36Sopenharmony_ci tgt->scsi_id = tgt->new_scsi_id; 439962306a36Sopenharmony_ci tgt->ids.port_id = tgt->scsi_id; 440062306a36Sopenharmony_ci memcpy(&tgt->service_parms, &rsp->service_parms, 440162306a36Sopenharmony_ci sizeof(tgt->service_parms)); 440262306a36Sopenharmony_ci memcpy(&tgt->service_parms_change, &rsp->service_parms_change, 440362306a36Sopenharmony_ci sizeof(tgt->service_parms_change)); 440462306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli); 440562306a36Sopenharmony_ci break; 440662306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 440762306a36Sopenharmony_ci break; 440862306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 440962306a36Sopenharmony_ci ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login); 441062306a36Sopenharmony_ci break; 441162306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 441262306a36Sopenharmony_ci default: 441362306a36Sopenharmony_ci level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login); 441462306a36Sopenharmony_ci 441562306a36Sopenharmony_ci tgt_log(tgt, level, 441662306a36Sopenharmony_ci "Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n", 441762306a36Sopenharmony_ci tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags), 441862306a36Sopenharmony_ci status); 441962306a36Sopenharmony_ci break; 442062306a36Sopenharmony_ci } 442162306a36Sopenharmony_ci 442262306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 442362306a36Sopenharmony_ci ibmvfc_free_event(evt); 442462306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 442562306a36Sopenharmony_ci} 442662306a36Sopenharmony_ci 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci/** 442962306a36Sopenharmony_ci * ibmvfc_tgt_move_login - Initiate a move login for specified target 443062306a36Sopenharmony_ci * @tgt: ibmvfc target struct 443162306a36Sopenharmony_ci * 443262306a36Sopenharmony_ci **/ 443362306a36Sopenharmony_cistatic void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt) 443462306a36Sopenharmony_ci{ 443562306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 443662306a36Sopenharmony_ci struct ibmvfc_move_login *move; 443762306a36Sopenharmony_ci struct ibmvfc_event *evt; 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 444062306a36Sopenharmony_ci return; 444162306a36Sopenharmony_ci 444262306a36Sopenharmony_ci kref_get(&tgt->kref); 444362306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 444462306a36Sopenharmony_ci if (!evt) { 444562306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); 444662306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 444762306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 444862306a36Sopenharmony_ci return; 444962306a36Sopenharmony_ci } 445062306a36Sopenharmony_ci vhost->discovery_threads++; 445162306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 445262306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_tgt_move_login_done, IBMVFC_MAD_FORMAT); 445362306a36Sopenharmony_ci evt->tgt = tgt; 445462306a36Sopenharmony_ci move = &evt->iu.move_login; 445562306a36Sopenharmony_ci memset(move, 0, sizeof(*move)); 445662306a36Sopenharmony_ci move->common.version = cpu_to_be32(1); 445762306a36Sopenharmony_ci move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN); 445862306a36Sopenharmony_ci move->common.length = cpu_to_be16(sizeof(*move)); 445962306a36Sopenharmony_ci 446062306a36Sopenharmony_ci move->old_scsi_id = cpu_to_be64(tgt->scsi_id); 446162306a36Sopenharmony_ci move->new_scsi_id = cpu_to_be64(tgt->new_scsi_id); 446262306a36Sopenharmony_ci move->wwpn = cpu_to_be64(tgt->wwpn); 446362306a36Sopenharmony_ci move->node_name = cpu_to_be64(tgt->ids.node_name); 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, default_timeout)) { 446662306a36Sopenharmony_ci vhost->discovery_threads--; 446762306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); 446862306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 446962306a36Sopenharmony_ci } else 447062306a36Sopenharmony_ci tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id); 447162306a36Sopenharmony_ci} 447262306a36Sopenharmony_ci 447362306a36Sopenharmony_ci/** 447462306a36Sopenharmony_ci * ibmvfc_adisc_needs_plogi - Does device need PLOGI? 447562306a36Sopenharmony_ci * @mad: ibmvfc passthru mad struct 447662306a36Sopenharmony_ci * @tgt: ibmvfc target struct 447762306a36Sopenharmony_ci * 447862306a36Sopenharmony_ci * Returns: 447962306a36Sopenharmony_ci * 1 if PLOGI needed / 0 if PLOGI not needed 448062306a36Sopenharmony_ci **/ 448162306a36Sopenharmony_cistatic int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad, 448262306a36Sopenharmony_ci struct ibmvfc_target *tgt) 448362306a36Sopenharmony_ci{ 448462306a36Sopenharmony_ci if (wwn_to_u64((u8 *)&mad->fc_iu.response[2]) != tgt->ids.port_name) 448562306a36Sopenharmony_ci return 1; 448662306a36Sopenharmony_ci if (wwn_to_u64((u8 *)&mad->fc_iu.response[4]) != tgt->ids.node_name) 448762306a36Sopenharmony_ci return 1; 448862306a36Sopenharmony_ci if (be32_to_cpu(mad->fc_iu.response[6]) != tgt->scsi_id) 448962306a36Sopenharmony_ci return 1; 449062306a36Sopenharmony_ci return 0; 449162306a36Sopenharmony_ci} 449262306a36Sopenharmony_ci 449362306a36Sopenharmony_ci/** 449462306a36Sopenharmony_ci * ibmvfc_tgt_adisc_done - Completion handler for ADISC 449562306a36Sopenharmony_ci * @evt: ibmvfc event struct 449662306a36Sopenharmony_ci * 449762306a36Sopenharmony_ci **/ 449862306a36Sopenharmony_cistatic void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt) 449962306a36Sopenharmony_ci{ 450062306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 450162306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 450262306a36Sopenharmony_ci struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru; 450362306a36Sopenharmony_ci u32 status = be16_to_cpu(mad->common.status); 450462306a36Sopenharmony_ci u8 fc_reason, fc_explain; 450562306a36Sopenharmony_ci 450662306a36Sopenharmony_ci vhost->discovery_threads--; 450762306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 450862306a36Sopenharmony_ci del_timer(&tgt->timer); 450962306a36Sopenharmony_ci 451062306a36Sopenharmony_ci switch (status) { 451162306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 451262306a36Sopenharmony_ci tgt_dbg(tgt, "ADISC succeeded\n"); 451362306a36Sopenharmony_ci if (ibmvfc_adisc_needs_plogi(mad, tgt)) 451462306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 451562306a36Sopenharmony_ci break; 451662306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 451762306a36Sopenharmony_ci break; 451862306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 451962306a36Sopenharmony_ci default: 452062306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 452162306a36Sopenharmony_ci fc_reason = (be32_to_cpu(mad->fc_iu.response[1]) & 0x00ff0000) >> 16; 452262306a36Sopenharmony_ci fc_explain = (be32_to_cpu(mad->fc_iu.response[1]) & 0x0000ff00) >> 8; 452362306a36Sopenharmony_ci tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", 452462306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)), 452562306a36Sopenharmony_ci be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error), 452662306a36Sopenharmony_ci ibmvfc_get_fc_type(fc_reason), fc_reason, 452762306a36Sopenharmony_ci ibmvfc_get_ls_explain(fc_explain), fc_explain, status); 452862306a36Sopenharmony_ci break; 452962306a36Sopenharmony_ci } 453062306a36Sopenharmony_ci 453162306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 453262306a36Sopenharmony_ci ibmvfc_free_event(evt); 453362306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 453462306a36Sopenharmony_ci} 453562306a36Sopenharmony_ci 453662306a36Sopenharmony_ci/** 453762306a36Sopenharmony_ci * ibmvfc_init_passthru - Initialize an event struct for FC passthru 453862306a36Sopenharmony_ci * @evt: ibmvfc event struct 453962306a36Sopenharmony_ci * 454062306a36Sopenharmony_ci **/ 454162306a36Sopenharmony_cistatic void ibmvfc_init_passthru(struct ibmvfc_event *evt) 454262306a36Sopenharmony_ci{ 454362306a36Sopenharmony_ci struct ibmvfc_passthru_mad *mad = &evt->iu.passthru; 454462306a36Sopenharmony_ci 454562306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 454662306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 454762306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_PASSTHRU); 454862306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu)); 454962306a36Sopenharmony_ci mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) + 455062306a36Sopenharmony_ci offsetof(struct ibmvfc_passthru_mad, iu)); 455162306a36Sopenharmony_ci mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu)); 455262306a36Sopenharmony_ci mad->iu.cmd_len = cpu_to_be32(sizeof(mad->fc_iu.payload)); 455362306a36Sopenharmony_ci mad->iu.rsp_len = cpu_to_be32(sizeof(mad->fc_iu.response)); 455462306a36Sopenharmony_ci mad->iu.cmd.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) + 455562306a36Sopenharmony_ci offsetof(struct ibmvfc_passthru_mad, fc_iu) + 455662306a36Sopenharmony_ci offsetof(struct ibmvfc_passthru_fc_iu, payload)); 455762306a36Sopenharmony_ci mad->iu.cmd.len = cpu_to_be32(sizeof(mad->fc_iu.payload)); 455862306a36Sopenharmony_ci mad->iu.rsp.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) + 455962306a36Sopenharmony_ci offsetof(struct ibmvfc_passthru_mad, fc_iu) + 456062306a36Sopenharmony_ci offsetof(struct ibmvfc_passthru_fc_iu, response)); 456162306a36Sopenharmony_ci mad->iu.rsp.len = cpu_to_be32(sizeof(mad->fc_iu.response)); 456262306a36Sopenharmony_ci} 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci/** 456562306a36Sopenharmony_ci * ibmvfc_tgt_adisc_cancel_done - Completion handler when cancelling an ADISC 456662306a36Sopenharmony_ci * @evt: ibmvfc event struct 456762306a36Sopenharmony_ci * 456862306a36Sopenharmony_ci * Just cleanup this event struct. Everything else is handled by 456962306a36Sopenharmony_ci * the ADISC completion handler. If the ADISC never actually comes 457062306a36Sopenharmony_ci * back, we still have the timer running on the ADISC event struct 457162306a36Sopenharmony_ci * which will fire and cause the CRQ to get reset. 457262306a36Sopenharmony_ci * 457362306a36Sopenharmony_ci **/ 457462306a36Sopenharmony_cistatic void ibmvfc_tgt_adisc_cancel_done(struct ibmvfc_event *evt) 457562306a36Sopenharmony_ci{ 457662306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 457762306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 457862306a36Sopenharmony_ci 457962306a36Sopenharmony_ci tgt_dbg(tgt, "ADISC cancel complete\n"); 458062306a36Sopenharmony_ci vhost->abort_threads--; 458162306a36Sopenharmony_ci ibmvfc_free_event(evt); 458262306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 458362306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 458462306a36Sopenharmony_ci} 458562306a36Sopenharmony_ci 458662306a36Sopenharmony_ci/** 458762306a36Sopenharmony_ci * ibmvfc_adisc_timeout - Handle an ADISC timeout 458862306a36Sopenharmony_ci * @t: ibmvfc target struct 458962306a36Sopenharmony_ci * 459062306a36Sopenharmony_ci * If an ADISC times out, send a cancel. If the cancel times 459162306a36Sopenharmony_ci * out, reset the CRQ. When the ADISC comes back as cancelled, 459262306a36Sopenharmony_ci * log back into the target. 459362306a36Sopenharmony_ci **/ 459462306a36Sopenharmony_cistatic void ibmvfc_adisc_timeout(struct timer_list *t) 459562306a36Sopenharmony_ci{ 459662306a36Sopenharmony_ci struct ibmvfc_target *tgt = from_timer(tgt, t, timer); 459762306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 459862306a36Sopenharmony_ci struct ibmvfc_event *evt; 459962306a36Sopenharmony_ci struct ibmvfc_tmf *tmf; 460062306a36Sopenharmony_ci unsigned long flags; 460162306a36Sopenharmony_ci int rc; 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_ci tgt_dbg(tgt, "ADISC timeout\n"); 460462306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 460562306a36Sopenharmony_ci if (vhost->abort_threads >= disc_threads || 460662306a36Sopenharmony_ci tgt->action != IBMVFC_TGT_ACTION_INIT_WAIT || 460762306a36Sopenharmony_ci vhost->state != IBMVFC_INITIALIZING || 460862306a36Sopenharmony_ci vhost->action != IBMVFC_HOST_ACTION_QUERY_TGTS) { 460962306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 461062306a36Sopenharmony_ci return; 461162306a36Sopenharmony_ci } 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci vhost->abort_threads++; 461462306a36Sopenharmony_ci kref_get(&tgt->kref); 461562306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 461662306a36Sopenharmony_ci if (!evt) { 461762306a36Sopenharmony_ci tgt_err(tgt, "Failed to get cancel event for ADISC.\n"); 461862306a36Sopenharmony_ci vhost->abort_threads--; 461962306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 462062306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 462162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 462262306a36Sopenharmony_ci return; 462362306a36Sopenharmony_ci } 462462306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_tgt_adisc_cancel_done, IBMVFC_MAD_FORMAT); 462562306a36Sopenharmony_ci 462662306a36Sopenharmony_ci evt->tgt = tgt; 462762306a36Sopenharmony_ci tmf = &evt->iu.tmf; 462862306a36Sopenharmony_ci memset(tmf, 0, sizeof(*tmf)); 462962306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) { 463062306a36Sopenharmony_ci tmf->common.version = cpu_to_be32(2); 463162306a36Sopenharmony_ci tmf->target_wwpn = cpu_to_be64(tgt->wwpn); 463262306a36Sopenharmony_ci } else { 463362306a36Sopenharmony_ci tmf->common.version = cpu_to_be32(1); 463462306a36Sopenharmony_ci } 463562306a36Sopenharmony_ci tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD); 463662306a36Sopenharmony_ci tmf->common.length = cpu_to_be16(sizeof(*tmf)); 463762306a36Sopenharmony_ci tmf->scsi_id = cpu_to_be64(tgt->scsi_id); 463862306a36Sopenharmony_ci tmf->cancel_key = cpu_to_be32(tgt->cancel_key); 463962306a36Sopenharmony_ci 464062306a36Sopenharmony_ci rc = ibmvfc_send_event(evt, vhost, default_timeout); 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_ci if (rc) { 464362306a36Sopenharmony_ci tgt_err(tgt, "Failed to send cancel event for ADISC. rc=%d\n", rc); 464462306a36Sopenharmony_ci vhost->abort_threads--; 464562306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 464662306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 464762306a36Sopenharmony_ci } else 464862306a36Sopenharmony_ci tgt_dbg(tgt, "Attempting to cancel ADISC\n"); 464962306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 465062306a36Sopenharmony_ci} 465162306a36Sopenharmony_ci 465262306a36Sopenharmony_ci/** 465362306a36Sopenharmony_ci * ibmvfc_tgt_adisc - Initiate an ADISC for specified target 465462306a36Sopenharmony_ci * @tgt: ibmvfc target struct 465562306a36Sopenharmony_ci * 465662306a36Sopenharmony_ci * When sending an ADISC we end up with two timers running. The 465762306a36Sopenharmony_ci * first timer is the timer in the ibmvfc target struct. If this 465862306a36Sopenharmony_ci * fires, we send a cancel to the target. The second timer is the 465962306a36Sopenharmony_ci * timer on the ibmvfc event for the ADISC, which is longer. If that 466062306a36Sopenharmony_ci * fires, it means the ADISC timed out and our attempt to cancel it 466162306a36Sopenharmony_ci * also failed, so we need to reset the CRQ. 466262306a36Sopenharmony_ci **/ 466362306a36Sopenharmony_cistatic void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) 466462306a36Sopenharmony_ci{ 466562306a36Sopenharmony_ci struct ibmvfc_passthru_mad *mad; 466662306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 466762306a36Sopenharmony_ci struct ibmvfc_event *evt; 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 467062306a36Sopenharmony_ci return; 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_ci kref_get(&tgt->kref); 467362306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 467462306a36Sopenharmony_ci if (!evt) { 467562306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 467662306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 467762306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 467862306a36Sopenharmony_ci return; 467962306a36Sopenharmony_ci } 468062306a36Sopenharmony_ci vhost->discovery_threads++; 468162306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT); 468262306a36Sopenharmony_ci evt->tgt = tgt; 468362306a36Sopenharmony_ci 468462306a36Sopenharmony_ci ibmvfc_init_passthru(evt); 468562306a36Sopenharmony_ci mad = &evt->iu.passthru; 468662306a36Sopenharmony_ci mad->iu.flags = cpu_to_be32(IBMVFC_FC_ELS); 468762306a36Sopenharmony_ci mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id); 468862306a36Sopenharmony_ci mad->iu.cancel_key = cpu_to_be32(tgt->cancel_key); 468962306a36Sopenharmony_ci 469062306a36Sopenharmony_ci mad->fc_iu.payload[0] = cpu_to_be32(IBMVFC_ADISC); 469162306a36Sopenharmony_ci memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name, 469262306a36Sopenharmony_ci sizeof(vhost->login_buf->resp.port_name)); 469362306a36Sopenharmony_ci memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name, 469462306a36Sopenharmony_ci sizeof(vhost->login_buf->resp.node_name)); 469562306a36Sopenharmony_ci mad->fc_iu.payload[6] = cpu_to_be32(be64_to_cpu(vhost->login_buf->resp.scsi_id) & 0x00ffffff); 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci if (timer_pending(&tgt->timer)) 469862306a36Sopenharmony_ci mod_timer(&tgt->timer, jiffies + (IBMVFC_ADISC_TIMEOUT * HZ)); 469962306a36Sopenharmony_ci else { 470062306a36Sopenharmony_ci tgt->timer.expires = jiffies + (IBMVFC_ADISC_TIMEOUT * HZ); 470162306a36Sopenharmony_ci add_timer(&tgt->timer); 470262306a36Sopenharmony_ci } 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 470562306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT)) { 470662306a36Sopenharmony_ci vhost->discovery_threads--; 470762306a36Sopenharmony_ci del_timer(&tgt->timer); 470862306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 470962306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 471062306a36Sopenharmony_ci } else 471162306a36Sopenharmony_ci tgt_dbg(tgt, "Sent ADISC\n"); 471262306a36Sopenharmony_ci} 471362306a36Sopenharmony_ci 471462306a36Sopenharmony_ci/** 471562306a36Sopenharmony_ci * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD 471662306a36Sopenharmony_ci * @evt: ibmvfc event struct 471762306a36Sopenharmony_ci * 471862306a36Sopenharmony_ci **/ 471962306a36Sopenharmony_cistatic void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt) 472062306a36Sopenharmony_ci{ 472162306a36Sopenharmony_ci struct ibmvfc_target *tgt = evt->tgt; 472262306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 472362306a36Sopenharmony_ci struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt; 472462306a36Sopenharmony_ci u32 status = be16_to_cpu(rsp->common.status); 472562306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 472662306a36Sopenharmony_ci 472762306a36Sopenharmony_ci vhost->discovery_threads--; 472862306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 472962306a36Sopenharmony_ci switch (status) { 473062306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 473162306a36Sopenharmony_ci tgt_dbg(tgt, "Query Target succeeded\n"); 473262306a36Sopenharmony_ci if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id) 473362306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 473462306a36Sopenharmony_ci else 473562306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc); 473662306a36Sopenharmony_ci break; 473762306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 473862306a36Sopenharmony_ci break; 473962306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 474062306a36Sopenharmony_ci ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target); 474162306a36Sopenharmony_ci break; 474262306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 474362306a36Sopenharmony_ci default: 474462306a36Sopenharmony_ci if ((be16_to_cpu(rsp->status) & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED && 474562306a36Sopenharmony_ci be16_to_cpu(rsp->error) == IBMVFC_UNABLE_TO_PERFORM_REQ && 474662306a36Sopenharmony_ci be16_to_cpu(rsp->fc_explain) == IBMVFC_PORT_NAME_NOT_REG) 474762306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 474862306a36Sopenharmony_ci else if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error))) 474962306a36Sopenharmony_ci level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target); 475062306a36Sopenharmony_ci else 475162306a36Sopenharmony_ci ibmvfc_del_tgt(tgt); 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_ci tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", 475462306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), 475562306a36Sopenharmony_ci be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), 475662306a36Sopenharmony_ci ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type), 475762306a36Sopenharmony_ci ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), 475862306a36Sopenharmony_ci status); 475962306a36Sopenharmony_ci break; 476062306a36Sopenharmony_ci } 476162306a36Sopenharmony_ci 476262306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 476362306a36Sopenharmony_ci ibmvfc_free_event(evt); 476462306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 476562306a36Sopenharmony_ci} 476662306a36Sopenharmony_ci 476762306a36Sopenharmony_ci/** 476862306a36Sopenharmony_ci * ibmvfc_tgt_query_target - Initiate a Query Target for specified target 476962306a36Sopenharmony_ci * @tgt: ibmvfc target struct 477062306a36Sopenharmony_ci * 477162306a36Sopenharmony_ci **/ 477262306a36Sopenharmony_cistatic void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt) 477362306a36Sopenharmony_ci{ 477462306a36Sopenharmony_ci struct ibmvfc_query_tgt *query_tgt; 477562306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 477662306a36Sopenharmony_ci struct ibmvfc_event *evt; 477762306a36Sopenharmony_ci 477862306a36Sopenharmony_ci if (vhost->discovery_threads >= disc_threads) 477962306a36Sopenharmony_ci return; 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_ci kref_get(&tgt->kref); 478262306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 478362306a36Sopenharmony_ci if (!evt) { 478462306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 478562306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 478662306a36Sopenharmony_ci __ibmvfc_reset_host(vhost); 478762306a36Sopenharmony_ci return; 478862306a36Sopenharmony_ci } 478962306a36Sopenharmony_ci vhost->discovery_threads++; 479062306a36Sopenharmony_ci evt->tgt = tgt; 479162306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_tgt_query_target_done, IBMVFC_MAD_FORMAT); 479262306a36Sopenharmony_ci query_tgt = &evt->iu.query_tgt; 479362306a36Sopenharmony_ci memset(query_tgt, 0, sizeof(*query_tgt)); 479462306a36Sopenharmony_ci query_tgt->common.version = cpu_to_be32(1); 479562306a36Sopenharmony_ci query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET); 479662306a36Sopenharmony_ci query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt)); 479762306a36Sopenharmony_ci query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name); 479862306a36Sopenharmony_ci 479962306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 480062306a36Sopenharmony_ci if (ibmvfc_send_event(evt, vhost, default_timeout)) { 480162306a36Sopenharmony_ci vhost->discovery_threads--; 480262306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 480362306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 480462306a36Sopenharmony_ci } else 480562306a36Sopenharmony_ci tgt_dbg(tgt, "Sent Query Target\n"); 480662306a36Sopenharmony_ci} 480762306a36Sopenharmony_ci 480862306a36Sopenharmony_ci/** 480962306a36Sopenharmony_ci * ibmvfc_alloc_target - Allocate and initialize an ibmvfc target 481062306a36Sopenharmony_ci * @vhost: ibmvfc host struct 481162306a36Sopenharmony_ci * @target: Holds SCSI ID to allocate target forand the WWPN 481262306a36Sopenharmony_ci * 481362306a36Sopenharmony_ci * Returns: 481462306a36Sopenharmony_ci * 0 on success / other on failure 481562306a36Sopenharmony_ci **/ 481662306a36Sopenharmony_cistatic int ibmvfc_alloc_target(struct ibmvfc_host *vhost, 481762306a36Sopenharmony_ci struct ibmvfc_discover_targets_entry *target) 481862306a36Sopenharmony_ci{ 481962306a36Sopenharmony_ci struct ibmvfc_target *stgt = NULL; 482062306a36Sopenharmony_ci struct ibmvfc_target *wtgt = NULL; 482162306a36Sopenharmony_ci struct ibmvfc_target *tgt; 482262306a36Sopenharmony_ci unsigned long flags; 482362306a36Sopenharmony_ci u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK; 482462306a36Sopenharmony_ci u64 wwpn = be64_to_cpu(target->wwpn); 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci /* Look to see if we already have a target allocated for this SCSI ID or WWPN */ 482762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 482862306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 482962306a36Sopenharmony_ci if (tgt->wwpn == wwpn) { 483062306a36Sopenharmony_ci wtgt = tgt; 483162306a36Sopenharmony_ci break; 483262306a36Sopenharmony_ci } 483362306a36Sopenharmony_ci } 483462306a36Sopenharmony_ci 483562306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 483662306a36Sopenharmony_ci if (tgt->scsi_id == scsi_id) { 483762306a36Sopenharmony_ci stgt = tgt; 483862306a36Sopenharmony_ci break; 483962306a36Sopenharmony_ci } 484062306a36Sopenharmony_ci } 484162306a36Sopenharmony_ci 484262306a36Sopenharmony_ci if (wtgt && !stgt) { 484362306a36Sopenharmony_ci /* 484462306a36Sopenharmony_ci * A WWPN target has moved and we still are tracking the old 484562306a36Sopenharmony_ci * SCSI ID. The only way we should be able to get here is if 484662306a36Sopenharmony_ci * we attempted to send an implicit logout for the old SCSI ID 484762306a36Sopenharmony_ci * and it failed for some reason, such as there being I/O 484862306a36Sopenharmony_ci * pending to the target. In this case, we will have already 484962306a36Sopenharmony_ci * deleted the rport from the FC transport so we do a move 485062306a36Sopenharmony_ci * login, which works even with I/O pending, however, if 485162306a36Sopenharmony_ci * there is still I/O pending, it will stay outstanding, so 485262306a36Sopenharmony_ci * we only do this if fast fail is disabled for the rport, 485362306a36Sopenharmony_ci * otherwise we let terminate_rport_io clean up the port 485462306a36Sopenharmony_ci * before we login at the new location. 485562306a36Sopenharmony_ci */ 485662306a36Sopenharmony_ci if (wtgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) { 485762306a36Sopenharmony_ci if (wtgt->move_login) { 485862306a36Sopenharmony_ci /* 485962306a36Sopenharmony_ci * Do a move login here. The old target is no longer 486062306a36Sopenharmony_ci * known to the transport layer We don't use the 486162306a36Sopenharmony_ci * normal ibmvfc_set_tgt_action to set this, as we 486262306a36Sopenharmony_ci * don't normally want to allow this state change. 486362306a36Sopenharmony_ci */ 486462306a36Sopenharmony_ci wtgt->new_scsi_id = scsi_id; 486562306a36Sopenharmony_ci wtgt->action = IBMVFC_TGT_ACTION_INIT; 486662306a36Sopenharmony_ci wtgt->init_retries = 0; 486762306a36Sopenharmony_ci ibmvfc_init_tgt(wtgt, ibmvfc_tgt_move_login); 486862306a36Sopenharmony_ci } 486962306a36Sopenharmony_ci goto unlock_out; 487062306a36Sopenharmony_ci } else { 487162306a36Sopenharmony_ci tgt_err(wtgt, "Unexpected target state: %d, %p\n", 487262306a36Sopenharmony_ci wtgt->action, wtgt->rport); 487362306a36Sopenharmony_ci } 487462306a36Sopenharmony_ci } else if (stgt) { 487562306a36Sopenharmony_ci if (tgt->need_login) 487662306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); 487762306a36Sopenharmony_ci goto unlock_out; 487862306a36Sopenharmony_ci } 487962306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 488062306a36Sopenharmony_ci 488162306a36Sopenharmony_ci tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO); 488262306a36Sopenharmony_ci memset(tgt, 0, sizeof(*tgt)); 488362306a36Sopenharmony_ci tgt->scsi_id = scsi_id; 488462306a36Sopenharmony_ci tgt->wwpn = wwpn; 488562306a36Sopenharmony_ci tgt->vhost = vhost; 488662306a36Sopenharmony_ci tgt->need_login = 1; 488762306a36Sopenharmony_ci timer_setup(&tgt->timer, ibmvfc_adisc_timeout, 0); 488862306a36Sopenharmony_ci kref_init(&tgt->kref); 488962306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); 489062306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 489162306a36Sopenharmony_ci tgt->cancel_key = vhost->task_set++; 489262306a36Sopenharmony_ci list_add_tail(&tgt->queue, &vhost->targets); 489362306a36Sopenharmony_ci 489462306a36Sopenharmony_ciunlock_out: 489562306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 489662306a36Sopenharmony_ci return 0; 489762306a36Sopenharmony_ci} 489862306a36Sopenharmony_ci 489962306a36Sopenharmony_ci/** 490062306a36Sopenharmony_ci * ibmvfc_alloc_targets - Allocate and initialize ibmvfc targets 490162306a36Sopenharmony_ci * @vhost: ibmvfc host struct 490262306a36Sopenharmony_ci * 490362306a36Sopenharmony_ci * Returns: 490462306a36Sopenharmony_ci * 0 on success / other on failure 490562306a36Sopenharmony_ci **/ 490662306a36Sopenharmony_cistatic int ibmvfc_alloc_targets(struct ibmvfc_host *vhost) 490762306a36Sopenharmony_ci{ 490862306a36Sopenharmony_ci int i, rc; 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++) 491162306a36Sopenharmony_ci rc = ibmvfc_alloc_target(vhost, &vhost->disc_buf[i]); 491262306a36Sopenharmony_ci 491362306a36Sopenharmony_ci return rc; 491462306a36Sopenharmony_ci} 491562306a36Sopenharmony_ci 491662306a36Sopenharmony_ci/** 491762306a36Sopenharmony_ci * ibmvfc_discover_targets_done - Completion handler for discover targets MAD 491862306a36Sopenharmony_ci * @evt: ibmvfc event struct 491962306a36Sopenharmony_ci * 492062306a36Sopenharmony_ci **/ 492162306a36Sopenharmony_cistatic void ibmvfc_discover_targets_done(struct ibmvfc_event *evt) 492262306a36Sopenharmony_ci{ 492362306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 492462306a36Sopenharmony_ci struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets; 492562306a36Sopenharmony_ci u32 mad_status = be16_to_cpu(rsp->common.status); 492662306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci switch (mad_status) { 492962306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 493062306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Discover Targets succeeded\n"); 493162306a36Sopenharmony_ci vhost->num_targets = be32_to_cpu(rsp->num_written); 493262306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS); 493362306a36Sopenharmony_ci break; 493462306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 493562306a36Sopenharmony_ci level += ibmvfc_retry_host_init(vhost); 493662306a36Sopenharmony_ci ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n", 493762306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), 493862306a36Sopenharmony_ci be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)); 493962306a36Sopenharmony_ci break; 494062306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 494162306a36Sopenharmony_ci break; 494262306a36Sopenharmony_ci default: 494362306a36Sopenharmony_ci dev_err(vhost->dev, "Invalid Discover Targets response: 0x%x\n", mad_status); 494462306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 494562306a36Sopenharmony_ci break; 494662306a36Sopenharmony_ci } 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci ibmvfc_free_event(evt); 494962306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 495062306a36Sopenharmony_ci} 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_ci/** 495362306a36Sopenharmony_ci * ibmvfc_discover_targets - Send Discover Targets MAD 495462306a36Sopenharmony_ci * @vhost: ibmvfc host struct 495562306a36Sopenharmony_ci * 495662306a36Sopenharmony_ci **/ 495762306a36Sopenharmony_cistatic void ibmvfc_discover_targets(struct ibmvfc_host *vhost) 495862306a36Sopenharmony_ci{ 495962306a36Sopenharmony_ci struct ibmvfc_discover_targets *mad; 496062306a36Sopenharmony_ci struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq); 496162306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 496262306a36Sopenharmony_ci 496362306a36Sopenharmony_ci if (!evt) { 496462306a36Sopenharmony_ci ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n"); 496562306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 496662306a36Sopenharmony_ci return; 496762306a36Sopenharmony_ci } 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT); 497062306a36Sopenharmony_ci mad = &evt->iu.discover_targets; 497162306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 497262306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 497362306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS); 497462306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(*mad)); 497562306a36Sopenharmony_ci mad->bufflen = cpu_to_be32(vhost->disc_buf_sz); 497662306a36Sopenharmony_ci mad->buffer.va = cpu_to_be64(vhost->disc_buf_dma); 497762306a36Sopenharmony_ci mad->buffer.len = cpu_to_be32(vhost->disc_buf_sz); 497862306a36Sopenharmony_ci mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST); 497962306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); 498062306a36Sopenharmony_ci 498162306a36Sopenharmony_ci if (!ibmvfc_send_event(evt, vhost, default_timeout)) 498262306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Sent discover targets\n"); 498362306a36Sopenharmony_ci else 498462306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 498562306a36Sopenharmony_ci} 498662306a36Sopenharmony_ci 498762306a36Sopenharmony_cistatic void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) 498862306a36Sopenharmony_ci{ 498962306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 499062306a36Sopenharmony_ci struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf; 499162306a36Sopenharmony_ci struct ibmvfc_scsi_channels *scrqs = &vhost->scsi_scrqs; 499262306a36Sopenharmony_ci u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status); 499362306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 499462306a36Sopenharmony_ci int flags, active_queues, i; 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci ibmvfc_free_event(evt); 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci switch (mad_status) { 499962306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 500062306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Channel Setup succeeded\n"); 500162306a36Sopenharmony_ci flags = be32_to_cpu(setup->flags); 500262306a36Sopenharmony_ci vhost->do_enquiry = 0; 500362306a36Sopenharmony_ci active_queues = be32_to_cpu(setup->num_scsi_subq_channels); 500462306a36Sopenharmony_ci scrqs->active_queues = active_queues; 500562306a36Sopenharmony_ci 500662306a36Sopenharmony_ci if (flags & IBMVFC_CHANNELS_CANCELED) { 500762306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Channels Canceled\n"); 500862306a36Sopenharmony_ci vhost->using_channels = 0; 500962306a36Sopenharmony_ci } else { 501062306a36Sopenharmony_ci if (active_queues) 501162306a36Sopenharmony_ci vhost->using_channels = 1; 501262306a36Sopenharmony_ci for (i = 0; i < active_queues; i++) 501362306a36Sopenharmony_ci scrqs->scrqs[i].vios_cookie = 501462306a36Sopenharmony_ci be64_to_cpu(setup->channel_handles[i]); 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Using %u channels\n", 501762306a36Sopenharmony_ci vhost->scsi_scrqs.active_queues); 501862306a36Sopenharmony_ci } 501962306a36Sopenharmony_ci break; 502062306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 502162306a36Sopenharmony_ci level += ibmvfc_retry_host_init(vhost); 502262306a36Sopenharmony_ci ibmvfc_log(vhost, level, "Channel Setup failed\n"); 502362306a36Sopenharmony_ci fallthrough; 502462306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 502562306a36Sopenharmony_ci return; 502662306a36Sopenharmony_ci default: 502762306a36Sopenharmony_ci dev_err(vhost->dev, "Invalid Channel Setup response: 0x%x\n", 502862306a36Sopenharmony_ci mad_status); 502962306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 503062306a36Sopenharmony_ci return; 503162306a36Sopenharmony_ci } 503262306a36Sopenharmony_ci 503362306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); 503462306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 503562306a36Sopenharmony_ci} 503662306a36Sopenharmony_ci 503762306a36Sopenharmony_cistatic void ibmvfc_channel_setup(struct ibmvfc_host *vhost) 503862306a36Sopenharmony_ci{ 503962306a36Sopenharmony_ci struct ibmvfc_channel_setup_mad *mad; 504062306a36Sopenharmony_ci struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf; 504162306a36Sopenharmony_ci struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq); 504262306a36Sopenharmony_ci struct ibmvfc_scsi_channels *scrqs = &vhost->scsi_scrqs; 504362306a36Sopenharmony_ci unsigned int num_channels = 504462306a36Sopenharmony_ci min(vhost->client_scsi_channels, vhost->max_vios_scsi_channels); 504562306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 504662306a36Sopenharmony_ci int i; 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ci if (!evt) { 504962306a36Sopenharmony_ci ibmvfc_log(vhost, level, "Channel Setup failed: no available events\n"); 505062306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 505162306a36Sopenharmony_ci return; 505262306a36Sopenharmony_ci } 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci memset(setup_buf, 0, sizeof(*setup_buf)); 505562306a36Sopenharmony_ci if (num_channels == 0) 505662306a36Sopenharmony_ci setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS); 505762306a36Sopenharmony_ci else { 505862306a36Sopenharmony_ci setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels); 505962306a36Sopenharmony_ci for (i = 0; i < num_channels; i++) 506062306a36Sopenharmony_ci setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie); 506162306a36Sopenharmony_ci } 506262306a36Sopenharmony_ci 506362306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT); 506462306a36Sopenharmony_ci mad = &evt->iu.channel_setup; 506562306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 506662306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 506762306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_CHANNEL_SETUP); 506862306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(*mad)); 506962306a36Sopenharmony_ci mad->buffer.va = cpu_to_be64(vhost->channel_setup_dma); 507062306a36Sopenharmony_ci mad->buffer.len = cpu_to_be32(sizeof(*vhost->channel_setup_buf)); 507162306a36Sopenharmony_ci 507262306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); 507362306a36Sopenharmony_ci 507462306a36Sopenharmony_ci if (!ibmvfc_send_event(evt, vhost, default_timeout)) 507562306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Sent channel setup\n"); 507662306a36Sopenharmony_ci else 507762306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); 507862306a36Sopenharmony_ci} 507962306a36Sopenharmony_ci 508062306a36Sopenharmony_cistatic void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt) 508162306a36Sopenharmony_ci{ 508262306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 508362306a36Sopenharmony_ci struct ibmvfc_channel_enquiry *rsp = &evt->xfer_iu->channel_enquiry; 508462306a36Sopenharmony_ci u32 mad_status = be16_to_cpu(rsp->common.status); 508562306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 508662306a36Sopenharmony_ci 508762306a36Sopenharmony_ci switch (mad_status) { 508862306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 508962306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n"); 509062306a36Sopenharmony_ci vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels); 509162306a36Sopenharmony_ci ibmvfc_free_event(evt); 509262306a36Sopenharmony_ci break; 509362306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 509462306a36Sopenharmony_ci level += ibmvfc_retry_host_init(vhost); 509562306a36Sopenharmony_ci ibmvfc_log(vhost, level, "Channel Enquiry failed\n"); 509662306a36Sopenharmony_ci fallthrough; 509762306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 509862306a36Sopenharmony_ci ibmvfc_free_event(evt); 509962306a36Sopenharmony_ci return; 510062306a36Sopenharmony_ci default: 510162306a36Sopenharmony_ci dev_err(vhost->dev, "Invalid Channel Enquiry response: 0x%x\n", 510262306a36Sopenharmony_ci mad_status); 510362306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 510462306a36Sopenharmony_ci ibmvfc_free_event(evt); 510562306a36Sopenharmony_ci return; 510662306a36Sopenharmony_ci } 510762306a36Sopenharmony_ci 510862306a36Sopenharmony_ci ibmvfc_channel_setup(vhost); 510962306a36Sopenharmony_ci} 511062306a36Sopenharmony_ci 511162306a36Sopenharmony_cistatic void ibmvfc_channel_enquiry(struct ibmvfc_host *vhost) 511262306a36Sopenharmony_ci{ 511362306a36Sopenharmony_ci struct ibmvfc_channel_enquiry *mad; 511462306a36Sopenharmony_ci struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq); 511562306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_ci if (!evt) { 511862306a36Sopenharmony_ci ibmvfc_log(vhost, level, "Channel Enquiry failed: no available events\n"); 511962306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 512062306a36Sopenharmony_ci return; 512162306a36Sopenharmony_ci } 512262306a36Sopenharmony_ci 512362306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_channel_enquiry_done, IBMVFC_MAD_FORMAT); 512462306a36Sopenharmony_ci mad = &evt->iu.channel_enquiry; 512562306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 512662306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 512762306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_CHANNEL_ENQUIRY); 512862306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(*mad)); 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci if (mig_channels_only) 513162306a36Sopenharmony_ci mad->flags |= cpu_to_be32(IBMVFC_NO_CHANNELS_TO_CRQ_SUPPORT); 513262306a36Sopenharmony_ci if (mig_no_less_channels) 513362306a36Sopenharmony_ci mad->flags |= cpu_to_be32(IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT); 513462306a36Sopenharmony_ci 513562306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); 513662306a36Sopenharmony_ci 513762306a36Sopenharmony_ci if (!ibmvfc_send_event(evt, vhost, default_timeout)) 513862306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Send channel enquiry\n"); 513962306a36Sopenharmony_ci else 514062306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 514162306a36Sopenharmony_ci} 514262306a36Sopenharmony_ci 514362306a36Sopenharmony_ci/** 514462306a36Sopenharmony_ci * ibmvfc_npiv_login_done - Completion handler for NPIV Login 514562306a36Sopenharmony_ci * @evt: ibmvfc event struct 514662306a36Sopenharmony_ci * 514762306a36Sopenharmony_ci **/ 514862306a36Sopenharmony_cistatic void ibmvfc_npiv_login_done(struct ibmvfc_event *evt) 514962306a36Sopenharmony_ci{ 515062306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 515162306a36Sopenharmony_ci u32 mad_status = be16_to_cpu(evt->xfer_iu->npiv_login.common.status); 515262306a36Sopenharmony_ci struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp; 515362306a36Sopenharmony_ci unsigned int npiv_max_sectors; 515462306a36Sopenharmony_ci int level = IBMVFC_DEFAULT_LOG_LEVEL; 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_ci switch (mad_status) { 515762306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 515862306a36Sopenharmony_ci ibmvfc_free_event(evt); 515962306a36Sopenharmony_ci break; 516062306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 516162306a36Sopenharmony_ci if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error))) 516262306a36Sopenharmony_ci level += ibmvfc_retry_host_init(vhost); 516362306a36Sopenharmony_ci else 516462306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 516562306a36Sopenharmony_ci ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n", 516662306a36Sopenharmony_ci ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), 516762306a36Sopenharmony_ci be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)); 516862306a36Sopenharmony_ci ibmvfc_free_event(evt); 516962306a36Sopenharmony_ci return; 517062306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 517162306a36Sopenharmony_ci ibmvfc_retry_host_init(vhost); 517262306a36Sopenharmony_ci fallthrough; 517362306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 517462306a36Sopenharmony_ci ibmvfc_free_event(evt); 517562306a36Sopenharmony_ci return; 517662306a36Sopenharmony_ci default: 517762306a36Sopenharmony_ci dev_err(vhost->dev, "Invalid NPIV Login response: 0x%x\n", mad_status); 517862306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 517962306a36Sopenharmony_ci ibmvfc_free_event(evt); 518062306a36Sopenharmony_ci return; 518162306a36Sopenharmony_ci } 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_ci vhost->client_migrated = 0; 518462306a36Sopenharmony_ci 518562306a36Sopenharmony_ci if (!(be32_to_cpu(rsp->flags) & IBMVFC_NATIVE_FC)) { 518662306a36Sopenharmony_ci dev_err(vhost->dev, "Virtual adapter does not support FC. %x\n", 518762306a36Sopenharmony_ci rsp->flags); 518862306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 518962306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 519062306a36Sopenharmony_ci return; 519162306a36Sopenharmony_ci } 519262306a36Sopenharmony_ci 519362306a36Sopenharmony_ci if (be32_to_cpu(rsp->max_cmds) <= IBMVFC_NUM_INTERNAL_REQ) { 519462306a36Sopenharmony_ci dev_err(vhost->dev, "Virtual adapter supported queue depth too small: %d\n", 519562306a36Sopenharmony_ci rsp->max_cmds); 519662306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 519762306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 519862306a36Sopenharmony_ci return; 519962306a36Sopenharmony_ci } 520062306a36Sopenharmony_ci 520162306a36Sopenharmony_ci vhost->logged_in = 1; 520262306a36Sopenharmony_ci npiv_max_sectors = min((uint)(be64_to_cpu(rsp->max_dma_len) >> 9), IBMVFC_MAX_SECTORS); 520362306a36Sopenharmony_ci dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n", 520462306a36Sopenharmony_ci rsp->partition_name, rsp->device_name, rsp->port_loc_code, 520562306a36Sopenharmony_ci rsp->drc_name, npiv_max_sectors); 520662306a36Sopenharmony_ci 520762306a36Sopenharmony_ci fc_host_fabric_name(vhost->host) = be64_to_cpu(rsp->node_name); 520862306a36Sopenharmony_ci fc_host_node_name(vhost->host) = be64_to_cpu(rsp->node_name); 520962306a36Sopenharmony_ci fc_host_port_name(vhost->host) = be64_to_cpu(rsp->port_name); 521062306a36Sopenharmony_ci fc_host_port_id(vhost->host) = be64_to_cpu(rsp->scsi_id); 521162306a36Sopenharmony_ci fc_host_port_type(vhost->host) = FC_PORTTYPE_NPIV; 521262306a36Sopenharmony_ci fc_host_supported_classes(vhost->host) = 0; 521362306a36Sopenharmony_ci if (be32_to_cpu(rsp->service_parms.class1_parms[0]) & 0x80000000) 521462306a36Sopenharmony_ci fc_host_supported_classes(vhost->host) |= FC_COS_CLASS1; 521562306a36Sopenharmony_ci if (be32_to_cpu(rsp->service_parms.class2_parms[0]) & 0x80000000) 521662306a36Sopenharmony_ci fc_host_supported_classes(vhost->host) |= FC_COS_CLASS2; 521762306a36Sopenharmony_ci if (be32_to_cpu(rsp->service_parms.class3_parms[0]) & 0x80000000) 521862306a36Sopenharmony_ci fc_host_supported_classes(vhost->host) |= FC_COS_CLASS3; 521962306a36Sopenharmony_ci fc_host_maxframe_size(vhost->host) = 522062306a36Sopenharmony_ci be16_to_cpu(rsp->service_parms.common.bb_rcv_sz) & 0x0fff; 522162306a36Sopenharmony_ci 522262306a36Sopenharmony_ci vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ; 522362306a36Sopenharmony_ci vhost->host->max_sectors = npiv_max_sectors; 522462306a36Sopenharmony_ci 522562306a36Sopenharmony_ci if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) { 522662306a36Sopenharmony_ci ibmvfc_channel_enquiry(vhost); 522762306a36Sopenharmony_ci } else { 522862306a36Sopenharmony_ci vhost->do_enquiry = 0; 522962306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); 523062306a36Sopenharmony_ci wake_up(&vhost->work_wait_q); 523162306a36Sopenharmony_ci } 523262306a36Sopenharmony_ci} 523362306a36Sopenharmony_ci 523462306a36Sopenharmony_ci/** 523562306a36Sopenharmony_ci * ibmvfc_npiv_login - Sends NPIV login 523662306a36Sopenharmony_ci * @vhost: ibmvfc host struct 523762306a36Sopenharmony_ci * 523862306a36Sopenharmony_ci **/ 523962306a36Sopenharmony_cistatic void ibmvfc_npiv_login(struct ibmvfc_host *vhost) 524062306a36Sopenharmony_ci{ 524162306a36Sopenharmony_ci struct ibmvfc_npiv_login_mad *mad; 524262306a36Sopenharmony_ci struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq); 524362306a36Sopenharmony_ci 524462306a36Sopenharmony_ci if (!evt) { 524562306a36Sopenharmony_ci ibmvfc_dbg(vhost, "NPIV Login failed: no available events\n"); 524662306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 524762306a36Sopenharmony_ci return; 524862306a36Sopenharmony_ci } 524962306a36Sopenharmony_ci 525062306a36Sopenharmony_ci ibmvfc_gather_partition_info(vhost); 525162306a36Sopenharmony_ci ibmvfc_set_login_info(vhost); 525262306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_npiv_login_done, IBMVFC_MAD_FORMAT); 525362306a36Sopenharmony_ci 525462306a36Sopenharmony_ci memcpy(vhost->login_buf, &vhost->login_info, sizeof(vhost->login_info)); 525562306a36Sopenharmony_ci mad = &evt->iu.npiv_login; 525662306a36Sopenharmony_ci memset(mad, 0, sizeof(struct ibmvfc_npiv_login_mad)); 525762306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 525862306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_NPIV_LOGIN); 525962306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(struct ibmvfc_npiv_login_mad)); 526062306a36Sopenharmony_ci mad->buffer.va = cpu_to_be64(vhost->login_buf_dma); 526162306a36Sopenharmony_ci mad->buffer.len = cpu_to_be32(sizeof(*vhost->login_buf)); 526262306a36Sopenharmony_ci 526362306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); 526462306a36Sopenharmony_ci 526562306a36Sopenharmony_ci if (!ibmvfc_send_event(evt, vhost, default_timeout)) 526662306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Sent NPIV login\n"); 526762306a36Sopenharmony_ci else 526862306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 526962306a36Sopenharmony_ci} 527062306a36Sopenharmony_ci 527162306a36Sopenharmony_ci/** 527262306a36Sopenharmony_ci * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout 527362306a36Sopenharmony_ci * @evt: ibmvfc event struct 527462306a36Sopenharmony_ci * 527562306a36Sopenharmony_ci **/ 527662306a36Sopenharmony_cistatic void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt) 527762306a36Sopenharmony_ci{ 527862306a36Sopenharmony_ci struct ibmvfc_host *vhost = evt->vhost; 527962306a36Sopenharmony_ci u32 mad_status = be16_to_cpu(evt->xfer_iu->npiv_logout.common.status); 528062306a36Sopenharmony_ci 528162306a36Sopenharmony_ci ibmvfc_free_event(evt); 528262306a36Sopenharmony_ci 528362306a36Sopenharmony_ci switch (mad_status) { 528462306a36Sopenharmony_ci case IBMVFC_MAD_SUCCESS: 528562306a36Sopenharmony_ci if (list_empty(&vhost->crq.sent) && 528662306a36Sopenharmony_ci vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) { 528762306a36Sopenharmony_ci ibmvfc_init_host(vhost); 528862306a36Sopenharmony_ci return; 528962306a36Sopenharmony_ci } 529062306a36Sopenharmony_ci break; 529162306a36Sopenharmony_ci case IBMVFC_MAD_FAILED: 529262306a36Sopenharmony_ci case IBMVFC_MAD_NOT_SUPPORTED: 529362306a36Sopenharmony_ci case IBMVFC_MAD_CRQ_ERROR: 529462306a36Sopenharmony_ci case IBMVFC_MAD_DRIVER_FAILED: 529562306a36Sopenharmony_ci default: 529662306a36Sopenharmony_ci ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status); 529762306a36Sopenharmony_ci break; 529862306a36Sopenharmony_ci } 529962306a36Sopenharmony_ci 530062306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 530162306a36Sopenharmony_ci} 530262306a36Sopenharmony_ci 530362306a36Sopenharmony_ci/** 530462306a36Sopenharmony_ci * ibmvfc_npiv_logout - Issue an NPIV Logout 530562306a36Sopenharmony_ci * @vhost: ibmvfc host struct 530662306a36Sopenharmony_ci * 530762306a36Sopenharmony_ci **/ 530862306a36Sopenharmony_cistatic void ibmvfc_npiv_logout(struct ibmvfc_host *vhost) 530962306a36Sopenharmony_ci{ 531062306a36Sopenharmony_ci struct ibmvfc_npiv_logout_mad *mad; 531162306a36Sopenharmony_ci struct ibmvfc_event *evt; 531262306a36Sopenharmony_ci 531362306a36Sopenharmony_ci evt = ibmvfc_get_event(&vhost->crq); 531462306a36Sopenharmony_ci if (!evt) { 531562306a36Sopenharmony_ci ibmvfc_dbg(vhost, "NPIV Logout failed: no available events\n"); 531662306a36Sopenharmony_ci ibmvfc_hard_reset_host(vhost); 531762306a36Sopenharmony_ci return; 531862306a36Sopenharmony_ci } 531962306a36Sopenharmony_ci 532062306a36Sopenharmony_ci ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT); 532162306a36Sopenharmony_ci 532262306a36Sopenharmony_ci mad = &evt->iu.npiv_logout; 532362306a36Sopenharmony_ci memset(mad, 0, sizeof(*mad)); 532462306a36Sopenharmony_ci mad->common.version = cpu_to_be32(1); 532562306a36Sopenharmony_ci mad->common.opcode = cpu_to_be32(IBMVFC_NPIV_LOGOUT); 532662306a36Sopenharmony_ci mad->common.length = cpu_to_be16(sizeof(struct ibmvfc_npiv_logout_mad)); 532762306a36Sopenharmony_ci 532862306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT); 532962306a36Sopenharmony_ci 533062306a36Sopenharmony_ci if (!ibmvfc_send_event(evt, vhost, default_timeout)) 533162306a36Sopenharmony_ci ibmvfc_dbg(vhost, "Sent NPIV logout\n"); 533262306a36Sopenharmony_ci else 533362306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 533462306a36Sopenharmony_ci} 533562306a36Sopenharmony_ci 533662306a36Sopenharmony_ci/** 533762306a36Sopenharmony_ci * ibmvfc_dev_init_to_do - Is there target initialization work to do? 533862306a36Sopenharmony_ci * @vhost: ibmvfc host struct 533962306a36Sopenharmony_ci * 534062306a36Sopenharmony_ci * Returns: 534162306a36Sopenharmony_ci * 1 if work to do / 0 if not 534262306a36Sopenharmony_ci **/ 534362306a36Sopenharmony_cistatic int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost) 534462306a36Sopenharmony_ci{ 534562306a36Sopenharmony_ci struct ibmvfc_target *tgt; 534662306a36Sopenharmony_ci 534762306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 534862306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_INIT || 534962306a36Sopenharmony_ci tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) 535062306a36Sopenharmony_ci return 1; 535162306a36Sopenharmony_ci } 535262306a36Sopenharmony_ci 535362306a36Sopenharmony_ci return 0; 535462306a36Sopenharmony_ci} 535562306a36Sopenharmony_ci 535662306a36Sopenharmony_ci/** 535762306a36Sopenharmony_ci * ibmvfc_dev_logo_to_do - Is there target logout work to do? 535862306a36Sopenharmony_ci * @vhost: ibmvfc host struct 535962306a36Sopenharmony_ci * 536062306a36Sopenharmony_ci * Returns: 536162306a36Sopenharmony_ci * 1 if work to do / 0 if not 536262306a36Sopenharmony_ci **/ 536362306a36Sopenharmony_cistatic int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost) 536462306a36Sopenharmony_ci{ 536562306a36Sopenharmony_ci struct ibmvfc_target *tgt; 536662306a36Sopenharmony_ci 536762306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 536862306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT || 536962306a36Sopenharmony_ci tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT) 537062306a36Sopenharmony_ci return 1; 537162306a36Sopenharmony_ci } 537262306a36Sopenharmony_ci return 0; 537362306a36Sopenharmony_ci} 537462306a36Sopenharmony_ci 537562306a36Sopenharmony_ci/** 537662306a36Sopenharmony_ci * __ibmvfc_work_to_do - Is there task level work to do? (no locking) 537762306a36Sopenharmony_ci * @vhost: ibmvfc host struct 537862306a36Sopenharmony_ci * 537962306a36Sopenharmony_ci * Returns: 538062306a36Sopenharmony_ci * 1 if work to do / 0 if not 538162306a36Sopenharmony_ci **/ 538262306a36Sopenharmony_cistatic int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) 538362306a36Sopenharmony_ci{ 538462306a36Sopenharmony_ci struct ibmvfc_target *tgt; 538562306a36Sopenharmony_ci 538662306a36Sopenharmony_ci if (kthread_should_stop()) 538762306a36Sopenharmony_ci return 1; 538862306a36Sopenharmony_ci switch (vhost->action) { 538962306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_NONE: 539062306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT_WAIT: 539162306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_LOGO_WAIT: 539262306a36Sopenharmony_ci return 0; 539362306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_INIT: 539462306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_QUERY_TGTS: 539562306a36Sopenharmony_ci if (vhost->discovery_threads == disc_threads) 539662306a36Sopenharmony_ci return 0; 539762306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 539862306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_INIT) 539962306a36Sopenharmony_ci return 1; 540062306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 540162306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) 540262306a36Sopenharmony_ci return 0; 540362306a36Sopenharmony_ci return 1; 540462306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL: 540562306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: 540662306a36Sopenharmony_ci if (vhost->discovery_threads == disc_threads) 540762306a36Sopenharmony_ci return 0; 540862306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 540962306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) 541062306a36Sopenharmony_ci return 1; 541162306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 541262306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT) 541362306a36Sopenharmony_ci return 0; 541462306a36Sopenharmony_ci return 1; 541562306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_LOGO: 541662306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT: 541762306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_ALLOC_TGTS: 541862306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_QUERY: 541962306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_RESET: 542062306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_REENABLE: 542162306a36Sopenharmony_ci default: 542262306a36Sopenharmony_ci break; 542362306a36Sopenharmony_ci } 542462306a36Sopenharmony_ci 542562306a36Sopenharmony_ci return 1; 542662306a36Sopenharmony_ci} 542762306a36Sopenharmony_ci 542862306a36Sopenharmony_ci/** 542962306a36Sopenharmony_ci * ibmvfc_work_to_do - Is there task level work to do? 543062306a36Sopenharmony_ci * @vhost: ibmvfc host struct 543162306a36Sopenharmony_ci * 543262306a36Sopenharmony_ci * Returns: 543362306a36Sopenharmony_ci * 1 if work to do / 0 if not 543462306a36Sopenharmony_ci **/ 543562306a36Sopenharmony_cistatic int ibmvfc_work_to_do(struct ibmvfc_host *vhost) 543662306a36Sopenharmony_ci{ 543762306a36Sopenharmony_ci unsigned long flags; 543862306a36Sopenharmony_ci int rc; 543962306a36Sopenharmony_ci 544062306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 544162306a36Sopenharmony_ci rc = __ibmvfc_work_to_do(vhost); 544262306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 544362306a36Sopenharmony_ci return rc; 544462306a36Sopenharmony_ci} 544562306a36Sopenharmony_ci 544662306a36Sopenharmony_ci/** 544762306a36Sopenharmony_ci * ibmvfc_log_ae - Log async events if necessary 544862306a36Sopenharmony_ci * @vhost: ibmvfc host struct 544962306a36Sopenharmony_ci * @events: events to log 545062306a36Sopenharmony_ci * 545162306a36Sopenharmony_ci **/ 545262306a36Sopenharmony_cistatic void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events) 545362306a36Sopenharmony_ci{ 545462306a36Sopenharmony_ci if (events & IBMVFC_AE_RSCN) 545562306a36Sopenharmony_ci fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_RSCN, 0); 545662306a36Sopenharmony_ci if ((events & IBMVFC_AE_LINKDOWN) && 545762306a36Sopenharmony_ci vhost->state >= IBMVFC_HALTED) 545862306a36Sopenharmony_ci fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKDOWN, 0); 545962306a36Sopenharmony_ci if ((events & IBMVFC_AE_LINKUP) && 546062306a36Sopenharmony_ci vhost->state == IBMVFC_INITIALIZING) 546162306a36Sopenharmony_ci fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0); 546262306a36Sopenharmony_ci} 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_ci/** 546562306a36Sopenharmony_ci * ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port 546662306a36Sopenharmony_ci * @tgt: ibmvfc target struct 546762306a36Sopenharmony_ci * 546862306a36Sopenharmony_ci **/ 546962306a36Sopenharmony_cistatic void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) 547062306a36Sopenharmony_ci{ 547162306a36Sopenharmony_ci struct ibmvfc_host *vhost = tgt->vhost; 547262306a36Sopenharmony_ci struct fc_rport *rport; 547362306a36Sopenharmony_ci unsigned long flags; 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci tgt_dbg(tgt, "Adding rport\n"); 547662306a36Sopenharmony_ci rport = fc_remote_port_add(vhost->host, 0, &tgt->ids); 547762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 547862306a36Sopenharmony_ci 547962306a36Sopenharmony_ci if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { 548062306a36Sopenharmony_ci tgt_dbg(tgt, "Deleting rport\n"); 548162306a36Sopenharmony_ci list_del(&tgt->queue); 548262306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT); 548362306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 548462306a36Sopenharmony_ci fc_remote_port_delete(rport); 548562306a36Sopenharmony_ci del_timer_sync(&tgt->timer); 548662306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 548762306a36Sopenharmony_ci return; 548862306a36Sopenharmony_ci } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { 548962306a36Sopenharmony_ci tgt_dbg(tgt, "Deleting rport with outstanding I/O\n"); 549062306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT); 549162306a36Sopenharmony_ci tgt->rport = NULL; 549262306a36Sopenharmony_ci tgt->init_retries = 0; 549362306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 549462306a36Sopenharmony_ci fc_remote_port_delete(rport); 549562306a36Sopenharmony_ci return; 549662306a36Sopenharmony_ci } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) { 549762306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 549862306a36Sopenharmony_ci return; 549962306a36Sopenharmony_ci } 550062306a36Sopenharmony_ci 550162306a36Sopenharmony_ci if (rport) { 550262306a36Sopenharmony_ci tgt_dbg(tgt, "rport add succeeded\n"); 550362306a36Sopenharmony_ci tgt->rport = rport; 550462306a36Sopenharmony_ci rport->maxframe_size = be16_to_cpu(tgt->service_parms.common.bb_rcv_sz) & 0x0fff; 550562306a36Sopenharmony_ci rport->supported_classes = 0; 550662306a36Sopenharmony_ci tgt->target_id = rport->scsi_target_id; 550762306a36Sopenharmony_ci if (be32_to_cpu(tgt->service_parms.class1_parms[0]) & 0x80000000) 550862306a36Sopenharmony_ci rport->supported_classes |= FC_COS_CLASS1; 550962306a36Sopenharmony_ci if (be32_to_cpu(tgt->service_parms.class2_parms[0]) & 0x80000000) 551062306a36Sopenharmony_ci rport->supported_classes |= FC_COS_CLASS2; 551162306a36Sopenharmony_ci if (be32_to_cpu(tgt->service_parms.class3_parms[0]) & 0x80000000) 551262306a36Sopenharmony_ci rport->supported_classes |= FC_COS_CLASS3; 551362306a36Sopenharmony_ci if (rport->rqst_q) 551462306a36Sopenharmony_ci blk_queue_max_segments(rport->rqst_q, 1); 551562306a36Sopenharmony_ci } else 551662306a36Sopenharmony_ci tgt_dbg(tgt, "rport add failed\n"); 551762306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 551862306a36Sopenharmony_ci} 551962306a36Sopenharmony_ci 552062306a36Sopenharmony_ci/** 552162306a36Sopenharmony_ci * ibmvfc_do_work - Do task level work 552262306a36Sopenharmony_ci * @vhost: ibmvfc host struct 552362306a36Sopenharmony_ci * 552462306a36Sopenharmony_ci **/ 552562306a36Sopenharmony_cistatic void ibmvfc_do_work(struct ibmvfc_host *vhost) 552662306a36Sopenharmony_ci{ 552762306a36Sopenharmony_ci struct ibmvfc_target *tgt; 552862306a36Sopenharmony_ci unsigned long flags; 552962306a36Sopenharmony_ci struct fc_rport *rport; 553062306a36Sopenharmony_ci LIST_HEAD(purge); 553162306a36Sopenharmony_ci int rc; 553262306a36Sopenharmony_ci 553362306a36Sopenharmony_ci ibmvfc_log_ae(vhost, vhost->events_to_log); 553462306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 553562306a36Sopenharmony_ci vhost->events_to_log = 0; 553662306a36Sopenharmony_ci switch (vhost->action) { 553762306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_NONE: 553862306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_LOGO_WAIT: 553962306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT_WAIT: 554062306a36Sopenharmony_ci break; 554162306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_RESET: 554262306a36Sopenharmony_ci list_splice_init(&vhost->purge, &purge); 554362306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 554462306a36Sopenharmony_ci ibmvfc_complete_purge(&purge); 554562306a36Sopenharmony_ci rc = ibmvfc_reset_crq(vhost); 554662306a36Sopenharmony_ci 554762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 554862306a36Sopenharmony_ci if (!rc || rc == H_CLOSED) 554962306a36Sopenharmony_ci vio_enable_interrupts(to_vio_dev(vhost->dev)); 555062306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_RESET) { 555162306a36Sopenharmony_ci /* 555262306a36Sopenharmony_ci * The only action we could have changed to would have 555362306a36Sopenharmony_ci * been reenable, in which case, we skip the rest of 555462306a36Sopenharmony_ci * this path and wait until we've done the re-enable 555562306a36Sopenharmony_ci * before sending the crq init. 555662306a36Sopenharmony_ci */ 555762306a36Sopenharmony_ci vhost->action = IBMVFC_HOST_ACTION_TGT_DEL; 555862306a36Sopenharmony_ci 555962306a36Sopenharmony_ci if (rc || (rc = ibmvfc_send_crq_init(vhost)) || 556062306a36Sopenharmony_ci (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) { 556162306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 556262306a36Sopenharmony_ci dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc); 556362306a36Sopenharmony_ci } 556462306a36Sopenharmony_ci } 556562306a36Sopenharmony_ci break; 556662306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_REENABLE: 556762306a36Sopenharmony_ci list_splice_init(&vhost->purge, &purge); 556862306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 556962306a36Sopenharmony_ci ibmvfc_complete_purge(&purge); 557062306a36Sopenharmony_ci rc = ibmvfc_reenable_crq_queue(vhost); 557162306a36Sopenharmony_ci 557262306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 557362306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_REENABLE) { 557462306a36Sopenharmony_ci /* 557562306a36Sopenharmony_ci * The only action we could have changed to would have 557662306a36Sopenharmony_ci * been reset, in which case, we skip the rest of this 557762306a36Sopenharmony_ci * path and wait until we've done the reset before 557862306a36Sopenharmony_ci * sending the crq init. 557962306a36Sopenharmony_ci */ 558062306a36Sopenharmony_ci vhost->action = IBMVFC_HOST_ACTION_TGT_DEL; 558162306a36Sopenharmony_ci if (rc || (rc = ibmvfc_send_crq_init(vhost))) { 558262306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); 558362306a36Sopenharmony_ci dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc); 558462306a36Sopenharmony_ci } 558562306a36Sopenharmony_ci } 558662306a36Sopenharmony_ci break; 558762306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_LOGO: 558862306a36Sopenharmony_ci vhost->job_step(vhost); 558962306a36Sopenharmony_ci break; 559062306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_INIT: 559162306a36Sopenharmony_ci BUG_ON(vhost->state != IBMVFC_INITIALIZING); 559262306a36Sopenharmony_ci if (vhost->delay_init) { 559362306a36Sopenharmony_ci vhost->delay_init = 0; 559462306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 559562306a36Sopenharmony_ci ssleep(15); 559662306a36Sopenharmony_ci return; 559762306a36Sopenharmony_ci } else 559862306a36Sopenharmony_ci vhost->job_step(vhost); 559962306a36Sopenharmony_ci break; 560062306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_QUERY: 560162306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) 560262306a36Sopenharmony_ci ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target); 560362306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS); 560462306a36Sopenharmony_ci break; 560562306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_QUERY_TGTS: 560662306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 560762306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_INIT) { 560862306a36Sopenharmony_ci tgt->job_step(tgt); 560962306a36Sopenharmony_ci break; 561062306a36Sopenharmony_ci } 561162306a36Sopenharmony_ci } 561262306a36Sopenharmony_ci 561362306a36Sopenharmony_ci if (!ibmvfc_dev_init_to_do(vhost)) 561462306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL); 561562306a36Sopenharmony_ci break; 561662306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL: 561762306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: 561862306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 561962306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) { 562062306a36Sopenharmony_ci tgt->job_step(tgt); 562162306a36Sopenharmony_ci break; 562262306a36Sopenharmony_ci } 562362306a36Sopenharmony_ci } 562462306a36Sopenharmony_ci 562562306a36Sopenharmony_ci if (ibmvfc_dev_logo_to_do(vhost)) { 562662306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 562762306a36Sopenharmony_ci return; 562862306a36Sopenharmony_ci } 562962306a36Sopenharmony_ci 563062306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 563162306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { 563262306a36Sopenharmony_ci tgt_dbg(tgt, "Deleting rport\n"); 563362306a36Sopenharmony_ci rport = tgt->rport; 563462306a36Sopenharmony_ci tgt->rport = NULL; 563562306a36Sopenharmony_ci list_del(&tgt->queue); 563662306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT); 563762306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 563862306a36Sopenharmony_ci if (rport) 563962306a36Sopenharmony_ci fc_remote_port_delete(rport); 564062306a36Sopenharmony_ci del_timer_sync(&tgt->timer); 564162306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 564262306a36Sopenharmony_ci return; 564362306a36Sopenharmony_ci } else if (tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { 564462306a36Sopenharmony_ci tgt_dbg(tgt, "Deleting rport with I/O outstanding\n"); 564562306a36Sopenharmony_ci rport = tgt->rport; 564662306a36Sopenharmony_ci tgt->rport = NULL; 564762306a36Sopenharmony_ci tgt->init_retries = 0; 564862306a36Sopenharmony_ci ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT); 564962306a36Sopenharmony_ci 565062306a36Sopenharmony_ci /* 565162306a36Sopenharmony_ci * If fast fail is enabled, we wait for it to fire and then clean up 565262306a36Sopenharmony_ci * the old port, since we expect the fast fail timer to clean up the 565362306a36Sopenharmony_ci * outstanding I/O faster than waiting for normal command timeouts. 565462306a36Sopenharmony_ci * However, if fast fail is disabled, any I/O outstanding to the 565562306a36Sopenharmony_ci * rport LUNs will stay outstanding indefinitely, since the EH handlers 565662306a36Sopenharmony_ci * won't get invoked for I/O's timing out. If this is a NPIV failover 565762306a36Sopenharmony_ci * scenario, the better alternative is to use the move login. 565862306a36Sopenharmony_ci */ 565962306a36Sopenharmony_ci if (rport && rport->fast_io_fail_tmo == -1) 566062306a36Sopenharmony_ci tgt->move_login = 1; 566162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 566262306a36Sopenharmony_ci if (rport) 566362306a36Sopenharmony_ci fc_remote_port_delete(rport); 566462306a36Sopenharmony_ci return; 566562306a36Sopenharmony_ci } 566662306a36Sopenharmony_ci } 566762306a36Sopenharmony_ci 566862306a36Sopenharmony_ci if (vhost->state == IBMVFC_INITIALIZING) { 566962306a36Sopenharmony_ci if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) { 567062306a36Sopenharmony_ci if (vhost->reinit) { 567162306a36Sopenharmony_ci vhost->reinit = 0; 567262306a36Sopenharmony_ci scsi_block_requests(vhost->host); 567362306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); 567462306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 567562306a36Sopenharmony_ci } else { 567662306a36Sopenharmony_ci ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE); 567762306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); 567862306a36Sopenharmony_ci wake_up(&vhost->init_wait_q); 567962306a36Sopenharmony_ci schedule_work(&vhost->rport_add_work_q); 568062306a36Sopenharmony_ci vhost->init_retries = 0; 568162306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 568262306a36Sopenharmony_ci scsi_unblock_requests(vhost->host); 568362306a36Sopenharmony_ci } 568462306a36Sopenharmony_ci 568562306a36Sopenharmony_ci return; 568662306a36Sopenharmony_ci } else { 568762306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); 568862306a36Sopenharmony_ci vhost->job_step = ibmvfc_discover_targets; 568962306a36Sopenharmony_ci } 569062306a36Sopenharmony_ci } else { 569162306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); 569262306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 569362306a36Sopenharmony_ci scsi_unblock_requests(vhost->host); 569462306a36Sopenharmony_ci wake_up(&vhost->init_wait_q); 569562306a36Sopenharmony_ci return; 569662306a36Sopenharmony_ci } 569762306a36Sopenharmony_ci break; 569862306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_ALLOC_TGTS: 569962306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_INIT); 570062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 570162306a36Sopenharmony_ci ibmvfc_alloc_targets(vhost); 570262306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 570362306a36Sopenharmony_ci break; 570462306a36Sopenharmony_ci case IBMVFC_HOST_ACTION_TGT_INIT: 570562306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 570662306a36Sopenharmony_ci if (tgt->action == IBMVFC_TGT_ACTION_INIT) { 570762306a36Sopenharmony_ci tgt->job_step(tgt); 570862306a36Sopenharmony_ci break; 570962306a36Sopenharmony_ci } 571062306a36Sopenharmony_ci } 571162306a36Sopenharmony_ci 571262306a36Sopenharmony_ci if (!ibmvfc_dev_init_to_do(vhost)) 571362306a36Sopenharmony_ci ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED); 571462306a36Sopenharmony_ci break; 571562306a36Sopenharmony_ci default: 571662306a36Sopenharmony_ci break; 571762306a36Sopenharmony_ci } 571862306a36Sopenharmony_ci 571962306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 572062306a36Sopenharmony_ci} 572162306a36Sopenharmony_ci 572262306a36Sopenharmony_ci/** 572362306a36Sopenharmony_ci * ibmvfc_work - Do task level work 572462306a36Sopenharmony_ci * @data: ibmvfc host struct 572562306a36Sopenharmony_ci * 572662306a36Sopenharmony_ci * Returns: 572762306a36Sopenharmony_ci * zero 572862306a36Sopenharmony_ci **/ 572962306a36Sopenharmony_cistatic int ibmvfc_work(void *data) 573062306a36Sopenharmony_ci{ 573162306a36Sopenharmony_ci struct ibmvfc_host *vhost = data; 573262306a36Sopenharmony_ci int rc; 573362306a36Sopenharmony_ci 573462306a36Sopenharmony_ci set_user_nice(current, MIN_NICE); 573562306a36Sopenharmony_ci 573662306a36Sopenharmony_ci while (1) { 573762306a36Sopenharmony_ci rc = wait_event_interruptible(vhost->work_wait_q, 573862306a36Sopenharmony_ci ibmvfc_work_to_do(vhost)); 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_ci BUG_ON(rc); 574162306a36Sopenharmony_ci 574262306a36Sopenharmony_ci if (kthread_should_stop()) 574362306a36Sopenharmony_ci break; 574462306a36Sopenharmony_ci 574562306a36Sopenharmony_ci ibmvfc_do_work(vhost); 574662306a36Sopenharmony_ci } 574762306a36Sopenharmony_ci 574862306a36Sopenharmony_ci ibmvfc_dbg(vhost, "ibmvfc kthread exiting...\n"); 574962306a36Sopenharmony_ci return 0; 575062306a36Sopenharmony_ci} 575162306a36Sopenharmony_ci 575262306a36Sopenharmony_ci/** 575362306a36Sopenharmony_ci * ibmvfc_alloc_queue - Allocate queue 575462306a36Sopenharmony_ci * @vhost: ibmvfc host struct 575562306a36Sopenharmony_ci * @queue: ibmvfc queue to allocate 575662306a36Sopenharmony_ci * @fmt: queue format to allocate 575762306a36Sopenharmony_ci * 575862306a36Sopenharmony_ci * Returns: 575962306a36Sopenharmony_ci * 0 on success / non-zero on failure 576062306a36Sopenharmony_ci **/ 576162306a36Sopenharmony_cistatic int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, 576262306a36Sopenharmony_ci struct ibmvfc_queue *queue, 576362306a36Sopenharmony_ci enum ibmvfc_msg_fmt fmt) 576462306a36Sopenharmony_ci{ 576562306a36Sopenharmony_ci struct device *dev = vhost->dev; 576662306a36Sopenharmony_ci size_t fmt_size; 576762306a36Sopenharmony_ci unsigned int pool_size = 0; 576862306a36Sopenharmony_ci 576962306a36Sopenharmony_ci ENTER; 577062306a36Sopenharmony_ci spin_lock_init(&queue->_lock); 577162306a36Sopenharmony_ci queue->q_lock = &queue->_lock; 577262306a36Sopenharmony_ci 577362306a36Sopenharmony_ci switch (fmt) { 577462306a36Sopenharmony_ci case IBMVFC_CRQ_FMT: 577562306a36Sopenharmony_ci fmt_size = sizeof(*queue->msgs.crq); 577662306a36Sopenharmony_ci pool_size = max_requests + IBMVFC_NUM_INTERNAL_REQ; 577762306a36Sopenharmony_ci break; 577862306a36Sopenharmony_ci case IBMVFC_ASYNC_FMT: 577962306a36Sopenharmony_ci fmt_size = sizeof(*queue->msgs.async); 578062306a36Sopenharmony_ci break; 578162306a36Sopenharmony_ci case IBMVFC_SUB_CRQ_FMT: 578262306a36Sopenharmony_ci fmt_size = sizeof(*queue->msgs.scrq); 578362306a36Sopenharmony_ci /* We need one extra event for Cancel Commands */ 578462306a36Sopenharmony_ci pool_size = max_requests + 1; 578562306a36Sopenharmony_ci break; 578662306a36Sopenharmony_ci default: 578762306a36Sopenharmony_ci dev_warn(dev, "Unknown command/response queue message format: %d\n", fmt); 578862306a36Sopenharmony_ci return -EINVAL; 578962306a36Sopenharmony_ci } 579062306a36Sopenharmony_ci 579162306a36Sopenharmony_ci if (ibmvfc_init_event_pool(vhost, queue, pool_size)) { 579262306a36Sopenharmony_ci dev_err(dev, "Couldn't initialize event pool.\n"); 579362306a36Sopenharmony_ci return -ENOMEM; 579462306a36Sopenharmony_ci } 579562306a36Sopenharmony_ci 579662306a36Sopenharmony_ci queue->msgs.handle = (void *)get_zeroed_page(GFP_KERNEL); 579762306a36Sopenharmony_ci if (!queue->msgs.handle) 579862306a36Sopenharmony_ci return -ENOMEM; 579962306a36Sopenharmony_ci 580062306a36Sopenharmony_ci queue->msg_token = dma_map_single(dev, queue->msgs.handle, PAGE_SIZE, 580162306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 580262306a36Sopenharmony_ci 580362306a36Sopenharmony_ci if (dma_mapping_error(dev, queue->msg_token)) { 580462306a36Sopenharmony_ci free_page((unsigned long)queue->msgs.handle); 580562306a36Sopenharmony_ci queue->msgs.handle = NULL; 580662306a36Sopenharmony_ci return -ENOMEM; 580762306a36Sopenharmony_ci } 580862306a36Sopenharmony_ci 580962306a36Sopenharmony_ci queue->cur = 0; 581062306a36Sopenharmony_ci queue->fmt = fmt; 581162306a36Sopenharmony_ci queue->size = PAGE_SIZE / fmt_size; 581262306a36Sopenharmony_ci 581362306a36Sopenharmony_ci queue->vhost = vhost; 581462306a36Sopenharmony_ci return 0; 581562306a36Sopenharmony_ci} 581662306a36Sopenharmony_ci 581762306a36Sopenharmony_ci/** 581862306a36Sopenharmony_ci * ibmvfc_init_crq - Initializes and registers CRQ with hypervisor 581962306a36Sopenharmony_ci * @vhost: ibmvfc host struct 582062306a36Sopenharmony_ci * 582162306a36Sopenharmony_ci * Allocates a page for messages, maps it for dma, and registers 582262306a36Sopenharmony_ci * the crq with the hypervisor. 582362306a36Sopenharmony_ci * 582462306a36Sopenharmony_ci * Return value: 582562306a36Sopenharmony_ci * zero on success / other on failure 582662306a36Sopenharmony_ci **/ 582762306a36Sopenharmony_cistatic int ibmvfc_init_crq(struct ibmvfc_host *vhost) 582862306a36Sopenharmony_ci{ 582962306a36Sopenharmony_ci int rc, retrc = -ENOMEM; 583062306a36Sopenharmony_ci struct device *dev = vhost->dev; 583162306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(dev); 583262306a36Sopenharmony_ci struct ibmvfc_queue *crq = &vhost->crq; 583362306a36Sopenharmony_ci 583462306a36Sopenharmony_ci ENTER; 583562306a36Sopenharmony_ci if (ibmvfc_alloc_queue(vhost, crq, IBMVFC_CRQ_FMT)) 583662306a36Sopenharmony_ci return -ENOMEM; 583762306a36Sopenharmony_ci 583862306a36Sopenharmony_ci retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, 583962306a36Sopenharmony_ci crq->msg_token, PAGE_SIZE); 584062306a36Sopenharmony_ci 584162306a36Sopenharmony_ci if (rc == H_RESOURCE) 584262306a36Sopenharmony_ci /* maybe kexecing and resource is busy. try a reset */ 584362306a36Sopenharmony_ci retrc = rc = ibmvfc_reset_crq(vhost); 584462306a36Sopenharmony_ci 584562306a36Sopenharmony_ci if (rc == H_CLOSED) 584662306a36Sopenharmony_ci dev_warn(dev, "Partner adapter not ready\n"); 584762306a36Sopenharmony_ci else if (rc) { 584862306a36Sopenharmony_ci dev_warn(dev, "Error %d opening adapter\n", rc); 584962306a36Sopenharmony_ci goto reg_crq_failed; 585062306a36Sopenharmony_ci } 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_ci retrc = 0; 585362306a36Sopenharmony_ci 585462306a36Sopenharmony_ci tasklet_init(&vhost->tasklet, (void *)ibmvfc_tasklet, (unsigned long)vhost); 585562306a36Sopenharmony_ci 585662306a36Sopenharmony_ci if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) { 585762306a36Sopenharmony_ci dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc); 585862306a36Sopenharmony_ci goto req_irq_failed; 585962306a36Sopenharmony_ci } 586062306a36Sopenharmony_ci 586162306a36Sopenharmony_ci if ((rc = vio_enable_interrupts(vdev))) { 586262306a36Sopenharmony_ci dev_err(dev, "Error %d enabling interrupts\n", rc); 586362306a36Sopenharmony_ci goto req_irq_failed; 586462306a36Sopenharmony_ci } 586562306a36Sopenharmony_ci 586662306a36Sopenharmony_ci LEAVE; 586762306a36Sopenharmony_ci return retrc; 586862306a36Sopenharmony_ci 586962306a36Sopenharmony_cireq_irq_failed: 587062306a36Sopenharmony_ci tasklet_kill(&vhost->tasklet); 587162306a36Sopenharmony_ci do { 587262306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); 587362306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 587462306a36Sopenharmony_cireg_crq_failed: 587562306a36Sopenharmony_ci ibmvfc_free_queue(vhost, crq); 587662306a36Sopenharmony_ci return retrc; 587762306a36Sopenharmony_ci} 587862306a36Sopenharmony_ci 587962306a36Sopenharmony_cistatic int ibmvfc_register_scsi_channel(struct ibmvfc_host *vhost, 588062306a36Sopenharmony_ci int index) 588162306a36Sopenharmony_ci{ 588262306a36Sopenharmony_ci struct device *dev = vhost->dev; 588362306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(dev); 588462306a36Sopenharmony_ci struct ibmvfc_queue *scrq = &vhost->scsi_scrqs.scrqs[index]; 588562306a36Sopenharmony_ci int rc = -ENOMEM; 588662306a36Sopenharmony_ci 588762306a36Sopenharmony_ci ENTER; 588862306a36Sopenharmony_ci 588962306a36Sopenharmony_ci rc = h_reg_sub_crq(vdev->unit_address, scrq->msg_token, PAGE_SIZE, 589062306a36Sopenharmony_ci &scrq->cookie, &scrq->hw_irq); 589162306a36Sopenharmony_ci 589262306a36Sopenharmony_ci /* H_CLOSED indicates successful register, but no CRQ partner */ 589362306a36Sopenharmony_ci if (rc && rc != H_CLOSED) { 589462306a36Sopenharmony_ci dev_warn(dev, "Error registering sub-crq: %d\n", rc); 589562306a36Sopenharmony_ci if (rc == H_PARAMETER) 589662306a36Sopenharmony_ci dev_warn_once(dev, "Firmware may not support MQ\n"); 589762306a36Sopenharmony_ci goto reg_failed; 589862306a36Sopenharmony_ci } 589962306a36Sopenharmony_ci 590062306a36Sopenharmony_ci scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); 590162306a36Sopenharmony_ci 590262306a36Sopenharmony_ci if (!scrq->irq) { 590362306a36Sopenharmony_ci rc = -EINVAL; 590462306a36Sopenharmony_ci dev_err(dev, "Error mapping sub-crq[%d] irq\n", index); 590562306a36Sopenharmony_ci goto irq_failed; 590662306a36Sopenharmony_ci } 590762306a36Sopenharmony_ci 590862306a36Sopenharmony_ci snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%d", 590962306a36Sopenharmony_ci vdev->unit_address, index); 591062306a36Sopenharmony_ci rc = request_irq(scrq->irq, ibmvfc_interrupt_scsi, 0, scrq->name, scrq); 591162306a36Sopenharmony_ci 591262306a36Sopenharmony_ci if (rc) { 591362306a36Sopenharmony_ci dev_err(dev, "Couldn't register sub-crq[%d] irq\n", index); 591462306a36Sopenharmony_ci irq_dispose_mapping(scrq->irq); 591562306a36Sopenharmony_ci goto irq_failed; 591662306a36Sopenharmony_ci } 591762306a36Sopenharmony_ci 591862306a36Sopenharmony_ci scrq->hwq_id = index; 591962306a36Sopenharmony_ci 592062306a36Sopenharmony_ci LEAVE; 592162306a36Sopenharmony_ci return 0; 592262306a36Sopenharmony_ci 592362306a36Sopenharmony_ciirq_failed: 592462306a36Sopenharmony_ci do { 592562306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie); 592662306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 592762306a36Sopenharmony_cireg_failed: 592862306a36Sopenharmony_ci LEAVE; 592962306a36Sopenharmony_ci return rc; 593062306a36Sopenharmony_ci} 593162306a36Sopenharmony_ci 593262306a36Sopenharmony_cistatic void ibmvfc_deregister_scsi_channel(struct ibmvfc_host *vhost, int index) 593362306a36Sopenharmony_ci{ 593462306a36Sopenharmony_ci struct device *dev = vhost->dev; 593562306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(dev); 593662306a36Sopenharmony_ci struct ibmvfc_queue *scrq = &vhost->scsi_scrqs.scrqs[index]; 593762306a36Sopenharmony_ci long rc; 593862306a36Sopenharmony_ci 593962306a36Sopenharmony_ci ENTER; 594062306a36Sopenharmony_ci 594162306a36Sopenharmony_ci free_irq(scrq->irq, scrq); 594262306a36Sopenharmony_ci irq_dispose_mapping(scrq->irq); 594362306a36Sopenharmony_ci scrq->irq = 0; 594462306a36Sopenharmony_ci 594562306a36Sopenharmony_ci do { 594662306a36Sopenharmony_ci rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, 594762306a36Sopenharmony_ci scrq->cookie); 594862306a36Sopenharmony_ci } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); 594962306a36Sopenharmony_ci 595062306a36Sopenharmony_ci if (rc) 595162306a36Sopenharmony_ci dev_err(dev, "Failed to free sub-crq[%d]: rc=%ld\n", index, rc); 595262306a36Sopenharmony_ci 595362306a36Sopenharmony_ci /* Clean out the queue */ 595462306a36Sopenharmony_ci memset(scrq->msgs.crq, 0, PAGE_SIZE); 595562306a36Sopenharmony_ci scrq->cur = 0; 595662306a36Sopenharmony_ci 595762306a36Sopenharmony_ci LEAVE; 595862306a36Sopenharmony_ci} 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_cistatic void ibmvfc_reg_sub_crqs(struct ibmvfc_host *vhost) 596162306a36Sopenharmony_ci{ 596262306a36Sopenharmony_ci int i, j; 596362306a36Sopenharmony_ci 596462306a36Sopenharmony_ci ENTER; 596562306a36Sopenharmony_ci if (!vhost->mq_enabled || !vhost->scsi_scrqs.scrqs) 596662306a36Sopenharmony_ci return; 596762306a36Sopenharmony_ci 596862306a36Sopenharmony_ci for (i = 0; i < nr_scsi_hw_queues; i++) { 596962306a36Sopenharmony_ci if (ibmvfc_register_scsi_channel(vhost, i)) { 597062306a36Sopenharmony_ci for (j = i; j > 0; j--) 597162306a36Sopenharmony_ci ibmvfc_deregister_scsi_channel(vhost, j - 1); 597262306a36Sopenharmony_ci vhost->do_enquiry = 0; 597362306a36Sopenharmony_ci return; 597462306a36Sopenharmony_ci } 597562306a36Sopenharmony_ci } 597662306a36Sopenharmony_ci 597762306a36Sopenharmony_ci LEAVE; 597862306a36Sopenharmony_ci} 597962306a36Sopenharmony_ci 598062306a36Sopenharmony_cistatic void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost) 598162306a36Sopenharmony_ci{ 598262306a36Sopenharmony_ci int i; 598362306a36Sopenharmony_ci 598462306a36Sopenharmony_ci ENTER; 598562306a36Sopenharmony_ci if (!vhost->mq_enabled || !vhost->scsi_scrqs.scrqs) 598662306a36Sopenharmony_ci return; 598762306a36Sopenharmony_ci 598862306a36Sopenharmony_ci for (i = 0; i < nr_scsi_hw_queues; i++) 598962306a36Sopenharmony_ci ibmvfc_deregister_scsi_channel(vhost, i); 599062306a36Sopenharmony_ci 599162306a36Sopenharmony_ci LEAVE; 599262306a36Sopenharmony_ci} 599362306a36Sopenharmony_ci 599462306a36Sopenharmony_cistatic void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost) 599562306a36Sopenharmony_ci{ 599662306a36Sopenharmony_ci struct ibmvfc_queue *scrq; 599762306a36Sopenharmony_ci int i, j; 599862306a36Sopenharmony_ci 599962306a36Sopenharmony_ci ENTER; 600062306a36Sopenharmony_ci if (!vhost->mq_enabled) 600162306a36Sopenharmony_ci return; 600262306a36Sopenharmony_ci 600362306a36Sopenharmony_ci vhost->scsi_scrqs.scrqs = kcalloc(nr_scsi_hw_queues, 600462306a36Sopenharmony_ci sizeof(*vhost->scsi_scrqs.scrqs), 600562306a36Sopenharmony_ci GFP_KERNEL); 600662306a36Sopenharmony_ci if (!vhost->scsi_scrqs.scrqs) { 600762306a36Sopenharmony_ci vhost->do_enquiry = 0; 600862306a36Sopenharmony_ci return; 600962306a36Sopenharmony_ci } 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_ci for (i = 0; i < nr_scsi_hw_queues; i++) { 601262306a36Sopenharmony_ci scrq = &vhost->scsi_scrqs.scrqs[i]; 601362306a36Sopenharmony_ci if (ibmvfc_alloc_queue(vhost, scrq, IBMVFC_SUB_CRQ_FMT)) { 601462306a36Sopenharmony_ci for (j = i; j > 0; j--) { 601562306a36Sopenharmony_ci scrq = &vhost->scsi_scrqs.scrqs[j - 1]; 601662306a36Sopenharmony_ci ibmvfc_free_queue(vhost, scrq); 601762306a36Sopenharmony_ci } 601862306a36Sopenharmony_ci kfree(vhost->scsi_scrqs.scrqs); 601962306a36Sopenharmony_ci vhost->scsi_scrqs.scrqs = NULL; 602062306a36Sopenharmony_ci vhost->scsi_scrqs.active_queues = 0; 602162306a36Sopenharmony_ci vhost->do_enquiry = 0; 602262306a36Sopenharmony_ci vhost->mq_enabled = 0; 602362306a36Sopenharmony_ci return; 602462306a36Sopenharmony_ci } 602562306a36Sopenharmony_ci } 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci ibmvfc_reg_sub_crqs(vhost); 602862306a36Sopenharmony_ci 602962306a36Sopenharmony_ci LEAVE; 603062306a36Sopenharmony_ci} 603162306a36Sopenharmony_ci 603262306a36Sopenharmony_cistatic void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost) 603362306a36Sopenharmony_ci{ 603462306a36Sopenharmony_ci struct ibmvfc_queue *scrq; 603562306a36Sopenharmony_ci int i; 603662306a36Sopenharmony_ci 603762306a36Sopenharmony_ci ENTER; 603862306a36Sopenharmony_ci if (!vhost->scsi_scrqs.scrqs) 603962306a36Sopenharmony_ci return; 604062306a36Sopenharmony_ci 604162306a36Sopenharmony_ci ibmvfc_dereg_sub_crqs(vhost); 604262306a36Sopenharmony_ci 604362306a36Sopenharmony_ci for (i = 0; i < nr_scsi_hw_queues; i++) { 604462306a36Sopenharmony_ci scrq = &vhost->scsi_scrqs.scrqs[i]; 604562306a36Sopenharmony_ci ibmvfc_free_queue(vhost, scrq); 604662306a36Sopenharmony_ci } 604762306a36Sopenharmony_ci 604862306a36Sopenharmony_ci kfree(vhost->scsi_scrqs.scrqs); 604962306a36Sopenharmony_ci vhost->scsi_scrqs.scrqs = NULL; 605062306a36Sopenharmony_ci vhost->scsi_scrqs.active_queues = 0; 605162306a36Sopenharmony_ci LEAVE; 605262306a36Sopenharmony_ci} 605362306a36Sopenharmony_ci 605462306a36Sopenharmony_ci/** 605562306a36Sopenharmony_ci * ibmvfc_free_mem - Free memory for vhost 605662306a36Sopenharmony_ci * @vhost: ibmvfc host struct 605762306a36Sopenharmony_ci * 605862306a36Sopenharmony_ci * Return value: 605962306a36Sopenharmony_ci * none 606062306a36Sopenharmony_ci **/ 606162306a36Sopenharmony_cistatic void ibmvfc_free_mem(struct ibmvfc_host *vhost) 606262306a36Sopenharmony_ci{ 606362306a36Sopenharmony_ci struct ibmvfc_queue *async_q = &vhost->async_crq; 606462306a36Sopenharmony_ci 606562306a36Sopenharmony_ci ENTER; 606662306a36Sopenharmony_ci mempool_destroy(vhost->tgt_pool); 606762306a36Sopenharmony_ci kfree(vhost->trace); 606862306a36Sopenharmony_ci dma_free_coherent(vhost->dev, vhost->disc_buf_sz, vhost->disc_buf, 606962306a36Sopenharmony_ci vhost->disc_buf_dma); 607062306a36Sopenharmony_ci dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf), 607162306a36Sopenharmony_ci vhost->login_buf, vhost->login_buf_dma); 607262306a36Sopenharmony_ci dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf), 607362306a36Sopenharmony_ci vhost->channel_setup_buf, vhost->channel_setup_dma); 607462306a36Sopenharmony_ci dma_pool_destroy(vhost->sg_pool); 607562306a36Sopenharmony_ci ibmvfc_free_queue(vhost, async_q); 607662306a36Sopenharmony_ci LEAVE; 607762306a36Sopenharmony_ci} 607862306a36Sopenharmony_ci 607962306a36Sopenharmony_ci/** 608062306a36Sopenharmony_ci * ibmvfc_alloc_mem - Allocate memory for vhost 608162306a36Sopenharmony_ci * @vhost: ibmvfc host struct 608262306a36Sopenharmony_ci * 608362306a36Sopenharmony_ci * Return value: 608462306a36Sopenharmony_ci * 0 on success / non-zero on failure 608562306a36Sopenharmony_ci **/ 608662306a36Sopenharmony_cistatic int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) 608762306a36Sopenharmony_ci{ 608862306a36Sopenharmony_ci struct ibmvfc_queue *async_q = &vhost->async_crq; 608962306a36Sopenharmony_ci struct device *dev = vhost->dev; 609062306a36Sopenharmony_ci 609162306a36Sopenharmony_ci ENTER; 609262306a36Sopenharmony_ci if (ibmvfc_alloc_queue(vhost, async_q, IBMVFC_ASYNC_FMT)) { 609362306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate/map async queue.\n"); 609462306a36Sopenharmony_ci goto nomem; 609562306a36Sopenharmony_ci } 609662306a36Sopenharmony_ci 609762306a36Sopenharmony_ci vhost->sg_pool = dma_pool_create(IBMVFC_NAME, dev, 609862306a36Sopenharmony_ci SG_ALL * sizeof(struct srp_direct_buf), 609962306a36Sopenharmony_ci sizeof(struct srp_direct_buf), 0); 610062306a36Sopenharmony_ci 610162306a36Sopenharmony_ci if (!vhost->sg_pool) { 610262306a36Sopenharmony_ci dev_err(dev, "Failed to allocate sg pool\n"); 610362306a36Sopenharmony_ci goto unmap_async_crq; 610462306a36Sopenharmony_ci } 610562306a36Sopenharmony_ci 610662306a36Sopenharmony_ci vhost->login_buf = dma_alloc_coherent(dev, sizeof(*vhost->login_buf), 610762306a36Sopenharmony_ci &vhost->login_buf_dma, GFP_KERNEL); 610862306a36Sopenharmony_ci 610962306a36Sopenharmony_ci if (!vhost->login_buf) { 611062306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate NPIV login buffer\n"); 611162306a36Sopenharmony_ci goto free_sg_pool; 611262306a36Sopenharmony_ci } 611362306a36Sopenharmony_ci 611462306a36Sopenharmony_ci vhost->disc_buf_sz = sizeof(*vhost->disc_buf) * max_targets; 611562306a36Sopenharmony_ci vhost->disc_buf = dma_alloc_coherent(dev, vhost->disc_buf_sz, 611662306a36Sopenharmony_ci &vhost->disc_buf_dma, GFP_KERNEL); 611762306a36Sopenharmony_ci 611862306a36Sopenharmony_ci if (!vhost->disc_buf) { 611962306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate Discover Targets buffer\n"); 612062306a36Sopenharmony_ci goto free_login_buffer; 612162306a36Sopenharmony_ci } 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci vhost->trace = kcalloc(IBMVFC_NUM_TRACE_ENTRIES, 612462306a36Sopenharmony_ci sizeof(struct ibmvfc_trace_entry), GFP_KERNEL); 612562306a36Sopenharmony_ci atomic_set(&vhost->trace_index, -1); 612662306a36Sopenharmony_ci 612762306a36Sopenharmony_ci if (!vhost->trace) 612862306a36Sopenharmony_ci goto free_disc_buffer; 612962306a36Sopenharmony_ci 613062306a36Sopenharmony_ci vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ, 613162306a36Sopenharmony_ci sizeof(struct ibmvfc_target)); 613262306a36Sopenharmony_ci 613362306a36Sopenharmony_ci if (!vhost->tgt_pool) { 613462306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate target memory pool\n"); 613562306a36Sopenharmony_ci goto free_trace; 613662306a36Sopenharmony_ci } 613762306a36Sopenharmony_ci 613862306a36Sopenharmony_ci vhost->channel_setup_buf = dma_alloc_coherent(dev, sizeof(*vhost->channel_setup_buf), 613962306a36Sopenharmony_ci &vhost->channel_setup_dma, 614062306a36Sopenharmony_ci GFP_KERNEL); 614162306a36Sopenharmony_ci 614262306a36Sopenharmony_ci if (!vhost->channel_setup_buf) { 614362306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate Channel Setup buffer\n"); 614462306a36Sopenharmony_ci goto free_tgt_pool; 614562306a36Sopenharmony_ci } 614662306a36Sopenharmony_ci 614762306a36Sopenharmony_ci LEAVE; 614862306a36Sopenharmony_ci return 0; 614962306a36Sopenharmony_ci 615062306a36Sopenharmony_cifree_tgt_pool: 615162306a36Sopenharmony_ci mempool_destroy(vhost->tgt_pool); 615262306a36Sopenharmony_cifree_trace: 615362306a36Sopenharmony_ci kfree(vhost->trace); 615462306a36Sopenharmony_cifree_disc_buffer: 615562306a36Sopenharmony_ci dma_free_coherent(dev, vhost->disc_buf_sz, vhost->disc_buf, 615662306a36Sopenharmony_ci vhost->disc_buf_dma); 615762306a36Sopenharmony_cifree_login_buffer: 615862306a36Sopenharmony_ci dma_free_coherent(dev, sizeof(*vhost->login_buf), 615962306a36Sopenharmony_ci vhost->login_buf, vhost->login_buf_dma); 616062306a36Sopenharmony_cifree_sg_pool: 616162306a36Sopenharmony_ci dma_pool_destroy(vhost->sg_pool); 616262306a36Sopenharmony_ciunmap_async_crq: 616362306a36Sopenharmony_ci ibmvfc_free_queue(vhost, async_q); 616462306a36Sopenharmony_cinomem: 616562306a36Sopenharmony_ci LEAVE; 616662306a36Sopenharmony_ci return -ENOMEM; 616762306a36Sopenharmony_ci} 616862306a36Sopenharmony_ci 616962306a36Sopenharmony_ci/** 617062306a36Sopenharmony_ci * ibmvfc_rport_add_thread - Worker thread for rport adds 617162306a36Sopenharmony_ci * @work: work struct 617262306a36Sopenharmony_ci * 617362306a36Sopenharmony_ci **/ 617462306a36Sopenharmony_cistatic void ibmvfc_rport_add_thread(struct work_struct *work) 617562306a36Sopenharmony_ci{ 617662306a36Sopenharmony_ci struct ibmvfc_host *vhost = container_of(work, struct ibmvfc_host, 617762306a36Sopenharmony_ci rport_add_work_q); 617862306a36Sopenharmony_ci struct ibmvfc_target *tgt; 617962306a36Sopenharmony_ci struct fc_rport *rport; 618062306a36Sopenharmony_ci unsigned long flags; 618162306a36Sopenharmony_ci int did_work; 618262306a36Sopenharmony_ci 618362306a36Sopenharmony_ci ENTER; 618462306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 618562306a36Sopenharmony_ci do { 618662306a36Sopenharmony_ci did_work = 0; 618762306a36Sopenharmony_ci if (vhost->state != IBMVFC_ACTIVE) 618862306a36Sopenharmony_ci break; 618962306a36Sopenharmony_ci 619062306a36Sopenharmony_ci list_for_each_entry(tgt, &vhost->targets, queue) { 619162306a36Sopenharmony_ci if (tgt->add_rport) { 619262306a36Sopenharmony_ci did_work = 1; 619362306a36Sopenharmony_ci tgt->add_rport = 0; 619462306a36Sopenharmony_ci kref_get(&tgt->kref); 619562306a36Sopenharmony_ci rport = tgt->rport; 619662306a36Sopenharmony_ci if (!rport) { 619762306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 619862306a36Sopenharmony_ci ibmvfc_tgt_add_rport(tgt); 619962306a36Sopenharmony_ci } else if (get_device(&rport->dev)) { 620062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 620162306a36Sopenharmony_ci tgt_dbg(tgt, "Setting rport roles\n"); 620262306a36Sopenharmony_ci fc_remote_port_rolechg(rport, tgt->ids.roles); 620362306a36Sopenharmony_ci put_device(&rport->dev); 620462306a36Sopenharmony_ci } else { 620562306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 620662306a36Sopenharmony_ci } 620762306a36Sopenharmony_ci 620862306a36Sopenharmony_ci kref_put(&tgt->kref, ibmvfc_release_tgt); 620962306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 621062306a36Sopenharmony_ci break; 621162306a36Sopenharmony_ci } 621262306a36Sopenharmony_ci } 621362306a36Sopenharmony_ci } while(did_work); 621462306a36Sopenharmony_ci 621562306a36Sopenharmony_ci if (vhost->state == IBMVFC_ACTIVE) 621662306a36Sopenharmony_ci vhost->scan_complete = 1; 621762306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 621862306a36Sopenharmony_ci LEAVE; 621962306a36Sopenharmony_ci} 622062306a36Sopenharmony_ci 622162306a36Sopenharmony_ci/** 622262306a36Sopenharmony_ci * ibmvfc_probe - Adapter hot plug add entry point 622362306a36Sopenharmony_ci * @vdev: vio device struct 622462306a36Sopenharmony_ci * @id: vio device id struct 622562306a36Sopenharmony_ci * 622662306a36Sopenharmony_ci * Return value: 622762306a36Sopenharmony_ci * 0 on success / non-zero on failure 622862306a36Sopenharmony_ci **/ 622962306a36Sopenharmony_cistatic int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) 623062306a36Sopenharmony_ci{ 623162306a36Sopenharmony_ci struct ibmvfc_host *vhost; 623262306a36Sopenharmony_ci struct Scsi_Host *shost; 623362306a36Sopenharmony_ci struct device *dev = &vdev->dev; 623462306a36Sopenharmony_ci int rc = -ENOMEM; 623562306a36Sopenharmony_ci unsigned int max_scsi_queues = IBMVFC_MAX_SCSI_QUEUES; 623662306a36Sopenharmony_ci 623762306a36Sopenharmony_ci ENTER; 623862306a36Sopenharmony_ci shost = scsi_host_alloc(&driver_template, sizeof(*vhost)); 623962306a36Sopenharmony_ci if (!shost) { 624062306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate host data\n"); 624162306a36Sopenharmony_ci goto out; 624262306a36Sopenharmony_ci } 624362306a36Sopenharmony_ci 624462306a36Sopenharmony_ci shost->transportt = ibmvfc_transport_template; 624562306a36Sopenharmony_ci shost->can_queue = max_requests; 624662306a36Sopenharmony_ci shost->max_lun = max_lun; 624762306a36Sopenharmony_ci shost->max_id = max_targets; 624862306a36Sopenharmony_ci shost->max_sectors = IBMVFC_MAX_SECTORS; 624962306a36Sopenharmony_ci shost->max_cmd_len = IBMVFC_MAX_CDB_LEN; 625062306a36Sopenharmony_ci shost->unique_id = shost->host_no; 625162306a36Sopenharmony_ci shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1; 625262306a36Sopenharmony_ci 625362306a36Sopenharmony_ci vhost = shost_priv(shost); 625462306a36Sopenharmony_ci INIT_LIST_HEAD(&vhost->targets); 625562306a36Sopenharmony_ci INIT_LIST_HEAD(&vhost->purge); 625662306a36Sopenharmony_ci sprintf(vhost->name, IBMVFC_NAME); 625762306a36Sopenharmony_ci vhost->host = shost; 625862306a36Sopenharmony_ci vhost->dev = dev; 625962306a36Sopenharmony_ci vhost->partition_number = -1; 626062306a36Sopenharmony_ci vhost->log_level = log_level; 626162306a36Sopenharmony_ci vhost->task_set = 1; 626262306a36Sopenharmony_ci 626362306a36Sopenharmony_ci vhost->mq_enabled = mq_enabled; 626462306a36Sopenharmony_ci vhost->client_scsi_channels = min(shost->nr_hw_queues, nr_scsi_channels); 626562306a36Sopenharmony_ci vhost->using_channels = 0; 626662306a36Sopenharmony_ci vhost->do_enquiry = 1; 626762306a36Sopenharmony_ci vhost->scan_timeout = 0; 626862306a36Sopenharmony_ci 626962306a36Sopenharmony_ci strcpy(vhost->partition_name, "UNKNOWN"); 627062306a36Sopenharmony_ci init_waitqueue_head(&vhost->work_wait_q); 627162306a36Sopenharmony_ci init_waitqueue_head(&vhost->init_wait_q); 627262306a36Sopenharmony_ci INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread); 627362306a36Sopenharmony_ci mutex_init(&vhost->passthru_mutex); 627462306a36Sopenharmony_ci 627562306a36Sopenharmony_ci if ((rc = ibmvfc_alloc_mem(vhost))) 627662306a36Sopenharmony_ci goto free_scsi_host; 627762306a36Sopenharmony_ci 627862306a36Sopenharmony_ci vhost->work_thread = kthread_run(ibmvfc_work, vhost, "%s_%d", IBMVFC_NAME, 627962306a36Sopenharmony_ci shost->host_no); 628062306a36Sopenharmony_ci 628162306a36Sopenharmony_ci if (IS_ERR(vhost->work_thread)) { 628262306a36Sopenharmony_ci dev_err(dev, "Couldn't create kernel thread: %ld\n", 628362306a36Sopenharmony_ci PTR_ERR(vhost->work_thread)); 628462306a36Sopenharmony_ci rc = PTR_ERR(vhost->work_thread); 628562306a36Sopenharmony_ci goto free_host_mem; 628662306a36Sopenharmony_ci } 628762306a36Sopenharmony_ci 628862306a36Sopenharmony_ci if ((rc = ibmvfc_init_crq(vhost))) { 628962306a36Sopenharmony_ci dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc); 629062306a36Sopenharmony_ci goto kill_kthread; 629162306a36Sopenharmony_ci } 629262306a36Sopenharmony_ci 629362306a36Sopenharmony_ci if ((rc = scsi_add_host(shost, dev))) 629462306a36Sopenharmony_ci goto release_crq; 629562306a36Sopenharmony_ci 629662306a36Sopenharmony_ci fc_host_dev_loss_tmo(shost) = IBMVFC_DEV_LOSS_TMO; 629762306a36Sopenharmony_ci 629862306a36Sopenharmony_ci if ((rc = ibmvfc_create_trace_file(&shost->shost_dev.kobj, 629962306a36Sopenharmony_ci &ibmvfc_trace_attr))) { 630062306a36Sopenharmony_ci dev_err(dev, "Failed to create trace file. rc=%d\n", rc); 630162306a36Sopenharmony_ci goto remove_shost; 630262306a36Sopenharmony_ci } 630362306a36Sopenharmony_ci 630462306a36Sopenharmony_ci ibmvfc_init_sub_crqs(vhost); 630562306a36Sopenharmony_ci 630662306a36Sopenharmony_ci if (shost_to_fc_host(shost)->rqst_q) 630762306a36Sopenharmony_ci blk_queue_max_segments(shost_to_fc_host(shost)->rqst_q, 1); 630862306a36Sopenharmony_ci dev_set_drvdata(dev, vhost); 630962306a36Sopenharmony_ci spin_lock(&ibmvfc_driver_lock); 631062306a36Sopenharmony_ci list_add_tail(&vhost->queue, &ibmvfc_head); 631162306a36Sopenharmony_ci spin_unlock(&ibmvfc_driver_lock); 631262306a36Sopenharmony_ci 631362306a36Sopenharmony_ci ibmvfc_send_crq_init(vhost); 631462306a36Sopenharmony_ci scsi_scan_host(shost); 631562306a36Sopenharmony_ci return 0; 631662306a36Sopenharmony_ci 631762306a36Sopenharmony_ciremove_shost: 631862306a36Sopenharmony_ci scsi_remove_host(shost); 631962306a36Sopenharmony_cirelease_crq: 632062306a36Sopenharmony_ci ibmvfc_release_crq_queue(vhost); 632162306a36Sopenharmony_cikill_kthread: 632262306a36Sopenharmony_ci kthread_stop(vhost->work_thread); 632362306a36Sopenharmony_cifree_host_mem: 632462306a36Sopenharmony_ci ibmvfc_free_mem(vhost); 632562306a36Sopenharmony_cifree_scsi_host: 632662306a36Sopenharmony_ci scsi_host_put(shost); 632762306a36Sopenharmony_ciout: 632862306a36Sopenharmony_ci LEAVE; 632962306a36Sopenharmony_ci return rc; 633062306a36Sopenharmony_ci} 633162306a36Sopenharmony_ci 633262306a36Sopenharmony_ci/** 633362306a36Sopenharmony_ci * ibmvfc_remove - Adapter hot plug remove entry point 633462306a36Sopenharmony_ci * @vdev: vio device struct 633562306a36Sopenharmony_ci * 633662306a36Sopenharmony_ci * Return value: 633762306a36Sopenharmony_ci * 0 633862306a36Sopenharmony_ci **/ 633962306a36Sopenharmony_cistatic void ibmvfc_remove(struct vio_dev *vdev) 634062306a36Sopenharmony_ci{ 634162306a36Sopenharmony_ci struct ibmvfc_host *vhost = dev_get_drvdata(&vdev->dev); 634262306a36Sopenharmony_ci LIST_HEAD(purge); 634362306a36Sopenharmony_ci unsigned long flags; 634462306a36Sopenharmony_ci 634562306a36Sopenharmony_ci ENTER; 634662306a36Sopenharmony_ci ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr); 634762306a36Sopenharmony_ci 634862306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 634962306a36Sopenharmony_ci ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); 635062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 635162306a36Sopenharmony_ci 635262306a36Sopenharmony_ci ibmvfc_wait_while_resetting(vhost); 635362306a36Sopenharmony_ci kthread_stop(vhost->work_thread); 635462306a36Sopenharmony_ci fc_remove_host(vhost->host); 635562306a36Sopenharmony_ci scsi_remove_host(vhost->host); 635662306a36Sopenharmony_ci 635762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 635862306a36Sopenharmony_ci ibmvfc_purge_requests(vhost, DID_ERROR); 635962306a36Sopenharmony_ci list_splice_init(&vhost->purge, &purge); 636062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 636162306a36Sopenharmony_ci ibmvfc_complete_purge(&purge); 636262306a36Sopenharmony_ci ibmvfc_release_sub_crqs(vhost); 636362306a36Sopenharmony_ci ibmvfc_release_crq_queue(vhost); 636462306a36Sopenharmony_ci 636562306a36Sopenharmony_ci ibmvfc_free_mem(vhost); 636662306a36Sopenharmony_ci spin_lock(&ibmvfc_driver_lock); 636762306a36Sopenharmony_ci list_del(&vhost->queue); 636862306a36Sopenharmony_ci spin_unlock(&ibmvfc_driver_lock); 636962306a36Sopenharmony_ci scsi_host_put(vhost->host); 637062306a36Sopenharmony_ci LEAVE; 637162306a36Sopenharmony_ci} 637262306a36Sopenharmony_ci 637362306a36Sopenharmony_ci/** 637462306a36Sopenharmony_ci * ibmvfc_resume - Resume from suspend 637562306a36Sopenharmony_ci * @dev: device struct 637662306a36Sopenharmony_ci * 637762306a36Sopenharmony_ci * We may have lost an interrupt across suspend/resume, so kick the 637862306a36Sopenharmony_ci * interrupt handler 637962306a36Sopenharmony_ci * 638062306a36Sopenharmony_ci */ 638162306a36Sopenharmony_cistatic int ibmvfc_resume(struct device *dev) 638262306a36Sopenharmony_ci{ 638362306a36Sopenharmony_ci unsigned long flags; 638462306a36Sopenharmony_ci struct ibmvfc_host *vhost = dev_get_drvdata(dev); 638562306a36Sopenharmony_ci struct vio_dev *vdev = to_vio_dev(dev); 638662306a36Sopenharmony_ci 638762306a36Sopenharmony_ci spin_lock_irqsave(vhost->host->host_lock, flags); 638862306a36Sopenharmony_ci vio_disable_interrupts(vdev); 638962306a36Sopenharmony_ci tasklet_schedule(&vhost->tasklet); 639062306a36Sopenharmony_ci spin_unlock_irqrestore(vhost->host->host_lock, flags); 639162306a36Sopenharmony_ci return 0; 639262306a36Sopenharmony_ci} 639362306a36Sopenharmony_ci 639462306a36Sopenharmony_ci/** 639562306a36Sopenharmony_ci * ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver 639662306a36Sopenharmony_ci * @vdev: vio device struct 639762306a36Sopenharmony_ci * 639862306a36Sopenharmony_ci * Return value: 639962306a36Sopenharmony_ci * Number of bytes the driver will need to DMA map at the same time in 640062306a36Sopenharmony_ci * order to perform well. 640162306a36Sopenharmony_ci */ 640262306a36Sopenharmony_cistatic unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev) 640362306a36Sopenharmony_ci{ 640462306a36Sopenharmony_ci unsigned long pool_dma = max_requests * sizeof(union ibmvfc_iu); 640562306a36Sopenharmony_ci return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun); 640662306a36Sopenharmony_ci} 640762306a36Sopenharmony_ci 640862306a36Sopenharmony_cistatic const struct vio_device_id ibmvfc_device_table[] = { 640962306a36Sopenharmony_ci {"fcp", "IBM,vfc-client"}, 641062306a36Sopenharmony_ci { "", "" } 641162306a36Sopenharmony_ci}; 641262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vio, ibmvfc_device_table); 641362306a36Sopenharmony_ci 641462306a36Sopenharmony_cistatic const struct dev_pm_ops ibmvfc_pm_ops = { 641562306a36Sopenharmony_ci .resume = ibmvfc_resume 641662306a36Sopenharmony_ci}; 641762306a36Sopenharmony_ci 641862306a36Sopenharmony_cistatic struct vio_driver ibmvfc_driver = { 641962306a36Sopenharmony_ci .id_table = ibmvfc_device_table, 642062306a36Sopenharmony_ci .probe = ibmvfc_probe, 642162306a36Sopenharmony_ci .remove = ibmvfc_remove, 642262306a36Sopenharmony_ci .get_desired_dma = ibmvfc_get_desired_dma, 642362306a36Sopenharmony_ci .name = IBMVFC_NAME, 642462306a36Sopenharmony_ci .pm = &ibmvfc_pm_ops, 642562306a36Sopenharmony_ci}; 642662306a36Sopenharmony_ci 642762306a36Sopenharmony_cistatic struct fc_function_template ibmvfc_transport_functions = { 642862306a36Sopenharmony_ci .show_host_fabric_name = 1, 642962306a36Sopenharmony_ci .show_host_node_name = 1, 643062306a36Sopenharmony_ci .show_host_port_name = 1, 643162306a36Sopenharmony_ci .show_host_supported_classes = 1, 643262306a36Sopenharmony_ci .show_host_port_type = 1, 643362306a36Sopenharmony_ci .show_host_port_id = 1, 643462306a36Sopenharmony_ci .show_host_maxframe_size = 1, 643562306a36Sopenharmony_ci 643662306a36Sopenharmony_ci .get_host_port_state = ibmvfc_get_host_port_state, 643762306a36Sopenharmony_ci .show_host_port_state = 1, 643862306a36Sopenharmony_ci 643962306a36Sopenharmony_ci .get_host_speed = ibmvfc_get_host_speed, 644062306a36Sopenharmony_ci .show_host_speed = 1, 644162306a36Sopenharmony_ci 644262306a36Sopenharmony_ci .issue_fc_host_lip = ibmvfc_issue_fc_host_lip, 644362306a36Sopenharmony_ci .terminate_rport_io = ibmvfc_terminate_rport_io, 644462306a36Sopenharmony_ci 644562306a36Sopenharmony_ci .show_rport_maxframe_size = 1, 644662306a36Sopenharmony_ci .show_rport_supported_classes = 1, 644762306a36Sopenharmony_ci 644862306a36Sopenharmony_ci .set_rport_dev_loss_tmo = ibmvfc_set_rport_dev_loss_tmo, 644962306a36Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 645062306a36Sopenharmony_ci 645162306a36Sopenharmony_ci .get_starget_node_name = ibmvfc_get_starget_node_name, 645262306a36Sopenharmony_ci .show_starget_node_name = 1, 645362306a36Sopenharmony_ci 645462306a36Sopenharmony_ci .get_starget_port_name = ibmvfc_get_starget_port_name, 645562306a36Sopenharmony_ci .show_starget_port_name = 1, 645662306a36Sopenharmony_ci 645762306a36Sopenharmony_ci .get_starget_port_id = ibmvfc_get_starget_port_id, 645862306a36Sopenharmony_ci .show_starget_port_id = 1, 645962306a36Sopenharmony_ci 646062306a36Sopenharmony_ci .bsg_request = ibmvfc_bsg_request, 646162306a36Sopenharmony_ci .bsg_timeout = ibmvfc_bsg_timeout, 646262306a36Sopenharmony_ci}; 646362306a36Sopenharmony_ci 646462306a36Sopenharmony_ci/** 646562306a36Sopenharmony_ci * ibmvfc_module_init - Initialize the ibmvfc module 646662306a36Sopenharmony_ci * 646762306a36Sopenharmony_ci * Return value: 646862306a36Sopenharmony_ci * 0 on success / other on failure 646962306a36Sopenharmony_ci **/ 647062306a36Sopenharmony_cistatic int __init ibmvfc_module_init(void) 647162306a36Sopenharmony_ci{ 647262306a36Sopenharmony_ci int rc; 647362306a36Sopenharmony_ci 647462306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_VIO)) 647562306a36Sopenharmony_ci return -ENODEV; 647662306a36Sopenharmony_ci 647762306a36Sopenharmony_ci printk(KERN_INFO IBMVFC_NAME": IBM Virtual Fibre Channel Driver version: %s %s\n", 647862306a36Sopenharmony_ci IBMVFC_DRIVER_VERSION, IBMVFC_DRIVER_DATE); 647962306a36Sopenharmony_ci 648062306a36Sopenharmony_ci ibmvfc_transport_template = fc_attach_transport(&ibmvfc_transport_functions); 648162306a36Sopenharmony_ci if (!ibmvfc_transport_template) 648262306a36Sopenharmony_ci return -ENOMEM; 648362306a36Sopenharmony_ci 648462306a36Sopenharmony_ci rc = vio_register_driver(&ibmvfc_driver); 648562306a36Sopenharmony_ci if (rc) 648662306a36Sopenharmony_ci fc_release_transport(ibmvfc_transport_template); 648762306a36Sopenharmony_ci return rc; 648862306a36Sopenharmony_ci} 648962306a36Sopenharmony_ci 649062306a36Sopenharmony_ci/** 649162306a36Sopenharmony_ci * ibmvfc_module_exit - Teardown the ibmvfc module 649262306a36Sopenharmony_ci * 649362306a36Sopenharmony_ci * Return value: 649462306a36Sopenharmony_ci * nothing 649562306a36Sopenharmony_ci **/ 649662306a36Sopenharmony_cistatic void __exit ibmvfc_module_exit(void) 649762306a36Sopenharmony_ci{ 649862306a36Sopenharmony_ci vio_unregister_driver(&ibmvfc_driver); 649962306a36Sopenharmony_ci fc_release_transport(ibmvfc_transport_template); 650062306a36Sopenharmony_ci} 650162306a36Sopenharmony_ci 650262306a36Sopenharmony_cimodule_init(ibmvfc_module_init); 650362306a36Sopenharmony_cimodule_exit(ibmvfc_module_exit); 6504