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 = &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